diff options
Diffstat (limited to 'gst/videomixer')
-rw-r--r-- | gst/videomixer/Makefile.am | 41 | ||||
-rw-r--r-- | gst/videomixer/Makefile.in | 946 | ||||
-rw-r--r-- | gst/videomixer/README | 27 | ||||
-rw-r--r-- | gst/videomixer/blend.c | 770 | ||||
-rw-r--r-- | gst/videomixer/blend.h | 96 | ||||
-rw-r--r-- | gst/videomixer/blendorc-dist.c | 2203 | ||||
-rw-r--r-- | gst/videomixer/blendorc-dist.h | 83 | ||||
-rw-r--r-- | gst/videomixer/blendorc.orc | 220 | ||||
-rw-r--r-- | gst/videomixer/videomixer.c | 1908 | ||||
-rw-r--r-- | gst/videomixer/videomixer.h | 132 | ||||
-rw-r--r-- | gst/videomixer/videomixer2.c | 2019 | ||||
-rw-r--r-- | gst/videomixer/videomixer2.h | 125 | ||||
-rw-r--r-- | gst/videomixer/videomixer2pad.h | 77 | ||||
-rw-r--r-- | gst/videomixer/videomixerpad.h | 78 |
14 files changed, 8725 insertions, 0 deletions
diff --git a/gst/videomixer/Makefile.am b/gst/videomixer/Makefile.am new file mode 100644 index 0000000..0cda826 --- /dev/null +++ b/gst/videomixer/Makefile.am @@ -0,0 +1,41 @@ +plugin_LTLIBRARIES = libgstvideomixer.la + +ORC_SOURCE=blendorc +include $(top_srcdir)/common/orc.mak + +libgstvideomixer_la_SOURCES = \ + videomixer.c \ + blend.c \ + videomixer2.c + +nodist_libgstvideomixer_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstvideomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) +libgstvideomixer_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(ORC_LIBS) +libgstvideomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvideomixer_la_LIBTOOLFLAGS = --tag=disable-static + +# headers we need but don't want installed +noinst_HEADERS = \ + videomixer.h \ + videomixerpad.h \ + blend.h \ + videomixer2.h \ + videomixer2pad.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstvideomixer -:SHARED libgstvideomixer \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstvideomixer_la_SOURCES) \ + $(nodist_libgstvideomixer_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstvideomixer_la_CFLAGS) \ + -:LDFLAGS $(libgstvideomixer_la_LDFLAGS) \ + $(libgstvideomixer_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/videomixer/Makefile.in b/gst/videomixer/Makefile.in new file mode 100644 index 0000000..d6445e3 --- /dev/null +++ b/gst/videomixer/Makefile.in @@ -0,0 +1,946 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# This is a makefile.am fragment to build Orc code. +# +# Define ORC_SOURCE and then include this file, such as: +# +# ORC_SOURCE=gstadderorc +# include $(top_srcdir)/common/orc.mak +# +# This fragment will create tmp-orc.c and gstadderorc.h from +# gstadderorc.orc. +# +# When 'make dist' is run at the top level, or 'make orc-update' +# in a directory including this fragment, the generated source +# files will be copied to $(ORC_SOURCE)-dist.[ch]. These files +# should be checked in to git, since they are used if Orc is +# disabled. +# +# Note that this file defines BUILT_SOURCES, so any later usage +# of BUILT_SOURCES in the Makefile.am that includes this file +# must use '+='. +# + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +DIST_COMMON = README $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/common/orc.mak +subdir = gst/videomixer +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-gcc-inline-assembly.m4 \ + $(top_srcdir)/common/m4/as-objc.m4 \ + $(top_srcdir)/common/m4/as-python.m4 \ + $(top_srcdir)/common/m4/as-scrub-include.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-platform.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst-x11.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/aalib.m4 $(top_srcdir)/m4/esd.m4 \ + $(top_srcdir)/m4/gconf-2.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-fionread.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstvideomixer_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgstvideomixer_la_OBJECTS = libgstvideomixer_la-videomixer.lo \ + libgstvideomixer_la-blend.lo \ + libgstvideomixer_la-videomixer2.lo +am__objects_1 = libgstvideomixer_la-tmp-orc.lo +nodist_libgstvideomixer_la_OBJECTS = $(am__objects_1) +libgstvideomixer_la_OBJECTS = $(am_libgstvideomixer_la_OBJECTS) \ + $(nodist_libgstvideomixer_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +libgstvideomixer_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) \ + $(libgstvideomixer_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libgstvideomixer_la_SOURCES) \ + $(nodist_libgstvideomixer_la_SOURCES) +DIST_SOURCES = $(libgstvideomixer_la_SOURCES) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +AALIB_CFLAGS = @AALIB_CFLAGS@ +AALIB_CONFIG = @AALIB_CONFIG@ +AALIB_LIBS = @AALIB_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +ANNODEX_CFLAGS = @ANNODEX_CFLAGS@ +ANNODEX_LIBS = @ANNODEX_LIBS@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BZ2_LIBS = @BZ2_LIBS@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_GOBJECT_CFLAGS = @CAIRO_GOBJECT_CFLAGS@ +CAIRO_GOBJECT_LIBS = @CAIRO_GOBJECT_LIBS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DIRECTSOUND_CFLAGS = @DIRECTSOUND_CFLAGS@ +DIRECTSOUND_LDFLAGS = @DIRECTSOUND_LDFLAGS@ +DIRECTSOUND_LIBS = @DIRECTSOUND_LIBS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DV1394_CFLAGS = @DV1394_CFLAGS@ +DV1394_LIBS = @DV1394_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +ESD_CFLAGS = @ESD_CFLAGS@ +ESD_CONFIG = @ESD_CONFIG@ +ESD_LIBS = @ESD_LIBS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +FLAC_CFLAGS = @FLAC_CFLAGS@ +FLAC_LIBS = @FLAC_LIBS@ +GCONFTOOL = @GCONFTOOL@ +GCONF_CFLAGS = @GCONF_CFLAGS@ +GCONF_LIBS = @GCONF_LIBS@ +GCONF_SCHEMA_CONFIG_SOURCE = @GCONF_SCHEMA_CONFIG_SOURCE@ +GCONF_SCHEMA_FILE_DIR = @GCONF_SCHEMA_FILE_DIR@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@ +GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CONTROLLER_CFLAGS = @GST_CONTROLLER_CFLAGS@ +GST_CONTROLLER_LIBS = @GST_CONTROLLER_LIBS@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_GDP_CFLAGS = @GST_GDP_CFLAGS@ +GST_GDP_LIBS = @GST_GDP_LIBS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_MAJORMINOR = @GST_MAJORMINOR@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +GTK_X11_CFLAGS = @GTK_X11_CFLAGS@ +GTK_X11_LIBS = @GTK_X11_LIBS@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HAL_CFLAGS = @HAL_CFLAGS@ +HAL_LIBS = @HAL_LIBS@ +HAVE_AVC1394 = @HAVE_AVC1394@ +HAVE_BZ2 = @HAVE_BZ2@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DIRECTSOUND = @HAVE_DIRECTSOUND@ +HAVE_GCONFTOOL = @HAVE_GCONFTOOL@ +HAVE_ROM1394 = @HAVE_ROM1394@ +HAVE_SPEEX = @HAVE_SPEEX@ +HAVE_X = @HAVE_X@ +HAVE_XSHM = @HAVE_XSHM@ +HAVE_ZLIB = @HAVE_ZLIB@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +JACK_0_120_1_CFLAGS = @JACK_0_120_1_CFLAGS@ +JACK_0_120_1_LIBS = @JACK_0_120_1_LIBS@ +JACK_1_9_7_CFLAGS = @JACK_1_9_7_CFLAGS@ +JACK_1_9_7_LIBS = @JACK_1_9_7_LIBS@ +JACK_CFLAGS = @JACK_CFLAGS@ +JACK_LIBS = @JACK_LIBS@ +JPEG_LIBS = @JPEG_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCACA_CFLAGS = @LIBCACA_CFLAGS@ +LIBCACA_LIBS = @LIBCACA_LIBS@ +LIBDV_CFLAGS = @LIBDV_CFLAGS@ +LIBDV_LIBS = @LIBDV_LIBS@ +LIBICONV = @LIBICONV@ +LIBIEC61883_CFLAGS = @LIBIEC61883_CFLAGS@ +LIBIEC61883_LIBS = @LIBIEC61883_LIBS@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBPNG_CFLAGS = @LIBPNG_CFLAGS@ +LIBPNG_LIBS = @LIBPNG_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBV4L2_CFLAGS = @LIBV4L2_CFLAGS@ +LIBV4L2_LIBS = @LIBV4L2_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJC = @OBJC@ +OBJCDEPMODE = @OBJCDEPMODE@ +OBJC_LDFLAGS = @OBJC_LDFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PULSE_0_9_20_CFLAGS = @PULSE_0_9_20_CFLAGS@ +PULSE_0_9_20_LIBS = @PULSE_0_9_20_LIBS@ +PULSE_1_0_CFLAGS = @PULSE_1_0_CFLAGS@ +PULSE_1_0_LIBS = @PULSE_1_0_LIBS@ +PULSE_CFLAGS = @PULSE_CFLAGS@ +PULSE_LIBS = @PULSE_LIBS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RAW1394_CFLAGS = @RAW1394_CFLAGS@ +RAW1394_LIBS = @RAW1394_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHOUT2_CFLAGS = @SHOUT2_CFLAGS@ +SHOUT2_LIBS = @SHOUT2_LIBS@ +SOUP_CFLAGS = @SOUP_CFLAGS@ +SOUP_LIBS = @SOUP_LIBS@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +TAGLIB_CFLAGS = @TAGLIB_CFLAGS@ +TAGLIB_CXXFLAGS = @TAGLIB_CXXFLAGS@ +TAGLIB_LIBS = @TAGLIB_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WAVPACK_CFLAGS = @WAVPACK_CFLAGS@ +WAVPACK_LIBS = @WAVPACK_LIBS@ +WIN32_LIBS = @WIN32_LIBS@ +XDAMAGE_CFLAGS = @XDAMAGE_CFLAGS@ +XDAMAGE_LIBS = @XDAMAGE_LIBS@ +XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XFIXES_LIBS = @XFIXES_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMKMF = @XMKMF@ +XSHM_LIBS = @XSHM_LIBS@ +XVIDEO_LIBS = @XVIDEO_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_OBJC = @ac_ct_OBJC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstvideomixer.la +ORC_SOURCE = blendorc +EXTRA_DIST = $(ORC_SOURCE).orc +ORC_NODIST_SOURCES = tmp-orc.c $(ORC_SOURCE).h +BUILT_SOURCES = tmp-orc.c $(ORC_SOURCE).h +orcc_v_gen = $(orcc_v_gen_$(V)) +orcc_v_gen_ = $(orcc_v_gen_$(AM_DEFAULT_VERBOSITY)) +orcc_v_gen_0 = @echo " ORCC $@"; +cp_v_gen = $(cp_v_gen_$(V)) +cp_v_gen_ = $(cp_v_gen_$(AM_DEFAULT_VERBOSITY)) +cp_v_gen_0 = @echo " CP $@"; +libgstvideomixer_la_SOURCES = \ + videomixer.c \ + blend.c \ + videomixer2.c + +nodist_libgstvideomixer_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstvideomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) + +libgstvideomixer_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(ORC_LIBS) + +libgstvideomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstvideomixer_la_LIBTOOLFLAGS = --tag=disable-static + +# headers we need but don't want installed +noinst_HEADERS = \ + videomixer.h \ + videomixerpad.h \ + blend.h \ + videomixer2.h \ + videomixer2pad.h + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common/orc.mak $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gst/videomixer/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/videomixer/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/common/orc.mak: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)" + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libgstvideomixer.la: $(libgstvideomixer_la_OBJECTS) $(libgstvideomixer_la_DEPENDENCIES) $(EXTRA_libgstvideomixer_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstvideomixer_la_LINK) -rpath $(plugindir) $(libgstvideomixer_la_OBJECTS) $(libgstvideomixer_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstvideomixer_la-blend.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstvideomixer_la-tmp-orc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstvideomixer_la-videomixer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstvideomixer_la-videomixer2.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libgstvideomixer_la-videomixer.lo: videomixer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -MT libgstvideomixer_la-videomixer.lo -MD -MP -MF $(DEPDIR)/libgstvideomixer_la-videomixer.Tpo -c -o libgstvideomixer_la-videomixer.lo `test -f 'videomixer.c' || echo '$(srcdir)/'`videomixer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstvideomixer_la-videomixer.Tpo $(DEPDIR)/libgstvideomixer_la-videomixer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='videomixer.c' object='libgstvideomixer_la-videomixer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -c -o libgstvideomixer_la-videomixer.lo `test -f 'videomixer.c' || echo '$(srcdir)/'`videomixer.c + +libgstvideomixer_la-blend.lo: blend.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -MT libgstvideomixer_la-blend.lo -MD -MP -MF $(DEPDIR)/libgstvideomixer_la-blend.Tpo -c -o libgstvideomixer_la-blend.lo `test -f 'blend.c' || echo '$(srcdir)/'`blend.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstvideomixer_la-blend.Tpo $(DEPDIR)/libgstvideomixer_la-blend.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='blend.c' object='libgstvideomixer_la-blend.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -c -o libgstvideomixer_la-blend.lo `test -f 'blend.c' || echo '$(srcdir)/'`blend.c + +libgstvideomixer_la-videomixer2.lo: videomixer2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -MT libgstvideomixer_la-videomixer2.lo -MD -MP -MF $(DEPDIR)/libgstvideomixer_la-videomixer2.Tpo -c -o libgstvideomixer_la-videomixer2.lo `test -f 'videomixer2.c' || echo '$(srcdir)/'`videomixer2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstvideomixer_la-videomixer2.Tpo $(DEPDIR)/libgstvideomixer_la-videomixer2.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='videomixer2.c' object='libgstvideomixer_la-videomixer2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -c -o libgstvideomixer_la-videomixer2.lo `test -f 'videomixer2.c' || echo '$(srcdir)/'`videomixer2.c + +libgstvideomixer_la-tmp-orc.lo: tmp-orc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -MT libgstvideomixer_la-tmp-orc.lo -MD -MP -MF $(DEPDIR)/libgstvideomixer_la-tmp-orc.Tpo -c -o libgstvideomixer_la-tmp-orc.lo `test -f 'tmp-orc.c' || echo '$(srcdir)/'`tmp-orc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstvideomixer_la-tmp-orc.Tpo $(DEPDIR)/libgstvideomixer_la-tmp-orc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tmp-orc.c' object='libgstvideomixer_la-tmp-orc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstvideomixer_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstvideomixer_la_CFLAGS) $(CFLAGS) -c -o libgstvideomixer_la-tmp-orc.lo `test -f 'tmp-orc.c' || echo '$(srcdir)/'`tmp-orc.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local \ + clean-pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-local clean-pluginLTLIBRARIES ctags \ + dist-hook distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +orc-update: tmp-orc.c $(ORC_SOURCE).h + $(top_srcdir)/common/gst-indent tmp-orc.c + cp tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c + cp $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h + +@HAVE_ORCC_TRUE@tmp-orc.c: $(srcdir)/$(ORC_SOURCE).orc +@HAVE_ORCC_TRUE@ $(orcc_v_gen)$(ORCC) $(ORCC_FLAGS) --implementation --include glib.h -o tmp-orc.c $(srcdir)/$(ORC_SOURCE).orc + +@HAVE_ORCC_TRUE@$(ORC_SOURCE).h: $(srcdir)/$(ORC_SOURCE).orc +@HAVE_ORCC_TRUE@ $(orcc_v_gen)$(ORCC) $(ORCC_FLAGS) --header --include glib.h -o $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE).orc +@HAVE_ORCC_FALSE@tmp-orc.c: $(srcdir)/$(ORC_SOURCE).orc $(srcdir)/$(ORC_SOURCE)-dist.c +@HAVE_ORCC_FALSE@ $(cp_v_gen)cp $(srcdir)/$(ORC_SOURCE)-dist.c tmp-orc.c + +@HAVE_ORCC_FALSE@$(ORC_SOURCE).h: $(srcdir)/$(ORC_SOURCE).orc $(srcdir)/$(ORC_SOURCE)-dist.c +@HAVE_ORCC_FALSE@ $(cp_v_gen)cp $(srcdir)/$(ORC_SOURCE)-dist.h $(ORC_SOURCE).h + +clean-local: clean-orc +.PHONY: clean-orc +clean-orc: + rm -f tmp-orc.c $(ORC_SOURCE).h + +dist-hook: dist-hook-orc +.PHONY: dist-hook-orc + +# we try and copy updated orc -dist files below, but don't fail if it +# doesn't work as the srcdir might not be writable +dist-hook-orc: tmp-orc.c $(ORC_SOURCE).h + $(top_srcdir)/common/gst-indent tmp-orc.c + rm -f tmp-orc.c~ + cmp -s tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c || \ + cp tmp-orc.c $(srcdir)/$(ORC_SOURCE)-dist.c || true + cmp -s $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h || \ + cp $(ORC_SOURCE).h $(srcdir)/$(ORC_SOURCE)-dist.h || true + cp -p tmp-orc.c $(distdir)/$(ORC_SOURCE)-dist.c + cp -p $(ORC_SOURCE).h $(distdir)/$(ORC_SOURCE)-dist.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstvideomixer -:SHARED libgstvideomixer \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstvideomixer_la_SOURCES) \ + $(nodist_libgstvideomixer_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstvideomixer_la_CFLAGS) \ + -:LDFLAGS $(libgstvideomixer_la_LDFLAGS) \ + $(libgstvideomixer_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/videomixer/README b/gst/videomixer/README new file mode 100644 index 0000000..6794a48 --- /dev/null +++ b/gst/videomixer/README @@ -0,0 +1,27 @@ +Video Mixer +----------- + +A generice video mixer, it blends the ayuv buffers from all pads onto +a new buffer. The new buffer has by default a checkerboard pattern but +its color can be changed with a property. +The mixer can mix streams with different framerates and video sizes. It +uses the duration value of the buffer to schedule the rendering of the +buffers. For streams with a different resoltion than the final output +resolution one can specify the position of the top left corner where this +image should be placed with the pad properties xpos and ypos. +The overall alpha value of a stream can also be specified with a pad +property. +By default, the streams are blended in the order that the pads were +requested from the element. This can be overridden by changing the +zorder pad property of the stream, a stream with lower zorder gets +drawn first. + + +TODO +---- + +- really implement zorder +- take I420 yuv as well +- output AYUV if possible. +- implement different blend modes, some code is already done +- use filter caps on srcpad to decide on the final output size diff --git a/gst/videomixer/blend.c b/gst/videomixer/blend.c new file mode 100644 index 0000000..aeeeef1 --- /dev/null +++ b/gst/videomixer/blend.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2006 Mindfruit Bv. + * Author: Sjoerd Simons <sjoerd@luon.net> + * Author: Alex Ugarte <alexugarte@gmail.com> + * Copyright (C) 2009 Alex Ugarte <augarte@vicomtech.org> + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "blend.h" +#include "blendorc.h" + +#include <string.h> + +#include <gst/video/video.h> + +#define BLEND(D,S,alpha) (((D) * (256 - (alpha)) + (S) * (alpha)) >> 8) + +GST_DEBUG_CATEGORY_STATIC (gst_videomixer_blend_debug); +#define GST_CAT_DEFAULT gst_videomixer_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 (const guint8 * src, gint xpos, gint ypos, \ + gint src_width, gint src_height, gdouble src_alpha, \ + guint8 * dest, gint dest_width, gint dest_height) \ +{ \ + guint s_alpha; \ + gint src_stride, dest_stride; \ + \ + src_stride = src_width * 4; \ + dest_stride = dest_width * 4; \ + \ + s_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \ + \ + /* 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; \ + } \ + \ + dest = dest + 4 * xpos + (ypos * dest_stride); \ + \ + LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha); \ +} + +#define BLEND_A32_LOOP(name, method) \ +static inline void \ +_##method##_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \ + gint src_width, gint src_stride, gint dest_stride, guint s_alpha) \ +{ \ + s_alpha = MIN (255, s_alpha); \ + orc_##method##_##name (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ +} + +BLEND_A32_LOOP (argb, blend); +BLEND_A32_LOOP (bgra, blend); +BLEND_A32_LOOP (argb, overlay); +BLEND_A32_LOOP (bgra, overlay); + +#if G_BYTE_ORDER == 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 (guint8 * dest, gint width, gint height) \ +{ \ + gint i, j; \ + gint val; \ + static const gint tab[] = { 80, 160, 80, 160 }; \ + \ + 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 (guint8 * dest, gint width, gint height, gint Y, gint U, gint V) \ +{ \ + gint c1, c2, c3; \ + guint32 val; \ + \ + 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)); \ + \ + 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) \ +{ \ + gint i; \ + gint b_alpha; \ + \ + /* 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 * 256), 0, 256); \ + \ + BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \ +} \ +\ +static void \ +blend_##format_name (const guint8 * src, gint xpos, gint ypos, \ + gint src_width, gint src_height, gdouble src_alpha, \ + guint8 * dest, gint dest_width, gint dest_height) \ +{ \ + const guint8 *b_src; \ + guint8 *b_dest; \ + gint b_src_width = src_width; \ + gint b_src_height = 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; \ + \ + xpos = x_round (xpos); \ + ypos = y_round (ypos); \ + \ + /* 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_width) { \ + 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 U, then V */ \ + b_src = src + gst_video_format_get_component_offset (format_enum, 0, src_width, src_height); \ + b_dest = dest + gst_video_format_get_component_offset (format_enum, 0, dest_width, dest_height); \ + src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 0, src_width); \ + dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 0, dest_width); \ + src_comp_height = gst_video_format_get_component_height (format_enum, 0, b_src_height); \ + src_comp_width = gst_video_format_get_component_width (format_enum, 0, b_src_width); \ + comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 0, xpos); \ + comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 0, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 0, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 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); \ + \ + b_src = src + gst_video_format_get_component_offset (format_enum, 1, src_width, src_height); \ + b_dest = dest + gst_video_format_get_component_offset (format_enum, 1, dest_width, dest_height); \ + src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 1, src_width); \ + dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 1, dest_width); \ + src_comp_height = gst_video_format_get_component_height (format_enum, 1, b_src_height); \ + src_comp_width = gst_video_format_get_component_width (format_enum, 1, b_src_width); \ + comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 1, xpos); \ + comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 1, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 1, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 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); \ + \ + b_src = src + gst_video_format_get_component_offset (format_enum, 2, src_width, src_height); \ + b_dest = dest + gst_video_format_get_component_offset (format_enum, 2, dest_width, dest_height); \ + src_comp_rowstride = gst_video_format_get_row_stride (format_enum, 2, src_width); \ + dest_comp_rowstride = gst_video_format_get_row_stride (format_enum, 2, dest_width); \ + src_comp_height = gst_video_format_get_component_height (format_enum, 2, b_src_height); \ + src_comp_width = gst_video_format_get_component_width (format_enum, 2, b_src_width); \ + comp_xpos = (xpos == 0) ? 0 : gst_video_format_get_component_width (format_enum, 2, xpos); \ + comp_ypos = (ypos == 0) ? 0 : gst_video_format_get_component_height (format_enum, 2, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : gst_video_format_get_component_width (format_enum, 2, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : gst_video_format_get_component_height (format_enum, 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); \ +} + +#define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \ +static void \ +fill_checker_##format_name (guint8 * dest, gint width, gint height) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + guint8 *p; \ + gint comp_width, comp_height; \ + gint rowstride; \ + \ + p = dest + gst_video_format_get_component_offset (format_enum, 0, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 0, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 0, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 0, width); \ + \ + 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 = dest + gst_video_format_get_component_offset (format_enum, 1, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 1, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 1, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 1, width); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, 0x80, comp_width); \ + p += rowstride; \ + } \ + \ + p = dest + gst_video_format_get_component_offset (format_enum, 2, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 2, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 2, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 2, width); \ + \ + 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 (guint8 * dest, gint width, gint height, \ + gint colY, gint colU, gint colV) \ +{ \ + guint8 *p; \ + gint comp_width, comp_height; \ + gint rowstride; \ + gint i; \ + \ + p = dest + gst_video_format_get_component_offset (format_enum, 0, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 0, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 0, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 0, width); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, colY, comp_width); \ + p += rowstride; \ + } \ + \ + p = dest + gst_video_format_get_component_offset (format_enum, 1, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 1, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 1, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 1, width); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, colU, comp_width); \ + p += rowstride; \ + } \ + \ + p = dest + gst_video_format_get_component_offset (format_enum, 2, width, height); \ + comp_width = gst_video_format_get_component_width (format_enum, 2, width); \ + comp_height = gst_video_format_get_component_height (format_enum, 2, height); \ + rowstride = gst_video_format_get_row_stride (format_enum, 2, width); \ + \ + 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, 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, 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, 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, orc_blend_u8); +PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset); +PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset); + +/* RGB, BGR, xRGB, xBGR, RGBx, BGRx */ + +#define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \ +static void \ +blend_##name (const guint8 * src, gint xpos, gint ypos, \ + gint src_width, gint src_height, gdouble src_alpha, \ + guint8 * dest, gint dest_width, gint dest_height) \ +{ \ + gint b_alpha; \ + gint i; \ + gint src_stride, dest_stride; \ + \ + src_stride = GST_ROUND_UP_4 (src_width * bpp); \ + dest_stride = GST_ROUND_UP_4 (dest_width * bpp); \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \ + \ + /* 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); \ + /* 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 (guint8 * dest, gint width, gint height) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + gint dest_add = GST_ROUND_UP_4 (width * bpp) - 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 (guint8 * dest, gint width, gint height, \ + gint colY, gint colU, gint colV) \ +{ \ + gint red, green, blue; \ + gint i; \ + gint dest_stride = GST_ROUND_UP_4 (width * bpp); \ + \ + 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)); \ + orc_splat_u32 ((guint32 *) dest, val, width); \ +} + +#define _orc_memcpy_u32(dest,src,len) orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4) + +RGB_BLEND (rgb, 3, memcpy, 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, 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 (const guint8 * src, gint xpos, gint ypos, \ + gint src_width, gint src_height, gdouble src_alpha, \ + guint8 * dest, gint dest_width, gint dest_height) \ +{ \ + gint b_alpha; \ + gint i; \ + gint src_stride, dest_stride; \ + \ + src_stride = GST_ROUND_UP_4 (src_width * 2); \ + dest_stride = GST_ROUND_UP_4 (dest_width * 2); \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 256), 0, 256); \ + \ + 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); \ + /* 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 (guint8 * dest, gint width, gint height) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + gint dest_add; \ + \ + width = GST_ROUND_UP_2 (width); \ + dest_add = GST_ROUND_UP_4 (width * 2) - 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 (guint8 * dest, gint width, gint height, \ + gint colY, gint colU, gint colV) \ +{ \ + gint i; \ + gint dest_stride; \ + guint32 val; \ + \ + width = GST_ROUND_UP_2 (width); \ + dest_stride = GST_ROUND_UP_4 (width * 2); \ + width /= 2; \ + \ + val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \ + \ + for (i = 0; i < height; i++) { \ + orc_splat_u32 ((guint32 *) dest, val, width); \ + dest += dest_stride; \ + } \ +} + +PACKED_422_BLEND (yuy2, memcpy, 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_video_mixer_blend_argb; +BlendFunction gst_video_mixer_blend_bgra; +BlendFunction gst_video_mixer_overlay_argb; +BlendFunction gst_video_mixer_overlay_bgra; +/* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */ +BlendFunction gst_video_mixer_blend_y444; +BlendFunction gst_video_mixer_blend_y42b; +BlendFunction gst_video_mixer_blend_i420; +/* I420 is equal to YV12 */ +BlendFunction gst_video_mixer_blend_y41b; +BlendFunction gst_video_mixer_blend_rgb; +/* BGR is equal to RGB */ +BlendFunction gst_video_mixer_blend_rgbx; +/* BGRx, xRGB, xBGR are equal to RGBx */ +BlendFunction gst_video_mixer_blend_yuy2; +/* YVYU and UYVY are equal to YUY2 */ + +FillCheckerFunction gst_video_mixer_fill_checker_argb; +FillCheckerFunction gst_video_mixer_fill_checker_bgra; +/* ABGR is equal to ARGB, RGBA is equal to BGRA */ +FillCheckerFunction gst_video_mixer_fill_checker_ayuv; +FillCheckerFunction gst_video_mixer_fill_checker_y444; +FillCheckerFunction gst_video_mixer_fill_checker_y42b; +FillCheckerFunction gst_video_mixer_fill_checker_i420; +/* I420 is equal to YV12 */ +FillCheckerFunction gst_video_mixer_fill_checker_y41b; +FillCheckerFunction gst_video_mixer_fill_checker_rgb; +/* BGR is equal to RGB */ +FillCheckerFunction gst_video_mixer_fill_checker_xrgb; +/* BGRx, xRGB, xBGR are equal to RGBx */ +FillCheckerFunction gst_video_mixer_fill_checker_yuy2; +/* YVYU is equal to YUY2 */ +FillCheckerFunction gst_video_mixer_fill_checker_uyvy; + +FillColorFunction gst_video_mixer_fill_color_argb; +FillColorFunction gst_video_mixer_fill_color_bgra; +FillColorFunction gst_video_mixer_fill_color_abgr; +FillColorFunction gst_video_mixer_fill_color_rgba; +FillColorFunction gst_video_mixer_fill_color_ayuv; +FillColorFunction gst_video_mixer_fill_color_y444; +FillColorFunction gst_video_mixer_fill_color_y42b; +FillColorFunction gst_video_mixer_fill_color_i420; +FillColorFunction gst_video_mixer_fill_color_yv12; +FillColorFunction gst_video_mixer_fill_color_y41b; +FillColorFunction gst_video_mixer_fill_color_rgb; +FillColorFunction gst_video_mixer_fill_color_bgr; +FillColorFunction gst_video_mixer_fill_color_xrgb; +FillColorFunction gst_video_mixer_fill_color_xbgr; +FillColorFunction gst_video_mixer_fill_color_rgbx; +FillColorFunction gst_video_mixer_fill_color_bgrx; +FillColorFunction gst_video_mixer_fill_color_yuy2; +FillColorFunction gst_video_mixer_fill_color_yvyu; +FillColorFunction gst_video_mixer_fill_color_uyvy; + +void +gst_video_mixer_init_blend (void) +{ + GST_DEBUG_CATEGORY_INIT (gst_videomixer_blend_debug, "videomixer_blend", 0, + "video mixer blending functions"); + + gst_video_mixer_blend_argb = blend_argb; + gst_video_mixer_blend_bgra = blend_bgra; + gst_video_mixer_overlay_argb = overlay_argb; + gst_video_mixer_overlay_bgra = overlay_bgra; + gst_video_mixer_blend_i420 = blend_i420; + gst_video_mixer_blend_y444 = blend_y444; + gst_video_mixer_blend_y42b = blend_y42b; + gst_video_mixer_blend_y41b = blend_y41b; + gst_video_mixer_blend_rgb = blend_rgb; + gst_video_mixer_blend_xrgb = blend_xrgb; + gst_video_mixer_blend_yuy2 = blend_yuy2; + + gst_video_mixer_fill_checker_argb = fill_checker_argb_c; + gst_video_mixer_fill_checker_bgra = fill_checker_bgra_c; + gst_video_mixer_fill_checker_ayuv = fill_checker_ayuv_c; + gst_video_mixer_fill_checker_i420 = fill_checker_i420; + gst_video_mixer_fill_checker_y444 = fill_checker_y444; + gst_video_mixer_fill_checker_y42b = fill_checker_y42b; + gst_video_mixer_fill_checker_y41b = fill_checker_y41b; + gst_video_mixer_fill_checker_rgb = fill_checker_rgb_c; + gst_video_mixer_fill_checker_xrgb = fill_checker_xrgb_c; + gst_video_mixer_fill_checker_yuy2 = fill_checker_yuy2_c; + gst_video_mixer_fill_checker_uyvy = fill_checker_uyvy_c; + + gst_video_mixer_fill_color_argb = fill_color_argb; + gst_video_mixer_fill_color_bgra = fill_color_bgra; + gst_video_mixer_fill_color_abgr = fill_color_abgr; + gst_video_mixer_fill_color_rgba = fill_color_rgba; + gst_video_mixer_fill_color_ayuv = fill_color_ayuv; + gst_video_mixer_fill_color_i420 = fill_color_i420; + gst_video_mixer_fill_color_yv12 = fill_color_yv12; + gst_video_mixer_fill_color_y444 = fill_color_y444; + gst_video_mixer_fill_color_y42b = fill_color_y42b; + gst_video_mixer_fill_color_y41b = fill_color_y41b; + gst_video_mixer_fill_color_rgb = fill_color_rgb_c; + gst_video_mixer_fill_color_bgr = fill_color_bgr_c; + gst_video_mixer_fill_color_xrgb = fill_color_xrgb; + gst_video_mixer_fill_color_xbgr = fill_color_xbgr; + gst_video_mixer_fill_color_rgbx = fill_color_rgbx; + gst_video_mixer_fill_color_bgrx = fill_color_bgrx; + gst_video_mixer_fill_color_yuy2 = fill_color_yuy2; + gst_video_mixer_fill_color_yvyu = fill_color_yvyu; + gst_video_mixer_fill_color_uyvy = fill_color_uyvy; +} diff --git a/gst/videomixer/blend.h b/gst/videomixer/blend.h new file mode 100644 index 0000000..ef60c91 --- /dev/null +++ b/gst/videomixer/blend.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __BLEND_H__ +#define __BLEND_H__ + +#include <gst/gst.h> + +typedef void (*BlendFunction) (const guint8 * src, gint xpos, gint ypos, gint src_width, gint src_height, gdouble src_alpha, guint8 * dest, gint dest_width, gint dest_height); +typedef void (*FillCheckerFunction) (guint8 * dest, gint width, gint height); +typedef void (*FillColorFunction) (guint8 * dest, gint width, gint height, gint c1, gint c2, gint c3); + +extern BlendFunction gst_video_mixer_blend_argb; +extern BlendFunction gst_video_mixer_blend_bgra; +#define gst_video_mixer_blend_ayuv gst_video_mixer_blend_argb +#define gst_video_mixer_blend_abgr gst_video_mixer_blend_argb +#define gst_video_mixer_blend_rgba gst_video_mixer_blend_bgra +extern BlendFunction gst_video_mixer_overlay_argb; +extern BlendFunction gst_video_mixer_overlay_bgra; +#define gst_video_mixer_overlay_ayuv gst_video_mixer_overlay_argb +#define gst_video_mixer_overlay_abgr gst_video_mixer_overlay_argb +#define gst_video_mixer_overlay_rgba gst_video_mixer_overlay_bgra +extern BlendFunction gst_video_mixer_blend_i420; +#define gst_video_mixer_blend_yv12 gst_video_mixer_blend_i420 +extern BlendFunction gst_video_mixer_blend_y41b; +extern BlendFunction gst_video_mixer_blend_y42b; +extern BlendFunction gst_video_mixer_blend_y444; +extern BlendFunction gst_video_mixer_blend_rgb; +#define gst_video_mixer_blend_bgr gst_video_mixer_blend_rgb +extern BlendFunction gst_video_mixer_blend_rgbx; +#define gst_video_mixer_blend_bgrx gst_video_mixer_blend_rgbx +#define gst_video_mixer_blend_xrgb gst_video_mixer_blend_rgbx +#define gst_video_mixer_blend_xbgr gst_video_mixer_blend_rgbx +extern BlendFunction gst_video_mixer_blend_yuy2; +#define gst_video_mixer_blend_uyvy gst_video_mixer_blend_yuy2; +#define gst_video_mixer_blend_yvyu gst_video_mixer_blend_yuy2; + +extern FillCheckerFunction gst_video_mixer_fill_checker_argb; +#define gst_video_mixer_fill_checker_abgr gst_video_mixer_fill_checker_argb +extern FillCheckerFunction gst_video_mixer_fill_checker_bgra; +#define gst_video_mixer_fill_checker_rgba gst_video_mixer_fill_checker_bgra +extern FillCheckerFunction gst_video_mixer_fill_checker_ayuv; +extern FillCheckerFunction gst_video_mixer_fill_checker_i420; +#define gst_video_mixer_fill_checker_yv12 gst_video_mixer_fill_checker_i420 +extern FillCheckerFunction gst_video_mixer_fill_checker_y41b; +extern FillCheckerFunction gst_video_mixer_fill_checker_y42b; +extern FillCheckerFunction gst_video_mixer_fill_checker_y444; +extern FillCheckerFunction gst_video_mixer_fill_checker_rgb; +#define gst_video_mixer_fill_checker_bgr gst_video_mixer_fill_checker_rgb +extern FillCheckerFunction gst_video_mixer_fill_checker_rgbx; +#define gst_video_mixer_fill_checker_bgrx gst_video_mixer_fill_checker_rgbx +#define gst_video_mixer_fill_checker_xrgb gst_video_mixer_fill_checker_rgbx +#define gst_video_mixer_fill_checker_xbgr gst_video_mixer_fill_checker_rgbx +extern FillCheckerFunction gst_video_mixer_fill_checker_yuy2; +#define gst_video_mixer_fill_checker_yvyu gst_video_mixer_fill_checker_yuy2; +extern FillCheckerFunction gst_video_mixer_fill_checker_uyvy; + +extern FillColorFunction gst_video_mixer_fill_color_argb; +extern FillColorFunction gst_video_mixer_fill_color_abgr; +extern FillColorFunction gst_video_mixer_fill_color_bgra; +extern FillColorFunction gst_video_mixer_fill_color_rgba; +extern FillColorFunction gst_video_mixer_fill_color_ayuv; +extern FillColorFunction gst_video_mixer_fill_color_i420; +extern FillColorFunction gst_video_mixer_fill_color_yv12; +extern FillColorFunction gst_video_mixer_fill_color_y41b; +extern FillColorFunction gst_video_mixer_fill_color_y42b; +extern FillColorFunction gst_video_mixer_fill_color_y444; +extern FillColorFunction gst_video_mixer_fill_color_rgb; +extern FillColorFunction gst_video_mixer_fill_color_bgr; +extern FillColorFunction gst_video_mixer_fill_color_xrgb; +extern FillColorFunction gst_video_mixer_fill_color_xbgr; +extern FillColorFunction gst_video_mixer_fill_color_rgbx; +extern FillColorFunction gst_video_mixer_fill_color_bgrx; +extern FillColorFunction gst_video_mixer_fill_color_yuy2; +extern FillColorFunction gst_video_mixer_fill_color_yvyu; +extern FillColorFunction gst_video_mixer_fill_color_uyvy; + +void gst_video_mixer_init_blend (void); + +#endif /* __BLEND_H__ */ diff --git a/gst/videomixer/blendorc-dist.c b/gst/videomixer/blendorc-dist.c new file mode 100644 index 0000000..ee783d8 --- /dev/null +++ b/gst/videomixer/blendorc-dist.c @@ -0,0 +1,2203 @@ + +/* autogenerated from blendorc.orc */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <glib.h> + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include <stdint.h> +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 <limits.h> +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 DISABLE_ORC +#include <orc/orc.h> +#endif +void orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n); +void orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, + int n); +void 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 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 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 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 orc_overlay_bgra (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 255 +#define ORC_UB_MIN 0 +#define ORC_SW_MAX 32767 +#define ORC_SW_MIN (-1-ORC_SW_MAX) +#define ORC_UW_MAX 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)&0xff)<<8) | (((x)&0xff00)>>8)) +#define ORC_SWAP_L(x) ((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>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 */ + + + +/* orc_splat_u32 */ +#ifdef DISABLE_ORC +void +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_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 +orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_name (p, "orc_splat_u32"); + orc_program_set_backup_function (p, _backup_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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_P1] = p1; + + func = p->code_exec; + func (ex); +} +#endif + + +/* orc_memcpy_u32 */ +#ifdef DISABLE_ORC +void +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_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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_name (p, "orc_memcpy_u32"); + orc_program_set_backup_function (p, _backup_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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = p->code_exec; + func (ex); +} +#endif + + +/* orc_blend_u8 */ +#ifdef DISABLE_ORC +void +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 = 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_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 = 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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "orc_blend_u8"); + orc_program_set_backup_function (p, _backup_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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + 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 = p->code_exec; + func (ex); +} +#endif + + +/* orc_blend_argb */ +#ifdef DISABLE_ORC +void +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; + orc_union32 var40; + 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 = (int) 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 = + ((var43 & 0xff) << 24) | ((var43 & 0xff) << 16) | ((var43 & 0xff) << + 8) | (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: shruw */ + var47.x4[0] = ((orc_uint16) var46.x4[0]) >> 8; + var47.x4[1] = ((orc_uint16) var46.x4[1]) >> 8; + var47.x4[2] = ((orc_uint16) var46.x4[2]) >> 8; + var47.x4[3] = ((orc_uint16) var46.x4[3]) >> 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_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; + orc_union32 var40; + 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 = (int) 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 = + ((var43 & 0xff) << 24) | ((var43 & 0xff) << 16) | ((var43 & 0xff) << + 8) | (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: shruw */ + var47.x4[0] = ((orc_uint16) var46.x4[0]) >> 8; + var47.x4[1] = ((orc_uint16) var46.x4[1]) >> 8; + var47.x4[2] = ((orc_uint16) var46.x4[2]) >> 8; + var47.x4[3] = ((orc_uint16) var46.x4[3]) >> 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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "orc_blend_argb"); + orc_program_set_backup_function (p, _backup_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_constant (p, 4, 0x00000008, "c2"); + 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, "shruw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_C2, + 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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + 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 = p->code_exec; + func (ex); +} +#endif + + +/* orc_blend_bgra */ +#ifdef DISABLE_ORC +void +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; + orc_union32 var41; + 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 = (int) 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 = + ((var45 & 0xff) << 24) | ((var45 & 0xff) << 16) | ((var45 & 0xff) << + 8) | (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: shruw */ + var49.x4[0] = ((orc_uint16) var48.x4[0]) >> 8; + var49.x4[1] = ((orc_uint16) var48.x4[1]) >> 8; + var49.x4[2] = ((orc_uint16) var48.x4[2]) >> 8; + var49.x4[3] = ((orc_uint16) var48.x4[3]) >> 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_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; + orc_union32 var41; + 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 = (int) 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 = + ((var45 & 0xff) << 24) | ((var45 & 0xff) << 16) | ((var45 & 0xff) << + 8) | (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: shruw */ + var49.x4[0] = ((orc_uint16) var48.x4[0]) >> 8; + var49.x4[1] = ((orc_uint16) var48.x4[1]) >> 8; + var49.x4[2] = ((orc_uint16) var48.x4[2]) >> 8; + var49.x4[3] = ((orc_uint16) var48.x4[3]) >> 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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "orc_blend_bgra"); + orc_program_set_backup_function (p, _backup_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_constant (p, 4, 0x00000008, "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_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, "shruw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_C3, + 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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + 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 = p->code_exec; + func (ex); +} +#endif + + +/* orc_overlay_argb */ +#ifdef DISABLE_ORC +void +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; + orc_union32 var42; + orc_union32 var43; + 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 = (int) 0xffffffff; /* -1 or 2.122e-314f */ + /* 26: loadpl */ + var42.i = (int) 0xffffff00; /* -256 or 2.122e-314f */ + /* 29: loadpl */ + var43.i = (int) 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 = + ((var46 & 0xff) << 24) | ((var46 & 0xff) << 16) | ((var46 & 0xff) << + 8) | (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: shruw */ + var50.x4[0] = ((orc_uint16) var49.x4[0]) >> 8; + var50.x4[1] = ((orc_uint16) var49.x4[1]) >> 8; + var50.x4[2] = ((orc_uint16) var49.x4[2]) >> 8; + var50.x4[3] = ((orc_uint16) var49.x4[3]) >> 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 = + ((var58 & 0xff) << 24) | ((var58 & 0xff) << 16) | ((var58 & 0xff) << + 8) | (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_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; + orc_union32 var42; + orc_union32 var43; + 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 = (int) 0xffffffff; /* -1 or 2.122e-314f */ + /* 26: loadpl */ + var42.i = (int) 0xffffff00; /* -256 or 2.122e-314f */ + /* 29: loadpl */ + var43.i = (int) 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 = + ((var46 & 0xff) << 24) | ((var46 & 0xff) << 16) | ((var46 & 0xff) << + 8) | (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: shruw */ + var50.x4[0] = ((orc_uint16) var49.x4[0]) >> 8; + var50.x4[1] = ((orc_uint16) var49.x4[1]) >> 8; + var50.x4[2] = ((orc_uint16) var49.x4[2]) >> 8; + var50.x4[3] = ((orc_uint16) var49.x4[3]) >> 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 = + ((var58 & 0xff) << 24) | ((var58 & 0xff) << 16) | ((var58 & 0xff) << + 8) | (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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "orc_overlay_argb"); + orc_program_set_backup_function (p, _backup_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_constant (p, 4, 0x00000008, "c4"); + 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, "shruw", 2, ORC_VAR_T4, ORC_VAR_T4, ORC_VAR_C4, + 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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + 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 = p->code_exec; + func (ex); +} +#endif + + +/* orc_overlay_bgra */ +#ifdef DISABLE_ORC +void +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; + orc_union32 var43; + orc_union32 var44; + 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 = (int) 0xffffffff; /* -1 or 2.122e-314f */ + /* 28: loadpl */ + var43.i = (int) 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 31: loadpl */ + var44.i = (int) 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 = + ((var48 & 0xff) << 24) | ((var48 & 0xff) << 16) | ((var48 & 0xff) << + 8) | (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: shruw */ + var52.x4[0] = ((orc_uint16) var51.x4[0]) >> 8; + var52.x4[1] = ((orc_uint16) var51.x4[1]) >> 8; + var52.x4[2] = ((orc_uint16) var51.x4[2]) >> 8; + var52.x4[3] = ((orc_uint16) var51.x4[3]) >> 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 = + ((var61 & 0xff) << 24) | ((var61 & 0xff) << 16) | ((var61 & 0xff) << + 8) | (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_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; + orc_union32 var43; + orc_union32 var44; + 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 = (int) 0xffffffff; /* -1 or 2.122e-314f */ + /* 28: loadpl */ + var43.i = (int) 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 31: loadpl */ + var44.i = (int) 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 = + ((var48 & 0xff) << 24) | ((var48 & 0xff) << 16) | ((var48 & 0xff) << + 8) | (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: shruw */ + var52.x4[0] = ((orc_uint16) var51.x4[0]) >> 8; + var52.x4[1] = ((orc_uint16) var51.x4[1]) >> 8; + var52.x4[2] = ((orc_uint16) var51.x4[2]) >> 8; + var52.x4[3] = ((orc_uint16) var51.x4[3]) >> 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 = + ((var61 & 0xff) << 24) | ((var61 & 0xff) << 16) | ((var61 & 0xff) << + 8) | (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 +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 OrcProgram *p = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "orc_overlay_bgra"); + orc_program_set_backup_function (p, _backup_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_constant (p, 4, 0x00000008, "c5"); + 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, "shruw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_C5, + 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); + + orc_program_compile (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->program = p; + + 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 = p->code_exec; + func (ex); +} +#endif diff --git a/gst/videomixer/blendorc-dist.h b/gst/videomixer/blendorc-dist.h new file mode 100644 index 0000000..9346540 --- /dev/null +++ b/gst/videomixer/blendorc-dist.h @@ -0,0 +1,83 @@ + +/* autogenerated from blendorc.orc */ + +#ifndef _BLENDORC_H_ +#define _BLENDORC_H_ + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include <stdint.h> +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 <limits.h> +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 +void orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n); +void orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int n); +void 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 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 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 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 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/videomixer/blendorc.orc b/gst/videomixer/blendorc.orc new file mode 100644 index 0000000..9140345 --- /dev/null +++ b/gst/videomixer/blendorc.orc @@ -0,0 +1,220 @@ +.function orc_splat_u32 +.dest 4 d1 guint32 +.param 4 p1 guint32 + +copyl d1, p1 + +.function orc_memcpy_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 + +copyl d1, s1 + +.function 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 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 shruw a_wide, a_wide, 8 +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 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 shruw a_wide, a_wide, 8 +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 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 / 256 +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 shruw alpha_s, alpha_s, 8 +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 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 / 256 +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 shruw alpha_s, alpha_s, 8 +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 diff --git a/gst/videomixer/videomixer.c b/gst/videomixer/videomixer.c new file mode 100644 index 0000000..5a49b39 --- /dev/null +++ b/gst/videomixer/videomixer.c @@ -0,0 +1,1908 @@ +/* Generic video mixer plugin + * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-videomixer + * + * Videomixer 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. + * + * All sink pads must be either AYUV, ARGB or BGRA, but a mixture of them is not + * supported. The src pad will have the same colorspace as the sinks. + * No colorspace conversion is done. + * + * Individual parameters for each input stream can be configured on the + * #GstVideoMixerPad. + * + * <refsect2> + * <title>Sample pipelines</title> + * |[ + * gst-launch-0.10 \ + * videotestsrc pattern=1 ! \ + * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \ + * videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \ + * videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! \ + * ffmpegcolorspace ! xvimagesink \ + * videotestsrc ! \ + * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix. + * ]| A pipeline to demonstrate videomixer 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 overlayed 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 videotestsrc pattern=1 ! \ + * video/x-raw-rgb, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * videomixer name=mix ! ffmpegcolorspace ! ximagesink \ + * videotestsrc ! \ + * video/x-raw-rgb, framerate=\(fraction\)5/1, width=320, height=240 ! mix. + * ]| A pipeline to demostrate bgra mixing. (This does not demonstrate alpha blending). + * |[ + * gst-launch videotestsrc pattern=1 ! \ + * video/x-raw-yuv,format =\(fourcc\)I420, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * videomixer name=mix ! ffmpegcolorspace ! ximagesink \ + * videotestsrc ! \ + * video/x-raw-yuv,format=\(fourcc\)I420, framerate=\(fraction\)5/1, width=320, height=240 ! mix. + * ]| A pipeline to test I420 + * |[ + * gst-launch videotestsrc pattern="snow" ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=200, height=150 ! videomixer name=mix sink_1::xpos=20 sink_1::ypos=20 sink_1::alpha=0.5 ! ffmpegcolorspace ! xvimagesink videotestsrc ! video/x-raw-yuv, framerate=\(fraction\)10/1, width=640, height=360 ! mix. + * ]| Set position and alpha on the mixer using #GstVideoMixerPad properties. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstcollectpads.h> +#include <gst/controller/gstcontroller.h> +#include <gst/video/video.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "videomixer.h" +#include "videomixer2.h" + +#include "gst/glib-compat-private.h" + +#ifdef DISABLE_ORC +#define orc_memset memset +#else +#include <orc/orcfunctions.h> +#endif + +GST_DEBUG_CATEGORY_STATIC (gst_videomixer_debug); +#define GST_CAT_DEFAULT gst_videomixer_debug + +#define GST_VIDEO_MIXER_GET_STATE_LOCK(mix) \ + (GST_VIDEO_MIXER(mix)->state_lock) +#define GST_VIDEO_MIXER_STATE_LOCK(mix) \ + (g_mutex_lock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix))) +#define GST_VIDEO_MIXER_STATE_UNLOCK(mix) \ + (g_mutex_unlock(GST_VIDEO_MIXER_GET_STATE_LOCK (mix))) + +static GType gst_videomixer_get_type (void); + +static void gst_videomixer_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_videomixer_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static gboolean gst_videomixer_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_videomixer_sink_event (GstPad * pad, GstEvent * event); + +static void gst_videomixer_sort_pads (GstVideoMixer * mix); + +#define DEFAULT_PAD_ZORDER 0 +#define DEFAULT_PAD_XPOS 0 +#define DEFAULT_PAD_YPOS 0 +#define DEFAULT_PAD_ALPHA 1.0 +enum +{ + PROP_PAD_0, + PROP_PAD_ZORDER, + PROP_PAD_XPOS, + PROP_PAD_YPOS, + PROP_PAD_ALPHA +}; + +GType gst_videomixer_pad_get_type (void); +G_DEFINE_TYPE (GstVideoMixerPad, gst_videomixer_pad, GST_TYPE_PAD); + +static void +gst_videomixer_pad_class_init (GstVideoMixerPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_videomixer_pad_set_property; + gobject_class->get_property = gst_videomixer_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, 10000, DEFAULT_PAD_ZORDER, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + 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_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)); +} + +static void +gst_videomixer_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object); + + switch (prop_id) { + case PROP_PAD_ZORDER: + g_value_set_uint (value, pad->zorder); + break; + 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_ALPHA: + g_value_set_double (value, pad->alpha); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_videomixer_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (object); + GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (GST_PAD (pad))); + + switch (prop_id) { + case PROP_PAD_ZORDER: + GST_VIDEO_MIXER_STATE_LOCK (mix); + pad->zorder = g_value_get_uint (value); + gst_videomixer_sort_pads (mix); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + break; + 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_ALPHA: + pad->alpha = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + gst_object_unref (mix); +} + +static void +gst_videomixer_update_qos (GstVideoMixer * mix, gdouble proportion, + GstClockTimeDiff diff, GstClockTime timestamp) +{ + GST_DEBUG_OBJECT (mix, + "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %" + GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "", + GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp)); + + GST_OBJECT_LOCK (mix); + mix->proportion = proportion; + if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) { + if (G_UNLIKELY (diff > 0)) + mix->earliest_time = + timestamp + 2 * diff + gst_util_uint64_scale_int (GST_SECOND, + mix->fps_d, mix->fps_n); + else + mix->earliest_time = timestamp + diff; + } else { + mix->earliest_time = GST_CLOCK_TIME_NONE; + } + GST_OBJECT_UNLOCK (mix); +} + +static void +gst_videomixer_reset_qos (GstVideoMixer * mix) +{ + gst_videomixer_update_qos (mix, 0.5, 0, GST_CLOCK_TIME_NONE); +} + +static void +gst_videomixer_read_qos (GstVideoMixer * mix, gdouble * proportion, + GstClockTime * time) +{ + GST_OBJECT_LOCK (mix); + *proportion = mix->proportion; + *time = mix->earliest_time; + GST_OBJECT_UNLOCK (mix); +} + +/* 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 gboolean +gst_videomixer_do_qos (GstVideoMixer * mix, GstClockTime timestamp) +{ + GstClockTime qostime, earliest_time; + gdouble proportion; + + /* no timestamp, can't do QoS => process frame */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) { + GST_LOG_OBJECT (mix, "invalid timestamp, can't do QoS, process frame"); + return TRUE; + } + + /* get latest QoS observation values */ + gst_videomixer_read_qos (mix, &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 (mix, "no observation yet, process frame"); + return TRUE; + } + + /* qos is done on running time */ + qostime = + gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, timestamp); + + /* see how our next timestamp relates to the latest qos timestamp */ + GST_LOG_OBJECT (mix, "qostime %" GST_TIME_FORMAT ", earliest %" + GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time)); + + if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) { + GST_DEBUG_OBJECT (mix, "we are late, drop frame"); + return FALSE; + } + + GST_LOG_OBJECT (mix, "process frame"); + return TRUE; +} + +static void +gst_videomixer_set_master_geometry (GstVideoMixer * mix) +{ + GSList *walk; + gint width = 0, height = 0, fps_n = 0, fps_d = 0, par_n = 0, par_d = 0; + GstVideoMixerPad *master = NULL; + + walk = mix->sinkpads; + while (walk) { + GstVideoMixerPad *mixpad = GST_VIDEO_MIXER_PAD (walk->data); + + walk = g_slist_next (walk); + + /* Biggest input geometry will be our output geometry */ + width = MAX (width, mixpad->in_width); + height = MAX (height, mixpad->in_height); + + /* If mix framerate < mixpad framerate, using fractions */ + GST_DEBUG_OBJECT (mixpad, "comparing framerate %d/%d to mixpad's %d/%d", + fps_n, fps_d, mixpad->fps_n, mixpad->fps_d); + if ((!fps_n && !fps_d) || + ((gint64) fps_n * mixpad->fps_d < (gint64) mixpad->fps_n * fps_d)) { + fps_n = mixpad->fps_n; + fps_d = mixpad->fps_d; + par_n = mixpad->par_n; + par_d = mixpad->par_d; + GST_DEBUG_OBJECT (mixpad, "becomes the master pad"); + master = mixpad; + } + } + + /* set results */ + if (mix->master != master || mix->in_width != width + || mix->in_height != height || mix->fps_n != fps_n + || mix->fps_d != fps_d || mix->par_n != par_n || mix->par_d != par_d) { + mix->setcaps = TRUE; + mix->sendseg = TRUE; + gst_videomixer_reset_qos (mix); + mix->master = master; + mix->in_width = width; + mix->in_height = height; + mix->fps_n = fps_n; + mix->fps_d = fps_d; + mix->par_n = par_n; + mix->par_d = par_d; + } +} + +static gboolean +gst_videomixer_pad_sink_setcaps (GstPad * pad, GstCaps * vscaps) +{ + GstVideoMixer *mix; + GstVideoMixerPad *mixpad; + GstStructure *structure; + gint in_width, in_height; + gboolean ret = FALSE; + const GValue *framerate, *par; + + GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, vscaps); + + mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + mixpad = GST_VIDEO_MIXER_PAD (pad); + + if (!mixpad) + goto beach; + + structure = gst_caps_get_structure (vscaps, 0); + + if (!gst_structure_get_int (structure, "width", &in_width) + || !gst_structure_get_int (structure, "height", &in_height) + || (framerate = gst_structure_get_value (structure, "framerate")) == NULL) + goto beach; + par = gst_structure_get_value (structure, "pixel-aspect-ratio"); + + GST_VIDEO_MIXER_STATE_LOCK (mix); + mixpad->fps_n = gst_value_get_fraction_numerator (framerate); + mixpad->fps_d = gst_value_get_fraction_denominator (framerate); + if (par) { + mixpad->par_n = gst_value_get_fraction_numerator (par); + mixpad->par_d = gst_value_get_fraction_denominator (par); + } else { + mixpad->par_n = mixpad->par_d = 1; + } + + mixpad->in_width = in_width; + mixpad->in_height = in_height; + + gst_videomixer_set_master_geometry (mix); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + + ret = TRUE; + +beach: + gst_object_unref (mix); + + return ret; +} + +static GstCaps * +gst_videomixer_pad_sink_getcaps (GstPad * pad) +{ + GstVideoMixer *mix; + GstVideoMixerPad *mixpad; + GstCaps *res = NULL; + GstCaps *mastercaps; + GstStructure *st; + + mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + mixpad = GST_VIDEO_MIXER_PAD (pad); + + if (!mixpad) + goto beach; + + /* Get downstream allowed caps */ + res = gst_pad_get_allowed_caps (mix->srcpad); + if (G_UNLIKELY (res == NULL)) { + res = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + goto beach; + } + + GST_VIDEO_MIXER_STATE_LOCK (mix); + + /* Return as-is if not other sinkpad set as master */ + if (mix->master == NULL) { + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + goto beach; + } + + mastercaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master)); + + /* If master pad caps aren't negotiated yet, return downstream + * allowed caps */ + if (!GST_CAPS_IS_SIMPLE (mastercaps)) { + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + gst_caps_unref (mastercaps); + goto beach; + } + + gst_caps_unref (res); + res = gst_caps_make_writable (mastercaps); + st = gst_caps_get_structure (res, 0); + gst_structure_set (st, "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); + if (!gst_structure_has_field (st, "pixel-aspect-ratio")) + gst_structure_set (st, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); + + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + + +beach: + GST_DEBUG_OBJECT (pad, "Returning %" GST_PTR_FORMAT, res); + + return res; +} + +/* +* We accept the caps if it has the same format as other sink pads in +* the element. +*/ +static gboolean +gst_videomixer_pad_sink_acceptcaps (GstPad * pad, GstCaps * vscaps) +{ + gboolean ret; + GstVideoMixer *mix; + GstCaps *acceptedCaps; + + mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, vscaps); + GST_VIDEO_MIXER_STATE_LOCK (mix); + + if (mix->master) { + acceptedCaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->master)); + acceptedCaps = gst_caps_make_writable (acceptedCaps); + GST_LOG_OBJECT (pad, "master's caps %" GST_PTR_FORMAT, acceptedCaps); + if (GST_CAPS_IS_SIMPLE (acceptedCaps)) { + GstStructure *s; + s = gst_caps_get_structure (acceptedCaps, 0); + 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); + if (!gst_structure_has_field (s, "pixel-aspect-ratio")) + gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + } + } else { + acceptedCaps = gst_pad_get_fixed_caps_func (pad); + } + + GST_INFO_OBJECT (pad, "vscaps: %" GST_PTR_FORMAT, vscaps); + GST_INFO_OBJECT (pad, "acceptedCaps: %" GST_PTR_FORMAT, acceptedCaps); + + ret = gst_caps_can_intersect (vscaps, acceptedCaps); + GST_INFO_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT, (ret ? "" : "not "), + vscaps); + gst_caps_unref (acceptedCaps); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + gst_object_unref (mix); + return ret; +} + + + +static void +gst_videomixer_pad_init (GstVideoMixerPad * mixerpad) +{ + /* setup some pad functions */ + gst_pad_set_setcaps_function (GST_PAD (mixerpad), + gst_videomixer_pad_sink_setcaps); + gst_pad_set_acceptcaps_function (GST_PAD (mixerpad), + GST_DEBUG_FUNCPTR (gst_videomixer_pad_sink_acceptcaps)); + gst_pad_set_getcaps_function (GST_PAD (mixerpad), + gst_videomixer_pad_sink_getcaps); + + mixerpad->zorder = DEFAULT_PAD_ZORDER; + mixerpad->xpos = DEFAULT_PAD_XPOS; + mixerpad->ypos = DEFAULT_PAD_YPOS; + mixerpad->alpha = DEFAULT_PAD_ALPHA; +} + +/* VideoMixer signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_BACKGROUND VIDEO_MIXER_BACKGROUND_CHECKER +enum +{ + PROP_0, + PROP_BACKGROUND +}; + +#define GST_TYPE_VIDEO_MIXER_BACKGROUND (gst_video_mixer_background_get_type()) +static GType +gst_video_mixer_background_get_type (void) +{ + static GType video_mixer_background_type = 0; + + static const GEnumValue video_mixer_background[] = { + {VIDEO_MIXER_BACKGROUND_CHECKER, "Checker pattern", "checker"}, + {VIDEO_MIXER_BACKGROUND_BLACK, "Black", "black"}, + {VIDEO_MIXER_BACKGROUND_WHITE, "White", "white"}, + {VIDEO_MIXER_BACKGROUND_TRANSPARENT, + "Transparent Background to enable further mixing", "transparent"}, + {0, NULL, NULL}, + }; + + if (!video_mixer_background_type) { + video_mixer_background_type = + g_enum_register_static ("GstVideoMixerBackground", + video_mixer_background); + } + return video_mixer_background_type; +} + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";" + GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";" + GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";" + GST_VIDEO_CAPS_YUV ("YVYU") ";" + GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";" + GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx) + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";" + GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";" + GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";" + GST_VIDEO_CAPS_YUV ("YVYU") ";" + GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";" + GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx) + ); + +static void gst_videomixer_finalize (GObject * object); + +static GstCaps *gst_videomixer_getcaps (GstPad * pad); +static gboolean gst_videomixer_setcaps (GstPad * pad, GstCaps * caps); +static gboolean gst_videomixer_query (GstPad * pad, GstQuery * query); + +static GstFlowReturn gst_videomixer_collected (GstCollectPads * pads, + GstVideoMixer * mix); +static GstPad *gst_videomixer_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void gst_videomixer_release_pad (GstElement * element, GstPad * pad); + +static void gst_videomixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_videomixer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_videomixer_change_state (GstElement * element, + GstStateChange transition); + +/*static guint gst_videomixer_signals[LAST_SIGNAL] = { 0 }; */ + +static void gst_videomixer_child_proxy_init (gpointer g_iface, + gpointer iface_data); +static void _do_init (GType object_type); + +GST_BOILERPLATE_FULL (GstVideoMixer, gst_videomixer, GstElement, + GST_TYPE_ELEMENT, _do_init); + +static void +_do_init (GType object_type) +{ + static const GInterfaceInfo child_proxy_info = { + (GInterfaceInitFunc) gst_videomixer_child_proxy_init, + NULL, + NULL + }; + + g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY, + &child_proxy_info); + GST_INFO ("GstChildProxy interface registered"); +} + +static GstObject * +gst_videomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy); + GstObject *obj; + + GST_VIDEO_MIXER_STATE_LOCK (mix); + if ((obj = g_slist_nth_data (mix->sinkpads, index))) + gst_object_ref (obj); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + return obj; +} + +static guint +gst_videomixer_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstVideoMixer *mix = GST_VIDEO_MIXER (child_proxy); + + GST_VIDEO_MIXER_STATE_LOCK (mix); + count = mix->numpads; + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + GST_INFO_OBJECT (mix, "Children Count: %d", count); + return count; +} + +static void +gst_videomixer_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + GST_INFO ("intializing child proxy interface"); + iface->get_child_by_index = gst_videomixer_child_proxy_get_child_by_index; + iface->get_children_count = gst_videomixer_child_proxy_get_children_count; +} + +static void +gst_videomixer_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_static_pad_template (element_class, &sink_factory); + + gst_element_class_set_details_simple (element_class, "Video mixer", + "Filter/Editor/Video", + "Mix multiple video streams", "Wim Taymans <wim@fluendo.com>"); +} + +static void +gst_videomixer_class_init (GstVideoMixerClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_videomixer_finalize; + + gobject_class->get_property = gst_videomixer_get_property; + gobject_class->set_property = gst_videomixer_set_property; + + g_object_class_install_property (gobject_class, PROP_BACKGROUND, + g_param_spec_enum ("background", "Background", "Background type", + GST_TYPE_VIDEO_MIXER_BACKGROUND, + DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_videomixer_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_videomixer_release_pad); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_videomixer_change_state); + + /* Register the pad class */ + (void) (GST_TYPE_VIDEO_MIXER_PAD); + /* Register the background enum */ + (void) (GST_TYPE_VIDEO_MIXER_BACKGROUND); +} + +static void +gst_videomixer_collect_free (GstVideoMixerCollect * mixcol) +{ + if (mixcol->buffer) { + gst_buffer_unref (mixcol->buffer); + mixcol->buffer = NULL; + } +} + +static void +gst_videomixer_reset (GstVideoMixer * mix) +{ + GSList *walk; + + mix->in_width = 0; + mix->in_height = 0; + mix->out_width = 0; + mix->out_height = 0; + mix->fps_n = mix->fps_d = 0; + mix->par_n = mix->par_d = 1; + mix->setcaps = FALSE; + mix->sendseg = FALSE; + + mix->segment_position = 0; + gst_segment_init (&mix->segment, GST_FORMAT_TIME); + + gst_videomixer_reset_qos (mix); + + mix->fmt = GST_VIDEO_FORMAT_UNKNOWN; + + mix->last_ts = 0; + mix->last_duration = -1; + + /* clean up collect data */ + walk = mix->collect->data; + while (walk) { + GstVideoMixerCollect *data = (GstVideoMixerCollect *) walk->data; + + gst_videomixer_collect_free (data); + walk = g_slist_next (walk); + } + + mix->next_sinkpad = 0; + mix->flush_stop_pending = FALSE; +} + +static void +gst_videomixer_init (GstVideoMixer * mix, GstVideoMixerClass * g_class) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix); + + mix->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "src"), "src"); + gst_pad_set_getcaps_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer_getcaps)); + gst_pad_set_setcaps_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer_setcaps)); + gst_pad_set_query_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer_query)); + gst_pad_set_event_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer_src_event)); + gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad); + + mix->collect = gst_collect_pads_new (); + mix->background = DEFAULT_BACKGROUND; + + gst_collect_pads_set_function (mix->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_videomixer_collected), + mix); + + mix->state_lock = g_mutex_new (); + /* initialize variables */ + gst_videomixer_reset (mix); +} + +static void +gst_videomixer_finalize (GObject * object) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (object); + + gst_object_unref (mix->collect); + g_mutex_free (mix->state_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_videomixer_query_duration (GstVideoMixer * mix, GstQuery * query) +{ + 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 (mix)); + while (!done) { + GstIteratorResult ires; + gpointer item; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (item); + gint64 duration; + + /* ask sink peer for duration */ + res &= gst_pad_query_peer_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; + } + gst_object_unref (pad); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (mix, "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_videomixer_query_latency (GstVideoMixer * mix, GstQuery * query) +{ + GstClockTime min, max; + gboolean live; + gboolean res; + GstIterator *it; + gboolean done; + + res = TRUE; + done = FALSE; + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + + /* Take maximum of all latency values */ + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix)); + while (!done) { + GstIteratorResult ires; + gpointer item; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (item); + + GstQuery *peerquery; + + GstClockTime min_cur, max_cur; + + gboolean live_cur; + + peerquery = gst_query_new_latency (); + + /* Ask peer for latency */ + res &= gst_pad_peer_query (pad, peerquery); + + /* take max from all valid return values */ + if (res) { + gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); + + if (min_cur > min) + min = min_cur; + + if (max_cur != GST_CLOCK_TIME_NONE && + ((max != GST_CLOCK_TIME_NONE && max_cur > max) || + (max == GST_CLOCK_TIME_NONE))) + max = max_cur; + + live = live || live_cur; + } + + gst_query_unref (peerquery); + gst_object_unref (pad); + break; + } + case GST_ITERATOR_RESYNC: + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + gst_iterator_free (it); + + if (res) { + /* store the results */ + GST_DEBUG_OBJECT (mix, "Calculated total latency: live %s, min %" + GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, + (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + gst_query_set_latency (query, live, min, max); + } + + return res; +} + +static gboolean +gst_videomixer_query (GstPad * pad, GstQuery * query) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + switch (format) { + case GST_FORMAT_TIME: + /* FIXME, bring to stream time, might be tricky */ + gst_query_set_position (query, format, mix->last_ts); + res = TRUE; + break; + default: + break; + } + break; + } + case GST_QUERY_DURATION: + res = gst_videomixer_query_duration (mix, query); + break; + case GST_QUERY_LATENCY: + res = gst_videomixer_query_latency (mix, query); + break; + default: + /* FIXME, needs a custom query handler because we have multiple + * sinkpads, send to the master pad until then */ + res = gst_pad_query (GST_PAD_CAST (mix->master), query); + break; + } + + gst_object_unref (mix); + return res; +} + +static GstCaps * +gst_videomixer_getcaps (GstPad * pad) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + GstCaps *caps; + GstStructure *structure; + int numCaps; + + if (mix->master) { + caps = + gst_caps_copy (gst_pad_get_pad_template_caps (GST_PAD (mix->master))); + } else { + caps = gst_caps_copy (gst_pad_get_pad_template_caps (mix->srcpad)); + } + + numCaps = gst_caps_get_size (caps) - 1; + for (; numCaps >= 0; numCaps--) { + structure = gst_caps_get_structure (caps, numCaps); + if (mix->out_width != 0) { + gst_structure_set (structure, "width", G_TYPE_INT, mix->out_width, NULL); + } + if (mix->out_height != 0) { + gst_structure_set (structure, "height", G_TYPE_INT, mix->out_height, + NULL); + } + if (mix->fps_d != 0) { + gst_structure_set (structure, + "framerate", GST_TYPE_FRACTION, mix->fps_n, mix->fps_d, NULL); + } + } + + gst_object_unref (mix); + + return caps; +} + +static gboolean +gst_videomixer_setcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoMixer *mixer = GST_VIDEO_MIXER (gst_pad_get_parent_element (pad)); + gboolean ret = FALSE; + + GST_INFO_OBJECT (mixer, "set src caps: %" GST_PTR_FORMAT, caps); + + mixer->blend = NULL; + mixer->overlay = NULL; + mixer->fill_checker = NULL; + mixer->fill_color = NULL; + + if (!gst_video_format_parse_caps (caps, &mixer->fmt, NULL, NULL)) + goto done; + + switch (mixer->fmt) { + case GST_VIDEO_FORMAT_AYUV: + mixer->blend = gst_video_mixer_blend_ayuv; + mixer->overlay = gst_video_mixer_overlay_ayuv; + mixer->fill_checker = gst_video_mixer_fill_checker_ayuv; + mixer->fill_color = gst_video_mixer_fill_color_ayuv; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ARGB: + mixer->blend = gst_video_mixer_blend_argb; + mixer->overlay = gst_video_mixer_overlay_argb; + mixer->fill_checker = gst_video_mixer_fill_checker_argb; + mixer->fill_color = gst_video_mixer_fill_color_argb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRA: + mixer->blend = gst_video_mixer_blend_bgra; + mixer->overlay = gst_video_mixer_overlay_bgra; + mixer->fill_checker = gst_video_mixer_fill_checker_bgra; + mixer->fill_color = gst_video_mixer_fill_color_bgra; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ABGR: + mixer->blend = gst_video_mixer_blend_abgr; + mixer->overlay = gst_video_mixer_overlay_abgr; + mixer->fill_checker = gst_video_mixer_fill_checker_abgr; + mixer->fill_color = gst_video_mixer_fill_color_abgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBA: + mixer->blend = gst_video_mixer_blend_rgba; + mixer->overlay = gst_video_mixer_overlay_rgba; + mixer->fill_checker = gst_video_mixer_fill_checker_rgba; + mixer->fill_color = gst_video_mixer_fill_color_rgba; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y444: + mixer->blend = gst_video_mixer_blend_y444; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_y444; + mixer->fill_color = gst_video_mixer_fill_color_y444; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y42B: + mixer->blend = gst_video_mixer_blend_y42b; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_y42b; + mixer->fill_color = gst_video_mixer_fill_color_y42b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YUY2: + mixer->blend = gst_video_mixer_blend_yuy2; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_yuy2; + mixer->fill_color = gst_video_mixer_fill_color_yuy2; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_UYVY: + mixer->blend = gst_video_mixer_blend_uyvy; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_uyvy; + mixer->fill_color = gst_video_mixer_fill_color_uyvy; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YVYU: + mixer->blend = gst_video_mixer_blend_yvyu; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_yvyu; + mixer->fill_color = gst_video_mixer_fill_color_yvyu; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_I420: + mixer->blend = gst_video_mixer_blend_i420; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_i420; + mixer->fill_color = gst_video_mixer_fill_color_i420; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YV12: + mixer->blend = gst_video_mixer_blend_yv12; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_yv12; + mixer->fill_color = gst_video_mixer_fill_color_yv12; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y41B: + mixer->blend = gst_video_mixer_blend_y41b; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_y41b; + mixer->fill_color = gst_video_mixer_fill_color_y41b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGB: + mixer->blend = gst_video_mixer_blend_rgb; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_rgb; + mixer->fill_color = gst_video_mixer_fill_color_rgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGR: + mixer->blend = gst_video_mixer_blend_bgr; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_bgr; + mixer->fill_color = gst_video_mixer_fill_color_bgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xRGB: + mixer->blend = gst_video_mixer_blend_xrgb; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_xrgb; + mixer->fill_color = gst_video_mixer_fill_color_xrgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xBGR: + mixer->blend = gst_video_mixer_blend_xbgr; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_xbgr; + mixer->fill_color = gst_video_mixer_fill_color_xbgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBx: + mixer->blend = gst_video_mixer_blend_rgbx; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_rgbx; + mixer->fill_color = gst_video_mixer_fill_color_rgbx; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRx: + mixer->blend = gst_video_mixer_blend_bgrx; + mixer->overlay = mixer->blend; + mixer->fill_checker = gst_video_mixer_fill_checker_bgrx; + mixer->fill_color = gst_video_mixer_fill_color_bgrx; + ret = TRUE; + break; + default: + break; + } + +done: + gst_object_unref (mixer); + + return ret; +} + +static GstPad * +gst_videomixer_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name) +{ + GstVideoMixer *mix = NULL; + GstVideoMixerPad *mixpad = NULL; + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + + g_return_val_if_fail (templ != NULL, NULL); + + if (G_UNLIKELY (templ->direction != GST_PAD_SINK)) { + g_warning ("videomixer: request pad that is not a SINK pad"); + return NULL; + } + + g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), NULL); + + mix = GST_VIDEO_MIXER (element); + + if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) { + gint serial = 0; + gchar *name = NULL; + GstVideoMixerCollect *mixcol = NULL; + + GST_VIDEO_MIXER_STATE_LOCK (mix); + if (req_name == NULL || strlen (req_name) < 6 + || !g_str_has_prefix (req_name, "sink_")) { + /* no name given when requesting the pad, use next available int */ + serial = mix->next_sinkpad++; + } else { + /* parse serial number from requested padname */ + serial = atoi (&req_name[5]); + if (serial >= mix->next_sinkpad) + mix->next_sinkpad = serial + 1; + } + /* create new pad with the name */ + name = g_strdup_printf ("sink_%d", serial); + mixpad = g_object_new (GST_TYPE_VIDEO_MIXER_PAD, "name", name, "direction", + templ->direction, "template", templ, NULL); + g_free (name); + + mixpad->zorder = mix->numpads; + mixpad->xpos = DEFAULT_PAD_XPOS; + mixpad->ypos = DEFAULT_PAD_YPOS; + mixpad->alpha = DEFAULT_PAD_ALPHA; + + mixcol = (GstVideoMixerCollect *) + gst_collect_pads_add_pad (mix->collect, GST_PAD (mixpad), + sizeof (GstVideoMixerCollect)); + + /* FIXME: hacked way to override/extend the event function of + * GstCollectPads; because it sets its own event function giving the + * element no access to events */ + mix->collect_event = + (GstPadEventFunction) GST_PAD_EVENTFUNC (GST_PAD (mixpad)); + gst_pad_set_event_function (GST_PAD (mixpad), + GST_DEBUG_FUNCPTR (gst_videomixer_sink_event)); + + /* Keep track of each other */ + mixcol->mixpad = mixpad; + mixpad->mixcol = mixcol; + + /* Keep an internal list of mixpads for zordering */ + mix->sinkpads = g_slist_append (mix->sinkpads, mixpad); + mix->numpads++; + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + } else { + g_warning ("videomixer: this is not our template!"); + return NULL; + } + + /* add the pad to the element */ + gst_element_add_pad (element, GST_PAD (mixpad)); + gst_child_proxy_child_added (GST_OBJECT (mix), GST_OBJECT (mixpad)); + + return GST_PAD (mixpad); +} + +static void +gst_videomixer_release_pad (GstElement * element, GstPad * pad) +{ + GstVideoMixer *mix = NULL; + GstVideoMixerPad *mixpad; + + mix = GST_VIDEO_MIXER (element); + GST_VIDEO_MIXER_STATE_LOCK (mix); + if (G_UNLIKELY (g_slist_find (mix->sinkpads, pad) == NULL)) { + g_warning ("Unknown pad %s", GST_PAD_NAME (pad)); + goto error; + } + + mixpad = GST_VIDEO_MIXER_PAD (pad); + + mix->sinkpads = g_slist_remove (mix->sinkpads, pad); + gst_videomixer_collect_free (mixpad->mixcol); + gst_collect_pads_remove_pad (mix->collect, pad); + gst_child_proxy_child_removed (GST_OBJECT (mix), GST_OBJECT (mixpad)); + /* determine possibly new geometry and master */ + gst_videomixer_set_master_geometry (mix); + mix->numpads--; + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + + gst_element_remove_pad (element, pad); + return; +error: + GST_VIDEO_MIXER_STATE_UNLOCK (mix); +} + +static int +pad_zorder_compare (const GstVideoMixerPad * pad1, + const GstVideoMixerPad * pad2) +{ + return pad1->zorder - pad2->zorder; +} + +static void +gst_videomixer_sort_pads (GstVideoMixer * mix) +{ + mix->sinkpads = g_slist_sort (mix->sinkpads, + (GCompareFunc) pad_zorder_compare); +} + +/* try to get a buffer on all pads. As long as the queued value is + * negative, we skip buffers */ +static gboolean +gst_videomixer_fill_queues (GstVideoMixer * mix) +{ + GSList *walk = NULL; + gboolean eos = TRUE; + + g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), FALSE); + + /* try to make sure we have a buffer from each usable pad first */ + walk = mix->collect->data; + while (walk) { + GstCollectData *data = (GstCollectData *) walk->data; + GstVideoMixerCollect *mixcol = (GstVideoMixerCollect *) data; + GstVideoMixerPad *mixpad = mixcol->mixpad; + + walk = g_slist_next (walk); + + if (mixcol->buffer == NULL) { + GstBuffer *buf = NULL; + + GST_LOG_OBJECT (mix, "we need a new buffer"); + + buf = gst_collect_pads_peek (mix->collect, data); + + if (buf) { + guint64 duration; + + mixcol->buffer = buf; + duration = GST_BUFFER_DURATION (mixcol->buffer); + + GST_LOG_OBJECT (mix, "we have a buffer with duration %" GST_TIME_FORMAT + ", queued %" GST_TIME_FORMAT, GST_TIME_ARGS (duration), + GST_TIME_ARGS (mixpad->queued)); + + /* no duration on the buffer, use the framerate */ + if (!GST_CLOCK_TIME_IS_VALID (duration)) { + if (mixpad->fps_n == 0) { + duration = GST_CLOCK_TIME_NONE; + } else { + duration = + gst_util_uint64_scale_int (GST_SECOND, mixpad->fps_d, + mixpad->fps_n); + } + } + if (GST_CLOCK_TIME_IS_VALID (duration)) + mixpad->queued += duration; + else if (!mixpad->queued) + mixpad->queued = GST_CLOCK_TIME_NONE; + + GST_LOG_OBJECT (mix, "now queued: %" GST_TIME_FORMAT, + GST_TIME_ARGS (mixpad->queued)); + } else { + GST_LOG_OBJECT (mix, "pop returned a NULL buffer"); + } + } + if (mix->sendseg && (mixpad == mix->master)) { + GstEvent *event; + gint64 stop, start; + GstSegment *segment = &data->segment; + + /* FIXME, use rate/applied_rate as set on all sinkpads. + * - currently we just set rate as received from last seek-event + * We could potentially figure out the duration as well using + * the current segment positions and the stated stop positions. + * Also we just start from stream time 0 which is rather + * weird. For non-synchronized mixing, the time should be + * the min of the stream times of all received segments, + * rationale being that the duration is at least going to + * be as long as the earliest stream we start mixing. This + * would also be correct for synchronized mixing but then + * the later streams would be delayed until the stream times + * match. + */ + GST_INFO_OBJECT (mix, "_sending play segment"); + + start = segment->accum; + + /* get the duration of the segment if we can and add it to the accumulated + * time on the segment. */ + if (segment->stop != -1 && segment->start != -1) + stop = start + (segment->stop - segment->start); + else + stop = -1; + + gst_segment_set_newsegment (&mix->segment, FALSE, segment->rate, + segment->format, start, stop, start + mix->segment_position); + event = + gst_event_new_new_segment_full (FALSE, segment->rate, 1.0, + segment->format, start, stop, start + mix->segment_position); + gst_pad_push_event (mix->srcpad, event); + mix->sendseg = FALSE; + } + + if (mixcol->buffer != NULL && GST_CLOCK_TIME_IS_VALID (mixpad->queued)) { + /* got a buffer somewhere so we're not eos */ + eos = FALSE; + } + } + + return eos; +} + +/* blend all buffers present on the pads */ +static void +gst_videomixer_blend_buffers (GstVideoMixer * mix, GstBuffer * outbuf) +{ + GSList *walk; + BlendFunction blend; + if (mix->background == VIDEO_MIXER_BACKGROUND_TRANSPARENT) { + blend = mix->overlay; + } else { + blend = mix->blend; + } + + + walk = mix->sinkpads; + while (walk) { /* We walk with this list because it's ordered */ + GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data); + GstVideoMixerCollect *mixcol = pad->mixcol; + + walk = g_slist_next (walk); + + if (mixcol->buffer != NULL) { + GstClockTime timestamp; + gint64 stream_time; + GstSegment *seg; + + seg = &mixcol->collect.segment; + + timestamp = GST_BUFFER_TIMESTAMP (mixcol->buffer); + + stream_time = + gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp); + + /* sync object properties on stream time */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (G_OBJECT (pad), stream_time); + + blend (GST_BUFFER_DATA (mixcol->buffer), + pad->xpos, pad->ypos, pad->in_width, pad->in_height, pad->alpha, + GST_BUFFER_DATA (outbuf), mix->out_width, mix->out_height); + } + } +} + +/* remove buffers from the queue that were expired in the + * interval of the master, we also prepare the queued value + * in the pad so that we can skip and fill buffers later on */ +static void +gst_videomixer_update_queues (GstVideoMixer * mix) +{ + GSList *walk; + gint64 interval; + + interval = mix->master->queued; + if (interval <= 0) { + if (mix->fps_n == 0) { + interval = G_MAXINT64; + } else { + interval = gst_util_uint64_scale_int (GST_SECOND, mix->fps_d, mix->fps_n); + } + GST_LOG_OBJECT (mix, "set interval to %" G_GINT64_FORMAT " nanoseconds", + interval); + } + + walk = mix->sinkpads; + while (walk) { + GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data); + GstVideoMixerCollect *mixcol = pad->mixcol; + + walk = g_slist_next (walk); + + if (mixcol->buffer != NULL) { + pad->queued -= interval; + GST_LOG_OBJECT (pad, "queued now %" G_GINT64_FORMAT, pad->queued); + if (pad->queued <= 0) { + GstBuffer *buffer = + gst_collect_pads_pop (mix->collect, &mixcol->collect); + + GST_LOG_OBJECT (pad, "unreffing buffer"); + if (buffer) + gst_buffer_unref (buffer); + else + GST_WARNING_OBJECT (pad, + "Buffer was removed by GstCollectPads in the meantime"); + + gst_buffer_unref (mixcol->buffer); + mixcol->buffer = NULL; + } + } + } +} + +static GstFlowReturn +gst_videomixer_collected (GstCollectPads * pads, GstVideoMixer * mix) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *outbuf = NULL; + size_t outsize = 0; + gboolean eos = FALSE; + GstClockTime timestamp = GST_CLOCK_TIME_NONE; + GstClockTime duration = GST_CLOCK_TIME_NONE; + + g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), GST_FLOW_ERROR); + + /* This must be set, otherwise we have no caps */ + if (G_UNLIKELY (mix->in_width == 0)) + return GST_FLOW_NOT_NEGOTIATED; + + if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, TRUE, FALSE)) { + GST_DEBUG_OBJECT (mix, "pending flush stop"); + gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ()); + } + + GST_LOG_OBJECT (mix, "all pads are collected"); + GST_VIDEO_MIXER_STATE_LOCK (mix); + + eos = gst_videomixer_fill_queues (mix); + + if (eos) { + /* Push EOS downstream */ + GST_LOG_OBJECT (mix, "all our sinkpads are EOS, pushing downstream"); + gst_pad_push_event (mix->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_WRONG_STATE; + goto error; + } + + /* If geometry has changed we need to set new caps on the buffer */ + if (mix->in_width != mix->out_width || mix->in_height != mix->out_height + || mix->setcaps) { + GstCaps *newcaps = NULL; + + newcaps = gst_caps_make_writable + (gst_pad_get_negotiated_caps (GST_PAD (mix->master))); + gst_caps_set_simple (newcaps, + "width", G_TYPE_INT, mix->in_width, + "height", G_TYPE_INT, mix->in_height, + "pixel-aspect-ratio", GST_TYPE_FRACTION, mix->par_n, mix->par_d, NULL); + + mix->out_width = mix->in_width; + mix->out_height = mix->in_height; + mix->setcaps = FALSE; + + /* Calculating out buffer size from input size */ + gst_pad_set_caps (mix->srcpad, newcaps); + gst_caps_unref (newcaps); + } + + /* Get timestamp & duration */ + if (mix->master->mixcol->buffer != NULL) { + GstClockTime in_ts; + GstSegment *seg; + GstVideoMixerCollect *mixcol = mix->master->mixcol; + + seg = &mixcol->collect.segment; + in_ts = GST_BUFFER_TIMESTAMP (mixcol->buffer); + + timestamp = gst_segment_to_running_time (seg, GST_FORMAT_TIME, in_ts); + duration = GST_BUFFER_DURATION (mixcol->buffer); + + mix->last_ts = timestamp; + mix->last_duration = duration; + } else { + timestamp = mix->last_ts; + duration = mix->last_duration; + } + + if (GST_CLOCK_TIME_IS_VALID (duration)) + mix->last_ts += duration; + + if (!gst_videomixer_do_qos (mix, timestamp)) { + gst_videomixer_update_queues (mix); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + ret = GST_FLOW_OK; + goto beach; + } + + /* allocate an output buffer */ + outsize = + gst_video_format_get_size (mix->fmt, mix->out_width, mix->out_height); + ret = + gst_pad_alloc_buffer_and_set_caps (mix->srcpad, GST_BUFFER_OFFSET_NONE, + outsize, GST_PAD_CAPS (mix->srcpad), &outbuf); + + /* This must be set at this point, otherwise we have no src caps */ + g_assert (mix->blend != NULL); + + if (ret != GST_FLOW_OK) { + goto error; + } + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + GST_BUFFER_DURATION (outbuf) = duration; + + switch (mix->background) { + case VIDEO_MIXER_BACKGROUND_CHECKER: + mix->fill_checker (GST_BUFFER_DATA (outbuf), mix->out_width, + mix->out_height); + break; + case VIDEO_MIXER_BACKGROUND_BLACK: + mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width, + mix->out_height, 16, 128, 128); + break; + case VIDEO_MIXER_BACKGROUND_WHITE: + mix->fill_color (GST_BUFFER_DATA (outbuf), mix->out_width, + mix->out_height, 240, 128, 128); + break; + case VIDEO_MIXER_BACKGROUND_TRANSPARENT: + orc_memset (GST_BUFFER_DATA (outbuf), 0, + gst_video_format_get_row_stride (mix->fmt, 0, + mix->out_width) * mix->out_height); + break; + } + + gst_videomixer_blend_buffers (mix, outbuf); + + gst_videomixer_update_queues (mix); + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + + ret = gst_pad_push (mix->srcpad, outbuf); + +beach: + return ret; + + /* ERRORS */ +error: + { + if (outbuf) + gst_buffer_unref (outbuf); + + GST_VIDEO_MIXER_STATE_UNLOCK (mix); + goto beach; + } +} + +static gboolean +forward_event_func (GstPad * pad, GValue * ret, GstEvent * event) +{ + gst_event_ref (event); + GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); + if (!gst_pad_push_event (pad, event)) { + g_value_set_boolean (ret, FALSE); + GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", + event, GST_EVENT_TYPE_NAME (event)); + } else { + GST_LOG_OBJECT (pad, "Sent event %p (%s).", + event, GST_EVENT_TYPE_NAME (event)); + } + gst_object_unref (pad); + return TRUE; +} + +/* forwards the event to all sinkpads, takes ownership of the + * event + * + * Returns: TRUE if the event could be forwarded on all + * sinkpads. + */ +static gboolean +forward_event (GstVideoMixer * mix, GstEvent * event) +{ + GstIterator *it; + GValue vret = { 0 }; + + GST_LOG_OBJECT (mix, "Forwarding event %p (%s)", event, + GST_EVENT_TYPE_NAME (event)); + + g_value_init (&vret, G_TYPE_BOOLEAN); + g_value_set_boolean (&vret, TRUE); + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix)); + gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret, + event); + gst_iterator_free (it); + gst_event_unref (event); + + return g_value_get_boolean (&vret); +} + +static gboolean +gst_videomixer_src_event (GstPad * pad, GstEvent * event) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + gboolean result; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_QOS:{ + GstClockTimeDiff diff; + GstClockTime timestamp; + gdouble proportion; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + gst_videomixer_update_qos (mix, proportion, diff, timestamp); + gst_event_unref (event); + + /* TODO: The QoS event should be transformed and send upstream */ + result = TRUE; + break; + } + case GST_EVENT_SEEK: + { + GstSeekFlags flags; + GstSeekType curtype; + gint64 cur; + + /* parse the seek parameters */ + gst_event_parse_seek (event, NULL, NULL, &flags, &curtype, + &cur, NULL, NULL); + + /* check if we are flushing */ + if (flags & GST_SEEK_FLAG_FLUSH) { + /* make sure we accept nothing anymore and return WRONG_STATE */ + gst_collect_pads_set_flushing (mix->collect, TRUE); + + /* flushing seek, start flush downstream, the flush will be done + * when all pads received a FLUSH_STOP. */ + gst_pad_push_event (mix->srcpad, gst_event_new_flush_start ()); + } + + /* now wait for the collected to be finished and mark a new + * segment */ + GST_OBJECT_LOCK (mix->collect); + if (curtype == GST_SEEK_TYPE_SET) + mix->segment_position = cur; + else + mix->segment_position = 0; + mix->sendseg = TRUE; + + if (flags & GST_SEEK_FLAG_FLUSH) { + gst_collect_pads_set_flushing (mix->collect, FALSE); + + /* we can't send FLUSH_STOP here since upstream could start pushing data + * after we unlock mix->collect. + * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after + * forwarding the seek upstream or from gst_videomixer_collected, + * whichever happens first. + */ + mix->flush_stop_pending = TRUE; + } + + GST_OBJECT_UNLOCK (mix->collect); + gst_videomixer_reset_qos (mix); + + result = forward_event (mix, event); + + if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, + TRUE, FALSE)) { + GST_DEBUG_OBJECT (mix, "pending flush stop"); + gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ()); + } + + break; + } + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + result = FALSE; + break; + default: + /* just forward the rest for now */ + result = forward_event (mix, event); + break; + } + gst_object_unref (mix); + + return result; +} + +static gboolean +gst_videomixer_sink_event (GstPad * pad, GstEvent * event) +{ + GstVideoMixerPad *vpad = GST_VIDEO_MIXER_PAD (pad); + GstVideoMixer *videomixer = GST_VIDEO_MIXER (gst_pad_get_parent (pad)); + gboolean ret; + + 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_FLUSH_STOP: + /* mark a pending new segment. This event is synchronized + * with the streaming thread so we can safely update the + * variable without races. It's somewhat weird because we + * assume the collectpads forwarded the FLUSH_STOP past us + * and downstream (using our source pad, the bastard!). + */ + videomixer->sendseg = TRUE; + videomixer->flush_stop_pending = FALSE; + gst_videomixer_reset_qos (videomixer); + + /* Reset pad state after FLUSH_STOP */ + if (vpad->mixcol->buffer) + gst_buffer_unref (vpad->mixcol->buffer); + vpad->mixcol->buffer = NULL; + vpad->queued = 0; + break; + case GST_EVENT_NEWSEGMENT: + if (!videomixer->master || vpad == videomixer->master) { + videomixer->sendseg = TRUE; + gst_videomixer_reset_qos (videomixer); + } + break; + default: + break; + } + + /* now GstCollectPads can take care of the rest, e.g. EOS */ + ret = videomixer->collect_event (pad, event); + + gst_object_unref (videomixer); + return ret; +} + + +static void +gst_videomixer_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (object); + + switch (prop_id) { + case PROP_BACKGROUND: + g_value_set_enum (value, mix->background); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_videomixer_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstVideoMixer *mix = GST_VIDEO_MIXER (object); + + switch (prop_id) { + case PROP_BACKGROUND: + mix->background = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_videomixer_change_state (GstElement * element, GstStateChange transition) +{ + GstVideoMixer *mix; + GstStateChangeReturn ret; + + g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), GST_STATE_CHANGE_FAILURE); + + mix = GST_VIDEO_MIXER (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_LOG_OBJECT (mix, "starting collectpads"); + gst_collect_pads_start (mix->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_LOG_OBJECT (mix, "stopping collectpads"); + gst_collect_pads_stop (mix->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_videomixer_reset (mix); + break; + default: + break; + } + + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_videomixer_debug, "videomixer", 0, + "video mixer"); + + gst_video_mixer_init_blend (); + + return gst_element_register (plugin, "videomixer", GST_RANK_PRIMARY, + GST_TYPE_VIDEO_MIXER) && gst_videomixer2_register (plugin); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "videomixer", + "Video mixer", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/gst/videomixer/videomixer.h b/gst/videomixer/videomixer.h new file mode 100644 index 0000000..fe55a5e --- /dev/null +++ b/gst/videomixer/videomixer.h @@ -0,0 +1,132 @@ +/* Generic video mixer plugin + * Copyright (C) 2008 Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_MIXER_H__ +#define __GST_VIDEO_MIXER_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> +#include "videomixerpad.h" +#include "blend.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_MIXER (gst_videomixer_get_type()) +#define GST_VIDEO_MIXER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER, GstVideoMixer)) +#define GST_VIDEO_MIXER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER, GstVideoMixerClass)) +#define GST_IS_VIDEO_MIXER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER)) +#define GST_IS_VIDEO_MIXER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER)) + +typedef struct _GstVideoMixer GstVideoMixer; +typedef struct _GstVideoMixerClass GstVideoMixerClass; + +/** + * GstVideoMixerBackground: + * @VIDEO_MIXER_BACKGROUND_CHECKER: checker pattern background + * @VIDEO_MIXER_BACKGROUND_BLACK: solid color black background + * @VIDEO_MIXER_BACKGROUND_WHITE: solid color white background + * @VIDEO_MIXER_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 videomixer can blend over. + */ +typedef enum +{ + VIDEO_MIXER_BACKGROUND_CHECKER, + VIDEO_MIXER_BACKGROUND_BLACK, + VIDEO_MIXER_BACKGROUND_WHITE, + VIDEO_MIXER_BACKGROUND_TRANSPARENT, +} +GstVideoMixerBackground; + +/** + * GstVideoMixer: + * + * The opaque #GstVideoMixer structure. + */ +struct _GstVideoMixer +{ + GstElement element; + + /* pad */ + GstPad *srcpad; + + /* Lock to prevent the state to change while blending */ + GMutex *state_lock; + /* Sink pads using Collect Pads from core's base library */ + GstCollectPads *collect; + /* sinkpads, a GSList of GstVideoMixerPads */ + GSList *sinkpads; + + gint numpads; + + GstClockTime last_ts; + GstClockTime last_duration; + + /* the master pad */ + GstVideoMixerPad *master; + + GstVideoFormat fmt; + + gint in_width, in_height; + gint out_width, out_height; + gboolean setcaps; + gboolean sendseg; + + GstVideoMixerBackground background; + + gint fps_n; + gint fps_d; + + gint par_n; + gint par_d; + + /* Next available sinkpad index */ + gint next_sinkpad; + + /* sink event handling */ + GstPadEventFunction collect_event; + guint64 segment_position; + + /* Current downstream segment */ + GstSegment segment; + + /* QoS stuff */ + gdouble proportion; + GstClockTime earliest_time; + + BlendFunction blend, overlay; + FillCheckerFunction fill_checker; + FillColorFunction fill_color; + + gboolean flush_stop_pending; +}; + +struct _GstVideoMixerClass +{ + GstElementClass parent_class; +}; + +GType gst_video_mixer_get_type (void); + +G_END_DECLS +#endif /* __GST_VIDEO_MIXER_H__ */ diff --git a/gst/videomixer/videomixer2.c b/gst/videomixer/videomixer2.c new file mode 100644 index 0000000..585f3aa --- /dev/null +++ b/gst/videomixer/videomixer2.c @@ -0,0 +1,2019 @@ +/* Generic video mixer plugin + * Copyright (C) 2004, 2008 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-videomixer2 + * + * Videomixer2 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. + * + * All sink pads must be either AYUV, ARGB or BGRA, but a mixture of them is not + * supported. The src pad will have the same colorspace as the sinks. + * No colorspace conversion is done. + * + * Individual parameters for each input stream can be configured on the + * #GstVideoMixer2Pad. + * + * At this stage, videomixer2 is considered UNSTABLE. The API provided in the + * properties may yet change in the near future. When videomixer2 is stable, + * it will replace #videomixer + * + * <refsect2> + * <title>Sample pipelines</title> + * |[ + * gst-launch-0.10 \ + * videotestsrc pattern=1 ! \ + * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \ + * videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \ + * videomixer2 name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! \ + * ffmpegcolorspace ! xvimagesink \ + * videotestsrc ! \ + * video/x-raw-yuv,format=\(fourcc\)AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix. + * ]| A pipeline to demonstrate videomixer 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 overlayed 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 videotestsrc pattern=1 ! \ + * video/x-raw-rgb, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * videomixer2 name=mix ! ffmpegcolorspace ! ximagesink \ + * videotestsrc ! \ + * video/x-raw-rgb, framerate=\(fraction\)5/1, width=320, height=240 ! mix. + * ]| A pipeline to demostrate bgra mixing. (This does not demonstrate alpha blending). + * |[ + * gst-launch videotestsrc pattern=1 ! \ + * video/x-raw-yuv,format =\(fourcc\)I420, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * videomixer2 name=mix ! ffmpegcolorspace ! ximagesink \ + * videotestsrc ! \ + * video/x-raw-yuv,format=\(fourcc\)I420, framerate=\(fraction\)5/1, width=320, height=240 ! mix. + * ]| A pipeline to test I420 + * |[ + * gst-launch videomixer2 name=mixer sink_1::alpha=0.5 sink_1::xpos=50 sink_1::ypos=50 ! \ + * ffmpegcolorspace ! ximagesink \ + * videotestsrc pattern=snow timestamp-offset=3000000000 ! \ + * "video/x-raw-yuv,format=(fourcc)AYUV,width=640,height=480,framerate=(fraction)30/1" ! \ + * timeoverlay ! queue2 ! mixer. \ + * videotestsrc pattern=smpte ! \ + * "video/x-raw-yuv,format=(fourcc)AYUV,width=800,height=600,framerate=(fraction)10/1" ! \ + * timeoverlay ! queue2 ! mixer. + * ]| A pipeline to demonstrate synchronized mixing (the second stream starts after 3 seconds) + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include <string.h> + +#include "videomixer2.h" +#include "videomixer2pad.h" + +#include <gst/controller/gstcontroller.h> + +#include "gst/glib-compat-private.h" + +#ifdef DISABLE_ORC +#define orc_memset memset +#else +#include <orc/orcfunctions.h> +#endif + +GST_DEBUG_CATEGORY_STATIC (gst_videomixer2_debug); +#define GST_CAT_DEFAULT gst_videomixer2_debug + +#define GST_VIDEO_MIXER2_GET_LOCK(mix) \ + (GST_VIDEO_MIXER2(mix)->lock) +#define GST_VIDEO_MIXER2_LOCK(mix) \ + (g_mutex_lock(GST_VIDEO_MIXER2_GET_LOCK (mix))) +#define GST_VIDEO_MIXER2_UNLOCK(mix) \ + (g_mutex_unlock(GST_VIDEO_MIXER2_GET_LOCK (mix))) + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";" + GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";" + GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";" + GST_VIDEO_CAPS_YUV ("YVYU") ";" + GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";" + GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx) + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ABGR ";" + GST_VIDEO_CAPS_YUV ("Y444") ";" GST_VIDEO_CAPS_YUV ("Y42B") ";" + GST_VIDEO_CAPS_YUV ("YUY2") ";" GST_VIDEO_CAPS_YUV ("UYVY") ";" + GST_VIDEO_CAPS_YUV ("YVYU") ";" + GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12") ";" + GST_VIDEO_CAPS_YUV ("Y41B") ";" GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_BGR ";" GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx) + ); + +static void gst_videomixer2_child_proxy_init (gpointer g_iface, + gpointer iface_data); +static gboolean gst_videomixer2_push_sink_event (GstVideoMixer2 * mix, + GstEvent * event); +static void gst_videomixer2_release_pad (GstElement * element, GstPad * pad); +static void gst_videomixer2_reset_qos (GstVideoMixer2 * mix); + +static void +_do_init (GType object_type) +{ + static const GInterfaceInfo child_proxy_info = { + (GInterfaceInitFunc) gst_videomixer2_child_proxy_init, + NULL, + NULL + }; + + g_type_add_interface_static (object_type, GST_TYPE_CHILD_PROXY, + &child_proxy_info); +} + +struct _GstVideoMixer2Collect +{ + GstCollectData2 collect; /* we extend the CollectData */ + + GstVideoMixer2Pad *mixpad; + + GstBuffer *queued; /* buffer for which we don't know the end time yet */ + + GstBuffer *buffer; /* buffer that should be blended now */ + GstClockTime start_time; + GstClockTime end_time; +}; + +#define DEFAULT_PAD_ZORDER 0 +#define DEFAULT_PAD_XPOS 0 +#define DEFAULT_PAD_YPOS 0 +#define DEFAULT_PAD_ALPHA 1.0 +enum +{ + PROP_PAD_0, + PROP_PAD_ZORDER, + PROP_PAD_XPOS, + PROP_PAD_YPOS, + PROP_PAD_ALPHA +}; + +G_DEFINE_TYPE (GstVideoMixer2Pad, gst_videomixer2_pad, GST_TYPE_PAD); + +static void +gst_videomixer2_collect_free (GstCollectData2 * data) +{ + GstVideoMixer2Collect *cdata = (GstVideoMixer2Collect *) data; + + gst_buffer_replace (&cdata->buffer, NULL); +} + +static gboolean +gst_videomixer2_update_src_caps (GstVideoMixer2 * mix) +{ + GSList *l; + gint best_width = -1, best_height = -1; + gdouble best_fps = -1, cur_fps; + gint best_fps_n = -1, best_fps_d = -1; + gboolean ret = TRUE; + + GST_VIDEO_MIXER2_LOCK (mix); + + for (l = mix->sinkpads; l; l = l->next) { + GstVideoMixer2Pad *mpad = l->data; + gint this_width, this_height; + + if (mpad->fps_n == 0 || mpad->fps_d == 0 || + mpad->width == 0 || mpad->height == 0) + continue; + + this_width = mpad->width + MAX (mpad->xpos, 0); + this_height = mpad->height + MAX (mpad->ypos, 0); + + if (best_width < this_width) + best_width = this_width; + if (best_height < this_height) + best_height = this_height; + + if (mpad->fps_d == 0) + cur_fps = 0.0; + else + gst_util_fraction_to_double (mpad->fps_n, mpad->fps_d, &cur_fps); + if (best_fps < cur_fps) { + best_fps = cur_fps; + best_fps_n = mpad->fps_n; + best_fps_d = mpad->fps_d; + } + } + + if (best_fps_n <= 0 && best_fps_d <= 0) { + best_fps_n = 25; + best_fps_d = 1; + best_fps = 25.0; + } + + if (best_width > 0 && best_height > 0 && best_fps > 0) { + GstCaps *caps, *peercaps; + GstStructure *s; + + if (mix->fps_n != best_fps_n || mix->fps_d != best_fps_d) { + if (mix->segment.last_stop != -1) { + mix->ts_offset = mix->segment.last_stop - mix->segment.start; + mix->nframes = 0; + } + } + + caps = gst_video_format_new_caps (mix->format, + best_width, best_height, best_fps_n, best_fps_d, + mix->par_n, mix->par_d); + + peercaps = gst_pad_peer_get_caps (mix->srcpad); + if (peercaps) { + GstCaps *tmp; + + s = gst_caps_get_structure (caps, 0); + 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); + + tmp = gst_caps_intersect (caps, peercaps); + gst_caps_unref (caps); + gst_caps_unref (peercaps); + caps = tmp; + if (gst_caps_is_empty (caps)) { + ret = FALSE; + GST_VIDEO_MIXER2_UNLOCK (mix); + goto done; + } + + gst_caps_truncate (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); + + gst_structure_get_int (s, "width", &best_width); + gst_structure_get_int (s, "height", &best_height); + gst_structure_get_fraction (s, "fraction", &best_fps_n, &best_fps_d); + } + + mix->fps_n = best_fps_n; + mix->fps_d = best_fps_d; + mix->width = best_width; + mix->height = best_height; + + GST_VIDEO_MIXER2_UNLOCK (mix); + ret = gst_pad_set_caps (mix->srcpad, caps); + gst_caps_unref (caps); + } else { + GST_VIDEO_MIXER2_UNLOCK (mix); + } + +done: + return ret; +} + + +static gboolean +gst_videomixer2_pad_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoMixer2 *mix; + GstVideoMixer2Pad *mixpad; + GstVideoFormat fmt; + gint width, height; + gint fps_n = 0, fps_d = 0; + gint par_n = 1, par_d = 1; + gboolean ret = FALSE; + GstStructure *s; + + GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps); + + mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + mixpad = GST_VIDEO_MIXER2_PAD (pad); + + if (!gst_video_format_parse_caps (caps, &fmt, &width, &height) || + !gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) { + GST_ERROR_OBJECT (pad, "Failed to parse caps"); + goto beach; + } + + s = gst_caps_get_structure (caps, 0); + if (gst_structure_has_field (s, "framerate") + && !gst_video_parse_caps_framerate (caps, &fps_n, &fps_d)) { + GST_ERROR_OBJECT (pad, "Failed to parse caps"); + goto beach; + } + + GST_VIDEO_MIXER2_LOCK (mix); + if (mix->format != GST_VIDEO_FORMAT_UNKNOWN) { + if (mix->format != fmt || mix->par_n != par_n || mix->par_d != par_d) { + GST_ERROR_OBJECT (pad, "Caps not compatible with other pads' caps"); + GST_VIDEO_MIXER2_UNLOCK (mix); + goto beach; + } + } + + mix->format = fmt; + mix->par_n = par_n; + mix->par_d = par_d; + mixpad->fps_n = fps_n; + mixpad->fps_d = fps_d; + mixpad->width = width; + mixpad->height = height; + + GST_VIDEO_MIXER2_UNLOCK (mix); + + ret = gst_videomixer2_update_src_caps (mix); + +beach: + gst_object_unref (mix); + + return ret; +} + +static GstCaps * +gst_videomixer2_pad_sink_getcaps (GstPad * pad) +{ + GstVideoMixer2 *mix; + GstCaps *srccaps; + GstStructure *s; + gint i, n; + + mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + + srccaps = gst_pad_get_fixed_caps_func (GST_PAD (mix->srcpad)); + srccaps = gst_caps_make_writable (srccaps); + + 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); + if (!gst_structure_has_field (s, "pixel-aspect-ratio")) + gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + } + + GST_DEBUG_OBJECT (pad, "Returning %" GST_PTR_FORMAT, srccaps); + + return srccaps; +} + +static gboolean +gst_videomixer2_pad_sink_acceptcaps (GstPad * pad, GstCaps * caps) +{ + gboolean ret; + GstVideoMixer2 *mix; + GstCaps *accepted_caps; + gint i, n; + GstStructure *s; + + mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps); + + accepted_caps = gst_pad_get_fixed_caps_func (GST_PAD (mix->srcpad)); + 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); + if (!gst_structure_has_field (s, "pixel-aspect-ratio")) + gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + NULL); + } + + ret = gst_caps_can_intersect (caps, accepted_caps); + GST_INFO_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT, (ret ? "" : "not "), + caps); + GST_INFO_OBJECT (pad, "acceptable caps are %" GST_PTR_FORMAT, accepted_caps); + + gst_caps_unref (accepted_caps); + + gst_object_unref (mix); + return ret; +} + +static void +gst_videomixer2_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (object); + + switch (prop_id) { + case PROP_PAD_ZORDER: + g_value_set_uint (value, pad->zorder); + break; + 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_ALPHA: + g_value_set_double (value, pad->alpha); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static int +pad_zorder_compare (const GstVideoMixer2Pad * pad1, + const GstVideoMixer2Pad * pad2) +{ + return pad1->zorder - pad2->zorder; +} + +static void +gst_videomixer2_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (object); + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (GST_PAD (pad))); + + switch (prop_id) { + case PROP_PAD_ZORDER: + GST_VIDEO_MIXER2_LOCK (mix); + pad->zorder = g_value_get_uint (value); + + mix->sinkpads = g_slist_sort (mix->sinkpads, + (GCompareFunc) pad_zorder_compare); + GST_VIDEO_MIXER2_UNLOCK (mix); + break; + 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_ALPHA: + pad->alpha = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + gst_object_unref (mix); +} + +static void +gst_videomixer2_pad_class_init (GstVideoMixer2PadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_videomixer2_pad_set_property; + gobject_class->get_property = gst_videomixer2_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, 10000, DEFAULT_PAD_ZORDER, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + 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_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)); +} + +static void +gst_videomixer2_pad_init (GstVideoMixer2Pad * mixerpad) +{ + /* setup some pad functions */ + gst_pad_set_setcaps_function (GST_PAD (mixerpad), + gst_videomixer2_pad_sink_setcaps); + gst_pad_set_acceptcaps_function (GST_PAD (mixerpad), + GST_DEBUG_FUNCPTR (gst_videomixer2_pad_sink_acceptcaps)); + gst_pad_set_getcaps_function (GST_PAD (mixerpad), + gst_videomixer2_pad_sink_getcaps); + + mixerpad->zorder = DEFAULT_PAD_ZORDER; + mixerpad->xpos = DEFAULT_PAD_XPOS; + mixerpad->ypos = DEFAULT_PAD_YPOS; + mixerpad->alpha = DEFAULT_PAD_ALPHA; +} + +/* GstVideoMixer2 */ +#define DEFAULT_BACKGROUND VIDEO_MIXER2_BACKGROUND_CHECKER +enum +{ + PROP_0, + PROP_BACKGROUND +}; + +#define GST_TYPE_VIDEO_MIXER2_BACKGROUND (gst_videomixer2_background_get_type()) +static GType +gst_videomixer2_background_get_type (void) +{ + static GType video_mixer_background_type = 0; + + static const GEnumValue video_mixer_background[] = { + {VIDEO_MIXER2_BACKGROUND_CHECKER, "Checker pattern", "checker"}, + {VIDEO_MIXER2_BACKGROUND_BLACK, "Black", "black"}, + {VIDEO_MIXER2_BACKGROUND_WHITE, "White", "white"}, + {VIDEO_MIXER2_BACKGROUND_TRANSPARENT, + "Transparent Background to enable further mixing", "transparent"}, + {0, NULL, NULL}, + }; + + if (!video_mixer_background_type) { + video_mixer_background_type = + g_enum_register_static ("GstVideoMixer2Background", + video_mixer_background); + } + return video_mixer_background_type; +} + + +GST_BOILERPLATE_FULL (GstVideoMixer2, gst_videomixer2, GstElement, + GST_TYPE_ELEMENT, _do_init); + +static void +gst_videomixer2_update_qos (GstVideoMixer2 * mix, gdouble proportion, + GstClockTimeDiff diff, GstClockTime timestamp) +{ + GST_DEBUG_OBJECT (mix, + "Updating QoS: proportion %lf, diff %s%" GST_TIME_FORMAT ", timestamp %" + GST_TIME_FORMAT, proportion, (diff < 0) ? "-" : "", + GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (timestamp)); + + GST_OBJECT_LOCK (mix); + mix->proportion = proportion; + if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) { + if (G_UNLIKELY (diff > 0)) + mix->earliest_time = + timestamp + 2 * diff + gst_util_uint64_scale_int (GST_SECOND, + mix->fps_d, mix->fps_n); + else + mix->earliest_time = timestamp + diff; + } else { + mix->earliest_time = GST_CLOCK_TIME_NONE; + } + GST_OBJECT_UNLOCK (mix); +} + +static void +gst_videomixer2_reset_qos (GstVideoMixer2 * mix) +{ + gst_videomixer2_update_qos (mix, 0.5, 0, GST_CLOCK_TIME_NONE); + mix->qos_processed = mix->qos_dropped = 0; +} + +static void +gst_videomixer2_read_qos (GstVideoMixer2 * mix, gdouble * proportion, + GstClockTime * time) +{ + GST_OBJECT_LOCK (mix); + *proportion = mix->proportion; + *time = mix->earliest_time; + GST_OBJECT_UNLOCK (mix); +} + +static void +gst_videomixer2_reset (GstVideoMixer2 * mix) +{ + GSList *l; + + mix->format = GST_VIDEO_FORMAT_UNKNOWN; + mix->width = mix->height = 0; + mix->fps_n = mix->fps_d = 0; + mix->par_n = mix->par_d = 0; + mix->ts_offset = 0; + mix->nframes = 0; + + gst_segment_init (&mix->segment, GST_FORMAT_TIME); + mix->segment.last_stop = -1; + + gst_videomixer2_reset_qos (mix); + + for (l = mix->sinkpads; l; l = l->next) { + GstVideoMixer2Pad *p = l->data; + GstVideoMixer2Collect *mixcol = p->mixcol; + + gst_buffer_replace (&mixcol->buffer, NULL); + mixcol->start_time = -1; + mixcol->end_time = -1; + + p->fps_n = p->fps_d = 0; + p->width = p->height = 0; + } + + mix->newseg_pending = TRUE; + mix->flush_stop_pending = FALSE; +} + +/* 1 == OK + * 0 == need more data + * -1 == EOS + * -2 == error + */ +static gint +gst_videomixer2_fill_queues (GstVideoMixer2 * mix, + GstClockTime output_start_time, GstClockTime output_end_time) +{ + GSList *l; + gboolean eos = TRUE; + gboolean need_more_data = FALSE; + + for (l = mix->sinkpads; l; l = l->next) { + GstVideoMixer2Pad *pad = l->data; + GstVideoMixer2Collect *mixcol = pad->mixcol; + GstSegment *segment = &pad->mixcol->collect.segment; + GstBuffer *buf; + + buf = gst_collect_pads2_peek (mix->collect, &mixcol->collect); + 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!"); + return -2; + } + + /* FIXME: Make all this work with negative rates */ + + if ((mixcol->buffer && start_time < GST_BUFFER_TIMESTAMP (mixcol->buffer)) + || (mixcol->queued + && start_time < GST_BUFFER_TIMESTAMP (mixcol->queued))) { + GST_WARNING_OBJECT (pad, "Buffer from the past, dropping"); + gst_buffer_unref (buf); + buf = gst_collect_pads2_pop (mix->collect, &mixcol->collect); + gst_buffer_unref (buf); + need_more_data = TRUE; + continue; + } + + if (mixcol->queued) { + end_time = start_time - GST_BUFFER_TIMESTAMP (mixcol->queued); + start_time = GST_BUFFER_TIMESTAMP (mixcol->queued); + gst_buffer_unref (buf); + buf = gst_buffer_ref (mixcol->queued); + } else { + end_time = GST_BUFFER_DURATION (buf); + + if (end_time == -1) { + mixcol->queued = buf; + need_more_data = TRUE; + continue; + } + } + + g_assert (start_time != -1 && end_time != -1); + end_time += start_time; /* convert from duration to position */ + + if (mixcol->end_time != -1 && mixcol->end_time > end_time) { + GST_WARNING_OBJECT (pad, "Buffer from the past, dropping"); + if (buf == mixcol->queued) { + gst_buffer_unref (buf); + gst_buffer_replace (&mixcol->queued, NULL); + } else { + gst_buffer_unref (buf); + buf = gst_collect_pads2_pop (mix->collect, &mixcol->collect); + gst_buffer_unref (buf); + } + + need_more_data = TRUE; + continue; + } + + /* Check if it's inside the segment */ + if (start_time >= segment->stop || end_time < segment->start) { + GST_DEBUG_OBJECT (pad, "Buffer outside the segment"); + + if (buf == mixcol->queued) { + gst_buffer_unref (buf); + gst_buffer_replace (&mixcol->queued, NULL); + } else { + gst_buffer_unref (buf); + buf = gst_collect_pads2_pop (mix->collect, &mixcol->collect); + gst_buffer_unref (buf); + } + + 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 (mix->segment.abs_rate != 1.0) { + start_time *= mix->segment.abs_rate; + end_time *= mix->segment.abs_rate; + } + + if (end_time >= output_start_time && start_time < output_end_time) { + GST_DEBUG_OBJECT (pad, + "Taking new buffer with start time %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + gst_buffer_replace (&mixcol->buffer, buf); + mixcol->start_time = start_time; + mixcol->end_time = end_time; + + if (buf == mixcol->queued) { + gst_buffer_unref (buf); + gst_buffer_replace (&mixcol->queued, NULL); + } else { + gst_buffer_unref (buf); + buf = gst_collect_pads2_pop (mix->collect, &mixcol->collect); + gst_buffer_unref (buf); + } + eos = FALSE; + } else if (start_time >= output_end_time) { + GST_DEBUG_OBJECT (pad, "Keeping buffer until %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + gst_buffer_unref (buf); + eos = FALSE; + } else { + GST_DEBUG_OBJECT (pad, "Too old buffer -- dropping"); + if (buf == mixcol->queued) { + gst_buffer_unref (buf); + gst_buffer_replace (&mixcol->queued, NULL); + } else { + gst_buffer_unref (buf); + buf = gst_collect_pads2_pop (mix->collect, &mixcol->collect); + gst_buffer_unref (buf); + } + + need_more_data = TRUE; + continue; + } + } else { + if (mixcol->end_time != -1) { + if (mixcol->end_time < output_start_time) { + gst_buffer_replace (&mixcol->buffer, NULL); + mixcol->start_time = mixcol->end_time = -1; + if (!GST_COLLECT_PADS2_STATE_IS_SET (mixcol, + GST_COLLECT_PADS2_STATE_EOS)) + need_more_data = TRUE; + } else { + eos = FALSE; + } + } + } + } + + if (need_more_data) + return 0; + if (eos) + return -1; + + return 1; +} + +static GstFlowReturn +gst_videomixer2_blend_buffers (GstVideoMixer2 * mix, + GstClockTime output_start_time, GstClockTime output_end_time, + GstBuffer ** outbuf) +{ + GSList *l; + GstFlowReturn ret; + guint outsize; + BlendFunction composite; + + outsize = gst_video_format_get_size (mix->format, mix->width, mix->height); + ret = gst_pad_alloc_buffer_and_set_caps (mix->srcpad, GST_BUFFER_OFFSET_NONE, + outsize, GST_PAD_CAPS (mix->srcpad), outbuf); + if (ret != GST_FLOW_OK) + return ret; + + GST_BUFFER_TIMESTAMP (*outbuf) = output_start_time; + GST_BUFFER_DURATION (*outbuf) = output_end_time - output_start_time; + + /* default to blending */ + composite = mix->blend; + switch (mix->background) { + case VIDEO_MIXER2_BACKGROUND_CHECKER: + mix->fill_checker (GST_BUFFER_DATA (*outbuf), mix->width, mix->height); + break; + case VIDEO_MIXER2_BACKGROUND_BLACK: + mix->fill_color (GST_BUFFER_DATA (*outbuf), mix->width, + mix->height, 16, 128, 128); + break; + case VIDEO_MIXER2_BACKGROUND_WHITE: + mix->fill_color (GST_BUFFER_DATA (*outbuf), mix->width, + mix->height, 240, 128, 128); + break; + case VIDEO_MIXER2_BACKGROUND_TRANSPARENT: + orc_memset (GST_BUFFER_DATA (*outbuf), 0, + gst_video_format_get_row_stride (mix->format, 0, + mix->width) * mix->height); + /* use overlay to keep background transparent */ + composite = mix->overlay; + break; + } + + for (l = mix->sinkpads; l; l = l->next) { + GstVideoMixer2Pad *pad = l->data; + GstVideoMixer2Collect *mixcol = pad->mixcol; + + if (mixcol->buffer != NULL) { + GstClockTime timestamp; + gint64 stream_time; + GstSegment *seg; + + seg = &mixcol->collect.segment; + + timestamp = GST_BUFFER_TIMESTAMP (mixcol->buffer); + + stream_time = + gst_segment_to_stream_time (seg, GST_FORMAT_TIME, timestamp); + + /* sync object properties on stream time */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (G_OBJECT (pad), stream_time); + + composite (GST_BUFFER_DATA (mixcol->buffer), + pad->xpos, pad->ypos, pad->width, pad->height, pad->alpha, + GST_BUFFER_DATA (*outbuf), mix->width, mix->height); + } + } + + return GST_FLOW_OK; +} + +/* 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_videomixer2_do_qos (GstVideoMixer2 * mix, GstClockTime timestamp) +{ + 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 (mix, "invalid timestamp, can't do QoS, process frame"); + return -1; + } + + /* get latest QoS observation values */ + gst_videomixer2_read_qos (mix, &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 (mix, "no observation yet, process frame"); + return -1; + } + + /* qos is done on running time */ + qostime = + gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, timestamp); + + /* see how our next timestamp relates to the latest qos timestamp */ + GST_LOG_OBJECT (mix, "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 (mix, "we are late, drop frame"); + return jitter; + } + + GST_LOG_OBJECT (mix, "process frame"); + return jitter; +} + +static GstFlowReturn +gst_videomixer2_collected (GstCollectPads2 * pads, GstVideoMixer2 * mix) +{ + GstFlowReturn ret; + GstClockTime output_start_time, output_end_time; + GstBuffer *outbuf = NULL; + gint res; + gint64 jitter; + + /* If we're not negotiated yet... */ + if (mix->format == GST_VIDEO_FORMAT_UNKNOWN) + return GST_FLOW_NOT_NEGOTIATED; + + if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, TRUE, FALSE)) { + GST_DEBUG_OBJECT (mix, "pending flush stop"); + gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ()); + } + + GST_VIDEO_MIXER2_LOCK (mix); + + if (mix->newseg_pending) { + GST_DEBUG_OBJECT (mix, "Sending NEWSEGMENT event"); + if (!gst_pad_push_event (mix->srcpad, gst_event_new_new_segment_full (FALSE, + mix->segment.rate, mix->segment.applied_rate, + mix->segment.format, mix->segment.start, mix->segment.stop, + mix->segment.time))) { + ret = GST_FLOW_ERROR; + goto done; + } + mix->newseg_pending = FALSE; + } + + if (mix->segment.last_stop == -1) + output_start_time = mix->segment.start; + else + output_start_time = mix->segment.last_stop; + + if (output_start_time >= mix->segment.stop) { + GST_DEBUG_OBJECT (mix, "Segment done"); + gst_pad_push_event (mix->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_UNEXPECTED; + goto done; + } + + output_end_time = + mix->ts_offset + gst_util_uint64_scale (mix->nframes + 1, + GST_SECOND * mix->fps_d, mix->fps_n); + if (mix->segment.stop != -1) + output_end_time = MIN (output_end_time, mix->segment.stop); + + res = gst_videomixer2_fill_queues (mix, output_start_time, output_end_time); + + if (res == 0) { + GST_DEBUG_OBJECT (mix, "Need more data for decisions"); + ret = GST_FLOW_OK; + goto done; + } else if (res == -1) { + GST_DEBUG_OBJECT (mix, "All sinkpads are EOS -- forwarding"); + gst_pad_push_event (mix->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_UNEXPECTED; + goto done; + } else if (res == -2) { + GST_ERROR_OBJECT (mix, "Error collecting buffers"); + ret = GST_FLOW_ERROR; + goto done; + } + + jitter = gst_videomixer2_do_qos (mix, output_start_time); + if (jitter <= 0) { + ret = + gst_videomixer2_blend_buffers (mix, output_start_time, + output_end_time, &outbuf); + mix->qos_processed++; + } else { + GstMessage *msg; + + mix->qos_dropped++; + + /* TODO: live */ + msg = + gst_message_new_qos (GST_OBJECT_CAST (mix), FALSE, + gst_segment_to_running_time (&mix->segment, GST_FORMAT_TIME, + output_start_time), gst_segment_to_stream_time (&mix->segment, + GST_FORMAT_TIME, output_start_time), output_start_time, + output_end_time - output_start_time); + gst_message_set_qos_values (msg, jitter, mix->proportion, 1000000); + gst_message_set_qos_stats (msg, GST_FORMAT_BUFFERS, mix->qos_processed, + mix->qos_dropped); + gst_element_post_message (GST_ELEMENT_CAST (mix), msg); + + ret = GST_FLOW_OK; + } + + gst_segment_set_last_stop (&mix->segment, GST_FORMAT_TIME, output_end_time); + mix->nframes++; + + GST_VIDEO_MIXER2_UNLOCK (mix); + if (outbuf) { + GST_LOG_OBJECT (mix, + "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))); + ret = gst_pad_push (mix->srcpad, outbuf); + } + GST_VIDEO_MIXER2_LOCK (mix); + +done: + GST_VIDEO_MIXER2_UNLOCK (mix); + + return ret; +} + +static GstCaps * +gst_videomixer2_src_getcaps (GstPad * pad) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + GstCaps *caps; + GstStructure *s; + gint n; + + if (mix->format != GST_VIDEO_FORMAT_UNKNOWN) { + caps = gst_caps_copy (GST_PAD_CAPS (mix->srcpad)); + } else { + caps = gst_caps_copy (gst_pad_get_pad_template_caps (mix->srcpad)); + } + + n = gst_caps_get_size (caps) - 1; + for (; n >= 0; n--) { + s = gst_caps_get_structure (caps, n); + gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); + if (mix->fps_d != 0) { + gst_structure_set (s, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + } + } + + gst_object_unref (mix); + + return caps; +} + +static gboolean +gst_videomixer2_query_duration (GstVideoMixer2 * mix, GstQuery * query) +{ + 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 (mix)); + while (!done) { + GstIteratorResult ires; + gpointer item; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (item); + gint64 duration; + + /* ask sink peer for duration */ + res &= gst_pad_query_peer_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; + } + gst_object_unref (pad); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (mix, "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_videomixer2_query_latency (GstVideoMixer2 * mix, GstQuery * query) +{ + GstClockTime min, max; + gboolean live; + gboolean res; + GstIterator *it; + gboolean done; + + res = TRUE; + done = FALSE; + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + + /* Take maximum of all latency values */ + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix)); + while (!done) { + GstIteratorResult ires; + gpointer item; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (item); + GstQuery *peerquery; + GstClockTime min_cur, max_cur; + gboolean live_cur; + + peerquery = gst_query_new_latency (); + + /* Ask peer for latency */ + res &= gst_pad_peer_query (pad, peerquery); + + /* take max from all valid return values */ + if (res) { + gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); + + if (min_cur > min) + min = min_cur; + + if (max_cur != GST_CLOCK_TIME_NONE && + ((max != GST_CLOCK_TIME_NONE && max_cur > max) || + (max == GST_CLOCK_TIME_NONE))) + max = max_cur; + + live = live || live_cur; + } + + gst_query_unref (peerquery); + gst_object_unref (pad); + break; + } + case GST_ITERATOR_RESYNC: + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + gst_iterator_free (it); + + if (res) { + /* store the results */ + GST_DEBUG_OBJECT (mix, "Calculated total latency: live %s, min %" + GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, + (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + gst_query_set_latency (query, live, min, max); + } + + return res; +} + +static gboolean +gst_videomixer2_src_query (GstPad * pad, GstQuery * query) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + gboolean res = FALSE; + + 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 (&mix->segment, GST_FORMAT_TIME, + mix->segment.last_stop)); + res = TRUE; + break; + default: + break; + } + break; + } + case GST_QUERY_DURATION: + res = gst_videomixer2_query_duration (mix, query); + break; + case GST_QUERY_LATENCY: + res = gst_videomixer2_query_latency (mix, query); + break; + default: + /* FIXME, needs a custom query handler because we have multiple + * sinkpads */ + res = FALSE; + gst_query_unref (query); + break; + } + + gst_object_unref (mix); + return res; +} + +static gboolean +gst_videomixer2_src_event (GstPad * pad, GstEvent * event) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent (pad)); + gboolean result; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_QOS:{ + GstClockTimeDiff diff; + GstClockTime timestamp; + gdouble proportion; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + + gst_videomixer2_update_qos (mix, proportion, diff, timestamp); + + result = gst_videomixer2_push_sink_event (mix, event); + break; + } + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat fmt; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + GSList *l; + gdouble abs_rate; + + /* parse the seek parameters */ + gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type, + &start, &stop_type, &stop); + + if (rate <= 0.0) { + GST_ERROR_OBJECT (mix, "Negative rates not supported yet"); + result = FALSE; + gst_event_unref (event); + break; + } + + GST_DEBUG_OBJECT (mix, "Handling SEEK event"); + + /* check if we are flushing */ + if (flags & GST_SEEK_FLAG_FLUSH) { + /* flushing seek, start flush downstream, the flush will be done + * when all pads received a FLUSH_STOP. */ + gst_pad_push_event (mix->srcpad, gst_event_new_flush_start ()); + + /* make sure we accept nothing anymore and return WRONG_STATE */ + gst_collect_pads2_set_flushing (mix->collect, TRUE); + } + + /* now wait for the collected to be finished and mark a new + * segment */ + GST_COLLECT_PADS2_STREAM_LOCK (mix->collect); + + abs_rate = ABS (rate); + + GST_VIDEO_MIXER2_LOCK (mix); + for (l = mix->sinkpads; l; l = l->next) { + GstVideoMixer2Pad *p = l->data; + + if (flags & GST_SEEK_FLAG_FLUSH) { + gst_buffer_replace (&p->mixcol->buffer, NULL); + p->mixcol->start_time = p->mixcol->end_time = -1; + continue; + } + + /* Convert to the output segment rate */ + if (mix->segment.abs_rate != abs_rate) { + if (mix->segment.abs_rate != 1.0 && p->mixcol->buffer) { + p->mixcol->start_time /= mix->segment.abs_rate; + p->mixcol->end_time /= mix->segment.abs_rate; + } + if (abs_rate != 1.0 && p->mixcol->buffer) { + p->mixcol->start_time *= abs_rate; + p->mixcol->end_time *= abs_rate; + } + } + } + GST_VIDEO_MIXER2_UNLOCK (mix); + + gst_segment_set_seek (&mix->segment, rate, fmt, flags, start_type, start, + stop_type, stop, NULL); + mix->segment.last_stop = -1; + mix->ts_offset = 0; + mix->nframes = 0; + mix->newseg_pending = TRUE; + + if (flags & GST_SEEK_FLAG_FLUSH) { + gst_collect_pads2_set_flushing (mix->collect, FALSE); + + /* we can't send FLUSH_STOP here since upstream could start pushing data + * after we unlock mix->collect. + * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after + * forwarding the seek upstream or from gst_videomixer_collected, + * whichever happens first. + */ + mix->flush_stop_pending = TRUE; + } + + GST_COLLECT_PADS2_STREAM_UNLOCK (mix->collect); + + gst_videomixer2_reset_qos (mix); + + result = gst_videomixer2_push_sink_event (mix, event); + + if (g_atomic_int_compare_and_exchange (&mix->flush_stop_pending, TRUE, + FALSE)) { + GST_DEBUG_OBJECT (mix, "pending flush stop"); + gst_pad_push_event (mix->srcpad, gst_event_new_flush_stop ()); + } + + break; + } + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + result = FALSE; + gst_event_unref (event); + break; + default: + /* just forward the rest for now */ + result = gst_videomixer2_push_sink_event (mix, event); + break; + } + gst_object_unref (mix); + + return result; +} + +static gboolean +gst_videomixer2_src_setcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (gst_pad_get_parent_element (pad)); + gboolean ret = FALSE; + GstVideoFormat fmt; + gint width, height; + gint fps_n, fps_d; + gint par_n, par_d; + + GST_INFO_OBJECT (pad, "set src caps: %" GST_PTR_FORMAT, caps); + + mix->blend = NULL; + mix->overlay = NULL; + mix->fill_checker = NULL; + mix->fill_color = NULL; + + if (!gst_video_format_parse_caps (caps, &fmt, &width, &height) || + !gst_video_parse_caps_framerate (caps, &fps_n, &fps_d) || + !gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) + goto done; + + GST_VIDEO_MIXER2_LOCK (mix); + + if (mix->fps_n != fps_n || mix->fps_d != fps_d) { + if (mix->segment.last_stop != -1) { + mix->ts_offset = mix->segment.last_stop - mix->segment.start; + mix->nframes = 0; + } + gst_videomixer2_reset_qos (mix); + } + + mix->format = fmt; + mix->width = width; + mix->height = height; + mix->fps_n = fps_n; + mix->fps_d = fps_d; + mix->par_n = par_n; + mix->par_d = par_d; + + switch (mix->format) { + case GST_VIDEO_FORMAT_AYUV: + mix->blend = gst_video_mixer_blend_ayuv; + mix->overlay = gst_video_mixer_overlay_ayuv; + mix->fill_checker = gst_video_mixer_fill_checker_ayuv; + mix->fill_color = gst_video_mixer_fill_color_ayuv; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ARGB: + mix->blend = gst_video_mixer_blend_argb; + mix->overlay = gst_video_mixer_overlay_argb; + mix->fill_checker = gst_video_mixer_fill_checker_argb; + mix->fill_color = gst_video_mixer_fill_color_argb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRA: + mix->blend = gst_video_mixer_blend_bgra; + mix->overlay = gst_video_mixer_overlay_bgra; + mix->fill_checker = gst_video_mixer_fill_checker_bgra; + mix->fill_color = gst_video_mixer_fill_color_bgra; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ABGR: + mix->blend = gst_video_mixer_blend_abgr; + mix->overlay = gst_video_mixer_overlay_abgr; + mix->fill_checker = gst_video_mixer_fill_checker_abgr; + mix->fill_color = gst_video_mixer_fill_color_abgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBA: + mix->blend = gst_video_mixer_blend_rgba; + mix->overlay = gst_video_mixer_overlay_rgba; + mix->fill_checker = gst_video_mixer_fill_checker_rgba; + mix->fill_color = gst_video_mixer_fill_color_rgba; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y444: + mix->blend = gst_video_mixer_blend_y444; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_y444; + mix->fill_color = gst_video_mixer_fill_color_y444; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y42B: + mix->blend = gst_video_mixer_blend_y42b; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_y42b; + mix->fill_color = gst_video_mixer_fill_color_y42b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YUY2: + mix->blend = gst_video_mixer_blend_yuy2; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_yuy2; + mix->fill_color = gst_video_mixer_fill_color_yuy2; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_UYVY: + mix->blend = gst_video_mixer_blend_uyvy; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_uyvy; + mix->fill_color = gst_video_mixer_fill_color_uyvy; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YVYU: + mix->blend = gst_video_mixer_blend_yvyu; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_yvyu; + mix->fill_color = gst_video_mixer_fill_color_yvyu; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_I420: + mix->blend = gst_video_mixer_blend_i420; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_i420; + mix->fill_color = gst_video_mixer_fill_color_i420; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YV12: + mix->blend = gst_video_mixer_blend_yv12; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_yv12; + mix->fill_color = gst_video_mixer_fill_color_yv12; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y41B: + mix->blend = gst_video_mixer_blend_y41b; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_y41b; + mix->fill_color = gst_video_mixer_fill_color_y41b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGB: + mix->blend = gst_video_mixer_blend_rgb; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_rgb; + mix->fill_color = gst_video_mixer_fill_color_rgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGR: + mix->blend = gst_video_mixer_blend_bgr; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_bgr; + mix->fill_color = gst_video_mixer_fill_color_bgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xRGB: + mix->blend = gst_video_mixer_blend_xrgb; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_xrgb; + mix->fill_color = gst_video_mixer_fill_color_xrgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xBGR: + mix->blend = gst_video_mixer_blend_xbgr; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_xbgr; + mix->fill_color = gst_video_mixer_fill_color_xbgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBx: + mix->blend = gst_video_mixer_blend_rgbx; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_rgbx; + mix->fill_color = gst_video_mixer_fill_color_rgbx; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRx: + mix->blend = gst_video_mixer_blend_bgrx; + mix->overlay = mix->blend; + mix->fill_checker = gst_video_mixer_fill_checker_bgrx; + mix->fill_color = gst_video_mixer_fill_color_bgrx; + ret = TRUE; + break; + default: + break; + } + GST_VIDEO_MIXER2_UNLOCK (mix); + +done: + gst_object_unref (mix); + + return ret; +} + +static GstFlowReturn +gst_videomixer2_sink_clip (GstCollectPads2 * pads, + GstCollectData2 * data, GstBuffer * buf, GstBuffer ** outbuf, + GstVideoMixer2 * mix) +{ + GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (data->pad); + GstVideoMixer2Collect *mixcol = pad->mixcol; + GstClockTime start_time, end_time; + + start_time = GST_BUFFER_TIMESTAMP (buf); + if (start_time == -1) { + GST_ERROR_OBJECT (pad, "Timestamped buffers required!"); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } + + end_time = GST_BUFFER_DURATION (buf); + if (end_time == -1) + end_time = gst_util_uint64_scale_int (GST_SECOND, pad->fps_d, pad->fps_n); + if (end_time == -1) { + *outbuf = buf; + return GST_FLOW_OK; + } + + start_time = MAX (start_time, mixcol->collect.segment.start); + start_time = + gst_segment_to_running_time (&mixcol->collect.segment, + GST_FORMAT_TIME, start_time); + + end_time += GST_BUFFER_TIMESTAMP (buf); + if (mixcol->collect.segment.stop != -1) + end_time = MIN (end_time, mixcol->collect.segment.stop); + end_time = + gst_segment_to_running_time (&mixcol->collect.segment, + GST_FORMAT_TIME, end_time); + + /* Convert to the output segment rate */ + if (mix->segment.abs_rate != 1.0) { + start_time *= mix->segment.abs_rate; + end_time *= mix->segment.abs_rate; + } + + if (mixcol->buffer != NULL && end_time < mixcol->end_time) { + gst_buffer_unref (buf); + *outbuf = NULL; + return GST_FLOW_OK; + } + + *outbuf = buf; + return GST_FLOW_OK; +} + +static gboolean +gst_videomixer2_sink_event (GstCollectPads2 * pads, GstCollectData2 * cdata, + GstEvent * event, GstVideoMixer2 * mix) +{ + GstVideoMixer2Pad *pad = GST_VIDEO_MIXER2_PAD (cdata->pad); + gboolean ret = TRUE; + + GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s", + GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad)); + + /* return FALSE => event will be forwarded */ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + GstFormat fmt; + gst_event_parse_new_segment (event, NULL, NULL, &fmt, NULL, NULL, NULL); + + g_assert (fmt == GST_FORMAT_TIME); + /* eat NEWSEGMENT events, collectpads2 unrefs the event */ + ret = FALSE; + break; + } + case GST_EVENT_FLUSH_STOP: + mix->newseg_pending = TRUE; + mix->flush_stop_pending = FALSE; + gst_videomixer2_reset_qos (mix); + gst_buffer_replace (&pad->mixcol->buffer, NULL); + pad->mixcol->start_time = -1; + pad->mixcol->end_time = -1; + + gst_segment_init (&mix->segment, GST_FORMAT_TIME); + mix->segment.last_stop = -1; + mix->ts_offset = 0; + mix->nframes = 0; + + gst_pad_push_event (mix->srcpad, event); + break; + default: + gst_pad_push_event (mix->srcpad, event); + break; + } + + return ret; +} + +static gboolean +forward_event_func (GstPad * pad, GValue * ret, GstEvent * event) +{ + gst_event_ref (event); + GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); + if (!gst_pad_push_event (pad, event)) { + g_value_set_boolean (ret, FALSE); + GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", + event, GST_EVENT_TYPE_NAME (event)); + } else { + GST_LOG_OBJECT (pad, "Sent event %p (%s).", + event, GST_EVENT_TYPE_NAME (event)); + } + gst_object_unref (pad); + return TRUE; +} + +static gboolean +gst_videomixer2_push_sink_event (GstVideoMixer2 * mix, GstEvent * event) +{ + GstIterator *it; + GValue vret = { 0 }; + + GST_LOG_OBJECT (mix, "Forwarding event %p (%s)", event, + GST_EVENT_TYPE_NAME (event)); + + g_value_init (&vret, G_TYPE_BOOLEAN); + g_value_set_boolean (&vret, TRUE); + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (mix)); + gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret, + event); + gst_iterator_free (it); + gst_event_unref (event); + + return g_value_get_boolean (&vret); +} + +/* GstElement vmethods */ +static GstStateChangeReturn +gst_videomixer2_change_state (GstElement * element, GstStateChange transition) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_LOG_OBJECT (mix, "starting collectpads"); + gst_collect_pads2_start (mix->collect); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_LOG_OBJECT (mix, "stopping collectpads"); + gst_collect_pads2_stop (mix->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_videomixer2_reset (mix); + break; + default: + break; + } + + return ret; +} + +static GstPad * +gst_videomixer2_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name) +{ + GstVideoMixer2 *mix; + GstVideoMixer2Pad *mixpad; + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + + mix = GST_VIDEO_MIXER2 (element); + + if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) { + gint serial = 0; + gchar *name = NULL; + GstVideoMixer2Collect *mixcol = NULL; + + GST_VIDEO_MIXER2_LOCK (mix); + if (req_name == NULL || strlen (req_name) < 6 + || !g_str_has_prefix (req_name, "sink_")) { + /* no name given when requesting the pad, use next available int */ + serial = mix->next_sinkpad++; + } else { + /* parse serial number from requested padname */ + serial = g_ascii_strtoull (&req_name[5], NULL, 10); + if (serial >= mix->next_sinkpad) + mix->next_sinkpad = serial + 1; + } + /* create new pad with the name */ + name = g_strdup_printf ("sink_%d", serial); + mixpad = g_object_new (GST_TYPE_VIDEO_MIXER2_PAD, "name", name, "direction", + templ->direction, "template", templ, NULL); + g_free (name); + + mixpad->zorder = mix->numpads; + mixpad->xpos = DEFAULT_PAD_XPOS; + mixpad->ypos = DEFAULT_PAD_YPOS; + mixpad->alpha = DEFAULT_PAD_ALPHA; + + mixcol = (GstVideoMixer2Collect *) + gst_collect_pads2_add_pad_full (mix->collect, GST_PAD (mixpad), + sizeof (GstVideoMixer2Collect), + (GstCollectData2DestroyNotify) gst_videomixer2_collect_free, TRUE); + + /* Keep track of each other */ + mixcol->mixpad = mixpad; + mixpad->mixcol = mixcol; + + mixcol->start_time = -1; + mixcol->end_time = -1; + + /* Keep an internal list of mixpads for zordering */ + mix->sinkpads = g_slist_append (mix->sinkpads, mixpad); + mix->numpads++; + GST_VIDEO_MIXER2_UNLOCK (mix); + } else { + return NULL; + } + + GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (mixpad)); + + /* add the pad to the element */ + gst_element_add_pad (element, GST_PAD (mixpad)); + gst_child_proxy_child_added (GST_OBJECT (mix), GST_OBJECT (mixpad)); + + return GST_PAD (mixpad); +} + +static void +gst_videomixer2_release_pad (GstElement * element, GstPad * pad) +{ + GstVideoMixer2 *mix = NULL; + GstVideoMixer2Pad *mixpad; + gboolean update_caps; + + mix = GST_VIDEO_MIXER2 (element); + + GST_VIDEO_MIXER2_LOCK (mix); + if (G_UNLIKELY (g_slist_find (mix->sinkpads, pad) == NULL)) { + g_warning ("Unknown pad %s", GST_PAD_NAME (pad)); + goto error; + } + + mixpad = GST_VIDEO_MIXER2_PAD (pad); + + mix->sinkpads = g_slist_remove (mix->sinkpads, pad); + gst_child_proxy_child_removed (GST_OBJECT (mix), GST_OBJECT (mixpad)); + mix->numpads--; + + update_caps = mix->format != GST_VIDEO_FORMAT_UNKNOWN; + GST_VIDEO_MIXER2_UNLOCK (mix); + + gst_collect_pads2_remove_pad (mix->collect, pad); + + if (update_caps) + gst_videomixer2_update_src_caps (mix); + + gst_element_remove_pad (element, pad); + return; +error: + GST_VIDEO_MIXER2_UNLOCK (mix); +} + +/* GObject vmethods */ +static void +gst_videomixer2_finalize (GObject * o) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (o); + + gst_object_unref (mix->collect); + g_mutex_free (mix->lock); + + G_OBJECT_CLASS (parent_class)->finalize (o); +} + +static void +gst_videomixer2_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (object); + + switch (prop_id) { + case PROP_BACKGROUND: + g_value_set_enum (value, mix->background); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_videomixer2_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (object); + + switch (prop_id) { + case PROP_BACKGROUND: + mix->background = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* GstChildProxy implementation */ +static GstObject * +gst_videomixer2_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (child_proxy); + GstObject *obj; + + GST_VIDEO_MIXER2_LOCK (mix); + if ((obj = g_slist_nth_data (mix->sinkpads, index))) + gst_object_ref (obj); + GST_VIDEO_MIXER2_UNLOCK (mix); + return obj; +} + +static guint +gst_videomixer2_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstVideoMixer2 *mix = GST_VIDEO_MIXER2 (child_proxy); + + GST_VIDEO_MIXER2_LOCK (mix); + count = mix->numpads; + GST_VIDEO_MIXER2_UNLOCK (mix); + GST_INFO_OBJECT (mix, "Children Count: %d", count); + return count; +} + +static void +gst_videomixer2_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + GST_INFO ("intializing child proxy interface"); + iface->get_child_by_index = gst_videomixer2_child_proxy_get_child_by_index; + iface->get_children_count = gst_videomixer2_child_proxy_get_children_count; +} + +/* GObject boilerplate */ +static void +gst_videomixer2_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_static_pad_template (element_class, &sink_factory); + + gst_element_class_set_details_simple (element_class, "Video mixer 2", + "Filter/Editor/Video", + "Mix multiple video streams", "Wim Taymans <wim@fluendo.com>, " + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); +} + +static void +gst_videomixer2_class_init (GstVideoMixer2Class * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_videomixer2_finalize; + + gobject_class->get_property = gst_videomixer2_get_property; + gobject_class->set_property = gst_videomixer2_set_property; + + g_object_class_install_property (gobject_class, PROP_BACKGROUND, + g_param_spec_enum ("background", "Background", "Background type", + GST_TYPE_VIDEO_MIXER2_BACKGROUND, + DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_videomixer2_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_videomixer2_release_pad); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_videomixer2_change_state); + + /* Register the pad class */ + g_type_class_ref (GST_TYPE_VIDEO_MIXER2_PAD); +} + +static void +gst_videomixer2_init (GstVideoMixer2 * mix, GstVideoMixer2Class * g_class) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix); + + mix->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "src"), "src"); + gst_pad_set_getcaps_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer2_src_getcaps)); + gst_pad_set_setcaps_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer2_src_setcaps)); + gst_pad_set_query_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer2_src_query)); + gst_pad_set_event_function (GST_PAD (mix->srcpad), + GST_DEBUG_FUNCPTR (gst_videomixer2_src_event)); + gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad); + + mix->collect = gst_collect_pads2_new (); + mix->background = DEFAULT_BACKGROUND; + + gst_collect_pads2_set_function (mix->collect, + (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_videomixer2_collected), + mix); + gst_collect_pads2_set_event_function (mix->collect, + (GstCollectPads2EventFunction) gst_videomixer2_sink_event, mix); + gst_collect_pads2_set_clip_function (mix->collect, + (GstCollectPads2ClipFunction) gst_videomixer2_sink_clip, mix); + + mix->lock = g_mutex_new (); + /* initialize variables */ + gst_videomixer2_reset (mix); +} + +/* Element registration */ +gboolean +gst_videomixer2_register (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_videomixer2_debug, "videomixer2", 0, + "video mixer 2"); + + return gst_element_register (plugin, "videomixer2", GST_RANK_SECONDARY, + GST_TYPE_VIDEO_MIXER2); +} diff --git a/gst/videomixer/videomixer2.h b/gst/videomixer/videomixer2.h new file mode 100644 index 0000000..3c73f4d --- /dev/null +++ b/gst/videomixer/videomixer2.h @@ -0,0 +1,125 @@ +/* Generic video mixer plugin + * Copyright (C) 2008 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_MIXER2_H__ +#define __GST_VIDEO_MIXER2_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> + +#include "blend.h" +#include <gst/base/gstcollectpads2.h> + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_MIXER2 (gst_videomixer2_get_type()) +#define GST_VIDEO_MIXER2(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER2, GstVideoMixer2)) +#define GST_VIDEO_MIXER2_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER2, GstVideoMixer2Class)) +#define GST_IS_VIDEO_MIXER2(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER2)) +#define GST_IS_VIDEO_MIXER2_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER2)) + +typedef struct _GstVideoMixer2 GstVideoMixer2; +typedef struct _GstVideoMixer2Class GstVideoMixer2Class; + +/** + * GstVideoMixer2Background: + * @VIDEO_MIXER2_BACKGROUND_CHECKER: checker pattern background + * @VIDEO_MIXER2_BACKGROUND_BLACK: solid color black background + * @VIDEO_MIXER2_BACKGROUND_WHITE: solid color white background + * @VIDEO_MIXER2_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 videomixer can blend over. + */ +typedef enum +{ + VIDEO_MIXER2_BACKGROUND_CHECKER, + VIDEO_MIXER2_BACKGROUND_BLACK, + VIDEO_MIXER2_BACKGROUND_WHITE, + VIDEO_MIXER2_BACKGROUND_TRANSPARENT, +} +GstVideoMixer2Background; + +/** + * GstVideoMixer2: + * + * The opaque #GstVideoMixer2 structure. + */ +struct _GstVideoMixer2 +{ + GstElement element; + + /* < private > */ + + /* pad */ + GstPad *srcpad; + + /* Lock to prevent the state to change while blending */ + GMutex *lock; + /* Sink pads using Collect Pads 2*/ + GstCollectPads2 *collect; + + /* sinkpads, a GSList of GstVideoMixer2Pads */ + GSList *sinkpads; + gint numpads; + /* Next available sinkpad index */ + gint next_sinkpad; + + /* Output caps */ + GstVideoFormat format; + gint width, height; + gint fps_n; + gint fps_d; + gint par_n; + gint par_d; + + gboolean newseg_pending; + gboolean flush_stop_pending; + + GstVideoMixer2Background background; + + /* Current downstream segment */ + GstSegment segment; + GstClockTime ts_offset; + guint64 nframes; + + /* QoS stuff */ + gdouble proportion; + GstClockTime earliest_time; + guint64 qos_processed, qos_dropped; + + BlendFunction blend, overlay; + FillCheckerFunction fill_checker; + FillColorFunction fill_color; +}; + +struct _GstVideoMixer2Class +{ + GstElementClass parent_class; +}; + +GType gst_videomixer2_get_type (void); +gboolean gst_videomixer2_register (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_VIDEO_MIXER2_H__ */ diff --git a/gst/videomixer/videomixer2pad.h b/gst/videomixer/videomixer2pad.h new file mode 100644 index 0000000..a2412da --- /dev/null +++ b/gst/videomixer/videomixer2pad.h @@ -0,0 +1,77 @@ +/* Generic video mixer plugin + * Copyright (C) 2008 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_MIXER2_PAD_H__ +#define __GST_VIDEO_MIXER2_PAD_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> + +#include <gst/base/gstcollectpads2.h> + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_MIXER2_PAD (gst_videomixer2_pad_get_type()) +#define GST_VIDEO_MIXER2_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER2_PAD, GstVideoMixer2Pad)) +#define GST_VIDEO_MIXER2_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER_PAD, GstVideoMixer2PadClass)) +#define GST_IS_VIDEO_MIXER2_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER2_PAD)) +#define GST_IS_VIDEO_MIXER2_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER2_PAD)) + +typedef struct _GstVideoMixer2Pad GstVideoMixer2Pad; +typedef struct _GstVideoMixer2PadClass GstVideoMixer2PadClass; +typedef struct _GstVideoMixer2Collect GstVideoMixer2Collect; + +/** + * GstVideoMixer2Pad: + * + * The opaque #GstVideoMixer2Pad structure. + */ +struct _GstVideoMixer2Pad +{ + GstPad parent; + + /* < private > */ + + /* caps */ + gint width, height; + gint fps_n; + gint fps_d; + + /* properties */ + gint xpos, ypos; + guint zorder; + gdouble alpha; + + GstVideoMixer2Collect *mixcol; +}; + +struct _GstVideoMixer2PadClass +{ + GstPadClass parent_class; +}; + +GType gst_videomixer2_pad_get_type (void); + +G_END_DECLS +#endif /* __GST_VIDEO_MIXER2_PAD_H__ */ diff --git a/gst/videomixer/videomixerpad.h b/gst/videomixer/videomixerpad.h new file mode 100644 index 0000000..03835d6 --- /dev/null +++ b/gst/videomixer/videomixerpad.h @@ -0,0 +1,78 @@ +/* Video mixer pad + * Copyright (C) 2008 Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_MIXER_PAD_H__ +#define __GST_VIDEO_MIXER_PAD_H__ + +#include <gst/gst.h> +#include <gst/base/gstcollectpads.h> + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_MIXER_PAD (gst_videomixer_pad_get_type()) +#define GST_VIDEO_MIXER_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER_PAD, GstVideoMixerPad)) +#define GST_VIDEO_MIXER_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER_PAD, GstVideoMixerPadiClass)) +#define GST_IS_VIDEO_MIXER_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER_PAD)) +#define GST_IS_VIDEO_MIXER_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER_PAD)) + +typedef struct _GstVideoMixerPad GstVideoMixerPad; +typedef struct _GstVideoMixerPadClass GstVideoMixerPadClass; +typedef struct _GstVideoMixerCollect GstVideoMixerCollect; + +struct _GstVideoMixerCollect +{ + GstCollectData collect; /* we extend the CollectData */ + + GstBuffer *buffer; /* the queued buffer for this pad */ + + GstVideoMixerPad *mixpad; +}; + +/* all information needed for one video stream */ +struct _GstVideoMixerPad +{ + GstPad parent; /* subclass the pad */ + + gint64 queued; + + guint in_width, in_height; + gint fps_n; + gint fps_d; + gint par_n; + gint par_d; + + gint xpos, ypos; + guint zorder; + gint blend_mode; + gdouble alpha; + + GstVideoMixerCollect *mixcol; +}; + +struct _GstVideoMixerPadClass +{ + GstPadClass parent_class; +}; + +G_END_DECLS +#endif /* __GST_VIDEO_MIXER_PAD_H__ */ |