diff options
Diffstat (limited to 'gst/udp')
-rw-r--r-- | gst/udp/Makefile.am | 48 | ||||
-rw-r--r-- | gst/udp/Makefile.in | 958 | ||||
-rw-r--r-- | gst/udp/README | 7 | ||||
-rw-r--r-- | gst/udp/gstdynudpsink.c | 397 | ||||
-rw-r--r-- | gst/udp/gstdynudpsink.h | 71 | ||||
-rw-r--r-- | gst/udp/gstmultiudpsink.c | 1292 | ||||
-rw-r--r-- | gst/udp/gstmultiudpsink.h | 109 | ||||
-rw-r--r-- | gst/udp/gstudp-marshal.list | 2 | ||||
-rw-r--r-- | gst/udp/gstudp.c | 65 | ||||
-rw-r--r-- | gst/udp/gstudp.h | 39 | ||||
-rw-r--r-- | gst/udp/gstudpnetutils.c | 462 | ||||
-rw-r--r-- | gst/udp/gstudpnetutils.h | 108 | ||||
-rw-r--r-- | gst/udp/gstudpsink.c | 298 | ||||
-rw-r--r-- | gst/udp/gstudpsink.h | 56 | ||||
-rw-r--r-- | gst/udp/gstudpsrc.c | 1071 | ||||
-rw-r--r-- | gst/udp/gstudpsrc.h | 88 |
16 files changed, 5071 insertions, 0 deletions
diff --git a/gst/udp/Makefile.am b/gst/udp/Makefile.am new file mode 100644 index 0000000..71a5971 --- /dev/null +++ b/gst/udp/Makefile.am @@ -0,0 +1,48 @@ +plugin_LTLIBRARIES = libgstudp.la + +# variables used for enum/marshal generation +glib_enum_headers = gstudp.h +glib_enum_define = GST_UDP +glib_gen_prefix = gst_udp +glib_gen_basename = gstudp + +include $(top_srcdir)/common/gst-glib-gen.mak + +built_sources = gstudp-enumtypes.c gstudp-marshal.c +built_headers = gstudp-enumtypes.h gstudp-marshal.h + +BUILT_SOURCES = $(built_sources) $(built_headers) + +libgstudp_la_SOURCES = gstudp.c gstudpsrc.c gstudpsink.c gstmultiudpsink.c gstdynudpsink.c gstudpnetutils.c + +# adding -D_GNU_SOURCE to get non-POSIX extensions like EAI_ADDRFAMILY +# with glibc >= 2.8 when including netdb.h (see glibc sources bug 6452) +libgstudp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) -D_GNU_SOURCE +libgstudp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstnetbuffer-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(WIN32_LIBS) +libgstudp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstudp_la_LIBTOOLFLAGS = --tag=disable-static + +nodist_libgstudp_la_SOURCES = \ + $(built_sources) + +noinst_HEADERS = gstudpsink.h gstudpsrc.h gstudp.h gstmultiudpsink.h gstdynudpsink.h gstudpnetutils.h + +EXTRA_DIST = README gstudp-marshal.list + +CLEANFILES = $(BUILT_SOURCES) + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstudp -:SHARED libgstudp \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstudp_la_SOURCES) \ + $(nodist_libgstudp_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstudp_la_CFLAGS) \ + -:LDFLAGS $(libgstudp_la_LDFLAGS) \ + $(libgstudp_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/udp/Makefile.in b/gst/udp/Makefile.in new file mode 100644 index 0000000..bda3e1c --- /dev/null +++ b/gst/udp/Makefile.in @@ -0,0 +1,958 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ + +# these are the variables your Makefile.am should set +# the example is based on the colorbalance interface + +#glib_enum_headers=$(colorbalance_headers) +#glib_enum_define=GST_COLOR_BALANCE +#glib_gen_prefix=gst_color_balance +#glib_gen_basename=colorbalance + + +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/gst-glib-gen.mak +subdir = gst/udp +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/gst-shout2.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__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstudp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgstudp_la_OBJECTS = libgstudp_la-gstudp.lo \ + libgstudp_la-gstudpsrc.lo libgstudp_la-gstudpsink.lo \ + libgstudp_la-gstmultiudpsink.lo libgstudp_la-gstdynudpsink.lo \ + libgstudp_la-gstudpnetutils.lo +am__objects_1 = libgstudp_la-gstudp-enumtypes.lo \ + libgstudp_la-gstudp-marshal.lo +nodist_libgstudp_la_OBJECTS = $(am__objects_1) +libgstudp_la_OBJECTS = $(am_libgstudp_la_OBJECTS) \ + $(nodist_libgstudp_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libgstudp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstudp_la_CFLAGS) $(CFLAGS) \ + $(libgstudp_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_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +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_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libgstudp_la_SOURCES) $(nodist_libgstudp_la_SOURCES) +DIST_SOURCES = $(libgstudp_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@ +GLIB_CFLAGS = @GLIB_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_CONFIG = @LIBCACA_CONFIG@ +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@ +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_11_CFLAGS = @PULSE_0_9_11_CFLAGS@ +PULSE_0_9_11_LIBS = @PULSE_0_9_11_LIBS@ +PULSE_0_9_12_CFLAGS = @PULSE_0_9_12_CFLAGS@ +PULSE_0_9_12_LIBS = @PULSE_0_9_12_LIBS@ +PULSE_0_9_13_CFLAGS = @PULSE_0_9_13_CFLAGS@ +PULSE_0_9_13_LIBS = @PULSE_0_9_13_LIBS@ +PULSE_0_9_15_CFLAGS = @PULSE_0_9_15_CFLAGS@ +PULSE_0_9_15_LIBS = @PULSE_0_9_15_LIBS@ +PULSE_0_9_16_CFLAGS = @PULSE_0_9_16_CFLAGS@ +PULSE_0_9_16_LIBS = @PULSE_0_9_16_LIBS@ +PULSE_0_9_20_CFLAGS = @PULSE_0_9_20_CFLAGS@ +PULSE_0_9_20_LIBS = @PULSE_0_9_20_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_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@ +lt_ECHO = @lt_ECHO@ +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 = libgstudp.la + +# variables used for enum/marshal generation +glib_enum_headers = gstudp.h +glib_enum_define = GST_UDP +glib_gen_prefix = gst_udp +glib_gen_basename = gstudp +enum_headers = $(foreach h,$(glib_enum_headers),\n\#include \"$(h)\") +built_sources = gstudp-enumtypes.c gstudp-marshal.c +built_headers = gstudp-enumtypes.h gstudp-marshal.h +BUILT_SOURCES = $(built_sources) $(built_headers) +libgstudp_la_SOURCES = gstudp.c gstudpsrc.c gstudpsink.c gstmultiudpsink.c gstdynudpsink.c gstudpnetutils.c + +# adding -D_GNU_SOURCE to get non-POSIX extensions like EAI_ADDRFAMILY +# with glibc >= 2.8 when including netdb.h (see glibc sources bug 6452) +libgstudp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) -D_GNU_SOURCE +libgstudp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstnetbuffer-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(WIN32_LIBS) + +libgstudp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstudp_la_LIBTOOLFLAGS = --tag=disable-static +nodist_libgstudp_la_SOURCES = \ + $(built_sources) + +noinst_HEADERS = gstudpsink.h gstudpsrc.h gstudp.h gstmultiudpsink.h gstdynudpsink.h gstudpnetutils.h +EXTRA_DIST = README gstudp-marshal.list +CLEANFILES = $(BUILT_SOURCES) +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/gst-glib-gen.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/udp/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/udp/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_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 +libgstudp.la: $(libgstudp_la_OBJECTS) $(libgstudp_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstudp_la_LINK) -rpath $(plugindir) $(libgstudp_la_OBJECTS) $(libgstudp_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstdynudpsink.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstmultiudpsink.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudp-enumtypes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudp-marshal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudpnetutils.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudpsink.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstudp_la-gstudpsrc.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 +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(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 +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(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 +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libgstudp_la-gstudp.lo: gstudp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudp.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudp.Tpo -c -o libgstudp_la-gstudp.lo `test -f 'gstudp.c' || echo '$(srcdir)/'`gstudp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudp.Tpo $(DEPDIR)/libgstudp_la-gstudp.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudp.c' object='libgstudp_la-gstudp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudp.lo `test -f 'gstudp.c' || echo '$(srcdir)/'`gstudp.c + +libgstudp_la-gstudpsrc.lo: gstudpsrc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudpsrc.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudpsrc.Tpo -c -o libgstudp_la-gstudpsrc.lo `test -f 'gstudpsrc.c' || echo '$(srcdir)/'`gstudpsrc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudpsrc.Tpo $(DEPDIR)/libgstudp_la-gstudpsrc.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudpsrc.c' object='libgstudp_la-gstudpsrc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudpsrc.lo `test -f 'gstudpsrc.c' || echo '$(srcdir)/'`gstudpsrc.c + +libgstudp_la-gstudpsink.lo: gstudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudpsink.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudpsink.Tpo -c -o libgstudp_la-gstudpsink.lo `test -f 'gstudpsink.c' || echo '$(srcdir)/'`gstudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudpsink.Tpo $(DEPDIR)/libgstudp_la-gstudpsink.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudpsink.c' object='libgstudp_la-gstudpsink.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudpsink.lo `test -f 'gstudpsink.c' || echo '$(srcdir)/'`gstudpsink.c + +libgstudp_la-gstmultiudpsink.lo: gstmultiudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstmultiudpsink.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstmultiudpsink.Tpo -c -o libgstudp_la-gstmultiudpsink.lo `test -f 'gstmultiudpsink.c' || echo '$(srcdir)/'`gstmultiudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstmultiudpsink.Tpo $(DEPDIR)/libgstudp_la-gstmultiudpsink.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmultiudpsink.c' object='libgstudp_la-gstmultiudpsink.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstmultiudpsink.lo `test -f 'gstmultiudpsink.c' || echo '$(srcdir)/'`gstmultiudpsink.c + +libgstudp_la-gstdynudpsink.lo: gstdynudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstdynudpsink.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstdynudpsink.Tpo -c -o libgstudp_la-gstdynudpsink.lo `test -f 'gstdynudpsink.c' || echo '$(srcdir)/'`gstdynudpsink.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstdynudpsink.Tpo $(DEPDIR)/libgstudp_la-gstdynudpsink.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstdynudpsink.c' object='libgstudp_la-gstdynudpsink.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstdynudpsink.lo `test -f 'gstdynudpsink.c' || echo '$(srcdir)/'`gstdynudpsink.c + +libgstudp_la-gstudpnetutils.lo: gstudpnetutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudpnetutils.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudpnetutils.Tpo -c -o libgstudp_la-gstudpnetutils.lo `test -f 'gstudpnetutils.c' || echo '$(srcdir)/'`gstudpnetutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudpnetutils.Tpo $(DEPDIR)/libgstudp_la-gstudpnetutils.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudpnetutils.c' object='libgstudp_la-gstudpnetutils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudpnetutils.lo `test -f 'gstudpnetutils.c' || echo '$(srcdir)/'`gstudpnetutils.c + +libgstudp_la-gstudp-enumtypes.lo: gstudp-enumtypes.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudp-enumtypes.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudp-enumtypes.Tpo -c -o libgstudp_la-gstudp-enumtypes.lo `test -f 'gstudp-enumtypes.c' || echo '$(srcdir)/'`gstudp-enumtypes.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudp-enumtypes.Tpo $(DEPDIR)/libgstudp_la-gstudp-enumtypes.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudp-enumtypes.c' object='libgstudp_la-gstudp-enumtypes.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudp-enumtypes.lo `test -f 'gstudp-enumtypes.c' || echo '$(srcdir)/'`gstudp-enumtypes.c + +libgstudp_la-gstudp-marshal.lo: gstudp-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -MT libgstudp_la-gstudp-marshal.lo -MD -MP -MF $(DEPDIR)/libgstudp_la-gstudp-marshal.Tpo -c -o libgstudp_la-gstudp-marshal.lo `test -f 'gstudp-marshal.c' || echo '$(srcdir)/'`gstudp-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstudp_la-gstudp-marshal.Tpo $(DEPDIR)/libgstudp_la-gstudp-marshal.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstudp-marshal.c' object='libgstudp_la-gstudp-marshal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstudp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstudp_la_CFLAGS) $(CFLAGS) -c -o libgstudp_la-gstudp-marshal.lo `test -f 'gstudp-marshal.c' || echo '$(srcdir)/'`gstudp-marshal.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 +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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-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-pluginLTLIBRARIES ctags 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 + + +# these are all the rules generating the relevant files +$(glib_gen_basename)-marshal.h: $(glib_gen_basename)-marshal.list + $(AM_V_GEN)glib-genmarshal --header --prefix=$(glib_gen_prefix)_marshal $^ > $(glib_gen_basename)-marshal.h.tmp && \ + mv $(glib_gen_basename)-marshal.h.tmp $(glib_gen_basename)-marshal.h + +$(glib_gen_basename)-marshal.c: $(glib_gen_basename)-marshal.list + $(AM_V_GEN)echo "#include \"$(glib_gen_basename)-marshal.h\"" >> $(glib_gen_basename)-marshal.c.tmp && \ + glib-genmarshal --body --prefix=$(glib_gen_prefix)_marshal $^ >> $(glib_gen_basename)-marshal.c.tmp && \ + mv $(glib_gen_basename)-marshal.c.tmp $(glib_gen_basename)-marshal.c + +$(glib_gen_basename)-enumtypes.h: $(glib_enum_headers) + $(AM_V_GEN)glib-mkenums \ + --fhead "#ifndef __$(glib_enum_define)_ENUM_TYPES_H__\n#define __$(glib_enum_define)_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ + --fprod "\n/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define GST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __$(glib_enum_define)_ENUM_TYPES_H__ */" \ + $^ > $@ + +$(glib_gen_basename)-enumtypes.c: $(glib_enum_headers) + @if test "x$(glib_enum_headers)" = "x"; then echo "ERROR: glib_enum_headers is empty, please fix Makefile"; exit 1; fi + $(AM_V_GEN)glib-mkenums \ + --fhead "#include \"$(glib_gen_basename)-enumtypes.h\"\n$(enum_headers)" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static volatile gsize g_define_type_id__volatile = 0;\n if (g_once_init_enter (&g_define_type_id__volatile)) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n GType g_define_type_id = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);\n }\n return g_define_type_id__volatile;\n}\n" \ + $^ > $@ + +# a hack rule to make sure .Plo files exist because they get include'd +# from Makefile's +.deps/%-marshal.Plo: + @touch $@ + +.deps/%-enumtypes.Plo: + @touch $@ + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstudp -:SHARED libgstudp \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstudp_la_SOURCES) \ + $(nodist_libgstudp_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstudp_la_CFLAGS) \ + -:LDFLAGS $(libgstudp_la_LDFLAGS) \ + $(libgstudp_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/udp/README b/gst/udp/README new file mode 100644 index 0000000..9b7cebb --- /dev/null +++ b/gst/udp/README @@ -0,0 +1,7 @@ +* What is UDP src/sink? + +It is a set of element to transfer data using UDP, nothing more, nothing less. +Its main purpose is to be used in conjunction with RTP but they are kept as +separate elements because we can. + + diff --git a/gst/udp/gstdynudpsink.c b/gst/udp/gstdynudpsink.c new file mode 100644 index 0000000..d7b5678 --- /dev/null +++ b/gst/udp/gstdynudpsink.c @@ -0,0 +1,397 @@ +/* GStreamer + * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org> + * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com> + * Copyright (C) <2006> Joni Valtanen <joni.valtanen@movial.fi> + * + * 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 "gstudp-marshal.h" +#include "gstdynudpsink.h" + +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/types.h> +#include <gst/netbuffer/gstnetbuffer.h> + +GST_DEBUG_CATEGORY_STATIC (dynudpsink_debug); +#define GST_CAT_DEFAULT (dynudpsink_debug) + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +/* DynUDPSink signals and args */ +enum +{ + /* methods */ + SIGNAL_GET_STATS, + + /* signals */ + + /* FILL ME */ + LAST_SIGNAL +}; + +#define UDP_DEFAULT_SOCKFD -1 +#define UDP_DEFAULT_CLOSEFD TRUE + +enum +{ + PROP_0, + PROP_SOCKFD, + PROP_CLOSEFD +}; + +#define CLOSE_IF_REQUESTED(udpctx) \ +G_STMT_START { \ + if ((!udpctx->externalfd) || (udpctx->externalfd && udpctx->closefd)) { \ + CLOSE_SOCKET(udpctx->sock); \ + if (udpctx->sock == udpctx->sockfd) \ + udpctx->sockfd = UDP_DEFAULT_SOCKFD; \ + } \ + udpctx->sock = -1; \ +} G_STMT_END + +static void gst_dynudpsink_base_init (gpointer g_class); +static void gst_dynudpsink_class_init (GstDynUDPSink * klass); +static void gst_dynudpsink_init (GstDynUDPSink * udpsink); +static void gst_dynudpsink_finalize (GObject * object); + +static GstFlowReturn gst_dynudpsink_render (GstBaseSink * sink, + GstBuffer * buffer); +static void gst_dynudpsink_close (GstDynUDPSink * sink); +static GstStateChangeReturn gst_dynudpsink_change_state (GstElement * element, + GstStateChange transition); + +static void gst_dynudpsink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_dynudpsink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstElementClass *parent_class = NULL; + +static guint gst_dynudpsink_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_dynudpsink_get_type (void) +{ + static GType dynudpsink_type = 0; + + if (!dynudpsink_type) { + static const GTypeInfo dynudpsink_info = { + sizeof (GstDynUDPSinkClass), + gst_dynudpsink_base_init, + NULL, + (GClassInitFunc) gst_dynudpsink_class_init, + NULL, + NULL, + sizeof (GstDynUDPSink), + 0, + (GInstanceInitFunc) gst_dynudpsink_init, + NULL + }; + + dynudpsink_type = + g_type_register_static (GST_TYPE_BASE_SINK, "GstDynUDPSink", + &dynudpsink_info, 0); + } + return dynudpsink_type; +} + +static void +gst_dynudpsink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_details_simple (element_class, "UDP packet sender", + "Sink/Network", + "Send data over the network via UDP", + "Philippe Khalaf <burger@speedy.org>"); +} + +static void +gst_dynudpsink_class_init (GstDynUDPSink * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_dynudpsink_set_property; + gobject_class->get_property = gst_dynudpsink_get_property; + gobject_class->finalize = gst_dynudpsink_finalize; + + gst_dynudpsink_signals[SIGNAL_GET_STATS] = + g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstDynUDPSinkClass, get_stats), + NULL, NULL, gst_udp_marshal_BOXED__STRING_INT, G_TYPE_VALUE_ARRAY, 2, + G_TYPE_STRING, G_TYPE_INT); + + g_object_class_install_property (gobject_class, PROP_SOCKFD, + g_param_spec_int ("sockfd", "socket handle", + "Socket to use for UDP sending. (-1 == allocate)", + -1, G_MAXINT16, UDP_DEFAULT_SOCKFD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLOSEFD, + g_param_spec_boolean ("closefd", "Close sockfd", + "Close sockfd if passed as property on state change", + UDP_DEFAULT_CLOSEFD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstelement_class->change_state = gst_dynudpsink_change_state; + + gstbasesink_class->render = gst_dynudpsink_render; + + GST_DEBUG_CATEGORY_INIT (dynudpsink_debug, "dynudpsink", 0, "UDP sink"); +} + +static void +gst_dynudpsink_init (GstDynUDPSink * sink) +{ + WSA_STARTUP (sink); + + sink->sockfd = UDP_DEFAULT_SOCKFD; + sink->closefd = UDP_DEFAULT_CLOSEFD; + sink->externalfd = FALSE; + + sink->sock = -1; +} + +static void +gst_dynudpsink_finalize (GObject * object) +{ + GstDynUDPSink *udpsink; + + udpsink = GST_DYNUDPSINK (object); + + if (udpsink->sockfd >= 0 && udpsink->closefd) + CLOSE_SOCKET (udpsink->sockfd); + + G_OBJECT_CLASS (parent_class)->finalize (object); + + WSA_CLEANUP (object); +} + +static GstFlowReturn +gst_dynudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) +{ + GstDynUDPSink *sink; + gint ret, size; + guint8 *data; + GstNetBuffer *netbuf; + struct sockaddr_in theiraddr; + guint16 destport; + guint32 destaddr; + + memset (&theiraddr, 0, sizeof (theiraddr)); + + if (GST_IS_NETBUFFER (buffer)) { + netbuf = GST_NETBUFFER (buffer); + } else { + GST_DEBUG ("Received buffer is not a GstNetBuffer, skipping"); + return GST_FLOW_OK; + } + + sink = GST_DYNUDPSINK (bsink); + + size = GST_BUFFER_SIZE (netbuf); + data = GST_BUFFER_DATA (netbuf); + + GST_DEBUG ("about to send %d bytes", size); + + // let's get the address from the netbuffer + gst_netaddress_get_ip4_address (&netbuf->to, &destaddr, &destport); + + GST_DEBUG ("sending %d bytes to client %d port %d", size, destaddr, destport); + + theiraddr.sin_family = AF_INET; + theiraddr.sin_addr.s_addr = destaddr; + theiraddr.sin_port = destport; +#ifdef G_OS_WIN32 + ret = sendto (sink->sock, (char *) data, size, 0, +#else + ret = sendto (sink->sock, data, size, 0, +#endif + (struct sockaddr *) &theiraddr, sizeof (theiraddr)); + + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + goto send_error; + } + } + + GST_DEBUG ("sent %d bytes", size); + + return GST_FLOW_OK; + +send_error: + { + GST_DEBUG ("got send error %s (%d)", g_strerror (errno), errno); + return GST_FLOW_ERROR; + } +} + +static void +gst_dynudpsink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstDynUDPSink *udpsink; + + udpsink = GST_DYNUDPSINK (object); + + switch (prop_id) { + case PROP_SOCKFD: + if (udpsink->sockfd >= 0 && udpsink->sockfd != udpsink->sock && + udpsink->closefd) + CLOSE_SOCKET (udpsink->sockfd); + udpsink->sockfd = g_value_get_int (value); + GST_DEBUG ("setting SOCKFD to %d", udpsink->sockfd); + break; + case PROP_CLOSEFD: + udpsink->closefd = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_dynudpsink_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstDynUDPSink *udpsink; + + udpsink = GST_DYNUDPSINK (object); + + switch (prop_id) { + case PROP_SOCKFD: + g_value_set_int (value, udpsink->sockfd); + break; + case PROP_CLOSEFD: + g_value_set_boolean (value, udpsink->closefd); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/* create a socket for sending to remote machine */ +static gboolean +gst_dynudpsink_init_send (GstDynUDPSink * sink) +{ + guint bc_val; + + if (sink->sockfd == -1) { + /* create sender socket if none available */ + if ((sink->sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + goto no_socket; + + bc_val = 1; + if (setsockopt (sink->sock, SOL_SOCKET, SO_BROADCAST, &bc_val, + sizeof (bc_val)) < 0) + goto no_broadcast; + + sink->externalfd = TRUE; + } else { + sink->sock = sink->sockfd; + sink->externalfd = TRUE; + } + return TRUE; + + /* ERRORS */ +no_socket: + { + perror ("socket"); + return FALSE; + } +no_broadcast: + { + perror ("setsockopt"); + CLOSE_IF_REQUESTED (sink); + return FALSE; + } +} + +GValueArray * +gst_dynudpsink_get_stats (GstDynUDPSink * sink, const gchar * host, gint port) +{ + return NULL; +} + +static void +gst_dynudpsink_close (GstDynUDPSink * sink) +{ + CLOSE_IF_REQUESTED (sink); +} + +static GstStateChangeReturn +gst_dynudpsink_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstDynUDPSink *sink; + + sink = GST_DYNUDPSINK (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!gst_dynudpsink_init_send (sink)) + goto no_init; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_dynudpsink_close (sink); + break; + default: + break; + } + return ret; + + /* ERRORS */ +no_init: + { + return GST_STATE_CHANGE_FAILURE; + } +} diff --git a/gst/udp/gstdynudpsink.h b/gst/udp/gstdynudpsink.h new file mode 100644 index 0000000..d46dfdf --- /dev/null +++ b/gst/udp/gstdynudpsink.h @@ -0,0 +1,71 @@ +/* GStreamer + * Copyright (C) <2005> Philippe Khalaf <burger@speedy.org> + * + * 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_DYNUDPSINK_H__ +#define __GST_DYNUDPSINK_H__ + +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> + +G_BEGIN_DECLS + +#include "gstudpnetutils.h" + +#include "gstudp.h" + +#define GST_TYPE_DYNUDPSINK (gst_dynudpsink_get_type()) +#define GST_DYNUDPSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DYNUDPSINK,GstDynUDPSink)) +#define GST_DYNUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DYNUDPSINK,GstDynUDPSinkClass)) +#define GST_IS_DYNUDPSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DYNUDPSINK)) +#define GST_IS_DYNUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DYNUDPSINK)) + +typedef struct _GstDynUDPSink GstDynUDPSink; +typedef struct _GstDynUDPSinkClass GstDynUDPSinkClass; + + +/* sends udp packets to host/port pairs contained in the GstNetBuffer received. + */ +struct _GstDynUDPSink { + GstBaseSink parent; + + /* properties */ + gint sockfd; + gboolean closefd; + + /* the socket in use */ + int sock; + gboolean externalfd; +}; + +struct _GstDynUDPSinkClass { + GstBaseSinkClass parent_class; + + /* element methods */ + GValueArray* (*get_stats) (GstDynUDPSink *sink, const gchar *host, gint port); + + /* signals */ +}; + +GType gst_dynudpsink_get_type(void); + +GValueArray* gst_dynudpsink_get_stats (GstDynUDPSink *sink, const gchar *host, gint port); + +G_END_DECLS + +#endif /* __GST_DYNUDPSINK_H__ */ diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c new file mode 100644 index 0000000..bcdb6cd --- /dev/null +++ b/gst/udp/gstmultiudpsink.c @@ -0,0 +1,1292 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * Copyright (C) <2009> Jarkko Palviainen <jarkko.palviainen@sesca.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-multiudpsink + * @see_also: udpsink, multifdsink + * + * multiudpsink is a network sink that sends UDP packets to multiple + * clients. + * It can be combined with rtp payload encoders to implement RTP streaming. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstudp-marshal.h" +#include "gstmultiudpsink.h" + +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> + +GST_DEBUG_CATEGORY_STATIC (multiudpsink_debug); +#define GST_CAT_DEFAULT (multiudpsink_debug) + +#define UDP_MAX_SIZE 65507 + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +/* MultiUDPSink signals and args */ +enum +{ + /* methods */ + SIGNAL_ADD, + SIGNAL_REMOVE, + SIGNAL_CLEAR, + SIGNAL_GET_STATS, + + /* signals */ + SIGNAL_CLIENT_ADDED, + SIGNAL_CLIENT_REMOVED, + + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_SOCKFD -1 +#define DEFAULT_CLOSEFD TRUE +#define DEFAULT_SOCK -1 +#define DEFAULT_CLIENTS NULL +#define DEFAULT_FAMILY 0 +/* FIXME, this should be disabled by default, we don't need to join a multicast + * group for sending, if this socket is also used for receiving, it should + * be configured in the element that does the receive. */ +#define DEFAULT_AUTO_MULTICAST TRUE +#define DEFAULT_TTL 64 +#define DEFAULT_TTL_MC 1 +#define DEFAULT_LOOP TRUE +#define DEFAULT_QOS_DSCP -1 +#define DEFAULT_SEND_DUPLICATES TRUE +#define DEFAULT_BUFFER_SIZE 0 + +enum +{ + PROP_0, + PROP_BYTES_TO_SERVE, + PROP_BYTES_SERVED, + PROP_SOCKFD, + PROP_CLOSEFD, + PROP_SOCK, + PROP_CLIENTS, + PROP_AUTO_MULTICAST, + PROP_TTL, + PROP_TTL_MC, + PROP_LOOP, + PROP_QOS_DSCP, + PROP_SEND_DUPLICATES, + PROP_BUFFER_SIZE, + PROP_LAST +}; + +#define CLOSE_IF_REQUESTED(udpctx) \ +G_STMT_START { \ + if ((!udpctx->externalfd) || (udpctx->externalfd && udpctx->closefd)) { \ + CLOSE_SOCKET(udpctx->sock); \ + if (udpctx->sock == udpctx->sockfd) \ + udpctx->sockfd = DEFAULT_SOCKFD; \ + } \ + udpctx->sock = DEFAULT_SOCK; \ +} G_STMT_END + +static void gst_multiudpsink_base_init (gpointer g_class); +static void gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass); +static void gst_multiudpsink_init (GstMultiUDPSink * udpsink); +static void gst_multiudpsink_finalize (GObject * object); + +static GstFlowReturn gst_multiudpsink_render (GstBaseSink * sink, + GstBuffer * buffer); +#ifndef G_OS_WIN32 /* sendmsg() is not available on Windows */ +static GstFlowReturn gst_multiudpsink_render_list (GstBaseSink * bsink, + GstBufferList * list); +#endif +static GstStateChangeReturn gst_multiudpsink_change_state (GstElement * + element, GstStateChange transition); + +static void gst_multiudpsink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_multiudpsink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_multiudpsink_add_internal (GstMultiUDPSink * sink, + const gchar * host, gint port, gboolean lock); +static void gst_multiudpsink_clear_internal (GstMultiUDPSink * sink, + gboolean lock); + +static GstElementClass *parent_class = NULL; + +static guint gst_multiudpsink_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_multiudpsink_get_type (void) +{ + static GType multiudpsink_type = 0; + + if (!multiudpsink_type) { + static const GTypeInfo multiudpsink_info = { + sizeof (GstMultiUDPSinkClass), + gst_multiudpsink_base_init, + NULL, + (GClassInitFunc) gst_multiudpsink_class_init, + NULL, + NULL, + sizeof (GstMultiUDPSink), + 0, + (GInstanceInitFunc) gst_multiudpsink_init, + NULL + }; + + multiudpsink_type = + g_type_register_static (GST_TYPE_BASE_SINK, "GstMultiUDPSink", + &multiudpsink_info, 0); + } + return multiudpsink_type; +} + +static void +gst_multiudpsink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_details_simple (element_class, "UDP packet sender", + "Sink/Network", + "Send data over the network via UDP", + "Wim Taymans <wim.taymans@gmail.com>"); +} + +static void +gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_multiudpsink_set_property; + gobject_class->get_property = gst_multiudpsink_get_property; + gobject_class->finalize = gst_multiudpsink_finalize; + + /** + * GstMultiUDPSink::add: + * @gstmultiudpsink: the sink on which the signal is emitted + * @host: the hostname/IP address of the client to add + * @port: the port of the client to add + * + * Add a client with destination @host and @port to the list of + * clients. When the same host/port pair is added multiple times, the + * send-duplicates property defines if the packets are sent multiple times to + * the same host/port pair or not. + * + * When a host/port pair is added multiple times, an equal amount of remove + * calls must be performed to actually remove the host/port pair from the list + * of destinations. + */ + gst_multiudpsink_signals[SIGNAL_ADD] = + g_signal_new ("add", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstMultiUDPSinkClass, add), + NULL, NULL, gst_udp_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, + G_TYPE_STRING, G_TYPE_INT); + /** + * GstMultiUDPSink::remove: + * @gstmultiudpsink: the sink on which the signal is emitted + * @host: the hostname/IP address of the client to remove + * @port: the port of the client to remove + * + * Remove the client with destination @host and @port from the list of + * clients. + */ + gst_multiudpsink_signals[SIGNAL_REMOVE] = + g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstMultiUDPSinkClass, remove), + NULL, NULL, gst_udp_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, + G_TYPE_STRING, G_TYPE_INT); + /** + * GstMultiUDPSink::clear: + * @gstmultiudpsink: the sink on which the signal is emitted + * + * Clear the list of clients. + */ + gst_multiudpsink_signals[SIGNAL_CLEAR] = + g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstMultiUDPSinkClass, clear), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * GstMultiUDPSink::get-stats: + * @gstmultiudpsink: the sink on which the signal is emitted + * @host: the hostname/IP address of the client to get stats on + * @port: the port of the client to get stats on + * + * Get the statistics of the client with destination @host and @port. + * + * Returns: a GValueArray of uint64: bytes_sent, packets_sent, + * connect_time (in epoch seconds), disconnect_time (in epoch seconds) + */ + gst_multiudpsink_signals[SIGNAL_GET_STATS] = + g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstMultiUDPSinkClass, get_stats), + NULL, NULL, gst_udp_marshal_BOXED__STRING_INT, G_TYPE_VALUE_ARRAY, 2, + G_TYPE_STRING, G_TYPE_INT); + /** + * GstMultiUDPSink::client-added: + * @gstmultiudpsink: the sink emitting the signal + * @host: the hostname/IP address of the added client + * @port: the port of the added client + * + * Signal emited when a new client is added to the list of + * clients. + */ + gst_multiudpsink_signals[SIGNAL_CLIENT_ADDED] = + g_signal_new ("client-added", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMultiUDPSinkClass, client_added), + NULL, NULL, gst_udp_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, + G_TYPE_STRING, G_TYPE_INT); + /** + * GstMultiUDPSink::client-removed: + * @gstmultiudpsink: the sink emitting the signal + * @host: the hostname/IP address of the removed client + * @port: the port of the removed client + * + * Signal emited when a client is removed from the list of + * clients. + */ + gst_multiudpsink_signals[SIGNAL_CLIENT_REMOVED] = + g_signal_new ("client-removed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMultiUDPSinkClass, + client_removed), NULL, NULL, gst_udp_marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BYTES_TO_SERVE, + g_param_spec_uint64 ("bytes-to-serve", "Bytes to serve", + "Number of bytes received to serve to clients", 0, G_MAXUINT64, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BYTES_SERVED, + g_param_spec_uint64 ("bytes-served", "Bytes served", + "Total number of bytes send to all clients", 0, G_MAXUINT64, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SOCKFD, + g_param_spec_int ("sockfd", "Socket Handle", + "Socket to use for UDP sending. (-1 == allocate)", + -1, G_MAXINT, DEFAULT_SOCKFD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLOSEFD, + g_param_spec_boolean ("closefd", "Close sockfd", + "Close sockfd if passed as property on state change", + DEFAULT_CLOSEFD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SOCK, + g_param_spec_int ("sock", "Socket Handle", + "Socket currently in use for UDP sending. (-1 == no socket)", + -1, G_MAXINT, DEFAULT_SOCK, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLIENTS, + g_param_spec_string ("clients", "Clients", + "A comma separated list of host:port pairs with destinations", + DEFAULT_CLIENTS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AUTO_MULTICAST, + g_param_spec_boolean ("auto-multicast", + "Automatically join/leave multicast groups", + "Automatically join/leave the multicast groups, FALSE means user" + " has to do it himself", DEFAULT_AUTO_MULTICAST, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TTL, + g_param_spec_int ("ttl", "Unicast TTL", + "Used for setting the unicast TTL parameter", + 0, 255, DEFAULT_TTL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TTL_MC, + g_param_spec_int ("ttl-mc", "Multicast TTL", + "Used for setting the multicast TTL parameter", + 0, 255, DEFAULT_TTL_MC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_LOOP, + g_param_spec_boolean ("loop", "Multicast Loopback", + "Used for setting the multicast loop parameter. TRUE = enable," + " FALSE = disable", DEFAULT_LOOP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QOS_DSCP, + g_param_spec_int ("qos-dscp", "QoS diff srv code point", + "Quality of Service, differentiated services code point (-1 default)", + -1, 63, DEFAULT_QOS_DSCP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstMultiUDPSink::send-duplicates + * + * When a host/port pair is added mutliple times, send the packet to the host + * multiple times as well. + * + * Since: 0.10.26 + */ + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEND_DUPLICATES, + g_param_spec_boolean ("send-duplicates", "Send Duplicates", + "When a distination/port pair is added multiple times, send packets " + "multiple times as well", DEFAULT_SEND_DUPLICATES, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_SIZE, + g_param_spec_int ("buffer-size", "Buffer Size", + "Size of the kernel send buffer in bytes, 0=default", 0, G_MAXINT, + DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstelement_class->change_state = gst_multiudpsink_change_state; + + gstbasesink_class->render = gst_multiudpsink_render; +#ifndef G_OS_WIN32 + gstbasesink_class->render_list = gst_multiudpsink_render_list; +#endif + klass->add = gst_multiudpsink_add; + klass->remove = gst_multiudpsink_remove; + klass->clear = gst_multiudpsink_clear; + klass->get_stats = gst_multiudpsink_get_stats; + + GST_DEBUG_CATEGORY_INIT (multiudpsink_debug, "multiudpsink", 0, "UDP sink"); +} + + +static void +gst_multiudpsink_init (GstMultiUDPSink * sink) +{ + WSA_STARTUP (sink); + + sink->client_lock = g_mutex_new (); + sink->sock = DEFAULT_SOCK; + sink->sockfd = DEFAULT_SOCKFD; + sink->closefd = DEFAULT_CLOSEFD; + sink->externalfd = (sink->sockfd != -1); + sink->auto_multicast = DEFAULT_AUTO_MULTICAST; + sink->ttl = DEFAULT_TTL; + sink->ttl_mc = DEFAULT_TTL_MC; + sink->loop = DEFAULT_LOOP; + sink->qos_dscp = DEFAULT_QOS_DSCP; + sink->ss_family = DEFAULT_FAMILY; + sink->send_duplicates = DEFAULT_SEND_DUPLICATES; +} + +static GstUDPClient * +create_client (GstMultiUDPSink * sink, const gchar * host, gint port) +{ + GstUDPClient *client; + + client = g_slice_new0 (GstUDPClient); + client->refcount = 1; + client->host = g_strdup (host); + client->port = port; + + return client; +} + +static void +free_client (GstUDPClient * client) +{ + g_free (client->host); + g_slice_free (GstUDPClient, client); +} + +static gint +client_compare (GstUDPClient * a, GstUDPClient * b) +{ + if ((a->port == b->port) && (strcmp (a->host, b->host) == 0)) + return 0; + + return 1; +} + +static void +gst_multiudpsink_finalize (GObject * object) +{ + GstMultiUDPSink *sink; + + sink = GST_MULTIUDPSINK (object); + + g_list_foreach (sink->clients, (GFunc) free_client, NULL); + g_list_free (sink->clients); + + if (sink->sockfd >= 0 && sink->closefd) + CLOSE_SOCKET (sink->sockfd); + + g_mutex_free (sink->client_lock); + + WSA_CLEANUP (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +socket_error_is_ignorable (void) +{ +#ifdef G_OS_WIN32 + /* Windows doesn't seem to have an EAGAIN for sockets */ + return WSAGetLastError () == WSAEINTR; +#else + return errno == EINTR || errno == EAGAIN; +#endif +} + +static int +socket_last_error_code (void) +{ +#ifdef G_OS_WIN32 + return WSAGetLastError (); +#else + return errno; +#endif +} + +static gchar * +socket_last_error_message (void) +{ +#ifdef G_OS_WIN32 + int errorcode = WSAGetLastError (); + wchar_t buf[1024]; + DWORD result = + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorcode, 0, (LPSTR) buf, sizeof (buf) / sizeof (wchar_t), NULL); + if (FAILED (result)) { + return g_strdup ("failed to get error message from system"); + } else { + gchar *res = + g_convert ((gchar *) buf, -1, "UTF-16", "UTF-8", NULL, NULL, NULL); + /* g_convert() internally calls windows functions which reset the + windows error code, so fix it up again like this */ + WSASetLastError (errorcode); + return res; + } +#else + return g_strdup (g_strerror (errno)); +#endif +} + +static GstFlowReturn +gst_multiudpsink_render (GstBaseSink * bsink, GstBuffer * buffer) +{ + GstMultiUDPSink *sink; + gint ret, size, num = 0, no_clients = 0; + guint8 *data; + GList *clients; + gint len; + + sink = GST_MULTIUDPSINK (bsink); + + size = GST_BUFFER_SIZE (buffer); + data = GST_BUFFER_DATA (buffer); + + if (size > UDP_MAX_SIZE) { + GST_WARNING ("Attempting to send a UDP packet larger than maximum " + "size (%d > %d)", size, UDP_MAX_SIZE); + } + + sink->bytes_to_serve += size; + + /* grab lock while iterating and sending to clients, this should be + * fast as UDP never blocks */ + g_mutex_lock (sink->client_lock); + GST_LOG_OBJECT (bsink, "about to send %d bytes", size); + + for (clients = sink->clients; clients; clients = g_list_next (clients)) { + GstUDPClient *client; + gint count; + + client = (GstUDPClient *) clients->data; + no_clients++; + GST_LOG_OBJECT (sink, "sending %d bytes to client %p", size, client); + + count = sink->send_duplicates ? client->refcount : 1; + + while (count--) { + while (TRUE) { + len = gst_udp_get_sockaddr_length (&client->theiraddr); + + ret = sendto (*client->sock, +#ifdef G_OS_WIN32 + (char *) data, +#else + data, +#endif + size, 0, (struct sockaddr *) &client->theiraddr, len); + + if (ret < 0) { + /* some error, just warn, it's likely recoverable and we don't want to + * break streaming. We break so that we stop retrying for this client. */ + if (!socket_error_is_ignorable ()) { + gchar *errormessage = socket_last_error_message (); + GST_WARNING_OBJECT (sink, "client %p gave error %d (%s)", client, + socket_last_error_code (), errormessage); + g_free (errormessage); + break; + } + } else { + num++; + client->bytes_sent += ret; + client->packets_sent++; + sink->bytes_served += ret; + break; + } + } + } + } + g_mutex_unlock (sink->client_lock); + + GST_LOG_OBJECT (sink, "sent %d bytes to %d (of %d) clients", size, num, + no_clients); + + return GST_FLOW_OK; +} + +#ifndef G_OS_WIN32 +static GstFlowReturn +gst_multiudpsink_render_list (GstBaseSink * bsink, GstBufferList * list) +{ + GstMultiUDPSink *sink; + GList *clients; + gint ret, size = 0, num = 0, no_clients = 0; + struct iovec *iov; + struct msghdr msg = { 0 }; + + GstBufferListIterator *it; + guint gsize; + GstBuffer *buf; + + sink = GST_MULTIUDPSINK (bsink); + + g_return_val_if_fail (list != NULL, GST_FLOW_ERROR); + + it = gst_buffer_list_iterate (list); + g_return_val_if_fail (it != NULL, GST_FLOW_ERROR); + + while (gst_buffer_list_iterator_next_group (it)) { + msg.msg_iovlen = 0; + size = 0; + + if ((gsize = gst_buffer_list_iterator_n_buffers (it)) == 0) { + goto invalid_list; + } + + iov = (struct iovec *) g_malloc (gsize * sizeof (struct iovec)); + msg.msg_iov = iov; + + while ((buf = gst_buffer_list_iterator_next (it))) { + if (GST_BUFFER_SIZE (buf) > UDP_MAX_SIZE) { + GST_WARNING ("Attempting to send a UDP packet larger than maximum " + "size (%d > %d)", GST_BUFFER_SIZE (buf), UDP_MAX_SIZE); + } + + msg.msg_iov[msg.msg_iovlen].iov_len = GST_BUFFER_SIZE (buf); + msg.msg_iov[msg.msg_iovlen].iov_base = GST_BUFFER_DATA (buf); + msg.msg_iovlen++; + size += GST_BUFFER_SIZE (buf); + } + + sink->bytes_to_serve += size; + + /* grab lock while iterating and sending to clients, this should be + * fast as UDP never blocks */ + g_mutex_lock (sink->client_lock); + GST_LOG_OBJECT (bsink, "about to send %d bytes", size); + + for (clients = sink->clients; clients; clients = g_list_next (clients)) { + GstUDPClient *client; + gint count; + + client = (GstUDPClient *) clients->data; + no_clients++; + GST_LOG_OBJECT (sink, "sending %d bytes to client %p", size, client); + + count = sink->send_duplicates ? client->refcount : 1; + + while (count--) { + while (TRUE) { + msg.msg_name = (void *) &client->theiraddr; + msg.msg_namelen = sizeof (client->theiraddr); + ret = sendmsg (*client->sock, &msg, 0); + + if (ret < 0) { + if (!socket_error_is_ignorable ()) { + break; + } + } else { + num++; + client->bytes_sent += ret; + client->packets_sent++; + sink->bytes_served += ret; + break; + } + } + } + } + g_mutex_unlock (sink->client_lock); + + g_free (iov); + msg.msg_iov = NULL; + + GST_LOG_OBJECT (sink, "sent %d bytes to %d (of %d) clients", size, num, + no_clients); + } + + gst_buffer_list_iterator_free (it); + + return GST_FLOW_OK; + +invalid_list: + gst_buffer_list_iterator_free (it); + return GST_FLOW_ERROR; +} +#endif + +static void +gst_multiudpsink_set_clients_string (GstMultiUDPSink * sink, + const gchar * string) +{ + gchar **clients; + gint i; + + clients = g_strsplit (string, ",", 0); + + g_mutex_lock (sink->client_lock); + /* clear all existing clients */ + gst_multiudpsink_clear_internal (sink, FALSE); + for (i = 0; clients[i]; i++) { + gchar *host, *p; + gint port = 0; + + host = clients[i]; + p = strstr (clients[i], ":"); + if (p != NULL) { + *p = '\0'; + port = atoi (p + 1); + } + if (port != 0) + gst_multiudpsink_add_internal (sink, host, port, FALSE); + } + g_mutex_unlock (sink->client_lock); + + g_strfreev (clients); +} + +static gchar * +gst_multiudpsink_get_clients_string (GstMultiUDPSink * sink) +{ + GString *str; + GList *clients; + + str = g_string_new (""); + + g_mutex_lock (sink->client_lock); + clients = sink->clients; + while (clients) { + GstUDPClient *client; + gint count; + + client = (GstUDPClient *) clients->data; + + clients = g_list_next (clients); + + count = client->refcount; + while (count--) { + g_string_append_printf (str, "%s:%d%s", client->host, client->port, + (clients || count > 1 ? "," : "")); + } + } + g_mutex_unlock (sink->client_lock); + + return g_string_free (str, FALSE); +} + +static void +gst_multiudpsink_setup_qos_dscp (GstMultiUDPSink * sink) +{ + gint tos; + + /* don't touch on -1 */ + if (sink->qos_dscp < 0) + return; + + if (sink->sock < 0) + return; + + GST_DEBUG_OBJECT (sink, "setting TOS to %d", sink->qos_dscp); + + /* Extract and shift 6 bits of DSFIELD */ + tos = (sink->qos_dscp & 0x3f) << 2; + + if (setsockopt (sink->sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0) { + gchar *errormessage = socket_last_error_message (); + GST_ERROR_OBJECT (sink, "could not set TOS: %s", errormessage); + g_free (errormessage); + } +#ifdef IPV6_TCLASS + if (setsockopt (sink->sock, IPPROTO_IPV6, IPV6_TCLASS, &tos, + sizeof (tos)) < 0) { + gchar *errormessage = socket_last_error_message (); + GST_ERROR_OBJECT (sink, "could not set TCLASS: %s", errormessage); + g_free (errormessage); + } +#endif +} + +static void +gst_multiudpsink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMultiUDPSink *udpsink; + + udpsink = GST_MULTIUDPSINK (object); + + switch (prop_id) { + case PROP_SOCKFD: + if (udpsink->sockfd >= 0 && udpsink->sockfd != udpsink->sock && + udpsink->closefd) + CLOSE_SOCKET (udpsink->sockfd); + udpsink->sockfd = g_value_get_int (value); + GST_DEBUG_OBJECT (udpsink, "setting SOCKFD to %d", udpsink->sockfd); + break; + case PROP_CLOSEFD: + udpsink->closefd = g_value_get_boolean (value); + break; + case PROP_CLIENTS: + gst_multiudpsink_set_clients_string (udpsink, g_value_get_string (value)); + break; + case PROP_AUTO_MULTICAST: + udpsink->auto_multicast = g_value_get_boolean (value); + break; + case PROP_TTL: + udpsink->ttl = g_value_get_int (value); + break; + case PROP_TTL_MC: + udpsink->ttl_mc = g_value_get_int (value); + break; + case PROP_LOOP: + udpsink->loop = g_value_get_boolean (value); + break; + case PROP_QOS_DSCP: + udpsink->qos_dscp = g_value_get_int (value); + gst_multiudpsink_setup_qos_dscp (udpsink); + break; + case PROP_SEND_DUPLICATES: + udpsink->send_duplicates = g_value_get_boolean (value); + break; + case PROP_BUFFER_SIZE: + udpsink->buffer_size = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstMultiUDPSink *udpsink; + + udpsink = GST_MULTIUDPSINK (object); + + switch (prop_id) { + case PROP_BYTES_TO_SERVE: + g_value_set_uint64 (value, udpsink->bytes_to_serve); + break; + case PROP_BYTES_SERVED: + g_value_set_uint64 (value, udpsink->bytes_served); + break; + case PROP_SOCKFD: + g_value_set_int (value, udpsink->sockfd); + break; + case PROP_CLOSEFD: + g_value_set_boolean (value, udpsink->closefd); + break; + case PROP_SOCK: + g_value_set_int (value, udpsink->sock); + break; + case PROP_CLIENTS: + g_value_take_string (value, + gst_multiudpsink_get_clients_string (udpsink)); + break; + case PROP_AUTO_MULTICAST: + g_value_set_boolean (value, udpsink->auto_multicast); + break; + case PROP_TTL: + g_value_set_int (value, udpsink->ttl); + break; + case PROP_TTL_MC: + g_value_set_int (value, udpsink->ttl_mc); + break; + case PROP_LOOP: + g_value_set_boolean (value, udpsink->loop); + break; + case PROP_QOS_DSCP: + g_value_set_int (value, udpsink->qos_dscp); + break; + case PROP_SEND_DUPLICATES: + g_value_set_boolean (value, udpsink->send_duplicates); + break; + case PROP_BUFFER_SIZE: + g_value_set_int (value, udpsink->buffer_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_multiudpsink_configure_client (GstMultiUDPSink * sink, + GstUDPClient * client) +{ + GST_DEBUG_OBJECT (sink, "configuring client %p", client); + + if (gst_udp_is_multicast (&client->theiraddr)) { + GST_DEBUG_OBJECT (sink, "we have a multicast client %p", client); + if (sink->auto_multicast) { + GST_DEBUG_OBJECT (sink, "autojoining group"); + if (gst_udp_join_group (*(client->sock), &client->theiraddr, NULL) + != 0) + goto join_group_failed; + } + GST_DEBUG_OBJECT (sink, "setting loop to %d", sink->loop); + if (gst_udp_set_loop (sink->sock, sink->ss_family, sink->loop) != 0) + goto loop_failed; + GST_DEBUG_OBJECT (sink, "setting ttl to %d", sink->ttl_mc); + if (gst_udp_set_ttl (sink->sock, sink->ss_family, sink->ttl_mc, TRUE) != 0) + goto ttl_failed; + } else { + GST_DEBUG_OBJECT (sink, "setting unicast ttl to %d", sink->ttl); + if (gst_udp_set_ttl (sink->sock, sink->ss_family, sink->ttl, FALSE) != 0) + goto ttl_failed; + } + return TRUE; + + /* ERRORS */ +join_group_failed: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not join multicast group (%d): %s", errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +ttl_failed: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not set TTL socket option (%d): %s", errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +loop_failed: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not set loopback socket option (%d): %s", + errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +} + +/* create a socket for sending to remote machine */ +static gboolean +gst_multiudpsink_init_send (GstMultiUDPSink * sink) +{ + guint bc_val; + GList *clients; + GstUDPClient *client; + int sndsize, ret; + socklen_t len; + + if (sink->sockfd == -1) { + GST_DEBUG_OBJECT (sink, "creating sockets"); + /* create sender socket try IP6, fall back to IP4 */ + sink->ss_family = AF_INET6; + if ((sink->sock = socket (AF_INET6, SOCK_DGRAM, 0)) == -1) { + sink->ss_family = AF_INET; + if ((sink->sock = socket (AF_INET, SOCK_DGRAM, 0)) == -1) + goto no_socket; + } + + GST_DEBUG_OBJECT (sink, "have socket"); + sink->externalfd = FALSE; + } else { + struct sockaddr_storage myaddr; + + GST_DEBUG_OBJECT (sink, "using configured socket"); + /* we use the configured socket, try to get some info about it */ + len = sizeof (myaddr); + if (getsockname (sink->sockfd, (struct sockaddr *) &myaddr, &len) < 0) + goto getsockname_error; + + sink->ss_family = myaddr.ss_family; + /* we use the configured socket */ + sink->sock = sink->sockfd; + sink->externalfd = TRUE; + } + + len = sizeof (sndsize); + if (sink->buffer_size != 0) { + sndsize = sink->buffer_size; + + GST_DEBUG_OBJECT (sink, "setting udp buffer of %d bytes", sndsize); + /* set buffer size, Note that on Linux this is typically limited to a + * maximum of around 100K. Also a minimum of 128 bytes is required on + * Linux. */ + ret = + setsockopt (sink->sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, + len); + if (ret != 0) { + GST_ELEMENT_WARNING (sink, RESOURCE, SETTINGS, (NULL), + ("Could not create a buffer of requested %d bytes, %d: %s (%d)", + sndsize, ret, g_strerror (errno), errno)); + } + } + + /* read the value of the receive buffer. Note that on linux this returns 2x the + * value we set because the kernel allocates extra memory for metadata. + * The default on Linux is about 100K (which is about 50K without metadata) */ + ret = + getsockopt (sink->sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &sndsize, &len); + if (ret == 0) + GST_DEBUG_OBJECT (sink, "have udp buffer of %d bytes", sndsize); + else + GST_DEBUG_OBJECT (sink, "could not get udp buffer size"); + + + bc_val = 1; + if (setsockopt (sink->sock, SOL_SOCKET, SO_BROADCAST, &bc_val, + sizeof (bc_val)) < 0) + goto no_broadcast; + + sink->bytes_to_serve = 0; + sink->bytes_served = 0; + + gst_multiudpsink_setup_qos_dscp (sink); + + /* look for multicast clients and join multicast groups appropriately + set also ttl and multicast loopback delivery appropriately */ + for (clients = sink->clients; clients; clients = g_list_next (clients)) { + client = (GstUDPClient *) clients->data; + + if (!gst_multiudpsink_configure_client (sink, client)) + return FALSE; + } + return TRUE; + + /* ERRORS */ +no_socket: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), + ("Could not create socket (%d): %s", errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +getsockname_error: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (NULL), + ("Could not getsockname (%d): %s", errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +no_broadcast: + { + gchar *errormessage = socket_last_error_message (); + int errorcode = socket_last_error_code (); + CLOSE_IF_REQUESTED (sink); + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not set broadcast socket option (%d): %s", + errorcode, errormessage)); + g_free (errormessage); + return FALSE; + } +} + +static void +gst_multiudpsink_close (GstMultiUDPSink * sink) +{ + CLOSE_IF_REQUESTED (sink); +} + +static void +gst_multiudpsink_add_internal (GstMultiUDPSink * sink, const gchar * host, + gint port, gboolean lock) +{ + GstUDPClient *client; + GstUDPClient udpclient; + GTimeVal now; + GList *find; + + udpclient.host = (gchar *) host; + udpclient.port = port; + + GST_DEBUG_OBJECT (sink, "adding client on host %s, port %d", host, port); + + if (lock) + g_mutex_lock (sink->client_lock); + + find = g_list_find_custom (sink->clients, &udpclient, + (GCompareFunc) client_compare); + if (find) { + client = (GstUDPClient *) find->data; + + GST_DEBUG_OBJECT (sink, "found %d existing clients with host %s, port %d", + client->refcount, host, port); + client->refcount++; + } else { + client = create_client (sink, host, port); + + client->sock = &sink->sock; + + if (gst_udp_get_addr (host, port, &client->theiraddr) < 0) + goto getaddrinfo_error; + + g_get_current_time (&now); + client->connect_time = GST_TIMEVAL_TO_TIME (now); + + if (*client->sock > 0) { + gst_multiudpsink_configure_client (sink, client); + } + + GST_DEBUG_OBJECT (sink, "add client with host %s, port %d", host, port); + sink->clients = g_list_prepend (sink->clients, client); + } + + if (lock) + g_mutex_unlock (sink->client_lock); + + g_signal_emit (G_OBJECT (sink), + gst_multiudpsink_signals[SIGNAL_CLIENT_ADDED], 0, host, port); + + GST_DEBUG_OBJECT (sink, "added client on host %s, port %d", host, port); + return; + + /* ERRORS */ +getaddrinfo_error: + { + GST_DEBUG_OBJECT (sink, "did not add client on host %s, port %d", host, + port); + GST_WARNING_OBJECT (sink, "getaddrinfo lookup error?"); + free_client (client); + if (lock) + g_mutex_unlock (sink->client_lock); + return; + } +} + +void +gst_multiudpsink_add (GstMultiUDPSink * sink, const gchar * host, gint port) +{ + gst_multiudpsink_add_internal (sink, host, port, TRUE); +} + +void +gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port) +{ + GList *find; + GstUDPClient udpclient; + GstUDPClient *client; + GTimeVal now; + + udpclient.host = (gchar *) host; + udpclient.port = port; + + g_mutex_lock (sink->client_lock); + find = g_list_find_custom (sink->clients, &udpclient, + (GCompareFunc) client_compare); + if (!find) + goto not_found; + + client = (GstUDPClient *) find->data; + + GST_DEBUG_OBJECT (sink, "found %d clients with host %s, port %d", + client->refcount, host, port); + + client->refcount--; + if (client->refcount == 0) { + GST_DEBUG_OBJECT (sink, "remove client with host %s, port %d", host, port); + + g_get_current_time (&now); + client->disconnect_time = GST_TIMEVAL_TO_TIME (now); + + if (*(client->sock) != -1 && sink->auto_multicast + && gst_udp_is_multicast (&client->theiraddr)) + gst_udp_leave_group (*(client->sock), &client->theiraddr); + + /* Unlock to emit signal before we delete the actual client */ + g_mutex_unlock (sink->client_lock); + g_signal_emit (G_OBJECT (sink), + gst_multiudpsink_signals[SIGNAL_CLIENT_REMOVED], 0, host, port); + g_mutex_lock (sink->client_lock); + + sink->clients = g_list_delete_link (sink->clients, find); + + free_client (client); + } + g_mutex_unlock (sink->client_lock); + + return; + + /* ERRORS */ +not_found: + { + g_mutex_unlock (sink->client_lock); + GST_WARNING_OBJECT (sink, "client at host %s, port %d not found", + host, port); + return; + } +} + +static void +gst_multiudpsink_clear_internal (GstMultiUDPSink * sink, gboolean lock) +{ + GST_DEBUG_OBJECT (sink, "clearing"); + /* we only need to remove the client structure, there is no additional + * socket or anything to free for UDP */ + if (lock) + g_mutex_lock (sink->client_lock); + g_list_foreach (sink->clients, (GFunc) free_client, sink); + g_list_free (sink->clients); + sink->clients = NULL; + if (lock) + g_mutex_unlock (sink->client_lock); +} + +void +gst_multiudpsink_clear (GstMultiUDPSink * sink) +{ + gst_multiudpsink_clear_internal (sink, TRUE); +} + +GValueArray * +gst_multiudpsink_get_stats (GstMultiUDPSink * sink, const gchar * host, + gint port) +{ + GstUDPClient *client; + GValueArray *result = NULL; + GstUDPClient udpclient; + GList *find; + GValue value = { 0 }; + + udpclient.host = (gchar *) host; + udpclient.port = port; + + g_mutex_lock (sink->client_lock); + + find = g_list_find_custom (sink->clients, &udpclient, + (GCompareFunc) client_compare); + if (!find) + goto not_found; + + GST_DEBUG_OBJECT (sink, "stats for client with host %s, port %d", host, port); + + client = (GstUDPClient *) find->data; + + /* Result is a value array of (bytes_sent, packets_sent, + * connect_time, disconnect_time), all as uint64 */ + result = g_value_array_new (4); + + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->bytes_sent); + result = g_value_array_append (result, &value); + g_value_unset (&value); + + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->packets_sent); + result = g_value_array_append (result, &value); + g_value_unset (&value); + + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->connect_time); + result = g_value_array_append (result, &value); + g_value_unset (&value); + + g_value_init (&value, G_TYPE_UINT64); + g_value_set_uint64 (&value, client->disconnect_time); + result = g_value_array_append (result, &value); + g_value_unset (&value); + + g_mutex_unlock (sink->client_lock); + + return result; + + /* ERRORS */ +not_found: + { + g_mutex_unlock (sink->client_lock); + GST_WARNING_OBJECT (sink, "client with host %s, port %d not found", + host, port); + /* Apparently (see comment in gstmultifdsink.c) returning NULL from here may + * confuse/break python bindings */ + return g_value_array_new (0); + } +} + +static GstStateChangeReturn +gst_multiudpsink_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstMultiUDPSink *sink; + + sink = GST_MULTIUDPSINK (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!gst_multiudpsink_init_send (sink)) + goto no_init; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_multiudpsink_close (sink); + break; + default: + break; + } + return ret; + + /* ERRORS */ +no_init: + { + /* _init_send() posted specific error already */ + return GST_STATE_CHANGE_FAILURE; + } +} diff --git a/gst/udp/gstmultiudpsink.h b/gst/udp/gstmultiudpsink.h new file mode 100644 index 0000000..c31dbad --- /dev/null +++ b/gst/udp/gstmultiudpsink.h @@ -0,0 +1,109 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymand <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_MULTIUDPSINK_H__ +#define __GST_MULTIUDPSINK_H__ + +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> + +G_BEGIN_DECLS + +#include "gstudpnetutils.h" +#include "gstudp.h" + +#define GST_TYPE_MULTIUDPSINK (gst_multiudpsink_get_type()) +#define GST_MULTIUDPSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MULTIUDPSINK,GstMultiUDPSink)) +#define GST_MULTIUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MULTIUDPSINK,GstMultiUDPSinkClass)) +#define GST_IS_MULTIUDPSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MULTIUDPSINK)) +#define GST_IS_MULTIUDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MULTIUDPSINK)) + +typedef struct _GstMultiUDPSink GstMultiUDPSink; +typedef struct _GstMultiUDPSinkClass GstMultiUDPSinkClass; + +typedef struct { + gint refcount; + + int *sock; + + struct sockaddr_storage theiraddr; + + gchar *host; + gint port; + + /* Per-client stats */ + guint64 bytes_sent; + guint64 packets_sent; + guint64 connect_time; + guint64 disconnect_time; +} GstUDPClient; + +/* sends udp packets to multiple host/port pairs. + */ +struct _GstMultiUDPSink { + GstBaseSink parent; + + int sock; + + GMutex *client_lock; + GList *clients; + + /* properties */ + guint64 bytes_to_serve; + guint64 bytes_served; + int sockfd; + gboolean closefd; + + gboolean externalfd; + + gboolean auto_multicast; + gint ttl; + gint ttl_mc; + gboolean loop; + gint qos_dscp; + guint16 ss_family; + + gboolean send_duplicates; + gint buffer_size; +}; + +struct _GstMultiUDPSinkClass { + GstBaseSinkClass parent_class; + + /* element methods */ + void (*add) (GstMultiUDPSink *sink, const gchar *host, gint port); + void (*remove) (GstMultiUDPSink *sink, const gchar *host, gint port); + void (*clear) (GstMultiUDPSink *sink); + GValueArray* (*get_stats) (GstMultiUDPSink *sink, const gchar *host, gint port); + + /* signals */ + void (*client_added) (GstElement *element, const gchar *host, gint port); + void (*client_removed) (GstElement *element, const gchar *host, gint port); +}; + +GType gst_multiudpsink_get_type(void); + +void gst_multiudpsink_add (GstMultiUDPSink *sink, const gchar *host, gint port); +void gst_multiudpsink_remove (GstMultiUDPSink *sink, const gchar *host, gint port); +void gst_multiudpsink_clear (GstMultiUDPSink *sink); +GValueArray* gst_multiudpsink_get_stats (GstMultiUDPSink *sink, const gchar *host, gint port); + +G_END_DECLS + +#endif /* __GST_MULTIUDPSINK_H__ */ diff --git a/gst/udp/gstudp-marshal.list b/gst/udp/gstudp-marshal.list new file mode 100644 index 0000000..b53e79c --- /dev/null +++ b/gst/udp/gstudp-marshal.list @@ -0,0 +1,2 @@ +VOID:STRING,INT +BOXED:STRING,INT diff --git a/gst/udp/gstudp.c b/gst/udp/gstudp.c new file mode 100644 index 0000000..fbdbfea --- /dev/null +++ b/gst/udp/gstudp.c @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * 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 <gst/netbuffer/gstnetbuffer.h> + +#include "gstudpsrc.h" +#include "gstmultiudpsink.h" +#include "gstudpsink.h" +#include "gstdynudpsink.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ +#ifdef G_OS_WIN32 + if (!gst_udp_net_utils_win32_wsa_startup (GST_OBJECT (plugin))) + return FALSE; +#endif + + /* register type of the netbuffer so that we can use it from multiple threads + * right away. Note that the plugin loading is always serialized */ + gst_netbuffer_get_type (); + + if (!gst_element_register (plugin, "udpsink", GST_RANK_NONE, + GST_TYPE_UDPSINK)) + return FALSE; + + if (!gst_element_register (plugin, "multiudpsink", GST_RANK_NONE, + GST_TYPE_MULTIUDPSINK)) + return FALSE; + + if (!gst_element_register (plugin, "dynudpsink", GST_RANK_NONE, + GST_TYPE_DYNUDPSINK)) + return FALSE; + + if (!gst_element_register (plugin, "udpsrc", GST_RANK_NONE, GST_TYPE_UDPSRC)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "udp", + "transfer data via UDP", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/udp/gstudp.h b/gst/udp/gstudp.h new file mode 100644 index 0000000..f687937 --- /dev/null +++ b/gst/udp/gstudp.h @@ -0,0 +1,39 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * 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. + */ + +#include "gstudp-enumtypes.h" +#include <glib.h> + +#ifndef __GST_UDP_H__ +#define __GST_UDP_H__ + +G_BEGIN_DECLS + +typedef enum +{ + CONTROL_ZERO, + CONTROL_NONE, + CONTROL_UDP, + CONTROL_TCP +} GstUDPControl; + +G_END_DECLS + +#endif /* __GST_UDP_H__ */ + diff --git a/gst/udp/gstudpnetutils.c b/gst/udp/gstudpnetutils.c new file mode 100644 index 0000000..f488fcc --- /dev/null +++ b/gst/udp/gstudpnetutils.c @@ -0,0 +1,462 @@ +/* GStreamer UDP network utility functions + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2006 Joni Valtanen <joni.valtanen@movial.fi> + * Copyright (C) 2009 Jarkko Palviainen <jarkko.palviainen@sesca.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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> + +#include <gst/gst.h> + +#include "gstudpnetutils.h" + +/* EAI_ADDRFAMILY was obsoleted in BSD at some point */ +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY 1 +#endif + +#ifdef G_OS_WIN32 + +gboolean +gst_udp_net_utils_win32_wsa_startup (GstObject * obj) +{ + WSADATA w; + int error; + + error = WSAStartup (0x0202, &w); + + if (error) { + GST_WARNING_OBJECT (obj, "WSAStartup error: %d", error); + return FALSE; + } + + if (w.wVersion != 0x0202) { + WSACleanup (); + GST_WARNING_OBJECT (obj, "Winsock version wrong : 0x%x", w.wVersion); + return FALSE; + } + + return TRUE; +} + +#endif + +int +gst_udp_get_sockaddr_length (struct sockaddr_storage *addr) +{ + /* MacOS is picky about passing precisely the correct length, + * so we calculate it here for the given socket type. + */ + switch (addr->ss_family) { + case AF_INET: + return sizeof (struct sockaddr_in); + case AF_INET6: + return sizeof (struct sockaddr_in6); + default: + /* don't know, Screw MacOS and use the full length */ + return sizeof (*addr); + } +} + +int +gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr) +{ + struct addrinfo hints, *res = NULL, *nres; + char service[NI_MAXSERV]; + int ret; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + g_snprintf (service, sizeof (service) - 1, "%d", port); + service[sizeof (service) - 1] = '\0'; + + if ((ret = getaddrinfo (hostname, (port == -1) ? NULL : service, &hints, + &res)) < 0) { + goto beach; + } + + nres = res; + while (nres) { + if (nres->ai_family == AF_INET || nres->ai_family == AF_INET6) + break; + nres = nres->ai_next; + } + + if (nres) { + memcpy (addr, nres->ai_addr, nres->ai_addrlen); + } else { + ret = EAI_ADDRFAMILY; + } + + freeaddrinfo (res); +beach: + return ret; +} + +int +gst_udp_set_loop (int sockfd, guint16 ss_family, gboolean loop) +{ + int ret = -1; + int l = (loop == FALSE) ? 0 : 1; + + switch (ss_family) { + case AF_INET: + { + ret = setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l, sizeof (l)); + if (ret < 0) + return ret; + + break; + } + case AF_INET6: + { + ret = + setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l, + sizeof (l)); + if (ret < 0) + return ret; + + break; + } + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + + return ret; +} + +int +gst_udp_set_ttl (int sockfd, guint16 ss_family, int ttl, gboolean is_multicast) +{ + int optname = -1; + int ret = -1; + + switch (ss_family) { + case AF_INET: + { + optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL; + ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl)); + if (ret < 0) + return ret; + break; + } + case AF_INET6: + { + optname = + (is_multicast == TRUE) ? IPV6_MULTICAST_HOPS : IPV6_UNICAST_HOPS; + ret = setsockopt (sockfd, IPPROTO_IPV6, optname, &ttl, sizeof (ttl)); + if (ret < 0) + return ret; + + /* When using IPV4 address with IPV6 socket, both TTL values + must be set in order to actually use the given value. + Has no effect when IPV6 address is used. */ + optname = (is_multicast == TRUE) ? IP_MULTICAST_TTL : IP_TTL; + ret = setsockopt (sockfd, IPPROTO_IP, optname, &ttl, sizeof (ttl)); + if (ret < 0) + return ret; + break; + } + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + return ret; +} + +/* FIXME: Add interface selection for windows hosts. */ +int +gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, gchar * iface) +{ + int ret = -1; + + switch (addr->ss_family) { + case AF_INET: + { +#ifdef HAVE_IP_MREQN + struct ip_mreqn mreq4; +#else + struct ip_mreq mreq4; +#endif + + memset (&mreq4, 0, sizeof (mreq4)); + mreq4.imr_multiaddr.s_addr = + ((struct sockaddr_in *) addr)->sin_addr.s_addr; +#ifdef HAVE_IP_MREQN + if (iface) + mreq4.imr_ifindex = if_nametoindex (iface); + else + mreq4.imr_ifindex = 0; /* Pick any. */ +#else + mreq4.imr_interface.s_addr = INADDR_ANY; +#endif + + if ((ret = + setsockopt (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const void *) &mreq4, sizeof (mreq4))) < 0) + return ret; + + break; + } + case AF_INET6: + { + struct ipv6_mreq mreq6; + + memset (&mreq6, 0, sizeof (mreq6)); + memcpy (&mreq6.ipv6mr_multiaddr, + &(((struct sockaddr_in6 *) addr)->sin6_addr), + sizeof (struct in6_addr)); + mreq6.ipv6mr_interface = 0; +#if !defined(G_OS_WIN32) + if (iface) + mreq6.ipv6mr_interface = if_nametoindex (iface); +#endif + + if ((ret = + setsockopt (sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (const void *) &mreq6, sizeof (mreq6))) < 0) + return ret; + + break; + } + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + return ret; +} + +int +gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr) +{ + int ret = -1; + + switch (addr->ss_family) { + case AF_INET: + { + struct ip_mreq mreq4; + + memset (&mreq4, 0, sizeof (mreq4)); + mreq4.imr_multiaddr.s_addr = + ((struct sockaddr_in *) addr)->sin_addr.s_addr; + mreq4.imr_interface.s_addr = INADDR_ANY; + + if ((ret = + setsockopt (sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (const void *) &mreq4, sizeof (mreq4))) < 0) + return ret; + } + break; + + case AF_INET6: + { + struct ipv6_mreq mreq6; + + memset (&mreq6, 0, sizeof (mreq6)); + memcpy (&mreq6.ipv6mr_multiaddr, + &(((struct sockaddr_in6 *) addr)->sin6_addr), + sizeof (struct in6_addr)); + mreq6.ipv6mr_interface = 0; + + if ((ret = + setsockopt (sockfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (const void *) &mreq6, sizeof (mreq6))) < 0) + return ret; + } + break; + + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + + return ret; +} + +int +gst_udp_is_multicast (struct sockaddr_storage *addr) +{ + int ret = -1; + + switch (addr->ss_family) { + case AF_INET: + { + struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; + + ret = IN_MULTICAST (g_ntohl (addr4->sin_addr.s_addr)); + } + break; + + case AF_INET6: + { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; + + ret = IN6_IS_ADDR_MULTICAST (&addr6->sin6_addr); + } + break; + + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + } + + return ret; +} + +void +gst_udp_uri_init (GstUDPUri * uri, const gchar * host, gint port) +{ + uri->host = NULL; + uri->port = -1; + gst_udp_uri_update (uri, host, port); +} + +int +gst_udp_uri_update (GstUDPUri * uri, const gchar * host, gint port) +{ + if (host) { + g_free (uri->host); + uri->host = g_strdup (host); + if (strchr (host, ':')) + uri->is_ipv6 = TRUE; + else + uri->is_ipv6 = FALSE; + } + if (port != -1) + uri->port = port; + + return 0; +} + +int +gst_udp_parse_uri (const gchar * uristr, GstUDPUri * uri) +{ + gchar *protocol, *location_start; + gchar *location, *location_end; + gchar *colptr; + + protocol = gst_uri_get_protocol (uristr); + if (strcmp (protocol, "udp") != 0) + goto wrong_protocol; + g_free (protocol); + + location_start = gst_uri_get_location (uristr); + if (!location_start) + return FALSE; + + GST_DEBUG ("got location '%s'", location_start); + + /* VLC compatibility, strip everything before the @ sign. VLC uses that as the + * remote address. */ + location = g_strstr_len (location_start, -1, "@"); + if (location == NULL) + location = location_start; + else + location += 1; + + if (location[0] == '[') { + GST_DEBUG ("parse IPV6 address '%s'", location); + location_end = strchr (location, ']'); + if (location_end == NULL) + goto wrong_address; + + uri->is_ipv6 = TRUE; + g_free (uri->host); + uri->host = g_strndup (location + 1, location_end - location - 1); + colptr = strrchr (location_end, ':'); + } else { + GST_DEBUG ("parse IPV4 address '%s'", location); + uri->is_ipv6 = FALSE; + colptr = strrchr (location, ':'); + + g_free (uri->host); + if (colptr != NULL) { + uri->host = g_strndup (location, colptr - location); + } else { + uri->host = g_strdup (location); + } + } + GST_DEBUG ("host set to '%s'", uri->host); + + if (colptr != NULL) { + uri->port = atoi (colptr + 1); + } + g_free (location_start); + + return 0; + + /* ERRORS */ +wrong_protocol: + { + GST_ERROR ("error parsing uri %s: wrong protocol (%s != udp)", uristr, + protocol); + g_free (protocol); + return -1; + } +wrong_address: + { + GST_ERROR ("error parsing uri %s", uristr); + g_free (location); + return -1; + } +} + +gchar * +gst_udp_uri_string (GstUDPUri * uri) +{ + gchar *result; + + if (uri->is_ipv6) { + result = g_strdup_printf ("udp://[%s]:%d", uri->host, uri->port); + } else { + result = g_strdup_printf ("udp://%s:%d", uri->host, uri->port); + } + return result; +} + +void +gst_udp_uri_free (GstUDPUri * uri) +{ + g_free (uri->host); + uri->host = NULL; + uri->port = -1; +} diff --git a/gst/udp/gstudpnetutils.h b/gst/udp/gstudpnetutils.h new file mode 100644 index 0000000..ec863bd --- /dev/null +++ b/gst/udp/gstudpnetutils.h @@ -0,0 +1,108 @@ +/* GStreamer UDP network utility functions + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2006 Joni Valtanen <joni.valtanen@movial.fi> + * + * 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_UDP_NET_UTILS_H__ +#define __GST_UDP_NET_UTILS_H__ + +#include <sys/types.h> + +/* Needed for G_OS_XXXX */ +#include <glib.h> + +#ifdef G_OS_WIN32 +/* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later. + * minwg32 headers check WINVER before allowing the use of these */ +#define WINVER 0x0501 +#include <winsock2.h> +#include <ws2tcpip.h> +#ifndef socklen_t +#define socklen_t int +#endif + +/* Needed for GstObject and GST_WARNING_OBJECT */ +#include <gst/gstobject.h> +#include <gst/gstinfo.h> + +#else +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include <netdb.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/ioctl.h> +#endif + +#include <fcntl.h> + +#ifdef G_OS_WIN32 + +#define IOCTL_SOCKET ioctlsocket +#define CLOSE_SOCKET(sock) closesocket(sock) +#define setsockopt(sock,l,opt,val,len) setsockopt(sock,l,opt,(char *)(val),len) +#define WSA_STARTUP(obj) gst_udp_net_utils_win32_wsa_startup(GST_OBJECT(obj)) +#define WSA_CLEANUP(obj) WSACleanup () + +#else + +#define IOCTL_SOCKET ioctl +#define CLOSE_SOCKET(sock) close(sock) +#define setsockopt(sock,l,opt,val,len) setsockopt(sock,l,opt,(void *)(val),len) +#define WSA_STARTUP(obj) +#define WSA_CLEANUP(obj) + +#endif + +#ifdef G_OS_WIN32 + +gboolean gst_udp_net_utils_win32_wsa_startup (GstObject * obj); + +#endif + +typedef struct { + gchar *host; + gint port; + gboolean is_ipv6; +} GstUDPUri; + +int gst_udp_get_sockaddr_length (struct sockaddr_storage *addr); + +int gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr); +int gst_udp_is_multicast (struct sockaddr_storage *addr); + +int gst_udp_set_loop (int sockfd, guint16 ss_family, gboolean loop); +int gst_udp_set_ttl (int sockfd, guint16 ss_family, int ttl, gboolean is_multicast); + +/* multicast groups */ +int gst_udp_join_group (int sockfd, struct sockaddr_storage *addr, + gchar *iface); +int gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr); + +/* uri handling */ +void gst_udp_uri_init (GstUDPUri *uri, const gchar *host, gint port); +int gst_udp_uri_update (GstUDPUri *uri, const gchar *host, gint port); +int gst_udp_parse_uri (const gchar *uristr, GstUDPUri *uri); +gchar * gst_udp_uri_string (GstUDPUri *uri); +void gst_udp_uri_free (GstUDPUri *uri); + +#endif /* __GST_UDP_NET_UTILS_H__*/ + diff --git a/gst/udp/gstudpsink.c b/gst/udp/gstudpsink.c new file mode 100644 index 0000000..26c70cd --- /dev/null +++ b/gst/udp/gstudpsink.c @@ -0,0 +1,298 @@ +/* GStreamer + * Copyright (C) <2005> 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-udpsink + * @see_also: udpsrc, multifdsink + * + * udpsink is a network sink that sends UDP packets to the network. + * It can be combined with RTP payloaders to implement RTP streaming. + * + * <refsect2> + * <title>Examples</title> + * |[ + * gst-launch -v audiotestsrc ! udpsink + * ]| + * </refsect2> + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "gstudpsink.h" + +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> + +#define UDP_DEFAULT_HOST "localhost" +#define UDP_DEFAULT_PORT 4951 + +/* UDPSink signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_HOST, + PROP_PORT, + PROP_URI, + /* FILL ME */ +}; + +static void gst_udpsink_base_init (gpointer g_class); +static void gst_udpsink_class_init (GstUDPSink * klass); +static void gst_udpsink_init (GstUDPSink * udpsink); +static void gst_udpsink_finalize (GstUDPSink * udpsink); + +static void gst_udpsink_uri_handler_init (gpointer g_iface, + gpointer iface_data); + +static void gst_udpsink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_udpsink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstElementClass *parent_class = NULL; + +/*static guint gst_udpsink_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_udpsink_get_type (void) +{ + static GType udpsink_type = 0; + + if (!udpsink_type) { + static const GTypeInfo udpsink_info = { + sizeof (GstUDPSinkClass), + gst_udpsink_base_init, + NULL, + (GClassInitFunc) gst_udpsink_class_init, + NULL, + NULL, + sizeof (GstUDPSink), + 0, + (GInstanceInitFunc) gst_udpsink_init, + NULL + }; + static const GInterfaceInfo urihandler_info = { + gst_udpsink_uri_handler_init, + NULL, + NULL + }; + + udpsink_type = + g_type_register_static (GST_TYPE_MULTIUDPSINK, "GstUDPSink", + &udpsink_info, 0); + + g_type_add_interface_static (udpsink_type, GST_TYPE_URI_HANDLER, + &urihandler_info); + + } + return udpsink_type; +} + +static void +gst_udpsink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, "UDP packet sender", + "Sink/Network", + "Send data over the network via UDP", "Wim Taymans <wim@fluendo.com>"); +} + +static void +gst_udpsink_class_init (GstUDPSink * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_udpsink_set_property; + gobject_class->get_property = gst_udpsink_get_property; + + gobject_class->finalize = (GObjectFinalizeFunc) gst_udpsink_finalize; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HOST, + g_param_spec_string ("host", "host", + "The host/IP/Multicast group to send the packets to", + UDP_DEFAULT_HOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PORT, + g_param_spec_int ("port", "port", "The port to send the packets to", + 0, 65535, UDP_DEFAULT_PORT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + + +static void +gst_udpsink_init (GstUDPSink * udpsink) +{ + gst_udp_uri_init (&udpsink->uri, UDP_DEFAULT_HOST, UDP_DEFAULT_PORT); + + gst_multiudpsink_add (GST_MULTIUDPSINK (udpsink), udpsink->uri.host, + udpsink->uri.port); +} + +static void +gst_udpsink_finalize (GstUDPSink * udpsink) +{ + gst_udp_uri_free (&udpsink->uri); + g_free (udpsink->uristr); + + G_OBJECT_CLASS (parent_class)->finalize ((GObject *) udpsink); +} + +static gboolean +gst_udpsink_set_uri (GstUDPSink * sink, const gchar * uri) +{ + gst_multiudpsink_remove (GST_MULTIUDPSINK (sink), sink->uri.host, + sink->uri.port); + + if (gst_udp_parse_uri (uri, &sink->uri) < 0) + goto wrong_uri; + + gst_multiudpsink_add (GST_MULTIUDPSINK (sink), sink->uri.host, + sink->uri.port); + + return TRUE; + + /* ERRORS */ +wrong_uri: + { + GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL), + ("error parsing uri %s", uri)); + return FALSE; + } +} + +static void +gst_udpsink_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + GstUDPSink *udpsink; + + udpsink = GST_UDPSINK (object); + + /* remove old host */ + gst_multiudpsink_remove (GST_MULTIUDPSINK (udpsink), + udpsink->uri.host, udpsink->uri.port); + + switch (prop_id) { + case PROP_HOST: + { + const gchar *host; + + host = g_value_get_string (value); + + if (host) + gst_udp_uri_update (&udpsink->uri, host, -1); + else + gst_udp_uri_update (&udpsink->uri, UDP_DEFAULT_HOST, -1); + break; + } + case PROP_PORT: + gst_udp_uri_update (&udpsink->uri, NULL, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + /* add new host */ + gst_multiudpsink_add (GST_MULTIUDPSINK (udpsink), + udpsink->uri.host, udpsink->uri.port); +} + +static void +gst_udpsink_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstUDPSink *udpsink; + + udpsink = GST_UDPSINK (object); + + switch (prop_id) { + case PROP_HOST: + g_value_set_string (value, udpsink->uri.host); + break; + case PROP_PORT: + g_value_set_int (value, udpsink->uri.port); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static GstURIType +gst_udpsink_uri_get_type (void) +{ + return GST_URI_SINK; +} + +static gchar ** +gst_udpsink_uri_get_protocols (void) +{ + static gchar *protocols[] = { (char *) "udp", NULL }; + + return protocols; +} + +static const gchar * +gst_udpsink_uri_get_uri (GstURIHandler * handler) +{ + GstUDPSink *sink = GST_UDPSINK (handler); + + g_free (sink->uristr); + sink->uristr = gst_udp_uri_string (&sink->uri); + + return sink->uristr; +} + +static gboolean +gst_udpsink_uri_set_uri (GstURIHandler * handler, const gchar * uri) +{ + gboolean ret; + GstUDPSink *sink = GST_UDPSINK (handler); + + ret = gst_udpsink_set_uri (sink, uri); + + return ret; +} + +static void +gst_udpsink_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_udpsink_uri_get_type; + iface->get_protocols = gst_udpsink_uri_get_protocols; + iface->get_uri = gst_udpsink_uri_get_uri; + iface->set_uri = gst_udpsink_uri_set_uri; +} diff --git a/gst/udp/gstudpsink.h b/gst/udp/gstudpsink.h new file mode 100644 index 0000000..d45045e --- /dev/null +++ b/gst/udp/gstudpsink.h @@ -0,0 +1,56 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * 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_UDPSINK_H__ +#define __GST_UDPSINK_H__ + +#include <gst/gst.h> +#include "gstmultiudpsink.h" + +G_BEGIN_DECLS + +#include "gstudp.h" +#include "gstudpnetutils.h" + +#define GST_TYPE_UDPSINK (gst_udpsink_get_type()) +#define GST_UDPSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_UDPSINK,GstUDPSink)) +#define GST_UDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_UDPSINK,GstUDPSinkClass)) +#define GST_IS_UDPSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_UDPSINK)) +#define GST_IS_UDPSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_UDPSINK)) + +typedef struct _GstUDPSink GstUDPSink; +typedef struct _GstUDPSinkClass GstUDPSinkClass; + +struct _GstUDPSink { + GstMultiUDPSink parent; + + GstUDPUri uri; + gchar *uristr; +}; + +struct _GstUDPSinkClass { + GstMultiUDPSinkClass parent_class; +}; + +GType gst_udpsink_get_type(void); + +G_END_DECLS + +#endif /* __GST_UDPSINK_H__ */ diff --git a/gst/udp/gstudpsrc.c b/gst/udp/gstudpsrc.c new file mode 100644 index 0000000..4363044 --- /dev/null +++ b/gst/udp/gstudpsrc.c @@ -0,0 +1,1071 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim@fluendo.com> + * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.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-udpsrc + * @see_also: udpsink, multifdsink + * + * udpsrc is a network source that reads UDP packets from the network. + * It can be combined with RTP depayloaders to implement RTP streaming. + * + * The udpsrc element supports automatic port allocation by setting the + * #GstUDPSrc:port property to 0. After setting the udpsrc to PAUSED, the + * allocated port can be obtained by reading the port property. + * + * udpsrc can read from multicast groups by setting the #GstUDPSrc:multicast-group + * property to the IP address of the multicast group. + * + * Alternatively one can provide a custom socket to udpsrc with the #GstUDPSrc:sockfd + * property, udpsrc will then not allocate a socket itself but use the provided + * one. + * + * The #GstUDPSrc:caps property is mainly used to give a type to the UDP packet + * so that they can be autoplugged in GStreamer pipelines. This is very usefull + * for RTP implementations where the contents of the UDP packets is transfered + * out-of-bounds using SDP or other means. + * + * The #GstUDPSrc:buffer-size property is used to change the default kernel + * buffersizes used for receiving packets. The buffer size may be increased for + * high-volume connections, or may be decreased to limit the possible backlog of + * incoming data. The system places an absolute limit on these values, on Linux, + * for example, the default buffer size is typically 50K and can be increased to + * maximally 100K. + * + * The #GstUDPSrc:skip-first-bytes property is used to strip off an arbitrary + * number of bytes from the start of the raw udp packet and can be used to strip + * off proprietary header, for example. + * + * The udpsrc is always a live source. It does however not provide a #GstClock, + * this is left for upstream elements such as an RTP session manager or demuxer + * (such as an MPEG demuxer). As with all live sources, the captured buffers + * will have their timestamp set to the current running time of the pipeline. + * + * udpsrc implements a #GstURIHandler interface that handles udp://host:port + * type URIs. + * + * If the #GstUDPSrc:timeout property is set to a value bigger than 0, udpsrc + * will generate an element message named + * <classname>"GstUDPSrcTimeout"</classname> + * if no data was recieved in the given timeout. + * The message's structure contains one field: + * <itemizedlist> + * <listitem> + * <para> + * #guint64 + * <classname>"timeout"</classname>: the timeout in microseconds that + * expired when waiting for data. + * </para> + * </listitem> + * </itemizedlist> + * The message is typically used to detect that no UDP arrives in the receiver + * because it is blocked by a firewall. + * </para> + * <para> + * A custom file descriptor can be configured with the + * #GstUDPSrc:sockfd property. The socket will be closed when setting the + * element to READY by default. This behaviour can be + * overriden with the #GstUDPSrc:closefd property, in which case the application + * is responsible for closing the file descriptor. + * + * <refsect2> + * <title>Examples</title> + * |[ + * gst-launch -v udpsrc ! fakesink dump=1 + * ]| A pipeline to read from the default port and dump the udp packets. + * To actually generate udp packets on the default port one can use the + * udpsink element. When running the following pipeline in another terminal, the + * above mentioned pipeline should dump data packets to the console. + * |[ + * gst-launch -v audiotestsrc ! udpsink + * ]| + * |[ + * gst-launch -v udpsrc port=0 ! fakesink + * ]| read udp packets from a free port. + * </refsect2> + * + * Last reviewed on 2007-09-20 (0.10.7) + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstudpsrc.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> + +#if defined _MSC_VER && (_MSC_VER >= 1400) +#include <io.h> +#endif + +#include <gst/netbuffer/gstnetbuffer.h> + +#ifdef HAVE_FIONREAD_IN_SYS_FILIO +#include <sys/filio.h> +#endif + +GST_DEBUG_CATEGORY_STATIC (udpsrc_debug); +#define GST_CAT_DEFAULT (udpsrc_debug) + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define UDP_DEFAULT_PORT 4951 +#define UDP_DEFAULT_MULTICAST_GROUP "0.0.0.0" +#define UDP_DEFAULT_MULTICAST_IFACE NULL +#define UDP_DEFAULT_URI "udp://"UDP_DEFAULT_MULTICAST_GROUP":"G_STRINGIFY(UDP_DEFAULT_PORT) +#define UDP_DEFAULT_CAPS NULL +#define UDP_DEFAULT_SOCKFD -1 +#define UDP_DEFAULT_BUFFER_SIZE 0 +#define UDP_DEFAULT_TIMEOUT 0 +#define UDP_DEFAULT_SKIP_FIRST_BYTES 0 +#define UDP_DEFAULT_CLOSEFD TRUE +#define UDP_DEFAULT_SOCK -1 +#define UDP_DEFAULT_AUTO_MULTICAST TRUE +#define UDP_DEFAULT_REUSE TRUE + +enum +{ + PROP_0, + + PROP_PORT, + PROP_MULTICAST_GROUP, + PROP_MULTICAST_IFACE, + PROP_URI, + PROP_CAPS, + PROP_SOCKFD, + PROP_BUFFER_SIZE, + PROP_TIMEOUT, + PROP_SKIP_FIRST_BYTES, + PROP_CLOSEFD, + PROP_SOCK, + PROP_AUTO_MULTICAST, + PROP_REUSE, + + PROP_LAST +}; + +#define CLOSE_IF_REQUESTED(udpctx) \ +G_STMT_START { \ + if ((!udpctx->externalfd) || (udpctx->externalfd && udpctx->closefd)) { \ + CLOSE_SOCKET(udpctx->sock.fd); \ + if (udpctx->sock.fd == udpctx->sockfd) \ + udpctx->sockfd = UDP_DEFAULT_SOCKFD; \ + } \ + udpctx->sock.fd = UDP_DEFAULT_SOCK; \ +} G_STMT_END + +static void gst_udpsrc_uri_handler_init (gpointer g_iface, gpointer iface_data); + +static GstCaps *gst_udpsrc_getcaps (GstBaseSrc * src); + +static GstFlowReturn gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf); + +static gboolean gst_udpsrc_start (GstBaseSrc * bsrc); + +static gboolean gst_udpsrc_stop (GstBaseSrc * bsrc); + +static gboolean gst_udpsrc_unlock (GstBaseSrc * bsrc); + +static gboolean gst_udpsrc_unlock_stop (GstBaseSrc * bsrc); + +static void gst_udpsrc_finalize (GObject * object); + +static void gst_udpsrc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_udpsrc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +_do_init (GType type) +{ + static const GInterfaceInfo urihandler_info = { + gst_udpsrc_uri_handler_init, + NULL, + NULL + }; + + g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info); + + GST_DEBUG_CATEGORY_INIT (udpsrc_debug, "udpsrc", 0, "UDP src"); +} + +GST_BOILERPLATE_FULL (GstUDPSrc, gst_udpsrc, GstPushSrc, GST_TYPE_PUSH_SRC, + _do_init); + +static void +gst_udpsrc_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_details_simple (element_class, "UDP packet receiver", + "Source/Network", + "Receive data over the network via UDP", + "Wim Taymans <wim@fluendo.com>, " + "Thijs Vermeir <thijs.vermeir@barco.com>"); +} + +static void +gst_udpsrc_class_init (GstUDPSrcClass * klass) +{ + GObjectClass *gobject_class; + GstBaseSrcClass *gstbasesrc_class; + GstPushSrcClass *gstpushsrc_class; + + gobject_class = (GObjectClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + + gobject_class->set_property = gst_udpsrc_set_property; + gobject_class->get_property = gst_udpsrc_get_property; + gobject_class->finalize = gst_udpsrc_finalize; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PORT, + g_param_spec_int ("port", "Port", + "The port to receive the packets from, 0=allocate", 0, G_MAXUINT16, + UDP_DEFAULT_PORT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MULTICAST_GROUP, + g_param_spec_string ("multicast-group", "Multicast Group", + "The Address of multicast group to join", UDP_DEFAULT_MULTICAST_GROUP, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MULTICAST_IFACE, + g_param_spec_string ("multicast-iface", "Multicast Interface", + "The network interface on which to join the multicast group", + UDP_DEFAULT_MULTICAST_IFACE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_URI, + g_param_spec_string ("uri", "URI", + "URI in the form of udp://multicast_group:port", UDP_DEFAULT_URI, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CAPS, + g_param_spec_boxed ("caps", "Caps", + "The caps of the source pad", GST_TYPE_CAPS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SOCKFD, + g_param_spec_int ("sockfd", "Socket Handle", + "Socket to use for UDP reception. (-1 == allocate)", + -1, G_MAXINT, UDP_DEFAULT_SOCKFD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_SIZE, + g_param_spec_int ("buffer-size", "Buffer Size", + "Size of the kernel receive buffer in bytes, 0=default", 0, G_MAXINT, + UDP_DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMEOUT, + g_param_spec_uint64 ("timeout", "Timeout", + "Post a message after timeout microseconds (0 = disabled)", 0, + G_MAXUINT64, UDP_DEFAULT_TIMEOUT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_SKIP_FIRST_BYTES, g_param_spec_int ("skip-first-bytes", + "Skip first bytes", "number of bytes to skip for each udp packet", 0, + G_MAXINT, UDP_DEFAULT_SKIP_FIRST_BYTES, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CLOSEFD, + g_param_spec_boolean ("closefd", "Close sockfd", + "Close sockfd if passed as property on state change", + UDP_DEFAULT_CLOSEFD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SOCK, + g_param_spec_int ("sock", "Socket Handle", + "Socket currently in use for UDP reception. (-1 = no socket)", + -1, G_MAXINT, UDP_DEFAULT_SOCK, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AUTO_MULTICAST, + g_param_spec_boolean ("auto-multicast", "Auto Multicast", + "Automatically join/leave multicast groups", + UDP_DEFAULT_AUTO_MULTICAST, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_REUSE, + g_param_spec_boolean ("reuse", "Reuse", "Enable reuse of the port", + UDP_DEFAULT_REUSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstbasesrc_class->start = gst_udpsrc_start; + gstbasesrc_class->stop = gst_udpsrc_stop; + gstbasesrc_class->unlock = gst_udpsrc_unlock; + gstbasesrc_class->unlock_stop = gst_udpsrc_unlock_stop; + gstbasesrc_class->get_caps = gst_udpsrc_getcaps; + + gstpushsrc_class->create = gst_udpsrc_create; +} + +static void +gst_udpsrc_init (GstUDPSrc * udpsrc, GstUDPSrcClass * g_class) +{ + WSA_STARTUP (udpsrc); + + gst_udp_uri_init (&udpsrc->uri, UDP_DEFAULT_MULTICAST_GROUP, + UDP_DEFAULT_PORT); + + udpsrc->sockfd = UDP_DEFAULT_SOCKFD; + udpsrc->multi_iface = g_strdup (UDP_DEFAULT_MULTICAST_IFACE); + udpsrc->buffer_size = UDP_DEFAULT_BUFFER_SIZE; + udpsrc->timeout = UDP_DEFAULT_TIMEOUT; + udpsrc->skip_first_bytes = UDP_DEFAULT_SKIP_FIRST_BYTES; + udpsrc->closefd = UDP_DEFAULT_CLOSEFD; + udpsrc->externalfd = (udpsrc->sockfd != -1); + udpsrc->auto_multicast = UDP_DEFAULT_AUTO_MULTICAST; + udpsrc->sock.fd = UDP_DEFAULT_SOCK; + udpsrc->reuse = UDP_DEFAULT_REUSE; + + /* configure basesrc to be a live source */ + gst_base_src_set_live (GST_BASE_SRC (udpsrc), TRUE); + /* make basesrc output a segment in time */ + gst_base_src_set_format (GST_BASE_SRC (udpsrc), GST_FORMAT_TIME); + /* make basesrc set timestamps on outgoing buffers based on the running_time + * when they were captured */ + gst_base_src_set_do_timestamp (GST_BASE_SRC (udpsrc), TRUE); +} + +static void +gst_udpsrc_finalize (GObject * object) +{ + GstUDPSrc *udpsrc; + + udpsrc = GST_UDPSRC (object); + + if (udpsrc->caps) + gst_caps_unref (udpsrc->caps); + + g_free (udpsrc->multi_iface); + + gst_udp_uri_free (&udpsrc->uri); + g_free (udpsrc->uristr); + + if (udpsrc->sockfd >= 0 && udpsrc->closefd) + CLOSE_SOCKET (udpsrc->sockfd); + + WSA_CLEANUP (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstCaps * +gst_udpsrc_getcaps (GstBaseSrc * src) +{ + GstUDPSrc *udpsrc; + + udpsrc = GST_UDPSRC (src); + + if (udpsrc->caps) + return gst_caps_ref (udpsrc->caps); + else + return gst_caps_new_any (); +} + +/* read a message from the error queue */ +static void +clear_error (GstUDPSrc * udpsrc) +{ +#if defined (MSG_ERRQUEUE) + struct msghdr cmsg; + char cbuf[128]; + char msgbuf[CMSG_SPACE (128)]; + struct iovec iov; + + /* Flush ERRORS from fd so next poll will not return at once */ + /* No need for address : We look for local error */ + cmsg.msg_name = NULL; + cmsg.msg_namelen = 0; + + /* IOV */ + memset (&cbuf, 0, sizeof (cbuf)); + iov.iov_base = cbuf; + iov.iov_len = sizeof (cbuf); + cmsg.msg_iov = &iov; + cmsg.msg_iovlen = 1; + + /* msg_control */ + memset (&msgbuf, 0, sizeof (msgbuf)); + cmsg.msg_control = &msgbuf; + cmsg.msg_controllen = sizeof (msgbuf); + + recvmsg (udpsrc->sock.fd, &cmsg, MSG_ERRQUEUE); +#endif +} + +static GstFlowReturn +gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf) +{ + GstUDPSrc *udpsrc; + GstNetBuffer *outbuf; + union gst_sockaddr + { + struct sockaddr sa; + struct sockaddr_in sa_in; + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; + } sa; + socklen_t slen; + guint8 *pktdata; + gint pktsize; +#ifdef G_OS_UNIX + gint readsize; +#elif defined G_OS_WIN32 + gulong readsize; +#endif + GstClockTime timeout; + gint ret; + gboolean try_again; + + udpsrc = GST_UDPSRC_CAST (psrc); + +retry: + /* quick check, avoid going in select when we already have data */ + readsize = 0; + if (G_UNLIKELY ((ret = + IOCTL_SOCKET (udpsrc->sock.fd, FIONREAD, &readsize)) < 0)) + goto ioctl_failed; + + if (readsize > 0) + goto no_select; + + if (udpsrc->timeout > 0) { + timeout = udpsrc->timeout * GST_USECOND; + } else { + timeout = GST_CLOCK_TIME_NONE; + } + + do { + try_again = FALSE; + + GST_LOG_OBJECT (udpsrc, "doing select, timeout %" G_GUINT64_FORMAT, + udpsrc->timeout); + + ret = gst_poll_wait (udpsrc->fdset, timeout); + GST_LOG_OBJECT (udpsrc, "select returned %d", ret); + if (G_UNLIKELY (ret < 0)) { + if (errno == EBUSY) + goto stopped; +#ifdef G_OS_WIN32 + if (WSAGetLastError () != WSAEINTR) + goto select_error; +#else + if (errno != EAGAIN && errno != EINTR) + goto select_error; +#endif + try_again = TRUE; + } else if (G_UNLIKELY (ret == 0)) { + /* timeout, post element message */ + gst_element_post_message (GST_ELEMENT_CAST (udpsrc), + gst_message_new_element (GST_OBJECT_CAST (udpsrc), + gst_structure_new ("GstUDPSrcTimeout", + "timeout", G_TYPE_UINT64, udpsrc->timeout, NULL))); + try_again = TRUE; + } + } while (G_UNLIKELY (try_again)); + + /* ask how much is available for reading on the socket, this should be exactly + * one UDP packet. We will check the return value, though, because in some + * case it can return 0 and we don't want a 0 sized buffer. */ + readsize = 0; + if (G_UNLIKELY ((ret = + IOCTL_SOCKET (udpsrc->sock.fd, FIONREAD, &readsize)) < 0)) + goto ioctl_failed; + + /* if we get here and there is nothing to read from the socket, the select got + * woken up by activity on the socket but it was not a read. We know someone + * will also do something with the socket so that we don't go into an infinite + * loop in the select(). */ + if (G_UNLIKELY (!readsize)) { + clear_error (udpsrc); + goto retry; + } + +no_select: + GST_LOG_OBJECT (udpsrc, "ioctl says %d bytes available", (int) readsize); + + pktdata = g_malloc (readsize); + pktsize = readsize; + + while (TRUE) { + slen = sizeof (sa); +#ifdef G_OS_WIN32 + ret = recvfrom (udpsrc->sock.fd, (char *) pktdata, pktsize, 0, &sa.sa, + &slen); +#else + ret = recvfrom (udpsrc->sock.fd, pktdata, pktsize, 0, &sa.sa, &slen); +#endif + if (G_UNLIKELY (ret < 0)) { +#ifdef G_OS_WIN32 + /* WSAECONNRESET for a UDP socket means that a packet sent with udpsink + * generated a "port unreachable" ICMP response. We ignore that and try + * again. */ + if (WSAGetLastError () == WSAECONNRESET) { + g_free (pktdata); + pktdata = NULL; + goto retry; + } + if (WSAGetLastError () != WSAEINTR) + goto receive_error; +#else + if (errno != EAGAIN && errno != EINTR) + goto receive_error; +#endif + } else + break; + } + + /* special case buffer so receivers can also track the address */ + outbuf = gst_netbuffer_new (); + GST_BUFFER_MALLOCDATA (outbuf) = pktdata; + + /* patch pktdata and len when stripping off the headers */ + if (G_UNLIKELY (udpsrc->skip_first_bytes != 0)) { + if (G_UNLIKELY (readsize <= udpsrc->skip_first_bytes)) + goto skip_error; + + pktdata += udpsrc->skip_first_bytes; + ret -= udpsrc->skip_first_bytes; + } + GST_BUFFER_DATA (outbuf) = pktdata; + GST_BUFFER_SIZE (outbuf) = ret; + + switch (sa.sa.sa_family) { + case AF_INET: + { + gst_netaddress_set_ip4_address (&outbuf->from, sa.sa_in.sin_addr.s_addr, + sa.sa_in.sin_port); + } + break; + case AF_INET6: + { + guint8 ip6[16]; + + memcpy (ip6, &sa.sa_in6.sin6_addr, sizeof (ip6)); + gst_netaddress_set_ip6_address (&outbuf->from, ip6, sa.sa_in6.sin6_port); + } + break; + default: +#ifdef G_OS_WIN32 + WSASetLastError (WSAEAFNOSUPPORT); +#else + errno = EAFNOSUPPORT; +#endif + goto receive_error; + } + GST_LOG_OBJECT (udpsrc, "read %d bytes", (int) readsize); + + *buf = GST_BUFFER_CAST (outbuf); + + return GST_FLOW_OK; + + /* ERRORS */ +select_error: + { + GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL), + ("select error %d: %s (%d)", ret, g_strerror (errno), errno)); + return GST_FLOW_ERROR; + } +stopped: + { + GST_DEBUG ("stop called"); + return GST_FLOW_WRONG_STATE; + } +ioctl_failed: + { + GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL), + ("ioctl failed %d: %s (%d)", ret, g_strerror (errno), errno)); + return GST_FLOW_ERROR; + } +receive_error: + { + g_free (pktdata); +#ifdef G_OS_WIN32 + GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL), + ("receive error %d (WSA error: %d)", ret, WSAGetLastError ())); +#else + GST_ELEMENT_ERROR (udpsrc, RESOURCE, READ, (NULL), + ("receive error %d: %s (%d)", ret, g_strerror (errno), errno)); +#endif + return GST_FLOW_ERROR; + } +skip_error: + { + GST_ELEMENT_ERROR (udpsrc, STREAM, DECODE, (NULL), + ("UDP buffer to small to skip header")); + return GST_FLOW_ERROR; + } +} + +static gboolean +gst_udpsrc_set_uri (GstUDPSrc * src, const gchar * uri) +{ + if (gst_udp_parse_uri (uri, &src->uri) < 0) + goto wrong_uri; + + if (src->uri.port == -1) + src->uri.port = UDP_DEFAULT_PORT; + + return TRUE; + + /* ERRORS */ +wrong_uri: + { + GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), + ("error parsing uri %s", uri)); + return FALSE; + } +} + +static void +gst_udpsrc_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ + GstUDPSrc *udpsrc = GST_UDPSRC (object); + + switch (prop_id) { + case PROP_BUFFER_SIZE: + udpsrc->buffer_size = g_value_get_int (value); + break; + case PROP_PORT: + gst_udp_uri_update (&udpsrc->uri, NULL, g_value_get_int (value)); + break; + case PROP_MULTICAST_GROUP: + { + const gchar *group; + + if ((group = g_value_get_string (value))) + gst_udp_uri_update (&udpsrc->uri, group, -1); + else + gst_udp_uri_update (&udpsrc->uri, UDP_DEFAULT_MULTICAST_GROUP, -1); + break; + } + case PROP_MULTICAST_IFACE: + g_free (udpsrc->multi_iface); + + if (g_value_get_string (value) == NULL) + udpsrc->multi_iface = g_strdup (UDP_DEFAULT_MULTICAST_IFACE); + else + udpsrc->multi_iface = g_value_dup_string (value); + break; + case PROP_URI: + gst_udpsrc_set_uri (udpsrc, g_value_get_string (value)); + break; + case PROP_CAPS: + { + const GstCaps *new_caps_val = gst_value_get_caps (value); + + GstCaps *new_caps; + + GstCaps *old_caps; + + if (new_caps_val == NULL) { + new_caps = gst_caps_new_any (); + } else { + new_caps = gst_caps_copy (new_caps_val); + } + + old_caps = udpsrc->caps; + udpsrc->caps = new_caps; + if (old_caps) + gst_caps_unref (old_caps); + gst_pad_set_caps (GST_BASE_SRC (udpsrc)->srcpad, new_caps); + break; + } + case PROP_SOCKFD: + if (udpsrc->sockfd >= 0 && udpsrc->sockfd != udpsrc->sock.fd && + udpsrc->closefd) + CLOSE_SOCKET (udpsrc->sockfd); + udpsrc->sockfd = g_value_get_int (value); + GST_DEBUG ("setting SOCKFD to %d", udpsrc->sockfd); + break; + case PROP_TIMEOUT: + udpsrc->timeout = g_value_get_uint64 (value); + break; + case PROP_SKIP_FIRST_BYTES: + udpsrc->skip_first_bytes = g_value_get_int (value); + break; + case PROP_CLOSEFD: + udpsrc->closefd = g_value_get_boolean (value); + break; + case PROP_AUTO_MULTICAST: + udpsrc->auto_multicast = g_value_get_boolean (value); + break; + case PROP_REUSE: + udpsrc->reuse = g_value_get_boolean (value); + break; + default: + break; + } +} + +static void +gst_udpsrc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstUDPSrc *udpsrc = GST_UDPSRC (object); + + switch (prop_id) { + case PROP_BUFFER_SIZE: + g_value_set_int (value, udpsrc->buffer_size); + break; + case PROP_PORT: + g_value_set_int (value, udpsrc->uri.port); + break; + case PROP_MULTICAST_GROUP: + g_value_set_string (value, udpsrc->uri.host); + break; + case PROP_MULTICAST_IFACE: + g_value_set_string (value, udpsrc->multi_iface); + break; + case PROP_URI: + g_value_take_string (value, gst_udp_uri_string (&udpsrc->uri)); + break; + case PROP_CAPS: + gst_value_set_caps (value, udpsrc->caps); + break; + case PROP_SOCKFD: + g_value_set_int (value, udpsrc->sockfd); + break; + case PROP_TIMEOUT: + g_value_set_uint64 (value, udpsrc->timeout); + break; + case PROP_SKIP_FIRST_BYTES: + g_value_set_int (value, udpsrc->skip_first_bytes); + break; + case PROP_CLOSEFD: + g_value_set_boolean (value, udpsrc->closefd); + break; + case PROP_SOCK: + g_value_set_int (value, udpsrc->sock.fd); + break; + case PROP_AUTO_MULTICAST: + g_value_set_boolean (value, udpsrc->auto_multicast); + break; + case PROP_REUSE: + g_value_set_boolean (value, udpsrc->reuse); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* create a socket for sending to remote machine */ +static gboolean +gst_udpsrc_start (GstBaseSrc * bsrc) +{ + guint bc_val; + guint err_val; + gint reuse; + int port; + GstUDPSrc *src; + gint ret; + int rcvsize; + struct sockaddr_storage bind_address; + socklen_t len; + src = GST_UDPSRC (bsrc); + + if (src->sockfd == -1) { + /* need to allocate a socket */ + GST_DEBUG_OBJECT (src, "allocating socket for %s:%d", src->uri.host, + src->uri.port); + if ((ret = + gst_udp_get_addr (src->uri.host, src->uri.port, &src->myaddr)) < 0) + goto getaddrinfo_error; + + if ((ret = socket (src->myaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) + goto no_socket; + + src->sock.fd = ret; + src->externalfd = FALSE; + + GST_DEBUG_OBJECT (src, "got socket %d", src->sock.fd); + + GST_DEBUG_OBJECT (src, "setting reuse %d", src->reuse); + reuse = src->reuse ? 1 : 0; + if ((ret = + setsockopt (src->sock.fd, SOL_SOCKET, SO_REUSEADDR, &reuse, + sizeof (reuse))) < 0) + goto setsockopt_error; + + GST_DEBUG_OBJECT (src, "binding on port %d", src->uri.port); + + /* Take a temporary copy of the address in case we need to fix it for bind */ + memcpy (&bind_address, &src->myaddr, sizeof (struct sockaddr_storage)); + +#ifdef G_OS_WIN32 + /* Windows does not allow binding to a multicast group so fix source address */ + if (gst_udp_is_multicast (&src->myaddr)) { + switch (((struct sockaddr *) &bind_address)->sa_family) { + case AF_INET: + ((struct sockaddr_in *) &bind_address)->sin_addr.s_addr = + htonl (INADDR_ANY); + break; + case AF_INET6: + ((struct sockaddr_in6 *) &bind_address)->sin6_addr = in6addr_any; + break; + default: + break; + } + } +#endif + + len = gst_udp_get_sockaddr_length (&bind_address); + if ((ret = bind (src->sock.fd, (struct sockaddr *) &bind_address, len)) < 0) + goto bind_error; + + if (!gst_udp_is_multicast (&src->myaddr)) { + len = sizeof (src->myaddr); + if ((ret = getsockname (src->sock.fd, (struct sockaddr *) &src->myaddr, + &len)) < 0) + goto getsockname_error; + } + } else { + GST_DEBUG_OBJECT (src, "using provided socket %d", src->sockfd); + /* we use the configured socket, try to get some info about it */ + len = sizeof (src->myaddr); + if ((ret = + getsockname (src->sockfd, (struct sockaddr *) &src->myaddr, + &len)) < 0) + goto getsockname_error; + + src->sock.fd = src->sockfd; + src->externalfd = TRUE; + } + + len = sizeof (rcvsize); + if (src->buffer_size != 0) { + rcvsize = src->buffer_size; + + GST_DEBUG_OBJECT (src, "setting udp buffer of %d bytes", rcvsize); + /* set buffer size, Note that on Linux this is typically limited to a + * maximum of around 100K. Also a minimum of 128 bytes is required on + * Linux. */ + ret = + setsockopt (src->sock.fd, SOL_SOCKET, SO_RCVBUF, (void *) &rcvsize, + len); + if (ret != 0) { + GST_ELEMENT_WARNING (src, RESOURCE, SETTINGS, (NULL), + ("Could not create a buffer of requested %d bytes, %d: %s (%d)", + rcvsize, ret, g_strerror (errno), errno)); + } + } + + /* read the value of the receive buffer. Note that on linux this returns 2x the + * value we set because the kernel allocates extra memory for metadata. + * The default on Linux is about 100K (which is about 50K without metadata) */ + ret = + getsockopt (src->sock.fd, SOL_SOCKET, SO_RCVBUF, (void *) &rcvsize, &len); + if (ret == 0) + GST_DEBUG_OBJECT (src, "have udp buffer of %d bytes", rcvsize); + else + GST_DEBUG_OBJECT (src, "could not get udp buffer size"); + + bc_val = 1; + if ((ret = setsockopt (src->sock.fd, SOL_SOCKET, SO_BROADCAST, &bc_val, + sizeof (bc_val))) < 0) { + GST_ELEMENT_WARNING (src, RESOURCE, SETTINGS, (NULL), + ("could not configure socket for broadcast %d: %s (%d)", ret, + g_strerror (errno), errno)); + } + + /* Accept ERRQUEUE to get and flush icmp errors */ + err_val = 1; +#if defined (IP_RECVERR) + if ((ret = setsockopt (src->sock.fd, IPPROTO_IP, IP_RECVERR, &err_val, + sizeof (err_val))) < 0) { + GST_ELEMENT_WARNING (src, RESOURCE, SETTINGS, (NULL), + ("could not configure socket for IP_RECVERR %d: %s (%d)", ret, + g_strerror (errno), errno)); + } +#endif + + if (src->auto_multicast && gst_udp_is_multicast (&src->myaddr)) { + GST_DEBUG_OBJECT (src, "joining multicast group %s", src->uri.host); + ret = gst_udp_join_group (src->sock.fd, &src->myaddr, src->multi_iface); + if (ret < 0) + goto membership; + } + + /* NOTE: sockaddr_in.sin_port works for ipv4 and ipv6 because sin_port + * follows ss_family on both */ + port = g_ntohs (((struct sockaddr_in *) &src->myaddr)->sin_port); + GST_DEBUG_OBJECT (src, "bound, on port %d", port); + if (port != src->uri.port) { + src->uri.port = port; + GST_DEBUG_OBJECT (src, "notifying port %d", port); + g_object_notify (G_OBJECT (src), "port"); + } + + if ((src->fdset = gst_poll_new (TRUE)) == NULL) + goto no_fdset; + + gst_poll_add_fd (src->fdset, &src->sock); + gst_poll_fd_ctl_read (src->fdset, &src->sock, TRUE); + + return TRUE; + + /* ERRORS */ +getaddrinfo_error: + { + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("getaddrinfo failed: %s (%d)", gai_strerror (ret), ret)); + return FALSE; + } +no_socket: + { + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), + ("no socket error %d: %s (%d)", ret, g_strerror (errno), errno)); + return FALSE; + } +setsockopt_error: + { + CLOSE_IF_REQUESTED (src); + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("setsockopt failed %d: %s (%d)", ret, g_strerror (errno), errno)); + return FALSE; + } +bind_error: + { + CLOSE_IF_REQUESTED (src); + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("bind failed %d: %s (%d)", ret, g_strerror (errno), errno)); + return FALSE; + } +membership: + { + CLOSE_IF_REQUESTED (src); + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("could add membership %d: %s (%d)", ret, g_strerror (errno), errno)); + return FALSE; + } +getsockname_error: + { + CLOSE_IF_REQUESTED (src); + GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), + ("getsockname failed %d: %s (%d)", ret, g_strerror (errno), errno)); + return FALSE; + } +no_fdset: + { + CLOSE_IF_REQUESTED (src); + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ_WRITE, (NULL), + ("could not create an fdset %d: %s (%d)", ret, g_strerror (errno), + errno)); + return FALSE; + } +} + +static gboolean +gst_udpsrc_unlock (GstBaseSrc * bsrc) +{ + GstUDPSrc *src; + + src = GST_UDPSRC (bsrc); + + GST_LOG_OBJECT (src, "Flushing"); + gst_poll_set_flushing (src->fdset, TRUE); + + return TRUE; +} + +static gboolean +gst_udpsrc_unlock_stop (GstBaseSrc * bsrc) +{ + GstUDPSrc *src; + + src = GST_UDPSRC (bsrc); + + GST_LOG_OBJECT (src, "No longer flushing"); + gst_poll_set_flushing (src->fdset, FALSE); + + return TRUE; +} + +static gboolean +gst_udpsrc_stop (GstBaseSrc * bsrc) +{ + GstUDPSrc *src; + + src = GST_UDPSRC (bsrc); + + GST_DEBUG ("stopping, closing sockets"); + + if (src->sock.fd >= 0) { + if (src->auto_multicast && gst_udp_is_multicast (&src->myaddr)) { + GST_DEBUG_OBJECT (src, "leaving multicast group %s", src->uri.host); + gst_udp_leave_group (src->sock.fd, &src->myaddr); + } + CLOSE_IF_REQUESTED (src); + } + + if (src->fdset) { + gst_poll_free (src->fdset); + src->fdset = NULL; + } + + return TRUE; +} + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static GstURIType +gst_udpsrc_uri_get_type (void) +{ + return GST_URI_SRC; +} + +static gchar ** +gst_udpsrc_uri_get_protocols (void) +{ + static gchar *protocols[] = { (char *) "udp", NULL }; + + return protocols; +} + +static const gchar * +gst_udpsrc_uri_get_uri (GstURIHandler * handler) +{ + GstUDPSrc *src = GST_UDPSRC (handler); + + g_free (src->uristr); + src->uristr = gst_udp_uri_string (&src->uri); + + return src->uristr; +} + +static gboolean +gst_udpsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri) +{ + gboolean ret; + + GstUDPSrc *src = GST_UDPSRC (handler); + + ret = gst_udpsrc_set_uri (src, uri); + + return ret; +} + +static void +gst_udpsrc_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_udpsrc_uri_get_type; + iface->get_protocols = gst_udpsrc_uri_get_protocols; + iface->get_uri = gst_udpsrc_uri_get_uri; + iface->set_uri = gst_udpsrc_uri_set_uri; +} diff --git a/gst/udp/gstudpsrc.h b/gst/udp/gstudpsrc.h new file mode 100644 index 0000000..48c9f16 --- /dev/null +++ b/gst/udp/gstudpsrc.h @@ -0,0 +1,88 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * 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_UDPSRC_H__ +#define __GST_UDPSRC_H__ + +#include <gst/gst.h> +#include <gst/base/gstpushsrc.h> + +G_BEGIN_DECLS + +#include <errno.h> +#include <string.h> +#include <sys/types.h> + +#include "gstudpnetutils.h" + +#include "gstudp.h" + +#define GST_TYPE_UDPSRC \ + (gst_udpsrc_get_type()) +#define GST_UDPSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_UDPSRC,GstUDPSrc)) +#define GST_UDPSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_UDPSRC,GstUDPSrcClass)) +#define GST_IS_UDPSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_UDPSRC)) +#define GST_IS_UDPSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_UDPSRC)) +#define GST_UDPSRC_CAST(obj) ((GstUDPSrc *)(obj)) + +typedef struct _GstUDPSrc GstUDPSrc; +typedef struct _GstUDPSrcClass GstUDPSrcClass; + +struct _GstUDPSrc { + GstPushSrc parent; + + /* properties */ + GstUDPUri uri; + gchar *multi_iface; + gint ttl; + GstCaps *caps; + gint buffer_size; + guint64 timeout; + gint skip_first_bytes; + int sockfd; + gboolean closefd; + gboolean auto_multicast; + gboolean reuse; + + /* our sockets */ + GstPollFD sock; + GstPoll *fdset; + gboolean externalfd; + gboolean is_ipv6; + + struct sockaddr_storage myaddr; + + gchar *uristr; +}; + +struct _GstUDPSrcClass { + GstPushSrcClass parent_class; +}; + +GType gst_udpsrc_get_type(void); + +G_END_DECLS + + +#endif /* __GST_UDPSRC_H__ */ |