diff options
Diffstat (limited to 'libgupnp-dlna')
-rw-r--r-- | libgupnp-dlna/Makefile.am | 86 | ||||
-rw-r--r-- | libgupnp-dlna/Makefile.in | 764 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-discoverer.c | 443 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-discoverer.h | 116 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-information.c | 265 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-information.h | 80 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-marshal.c | 153 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-marshal.h | 28 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-marshal.list | 2 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-profile-private.h | 44 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-profile.c | 386 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-profile.h | 77 | ||||
-rw-r--r-- | libgupnp-dlna/gupnp-dlna-profiles.c | 545 | ||||
-rw-r--r-- | libgupnp-dlna/profile-loading.c | 921 | ||||
-rw-r--r-- | libgupnp-dlna/profile-loading.h | 56 |
15 files changed, 3966 insertions, 0 deletions
diff --git a/libgupnp-dlna/Makefile.am b/libgupnp-dlna/Makefile.am new file mode 100644 index 0000000..55f4ed2 --- /dev/null +++ b/libgupnp-dlna/Makefile.am @@ -0,0 +1,86 @@ +# Version format current:revision:age +# If the library source code has changed at all since the last update, then +# increment revision (‘c:r:a’ becomes ‘c:r+1:a’). +# If any interfaces have been added, removed, or changed since the last update, +# increment current, and set revision to 0. +# If any interfaces have been added since the last public release, then +# increment age. +# If any interfaces have been removed since the last public release, then set +# age to 0. +LTVERSION = $(GUPNP_DLNA_VERSION_INFO) + +shareddir = $(datadir)/gupnp-dlna + +AM_CFLAGS = -I$(top_srcdir) \ + $(LIBXML_CFLAGS) \ + $(GST_CFLAGS) \ + $(GST_PBU_CFLAGS) \ + -DDATA_DIR='"$(shareddir)"' \ + -DG_LOG_DOMAIN='"gupnp-dlna"' + +lib_LTLIBRARIES = libgupnp-dlna-1.0.la + +gupnp-dlna-marshal.c: gupnp-dlna-marshal.list + $(AM_V_GEN) \ + $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header --body > gupnp-dlna-marshal.c + +gupnp-dlna-marshal.h: gupnp-dlna-marshal.list + $(AM_V_GEN) \ + $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header > gupnp-dlna-marshal.h + +BUILT_SOURCES = gupnp-dlna-marshal.c gupnp-dlna-marshal.h + +libgupnp_dlna_incdir = $(includedir)/gupnp-dlna-1.0/libgupnp-dlna + +libgupnp_dlna_1_0_la_LDFLAGS = -version-info $(LTVERSION) -no-undefined + +libgupnp_dlna_inc_HEADERS = gupnp-dlna-profile.h \ + gupnp-dlna-information.h \ + gupnp-dlna-discoverer.h + +noinst_HEADERS = profile-loading.h \ + gupnp-dlna-profile-private.h + +introspection_sources = $(libgupnp_dlna_inc_HEADERS) \ + gupnp-dlna-information.c \ + gupnp-dlna-discoverer.c \ + gupnp-dlna-profile.c \ + gupnp-dlna-profiles.c \ + profile-loading.c + +libgupnp_dlna_1_0_la_SOURCES = $(introspection_sources) \ + $(BUILT_SOURCES) + +libgupnp_dlna_1_0_la_LIBADD = $(LIBXML_LIBS) \ + $(GST_PBU_LIBS) + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --warn-all \ + --symbol-prefix=gupnp_dlna \ + --identifier-prefix=GUPnPDLNA \ + --add-include-path=$(top_srcdir) \ + --add-init-section="gst_init(NULL, NULL);" +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_srcdir) + +if HAVE_INTROSPECTION +GUPnPDLNA-1.0.gir: libgupnp-dlna-1.0.la +GUPnPDLNA_1_0_gir_INCLUDES = libxml2-2.0 GObject-2.0 GstPbutils-0.10 +GUPnPDLNA_1_0_gir_CFLAGS = $(INCLUDES) $(AM_CFLAGS) +GUPnPDLNA_1_0_gir_LIBS = libgupnp-dlna-1.0.la gstreamer-0.10 +GUPnPDLNA_1_0_gir_FILES = $(introspection_sources) +GUPnPDLNA_1_0_gir_NAMESPACE = GUPnPDLNA +GUPnPDLNA_1_0_gir_VERSION = 1.0 + +INTROSPECTION_GIRS += GUPnPDLNA-1.0.gir + +girdir = $(datadir)/gir-1.0 +gir_DATA = GUPnPDLNA-1.0.gir + +typelibdir = $(libdir)/girepository-1.0/ +typelib_DATA = $(gir_DATA:.gir=.typelib) +endif + +EXTRA_DIST = gupnp-dlna-marshal.list + +CLEANFILES = $(BUILT_SOURCES) $(gir_DATA) $(typelib_DATA) diff --git a/libgupnp-dlna/Makefile.in b/libgupnp-dlna/Makefile.in new file mode 100644 index 0000000..da6a808 --- /dev/null +++ b/libgupnp-dlna/Makefile.in @@ -0,0 +1,764 @@ +# Makefile.in generated by automake 1.11.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +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@ +@HAVE_INTROSPECTION_TRUE@am__append_1 = GUPnPDLNA-1.0.gir +subdir = libgupnp-dlna +DIST_COMMON = $(libgupnp_dlna_inc_HEADERS) $(noinst_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gtk-doc.m4 \ + $(top_srcdir)/m4/introspection.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)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(girdir)" \ + "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(libgupnp_dlna_incdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgupnp_dlna_1_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__objects_1 = +am__objects_2 = $(am__objects_1) gupnp-dlna-information.lo \ + gupnp-dlna-discoverer.lo gupnp-dlna-profile.lo \ + gupnp-dlna-profiles.lo profile-loading.lo +am__objects_3 = gupnp-dlna-marshal.lo +am_libgupnp_dlna_1_0_la_OBJECTS = $(am__objects_2) $(am__objects_3) +libgupnp_dlna_1_0_la_OBJECTS = $(am_libgupnp_dlna_1_0_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +libgupnp_dlna_1_0_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgupnp_dlna_1_0_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libgupnp_dlna_1_0_la_SOURCES) +DIST_SOURCES = $(libgupnp_dlna_1_0_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(gir_DATA) $(typelib_DATA) +HEADERS = $(libgupnp_dlna_inc_HEADERS) $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GREP = @GREP@ +GST_CFLAGS = @GST_CFLAGS@ +GST_LIBS = @GST_LIBS@ +GST_MAJORMINOR = @GST_MAJORMINOR@ +GST_PBU_CFLAGS = @GST_PBU_CFLAGS@ +GST_PBU_LIBS = @GST_PBU_LIBS@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GUPNP_DLNA_VERSION_INFO = @GUPNP_DLNA_VERSION_INFO@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@ +INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ +INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@ +INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@ +INTROSPECTION_LIBS = @INTROSPECTION_LIBS@ +INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ +INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBXML_CFLAGS = @LIBXML_CFLAGS@ +LIBXML_LIBS = @LIBXML_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +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@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +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@ + +# Version format current:revision:age +# If the library source code has changed at all since the last update, then +# increment revision (‘c:r:a’ becomes ‘c:r+1:a’). +# If any interfaces have been added, removed, or changed since the last update, +# increment current, and set revision to 0. +# If any interfaces have been added since the last public release, then +# increment age. +# If any interfaces have been removed since the last public release, then set +# age to 0. +LTVERSION = $(GUPNP_DLNA_VERSION_INFO) +shareddir = $(datadir)/gupnp-dlna +AM_CFLAGS = -I$(top_srcdir) \ + $(LIBXML_CFLAGS) \ + $(GST_CFLAGS) \ + $(GST_PBU_CFLAGS) \ + -DDATA_DIR='"$(shareddir)"' \ + -DG_LOG_DOMAIN='"gupnp-dlna"' + +lib_LTLIBRARIES = libgupnp-dlna-1.0.la +BUILT_SOURCES = gupnp-dlna-marshal.c gupnp-dlna-marshal.h +libgupnp_dlna_incdir = $(includedir)/gupnp-dlna-1.0/libgupnp-dlna +libgupnp_dlna_1_0_la_LDFLAGS = -version-info $(LTVERSION) -no-undefined +libgupnp_dlna_inc_HEADERS = gupnp-dlna-profile.h \ + gupnp-dlna-information.h \ + gupnp-dlna-discoverer.h + +noinst_HEADERS = profile-loading.h \ + gupnp-dlna-profile-private.h + +introspection_sources = $(libgupnp_dlna_inc_HEADERS) \ + gupnp-dlna-information.c \ + gupnp-dlna-discoverer.c \ + gupnp-dlna-profile.c \ + gupnp-dlna-profiles.c \ + profile-loading.c + +libgupnp_dlna_1_0_la_SOURCES = $(introspection_sources) \ + $(BUILT_SOURCES) + +libgupnp_dlna_1_0_la_LIBADD = $(LIBXML_LIBS) \ + $(GST_PBU_LIBS) + +INTROSPECTION_GIRS = $(am__append_1) +INTROSPECTION_SCANNER_ARGS = --warn-all \ + --symbol-prefix=gupnp_dlna \ + --identifier-prefix=GUPnPDLNA \ + --add-include-path=$(top_srcdir) \ + --add-init-section="gst_init(NULL, NULL);" + +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_srcdir) +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_INCLUDES = libxml2-2.0 GObject-2.0 GstPbutils-0.10 +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_CFLAGS = $(INCLUDES) $(AM_CFLAGS) +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_LIBS = libgupnp-dlna-1.0.la gstreamer-0.10 +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_FILES = $(introspection_sources) +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_NAMESPACE = GUPnPDLNA +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA_1_0_gir_VERSION = 1.0 +@HAVE_INTROSPECTION_TRUE@girdir = $(datadir)/gir-1.0 +@HAVE_INTROSPECTION_TRUE@gir_DATA = GUPnPDLNA-1.0.gir +@HAVE_INTROSPECTION_TRUE@typelibdir = $(libdir)/girepository-1.0/ +@HAVE_INTROSPECTION_TRUE@typelib_DATA = $(gir_DATA:.gir=.typelib) +EXTRA_DIST = gupnp-dlna-marshal.list +CLEANFILES = $(BUILT_SOURCES) $(gir_DATA) $(typelib_DATA) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(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 libgupnp-dlna/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libgupnp-dlna/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: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_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 +libgupnp-dlna-1.0.la: $(libgupnp_dlna_1_0_la_OBJECTS) $(libgupnp_dlna_1_0_la_DEPENDENCIES) $(EXTRA_libgupnp_dlna_1_0_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgupnp_dlna_1_0_la_LINK) -rpath $(libdir) $(libgupnp_dlna_1_0_la_OBJECTS) $(libgupnp_dlna_1_0_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gupnp-dlna-discoverer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gupnp-dlna-information.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gupnp-dlna-marshal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gupnp-dlna-profile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gupnp-dlna-profiles.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile-loading.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-girDATA: $(gir_DATA) + @$(NORMAL_INSTALL) + @list='$(gir_DATA)'; test -n "$(girdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(girdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(girdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(girdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(girdir)" || exit $$?; \ + done + +uninstall-girDATA: + @$(NORMAL_UNINSTALL) + @list='$(gir_DATA)'; test -n "$(girdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(girdir)'; $(am__uninstall_files_from_dir) +install-typelibDATA: $(typelib_DATA) + @$(NORMAL_INSTALL) + @list='$(typelib_DATA)'; test -n "$(typelibdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(typelibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(typelibdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(typelibdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(typelibdir)" || exit $$?; \ + done + +uninstall-typelibDATA: + @$(NORMAL_UNINSTALL) + @list='$(typelib_DATA)'; test -n "$(typelibdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(typelibdir)'; $(am__uninstall_files_from_dir) +install-libgupnp_dlna_incHEADERS: $(libgupnp_dlna_inc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libgupnp_dlna_inc_HEADERS)'; test -n "$(libgupnp_dlna_incdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libgupnp_dlna_incdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libgupnp_dlna_incdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgupnp_dlna_incdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgupnp_dlna_incdir)" || exit $$?; \ + done + +uninstall-libgupnp_dlna_incHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libgupnp_dlna_inc_HEADERS)'; test -n "$(libgupnp_dlna_incdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libgupnp_dlna_incdir)'; $(am__uninstall_files_from_dir) + +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) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(girdir)" "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(libgupnp_dlna_incdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -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-libLTLIBRARIES clean-libtool \ + 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-girDATA install-libgupnp_dlna_incHEADERS \ + install-typelibDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +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-girDATA uninstall-libLTLIBRARIES \ + uninstall-libgupnp_dlna_incHEADERS uninstall-typelibDATA + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool 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-girDATA \ + install-html install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-libgupnp_dlna_incHEADERS \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip install-typelibDATA 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-girDATA \ + uninstall-libLTLIBRARIES uninstall-libgupnp_dlna_incHEADERS \ + uninstall-typelibDATA + + +gupnp-dlna-marshal.c: gupnp-dlna-marshal.list + $(AM_V_GEN) \ + $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header --body > gupnp-dlna-marshal.c + +gupnp-dlna-marshal.h: gupnp-dlna-marshal.list + $(AM_V_GEN) \ + $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header > gupnp-dlna-marshal.h + +-include $(INTROSPECTION_MAKEFILE) + +@HAVE_INTROSPECTION_TRUE@GUPnPDLNA-1.0.gir: libgupnp-dlna-1.0.la + +# 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/libgupnp-dlna/gupnp-dlna-discoverer.c b/libgupnp-dlna/gupnp-dlna-discoverer.c new file mode 100644 index 0000000..faf21ff --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-discoverer.c @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "gupnp-dlna-discoverer.h" +#include "gupnp-dlna-marshal.h" +#include "profile-loading.h" + +/** + * SECTION:gupnp-dlna-discoverer + * @short_description: Utility API for discovering DLNA profile/mime type and + * other metadata for given media. + * + * The GUPnPDLNADiscoverer object provides a light-weight wrapper over the + * #GstDiscoverer API. The latter provides a simple interface to discover + * media metadata given a URI. GUPnPDLNADiscoverer extends this API to also + * provide a DLNA profile name and mime type for the media. + * + * The API provided corresponds very closely to the API provided by + * #GstDiscoverer - both synchronous and asynchronous discovery of metadata + * are possible. + * + * The asynchronous mode requires a running #GMainLoop in the default + * #GMainContext, where one connects to the various signals, appends the + * URIs to be processed and then asks for the discovery to begin. + */ +enum { + DONE, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST]; + + +G_DEFINE_TYPE (GUPnPDLNADiscoverer, gupnp_dlna_discoverer, GST_TYPE_DISCOVERER) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + GUPNP_TYPE_DLNA_DISCOVERER, \ + GUPnPDLNADiscovererPrivate)) + +typedef struct _GUPnPDLNADiscovererPrivate GUPnPDLNADiscovererPrivate; + +struct _GUPnPDLNADiscovererPrivate { + gboolean relaxed_mode; + gboolean extended_mode; +}; + +enum { + PROP_0, + PROP_DLNA_RELAXED_MODE, + PROP_DLNA_EXTENDED_MODE, +}; + +static void +gupnp_dlna_discoverer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNADiscoverer *self = GUPNP_DLNA_DISCOVERER (object); + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_RELAXED_MODE: + priv->relaxed_mode = g_value_get_boolean (value); + break; + + case PROP_DLNA_EXTENDED_MODE: + priv->extended_mode = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + property_id, + pspec); + break; + } +} + +static void +gupnp_dlna_discoverer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNADiscoverer *self = GUPNP_DLNA_DISCOVERER (object); + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_RELAXED_MODE: + g_value_set_boolean (value, priv->relaxed_mode); + break; + + case PROP_DLNA_EXTENDED_MODE: + g_value_set_boolean (value, priv->extended_mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + property_id, + pspec); + break; + } +} + +static void +gupnp_dlna_discoverer_dispose (GObject *object) +{ + G_OBJECT_CLASS (gupnp_dlna_discoverer_parent_class)->dispose (object); +} + +static void +gupnp_dlna_discoverer_finalize (GObject *object) +{ + G_OBJECT_CLASS (gupnp_dlna_discoverer_parent_class)->finalize (object); +} + +static void +gupnp_dlna_discovered_cb (GstDiscoverer *discoverer, + GstDiscovererInfo *info, + GError *err) +{ + GUPnPDLNAInformation *dlna = NULL; + GUPnPDLNADiscovererClass *klass = + GUPNP_DLNA_DISCOVERER_GET_CLASS (discoverer); + GUPnPDLNADiscovererPrivate *priv = + GET_PRIVATE (GUPNP_DLNA_DISCOVERER (discoverer)); + gboolean relaxed = priv->relaxed_mode; + gboolean extended = priv->extended_mode; + + if (info) + dlna = gupnp_dlna_information_new_from_discoverer_info + (info, + klass->profiles_list + [relaxed][extended]); + + g_signal_emit (GUPNP_DLNA_DISCOVERER (discoverer), + signals[DONE], 0, dlna, err); + + if (dlna) + g_object_unref (dlna); +} + +static void +gupnp_dlna_discoverer_class_init (GUPnPDLNADiscovererClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (GUPnPDLNADiscovererPrivate)); + + object_class->get_property = gupnp_dlna_discoverer_get_property; + object_class->set_property = gupnp_dlna_discoverer_set_property; + object_class->dispose = gupnp_dlna_discoverer_dispose; + object_class->finalize = gupnp_dlna_discoverer_finalize; + + /** + * GUPnPDLNADiscoverer::relaxed-mode: + * @relaxed_mode: setting to true will enable relaxed mode + * + * The current release does not support relaxed mode yet + */ + pspec = g_param_spec_boolean ("relaxed-mode", + "Relaxed mode property", + "Indicates that profile matching should" + "be strictly compliant with the DLNA " + "specification", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_DLNA_RELAXED_MODE, + pspec); + + /** + * GUPnPDLNADiscoverer::extended-mode: + * @extended: setting true will enable extended profile support + * + * The current release does not support extended mode yet + */ + pspec = g_param_spec_boolean ("extended-mode", + "Extended mode property", + "Indicates support for profiles that are " + "not part of the DLNA specification", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_DLNA_EXTENDED_MODE, + pspec); + + /** + * GUPnPDLNADiscoverer::done: + * @discoverer: the #GUPnPDLNADiscoverer + * @dlna: the results as #GUPnPDLNAInformation + * @err: contains details of the error if discovery fails, else is NULL + * + * Will be emitted when all information on a URI could be discovered. + * + * The reciever must unref @dlna with when done using it. + */ + signals[DONE] = + g_signal_new ("done", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GUPnPDLNADiscovererClass, done), + NULL, NULL, + gupnp_dlna_marshal_VOID__OBJECT_BOXED, + G_TYPE_NONE, 2, GUPNP_TYPE_DLNA_INFORMATION, + GST_TYPE_G_ERROR); + + /* Load DLNA profiles from disk */ + if (g_type_from_name ("GstElement")) { + klass->profiles_list [0][0] + = gupnp_dlna_load_profiles_from_disk (FALSE, + FALSE); + klass->profiles_list [0][1] + = gupnp_dlna_load_profiles_from_disk (FALSE, + TRUE); + klass->profiles_list [1][0] + = gupnp_dlna_load_profiles_from_disk (TRUE, + FALSE); + klass->profiles_list [1][1] + = gupnp_dlna_load_profiles_from_disk (TRUE, + TRUE); + } else { + klass->profiles_list [0][0] = NULL; + klass->profiles_list [0][1] = NULL; + klass->profiles_list [1][0] = NULL; + klass->profiles_list [1][1] = NULL; + g_warning ("GStreamer has not yet been initialised. You need " + "to call gst_init()/gst_init_check() for discovery " + "to work."); + } +} + +static void +gupnp_dlna_discoverer_init (GUPnPDLNADiscoverer *self) +{ + g_signal_connect (&self->parent, + "discovered", + G_CALLBACK (gupnp_dlna_discovered_cb), + NULL); +} + +/** + * gupnp_dlna_discoverer_new: + * @timeout: default discovery timeout, in nanoseconds + * @relaxed_mode: set to TRUE, to enable relaxed mode support. FALSE otherwise + * @extended_mode: set to TRUE, to enable extended mode support. FALSE otherwise + * + * Creates a new #GUPnPDLNADiscoverer object with the given default timeout + * value. + * + * Returns: A new #GUPnPDLNADiscoverer object. + */ +GUPnPDLNADiscoverer* +gupnp_dlna_discoverer_new (GstClockTime timeout, + gboolean relaxed_mode, + gboolean extended_mode) +{ + return g_object_new (GUPNP_TYPE_DLNA_DISCOVERER, + "timeout", timeout, + "relaxed-mode", relaxed_mode, + "extended-mode", extended_mode, + NULL); +} + +/* Asynchronous API */ + +/** + * gupnp_dlna_discoverer_start: + * @discoverer: #GUPnPDLNADiscoverer object to start discovery on + * + * Allows asynchronous discovery of URIs to begin. + */ + +/** + * gupnp_dlna_discoverer_stop: + * @discoverer: #GUPnPDLNADiscoverer object to stop discovery on + * + * Stops asynchronous discovery of URIs. + */ + +/** + * gupnp_dlna_discoverer_discover_uri: + * @discoverer: #GUPnPDLNADiscoverer object to use for discovery + * @uri: URI to gather metadata for + * + * Queues @uri for metadata discovery. When discovery is completed, the + * "discovered" signal is emitted on @discoverer. + * + * Returns: TRUE if @uri was successfully queued, FALSE otherwise. + */ +gboolean +gupnp_dlna_discoverer_discover_uri (GUPnPDLNADiscoverer *discoverer, + const gchar *uri) +{ + return gst_discoverer_discover_uri_async (GST_DISCOVERER (discoverer), + uri); +} + +/* Synchronous API */ + +/** + * gupnp_dlna_discoverer_discover_uri_sync: + * @discoverer: #GUPnPDLNADiscoverer object to use for discovery + * @uri: URI to gather metadata for + * @err: contains details of the error if discovery fails, else is NULL + * + * Synchronously gathers metadata for @uri. + * + * Returns: (transfer full): a #GUPnPDLNAInformation with the metadata for @uri + * on success, NULL otherwise + */ +GUPnPDLNAInformation * +gupnp_dlna_discoverer_discover_uri_sync (GUPnPDLNADiscoverer *discoverer, + const gchar *uri, + GError **err) +{ + GstDiscovererInfo *info; + GUPnPDLNADiscovererClass *klass = + GUPNP_DLNA_DISCOVERER_GET_CLASS (discoverer); + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (discoverer); + gboolean relaxed = priv->relaxed_mode; + gboolean extended = priv->extended_mode; + + info = gst_discoverer_discover_uri (GST_DISCOVERER (discoverer), + uri, + err); + + if (info) + return gupnp_dlna_information_new_from_discoverer_info + (info, klass->profiles_list [relaxed][extended]); + + return NULL; +} + +/** + * gupnp_dlna_discoverer_get_profile: + * @self: The #GUPnPDLNADiscoverer object + * @name: The name of the DLNA profile to be retrieved + * + * Given @name, this finds the corresponding DLNA profile information (stored + * as a #GUPnPDLNAProfile). + * + * Returns: (transfer full): a #GUPnPDLNAProfile on success, NULL otherwise. + **/ +GUPnPDLNAProfile * +gupnp_dlna_discoverer_get_profile (GUPnPDLNADiscoverer *self, + const gchar *name) +{ + GList *i; + GUPnPDLNADiscovererClass *klass; + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + gboolean relaxed = priv->relaxed_mode; + gboolean extended = priv->extended_mode; + + g_return_val_if_fail (self != NULL, NULL); + klass = GUPNP_DLNA_DISCOVERER_GET_CLASS (self); + + for (i = klass->profiles_list [relaxed][extended]; + i != NULL; + i = i->next) { + GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *) i->data; + + if (g_str_equal (gupnp_dlna_profile_get_name (profile), name)) { + g_object_ref (profile); + return profile; + } + } + + return NULL; +} + +/** + * gupnp_dlna_discoverer_list_profiles: + * @self: The #GUPnPDLNADiscoverer whose profile list is required + * + * Retuns a list of the all the DLNA profiles supported by @self. + * + * Returns: (transfer none) (element-type GUPnPDLNAProfile*): a #GList of + * #GUPnPDLNAProfile on success, NULL otherwise. + **/ +const GList * +gupnp_dlna_discoverer_list_profiles (GUPnPDLNADiscoverer *self) +{ + GUPnPDLNADiscovererClass *klass; + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + gboolean relaxed = priv->relaxed_mode; + gboolean extended = priv->extended_mode; + + g_return_val_if_fail (self != NULL, NULL); + + klass = GUPNP_DLNA_DISCOVERER_GET_CLASS (self); + + return klass->profiles_list [relaxed][extended]; +} + +/** + * gupnp_dlna_discoverer_get_relaxed_mode: + * @self: The #GUPnPDLNADiscoverer object + * + * Returns: true if relaxed mode is set and false otherwise + */ +gboolean +gupnp_dlna_discoverer_get_relaxed_mode (GUPnPDLNADiscoverer *self) +{ + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + + return priv->relaxed_mode; +} + +/** + * gupnp_dlna_discoverer_get_extended_mode: + * @self: The #GUPnPDLNADiscoverer object + * + * Returns: true if application is using extended mode and false otherwise + */ +gboolean +gupnp_dlna_discoverer_get_extended_mode (GUPnPDLNADiscoverer *self) +{ + GUPnPDLNADiscovererPrivate *priv = GET_PRIVATE (self); + + return priv->extended_mode; +} diff --git a/libgupnp-dlna/gupnp-dlna-discoverer.h b/libgupnp-dlna/gupnp-dlna-discoverer.h new file mode 100644 index 0000000..bdf9e45 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-discoverer.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 _GUPNP_DLNA_DISCOVERER +#define _GUPNP_DLNA_DISCOVERER + +#include <glib-object.h> +#include <gst/pbutils/pbutils.h> +#include "gupnp-dlna-information.h" +#include "gupnp-dlna-profile.h" + +G_BEGIN_DECLS + +#define GUPNP_TYPE_DLNA_DISCOVERER gupnp_dlna_discoverer_get_type() + +#define GUPNP_DLNA_DISCOVERER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GUPNP_TYPE_DLNA_DISCOVERER, \ + GUPnPDLNADiscoverer)) + +#define GUPNP_DLNA_DISCOVERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GUPNP_TYPE_DLNA_DISCOVERER, \ + GUPnPDLNADiscovererClass)) + +#define GUPNP_IS_DLNA_DISCOVERER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GUPNP_TYPE_DLNA_DISCOVERER)) + +#define GUPNP_IS_DLNA_DISCOVERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GUPNP_TYPE_DLNA_DISCOVERER)) + +#define GUPNP_DLNA_DISCOVERER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GUPNP_TYPE_DLNA_DISCOVERER, \ + GUPnPDLNADiscovererClass)) + +/** + * GUPnPDLNADiscoverer: + * + * The top-level object used to for metadata extraction. + */ +typedef struct { + GstDiscoverer parent; +} GUPnPDLNADiscoverer; + +typedef struct { + GstDiscovererClass parent_class; + + /*< signals >*/ + void (*done) (GUPnPDLNADiscoverer *discoverer, + GUPnPDLNAInformation *dlna, + GError *err); + + /*< private >*/ + GList *profiles_list[2][2]; + +} GUPnPDLNADiscovererClass; + +GType gupnp_dlna_discoverer_get_type (void); + +GUPnPDLNADiscoverer * +gupnp_dlna_discoverer_new (GstClockTime timeout, + gboolean relaxed_mode, + gboolean extended_mode); + +/* Asynchronous API */ +#define gupnp_dlna_discoverer_start(discoverer) \ + gst_discoverer_start(GST_DISCOVERER((discoverer))) +#define gupnp_dlna_discoverer_stop(discoverer) \ + gst_discoverer_stop(GST_DISCOVERER((discoverer))) +gboolean +gupnp_dlna_discoverer_discover_uri (GUPnPDLNADiscoverer *discoverer, + const gchar *uri); + +/* Synchronous API */ +GUPnPDLNAInformation * +gupnp_dlna_discoverer_discover_uri_sync (GUPnPDLNADiscoverer *discoverer, + const gchar *uri, + GError **err); + +/* Get a GUPnPDLNAProfile by name */ +GUPnPDLNAProfile * +gupnp_dlna_discoverer_get_profile (GUPnPDLNADiscoverer *self, + const gchar *name); + +/* API to list all available profiles */ +const GList * +gupnp_dlna_discoverer_list_profiles (GUPnPDLNADiscoverer *self); +gboolean +gupnp_dlna_discoverer_get_relaxed_mode (GUPnPDLNADiscoverer *self); +gboolean +gupnp_dlna_discoverer_get_extended_mode (GUPnPDLNADiscoverer *self); + +G_END_DECLS + +#endif /* _GUPNP_DLNA_DISCOVERER */ diff --git a/libgupnp-dlna/gupnp-dlna-information.c b/libgupnp-dlna/gupnp-dlna-information.c new file mode 100644 index 0000000..5585893 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-information.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "gupnp-dlna-information.h" +#include <gst/gstminiobject.h> + +/** + * SECTION:gupnp-dlna-information + * @short_description: Object containing metadata information returned by the + * #GUPnPDLNADiscoverer API + * + * The GUPnPDLNAInformation object holds metadata information discovered by the + * GUPnPDiscoverer API. The DLNA profile name and MIME type have their own + * fields, and other metadata is held in a GstDiscovererInfo structure. + * All fields are read-only. + */ + +G_DEFINE_TYPE (GUPnPDLNAInformation, gupnp_dlna_information, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + GUPNP_TYPE_DLNA_INFORMATION, \ + GUPnPDLNAInformationPrivate)) + +typedef struct _GUPnPDLNAInformationPrivate GUPnPDLNAInformationPrivate; + +struct _GUPnPDLNAInformationPrivate { + GstDiscovererInfo *info; + gchar *name; + gchar *mime; +}; + +enum { + PROP_0, + PROP_DLNA_NAME, + PROP_DLNA_MIME, + PROP_DISCOVERER_INFO, +}; + +static void +gupnp_dlna_information_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object); + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_NAME: + g_value_set_string (value, priv->name); + + break; + + case PROP_DLNA_MIME: + g_value_set_string (value, priv->mime); + + break; + + case PROP_DISCOVERER_INFO: + gst_value_set_mini_object (value, + GST_MINI_OBJECT(priv->info)); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + property_id, + pspec); + + break; + } +} + +static void +gupnp_dlna_information_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object); + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + + break; + + case PROP_DLNA_MIME: + g_free (priv->mime); + priv->mime = g_value_dup_string (value); + + break; + + case PROP_DISCOVERER_INFO: + if (priv->info) + gst_discoverer_info_unref (priv->info); + priv->info = GST_DISCOVERER_INFO + (gst_value_dup_mini_object (value)); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + property_id, + pspec); + + break; + } +} + + +static void +gupnp_dlna_information_finalize (GObject *object) +{ + GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object); + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + g_free (priv->name); + g_free (priv->mime); + if (priv->info) + gst_discoverer_info_unref (priv->info); + + G_OBJECT_CLASS (gupnp_dlna_information_parent_class)->finalize (object); +} + +static void +gupnp_dlna_information_class_init (GUPnPDLNAInformationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (GUPnPDLNAInformationPrivate)); + + object_class->get_property = gupnp_dlna_information_get_property; + object_class->set_property = gupnp_dlna_information_set_property; + object_class->finalize = gupnp_dlna_information_finalize; + + pspec = g_param_spec_string ("name", + "DLNA profile name", + "The name of the DLNA profile " + "corresponding to the strream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_DLNA_NAME, pspec); + + pspec = g_param_spec_string ("mime", + "DLNA profile MIME type corresponding " + "to the stream", + "The DLNA MIME type of the stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_DLNA_MIME, pspec); + + pspec = gst_param_spec_mini_object ("info", + "Stream metadata", + "Metadata of the stream in a " + "GstDiscovererInfo structure", + GST_TYPE_DISCOVERER_INFO, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_DISCOVERER_INFO, + pspec); +} + +static void +gupnp_dlna_information_init (GUPnPDLNAInformation *self) +{ + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + priv->name = NULL; + priv->mime = NULL; + priv->info = NULL; +} + +/** + * gupnp_dlna_information_new: + * @name: DLNA media profile name corresponding to the media + * @mime: DLNA MIME type for the media + * @info: #GstDiscovererInfo type with additional metadata about the + * stream + * + * Creates a new #GUPnPDLNAInformation object with the given properties. + * + * Returns: A newly created #GUPnPDLNAInformation object. + */ +GUPnPDLNAInformation* +gupnp_dlna_information_new (gchar *name, + gchar *mime, + GstDiscovererInfo *info) +{ + return g_object_new (GUPNP_TYPE_DLNA_INFORMATION, + "name", name, + "mime", mime, + "info", info, + NULL); +} + +/** + * gupnp_dlna_information_get_name: + * @self: The #GUPnPDLNAInformation object + * + * Returns: the DLNA profile name of the stream represented by @self. Do not + * free this string. + */ +const gchar * +gupnp_dlna_information_get_name (GUPnPDLNAInformation *self) +{ + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + return priv->name; +} + +/** + * gupnp_dlna_information_get_mime: + * @self: The #GUPnPDLNAInformation object + * + * Returns: the DLNA MIME type of the stream represented by @self. Do not + * free this string. + */ +const gchar * +gupnp_dlna_information_get_mime (GUPnPDLNAInformation *self) +{ + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + return priv->mime; +} + +/**finalize + * gupnp_dlna_information_get_info: + * @self: The #GUPnPDLNAInformation object + * + * Returns: additional stream metadata for @self in the form of a + * #GstDiscovererInfo structure. Do not free this structure. + */ +const GstDiscovererInfo * +gupnp_dlna_information_get_info (GUPnPDLNAInformation *self) +{ + GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self); + + return priv->info; +} diff --git a/libgupnp-dlna/gupnp-dlna-information.h b/libgupnp-dlna/gupnp-dlna-information.h new file mode 100644 index 0000000..5293fc1 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-information.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 __GUPNP_DLNA_INFORMATION_H__ +#define __GUPNP_DLNA_INFORMATION_H__ + +#include <gst/pbutils/pbutils.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GUPNP_TYPE_DLNA_INFORMATION gupnp_dlna_information_get_type() + +#define GUPNP_DLNA_INFORMATION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GUPNP_TYPE_DLNA_INFORMATION, \ + GUPnPDLNAInformation)) + +#define GUPNP_DLNA_INFORMATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GUPNP_TYPE_DLNA_INFORMATION, \ + GUPnPDLNAInformationClass)) + +#define GUPNP_IS_DLNA_INFORMATION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GUPNP_TYPE_DLNA_INFORMATION)) + +#define GUPNP_IS_DLNA_INFORMATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GUPNP_TYPE_DLNA_INFORMATION)) + +#define GUPNP_DLNA_INFORMATION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GUPNP_TYPE_DLNA_INFORMATION, \ + GUPnPDLNAInformationClass)) + +typedef struct { + GObject parent; +} GUPnPDLNAInformation; + +typedef struct { + GObjectClass parent_class; +} GUPnPDLNAInformationClass; + +GType gupnp_dlna_information_get_type (void); + +GUPnPDLNAInformation* +gupnp_dlna_information_new (gchar *name, + gchar *mime, + GstDiscovererInfo *info); + +const gchar * gupnp_dlna_information_get_name (GUPnPDLNAInformation *self); +const gchar * gupnp_dlna_information_get_mime (GUPnPDLNAInformation *self); +const GstDiscovererInfo * +gupnp_dlna_information_get_info (GUPnPDLNAInformation *self); + +G_GNUC_INTERNAL GUPnPDLNAInformation * +gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInfo *info, + GList *profiles); + + +G_END_DECLS + +#endif /* __GUPNP_DLNA_INFORMATION_H__ */ diff --git a/libgupnp-dlna/gupnp-dlna-marshal.c b/libgupnp-dlna/gupnp-dlna-marshal.c new file mode 100644 index 0000000..7b5a28e --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-marshal.c @@ -0,0 +1,153 @@ + +#ifndef __gupnp_dlna_marshal_MARSHAL_H__ +#define __gupnp_dlna_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* BOOLEAN:STRING,UINT,STRING,POINTER (./gupnp-dlna-marshal.list:1) */ +extern void gupnp_dlna_marshal_BOOLEAN__STRING_UINT_STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +void +gupnp_dlna_marshal_BOOLEAN__STRING_UINT_STRING_POINTER (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_UINT_STRING_POINTER) (gpointer data1, + gpointer arg_1, + guint arg_2, + gpointer arg_3, + gpointer arg_4, + gpointer data2); + register GMarshalFunc_BOOLEAN__STRING_UINT_STRING_POINTER callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__STRING_UINT_STRING_POINTER) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_string (param_values + 3), + g_marshal_value_peek_pointer (param_values + 4), + data2); + + g_value_set_boolean (return_value, v_return); +} + +/* VOID:OBJECT,BOXED (./gupnp-dlna-marshal.list:2) */ +extern void gupnp_dlna_marshal_VOID__OBJECT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); +void +gupnp_dlna_marshal_VOID__OBJECT_BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_BOXED) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_boxed (param_values + 2), + data2); +} + +G_END_DECLS + +#endif /* __gupnp_dlna_marshal_MARSHAL_H__ */ + diff --git a/libgupnp-dlna/gupnp-dlna-marshal.h b/libgupnp-dlna/gupnp-dlna-marshal.h new file mode 100644 index 0000000..437e4df --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-marshal.h @@ -0,0 +1,28 @@ + +#ifndef __gupnp_dlna_marshal_MARSHAL_H__ +#define __gupnp_dlna_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* BOOLEAN:STRING,UINT,STRING,POINTER (./gupnp-dlna-marshal.list:1) */ +extern void gupnp_dlna_marshal_BOOLEAN__STRING_UINT_STRING_POINTER (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT,BOXED (./gupnp-dlna-marshal.list:2) */ +extern void gupnp_dlna_marshal_VOID__OBJECT_BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __gupnp_dlna_marshal_MARSHAL_H__ */ + diff --git a/libgupnp-dlna/gupnp-dlna-marshal.list b/libgupnp-dlna/gupnp-dlna-marshal.list new file mode 100644 index 0000000..c03532c --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-marshal.list @@ -0,0 +1,2 @@ +BOOLEAN:STRING,UINT,STRING,POINTER +VOID:OBJECT,BOXED diff --git a/libgupnp-dlna/gupnp-dlna-profile-private.h b/libgupnp-dlna/gupnp-dlna-profile-private.h new file mode 100644 index 0000000..0e1f4ed --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-profile-private.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 Nokia Corporation. + * + * Authors: Parthasarathi Susarla <partha.susarla@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 __GUPNP_DLNA_PROFILE_PRIVATE_H__ +#define __GUPNP_DLNA_PROFILE_PRIVATE_H__ + +G_BEGIN_DECLS + +GUPnPDLNAProfile * gupnp_dlna_profile_new (gchar *name, + gchar *mime, + GstCaps *container_caps, + GstCaps *video_caps, + GstCaps *audio_caps, + gboolean extended); + + +const GstCaps * gupnp_dlna_profile_get_container_caps (GUPnPDLNAProfile *self); +const GstCaps * gupnp_dlna_profile_get_video_caps (GUPnPDLNAProfile *self); +const GstCaps * gupnp_dlna_profile_get_audio_caps (GUPnPDLNAProfile *self); + +void gupnp_dlna_profile_set_container_caps (GUPnPDLNAProfile *self, GstCaps *caps); +void gupnp_dlna_profile_set_video_caps (GUPnPDLNAProfile *self, GstCaps *caps); +void gupnp_dlna_profile_set_audio_caps (GUPnPDLNAProfile *self, GstCaps *caps); + +G_END_DECLS + +#endif /* __GUPNP_DLNA_PROFILE_PRIVATE_H__ */ diff --git a/libgupnp-dlna/gupnp-dlna-profile.c b/libgupnp-dlna/gupnp-dlna-profile.c new file mode 100644 index 0000000..b88fed6 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-profile.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "gupnp-dlna-profile.h" +#include "gupnp-dlna-profile-private.h" +#include <gst/gstminiobject.h> + +/** + * SECTION:gupnp-dlna-profile + * @short_description: Object representing a DLNA profile + * + * The #GUPnPDLNADiscoverer object provides a few APIs that return + * #GUPnPDLNAProfile objects. These represent a single DLNA profile. Each + * #GUPnPDLNAProfile has a name (the name of the DLNA profile), the + * corresponding MIME type, and a #GstEncodingProfile which represents the + * various audio/video/container restrictions specified for that DLNA profile. + */ +G_DEFINE_TYPE (GUPnPDLNAProfile, gupnp_dlna_profile, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + GUPNP_TYPE_DLNA_PROFILE, \ + GUPnPDLNAProfilePrivate)) + +typedef struct _GUPnPDLNAProfilePrivate GUPnPDLNAProfilePrivate; + +struct _GUPnPDLNAProfilePrivate { + gchar *name; + gchar *mime; + GstCaps *container_caps; + GstCaps *video_caps; + GstCaps *audio_caps; + gboolean extended; + GstEncodingProfile *enc_profile; +}; + +enum { + PROP_0, + PROP_DLNA_NAME, + PROP_DLNA_MIME, + PROP_ENCODING_PROFILE, + PROP_DLNA_EXTENDED, +}; + +static void +gupnp_dlna_profile_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNAProfile *self = GUPNP_DLNA_PROFILE (object); + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_DLNA_MIME: + g_value_set_string (value, priv->mime); + break; + + case PROP_ENCODING_PROFILE: + gst_value_set_mini_object (value, + GST_MINI_OBJECT (priv->enc_profile)); + break; + + case PROP_DLNA_EXTENDED: + g_value_set_boolean (value, priv->extended); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + property_id, + pspec); + break; + } +} + +static void +gupnp_dlna_profile_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GUPnPDLNAProfile *self = GUPNP_DLNA_PROFILE (object); + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + switch (property_id) { + case PROP_DLNA_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + break; + + case PROP_DLNA_MIME: + g_free (priv->mime); + priv->mime = g_value_dup_string (value); + break; + + case PROP_DLNA_EXTENDED: + priv->extended = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID + (object, property_id, pspec); + break; + } +} + +static void +gupnp_dlna_profile_finalize (GObject *object) +{ + GUPnPDLNAProfile *self = GUPNP_DLNA_PROFILE (object); + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + g_free (priv->name); + g_free (priv->mime); + + if (priv->container_caps) + gst_caps_unref (priv->container_caps); + if (priv->audio_caps) + gst_caps_unref (priv->audio_caps); + if (priv->video_caps) + gst_caps_unref (priv->video_caps); + + if (priv->enc_profile) + gst_encoding_profile_unref (priv->enc_profile); + + G_OBJECT_CLASS (gupnp_dlna_profile_parent_class)->finalize (object); +} + +static void +gupnp_dlna_profile_class_init (GUPnPDLNAProfileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (GUPnPDLNAProfilePrivate)); + + object_class->get_property = gupnp_dlna_profile_get_property; + object_class->set_property = gupnp_dlna_profile_set_property; + object_class->finalize = gupnp_dlna_profile_finalize; + + pspec = g_param_spec_string ("name", + "DLNA profile name", + "The name of the DLNA profile ", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_DLNA_NAME, pspec); + + pspec = g_param_spec_string ("mime", + "DLNA profile MIME type", + "The MIME type of the DLNA profile", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_DLNA_MIME, pspec); + + pspec = gst_param_spec_mini_object ("encoding-profile", + "Encoding Profile for the " + "DLNA Profile", + "GstEncodingProfile object" + "corresponding to the DLNA profile", + GST_TYPE_ENCODING_PROFILE, + G_PARAM_READABLE); + g_object_class_install_property (object_class, + PROP_ENCODING_PROFILE, + pspec); + + pspec = g_param_spec_boolean ("extended", + "Extended mode property", + "Indicates that this profile is not " + "part of the DLNA specification", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, + PROP_DLNA_EXTENDED, + pspec); + +} + +static void +gupnp_dlna_profile_init (GUPnPDLNAProfile *self) +{ +} + +const GstCaps * +gupnp_dlna_profile_get_container_caps (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->container_caps; +} + +const GstCaps * +gupnp_dlna_profile_get_video_caps (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->video_caps; +} + +const GstCaps * +gupnp_dlna_profile_get_audio_caps (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->audio_caps; +} + +void +gupnp_dlna_profile_set_container_caps (GUPnPDLNAProfile *self, GstCaps *caps) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + if (priv->container_caps) + gst_caps_unref (priv->container_caps); + priv->container_caps = gst_caps_copy (caps); +} + +void +gupnp_dlna_profile_set_video_caps (GUPnPDLNAProfile *self, GstCaps *caps) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + if (priv->video_caps) + gst_caps_unref (priv->video_caps); + priv->video_caps = gst_caps_copy (caps); +} + +void +gupnp_dlna_profile_set_audio_caps (GUPnPDLNAProfile *self, GstCaps *caps) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + if (priv->audio_caps) + gst_caps_unref (priv->audio_caps); + priv->audio_caps = gst_caps_copy (caps); +} + +GUPnPDLNAProfile * +gupnp_dlna_profile_new (gchar *name, + gchar *mime, + GstCaps *container_caps, + GstCaps *video_caps, + GstCaps *audio_caps, + gboolean extended) +{ + GUPnPDLNAProfile *prof; + + prof = g_object_new (GUPNP_TYPE_DLNA_PROFILE, + "name", name, + "mime", mime, + "extended", extended, + NULL); + + gupnp_dlna_profile_set_container_caps (prof, container_caps); + gupnp_dlna_profile_set_video_caps (prof, video_caps); + gupnp_dlna_profile_set_audio_caps (prof, audio_caps); + + return prof; +} + +/** + * gupnp_dlna_profile_get_name: + * @self: The #GUPnPDLNAProfile object + * + * Returns: the name of the DLNA profile represented by @self + */ +const gchar * +gupnp_dlna_profile_get_name (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->name; +} + +/** + * gupnp_dlna_profile_get_mime: + * @self: The #GUPnPDLNAProfile object + * + * Returns: the DLNA MIME type of the DLNA profile represented by @self + */ +const gchar * +gupnp_dlna_profile_get_mime (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->mime; +} + +/** + * gupnp_dlna_profile_get_encoding_profile: + * @self: The #GUPnPDLNAProfile object + * + * Returns: (transfer full): a #GstEncodingProfile object that, in a future + * version, can be used to transcode a given stream to match the DLNA + * profile represented by @self. + * The receiver must unref the returned #GstEncodingProfile when done + * using it. + */ +GstEncodingProfile * +gupnp_dlna_profile_get_encoding_profile (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + + /* create an encoding-profile if we don't have one */ + if (!priv->enc_profile) { + GstEncodingContainerProfile *container = NULL; + GstEncodingAudioProfile *audio_profile = NULL; + GstEncodingVideoProfile *video_profile = NULL; + + if (GST_IS_CAPS (priv->video_caps) && + !gst_caps_is_empty (priv->video_caps)) + video_profile = gst_encoding_video_profile_new + (priv->video_caps,NULL, NULL, 0); + + if (GST_IS_CAPS (priv->audio_caps) && + !gst_caps_is_empty (priv->audio_caps)) + audio_profile = gst_encoding_audio_profile_new + (priv->audio_caps,NULL, NULL, 0); + + if (GST_IS_CAPS (priv->container_caps)) { + container = gst_encoding_container_profile_new + (priv->name, + priv->mime, + priv->container_caps, + NULL); + + if (video_profile) + gst_encoding_container_profile_add_profile + (container, + (GstEncodingProfile *)video_profile); + + if (audio_profile) + gst_encoding_container_profile_add_profile + (container, + (GstEncodingProfile *) audio_profile); + + priv->enc_profile = (GstEncodingProfile *)container; + } else { + if(audio_profile) + /* Container-less audio */ + priv->enc_profile = + (GstEncodingProfile *)audio_profile; + + if (video_profile) + /* Container-less video isn't a possibility + yet */ + g_assert_not_reached (); + } + } + + gst_encoding_profile_ref (priv->enc_profile); + + return priv->enc_profile; +} + +/** + * gupnp_dlna_profile_get_extended: + * @self: The #GUPnPDLNAProfile object + * + * Returns: true if application is using extended mode and false otherwise + */ +gboolean +gupnp_dlna_profile_get_extended (GUPnPDLNAProfile *self) +{ + GUPnPDLNAProfilePrivate *priv = GET_PRIVATE (self); + return priv->extended; +} diff --git a/libgupnp-dlna/gupnp-dlna-profile.h b/libgupnp-dlna/gupnp-dlna-profile.h new file mode 100644 index 0000000..4708a39 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-profile.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 __GUPNP_DLNA_PROFILE_H__ +#define __GUPNP_DLNA_PROFILE_H__ + +#include <gst/pbutils/pbutils.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GUPNP_TYPE_DLNA_PROFILE gupnp_dlna_profile_get_type() + +#define GUPNP_DLNA_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GUPNP_TYPE_DLNA_PROFILE, \ + GUPnPDLNAProfile)) + +#define GUPNP_DLNA_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GUPNP_TYPE_DLNA_PROFILE, \ + GUPnPDLNAProfileClass)) + +#define GUPNP_IS_DLNA_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GUPNP_TYPE_DLNA_PROFILE)) + +#define GUPNP_IS_DLNA_PROFILE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GUPNP_TYPE_DLNA_PROFILE)) + +#define GUPNP_DLNA_PROFILE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GUPNP_TYPE_DLNA_PROFILE, \ + GUPnPDLNAProfileClass)) + +/** + * GUPnPDLNAProfile: + * + * The top-level object used for the in-memory representation of the DLNA + * Profiles. + */ +typedef struct { + GObject parent; +} GUPnPDLNAProfile; + +typedef struct { + GObjectClass parent_class; +} GUPnPDLNAProfileClass; + +GType gupnp_dlna_profile_get_type (void); + +const gchar * gupnp_dlna_profile_get_name (GUPnPDLNAProfile *self); +const gchar * gupnp_dlna_profile_get_mime (GUPnPDLNAProfile *self); +GstEncodingProfile * +gupnp_dlna_profile_get_encoding_profile (GUPnPDLNAProfile *self); +gboolean gupnp_dlna_profile_get_extended (GUPnPDLNAProfile *self); + +G_END_DECLS + +#endif /* __GUPNP_DLNA_PROFILE_H__ */ diff --git a/libgupnp-dlna/gupnp-dlna-profiles.c b/libgupnp-dlna/gupnp-dlna-profiles.c new file mode 100644 index 0000000..4207b58 --- /dev/null +++ b/libgupnp-dlna/gupnp-dlna-profiles.c @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <glib.h> +#include <gst/pbutils/pbutils.h> +#include "gupnp-dlna-discoverer.h" +#include "gupnp-dlna-profile.h" + +/* + * This file provides the infrastructure to load DLNA profiles and the + * corresponding restrictions from an on-disk representation, and use them to + * map a given stream to its DLNA profile, if possible. + * + * Each DLNA profile is represented as a GstEncodingProfile (there might be + * exceptions where a single DLNA profile is represented by multiple + * GstEncodingProfiles - right now that's only LPCM). + * + * For a GstEncodingProfile "profile", the following fields will be populated: + * + * profile.name = "<DLNA Profile Name>" + * profile.format = Muxing format caps (with restrictions) if specified, + * else GST_CAPS_NONE + * profile.encodingprofiles = GList of GstStreamEncodingProfiles + * + * For each stream of the given profile, "profile.encodingprofiles" will have + * a GstEncodingProfile representing the restrictions for that stream (for a + * video format there will be one audio and one video stream, for example). + * + * enc_profile.type = GST_ENCODING_PROFILE_{AUDIO,VIDEO,...} (UNKNOWN for + * container restrictions) + * enc_profile.format = GstCaps with all the restrictions for this format + * enc_profile.restriction = GST_CAPS_ANY + * + * We assume that all DLNA profiles have exactly one audio stream, or one audio + * stream and one video stream. + * + * Things yet to account for: + * + * 1. Multiple audio/video streams (we need to pick the "main" one - how? + * Possibly get information from the demuxer.) + * + * 2. How do we handle discovered metadata which is in tags, but not in caps? + * Could potentially move it to caps in a post-discovery, pre-guessing + * phase + */ + +/* New profile guessing API */ + +#define GUPNP_DLNA_DEBUG_ENV "GUPNP_DLNA_DEBUG" + +#define gupnp_dlna_debug(args...) \ +do { \ + const gchar *_e = g_getenv (GUPNP_DLNA_DEBUG_ENV); \ + if (_e && !g_str_equal (_e, "0")) \ + g_debug (args); \ +} while (0) + +static gboolean +is_video_profile (const GstEncodingProfile *profile) +{ + const GList *i, *profiles_list; + + if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { + profiles_list = gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (profile)); + + for (i = profiles_list ; i; i = i->next) + if (GST_IS_ENCODING_VIDEO_PROFILE (i->data)) + return TRUE; + } + + return FALSE; +} + +static gboolean +structure_can_intersect (const GstStructure *st1, const GstStructure *st2) +{ + /* Since there is no API to intersect GstStructures, we cheat (thanks + * for the idea, tpm!) and make caps from the structuresa */ + + GstCaps *caps1, *caps2; + gboolean ret; + + caps1 = gst_caps_new_full (gst_structure_copy (st1), NULL); + caps2 = gst_caps_new_full (gst_structure_copy (st2), NULL); + + ret = gst_caps_can_intersect (caps1, caps2); + + gst_caps_unref (caps1); + gst_caps_unref (caps2); + + return ret; +} + +static gboolean +structure_is_subset (const GstStructure *st1, const GstStructure *st2) +{ + int i; + + for (i = 0; i < gst_structure_n_fields (st2); i++) { + const gchar *name = gst_structure_nth_field_name (st2, i); + + if (!gst_structure_has_field(st1, name)) { + gupnp_dlna_debug (" missing field %s", name); + return FALSE; + } + } + + return TRUE; +} + +/* + * Returns TRUE if stream_caps and profile_caps can intersect, and the + * intersecting structure from profile_caps is a subset of stream_caps. Put + * simply, the condition being met is that stream_caps intersects with + * profile_caps, and that intersection includes *all* fields specified by + * profile_caps (viz. all the fields specified by the DLNA profile's + * restrictions) + */ +static gboolean +caps_can_intersect_and_is_subset (GstCaps *stream_caps, + const GstCaps *profile_caps) +{ + int i; + GstStructure *stream_st, *profile_st; + + stream_st = gst_caps_get_structure (stream_caps, 0); + + for (i = 0; i < gst_caps_get_size (profile_caps); i++) { + profile_st = gst_caps_get_structure (profile_caps, i); + + if (structure_can_intersect (stream_st, profile_st) && + structure_is_subset (stream_st, profile_st)) + return TRUE; + } + + return FALSE; +} + +static gboolean +match_profile (GstEncodingProfile *profile, + GstCaps *caps, + GType type) +{ + const GList *i, *profiles_list; + const gchar *name; + + /* Profiles with an empty name are used only for inheritance and should + * not be matched against. */ + name = gst_encoding_profile_get_name (profile); + if (name[0] == '\0') + return FALSE; + + profiles_list = gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (profile)); + + for (i = profiles_list; i; i = i->next){ + GstEncodingProfile *enc_profile = GST_ENCODING_PROFILE + (i->data); + const GstCaps *format = gst_encoding_profile_get_format + (enc_profile); + + if (type == G_TYPE_FROM_INSTANCE (enc_profile) && + caps_can_intersect_and_is_subset (caps, format)) + return TRUE; + } + + return FALSE; +} + +static gboolean +check_container (GstDiscovererInfo *info, + GstEncodingProfile *profile) +{ + GstDiscovererStreamInfo *stream_info; + GType stream_type; + GstCaps *stream_caps; + gboolean ret = FALSE; + + const GstCaps *profile_caps = gst_encoding_profile_get_format (profile); + + /* Top-level GstStreamInformation in the topology will be + * the container */ + stream_info = gst_discoverer_info_get_stream_info (info); + stream_caps = gst_discoverer_stream_info_get_caps (stream_info); + stream_type = G_TYPE_FROM_INSTANCE (stream_info); + + if (stream_type == GST_TYPE_DISCOVERER_CONTAINER_INFO && + gst_caps_can_intersect (stream_caps, profile_caps)) + ret = TRUE; + else if (stream_type != GST_TYPE_DISCOVERER_CONTAINER_INFO && + gst_caps_is_empty (profile_caps)) + ret = TRUE; + + gst_discoverer_stream_info_unref (stream_info); + gst_caps_unref (stream_caps); + + return ret; +} + +static GstCaps * +caps_from_audio_stream_info (GstDiscovererStreamInfo *info) +{ + GstCaps *temp = gst_discoverer_stream_info_get_caps (info); + GstCaps *caps = gst_caps_copy (temp); + const GstDiscovererAudioInfo *audio_info = + GST_DISCOVERER_AUDIO_INFO(info); + guint data; + + gst_caps_unref (temp); + + data = gst_discoverer_audio_info_get_sample_rate (audio_info); + if (data) + gst_caps_set_simple (caps, "rate", G_TYPE_INT, data, NULL); + + data = gst_discoverer_audio_info_get_channels (audio_info); + if (data) + gst_caps_set_simple (caps, "channels", G_TYPE_INT, data, NULL); + + data = gst_discoverer_audio_info_get_bitrate (audio_info); + if (data) + gst_caps_set_simple (caps, "bitrate", G_TYPE_INT, data, NULL); + + data = gst_discoverer_audio_info_get_max_bitrate (audio_info); + if (data) + gst_caps_set_simple + (caps, "maximum-bitrate", G_TYPE_INT, data, NULL); + + data = gst_discoverer_audio_info_get_depth (audio_info); + if (data) + gst_caps_set_simple (caps, "depth", G_TYPE_INT, data, NULL); + + return caps; +} + +static gboolean +check_audio_profile (GstDiscovererInfo *info, + GstEncodingProfile *profile) +{ + GstCaps *caps; + GList *i, *stream_list; + gboolean found = FALSE; + + /* Optimisation TODO: this can be pre-computed */ + if (is_video_profile (profile)) + return FALSE; + + stream_list = gst_discoverer_info_get_stream_list (info); + + for (i = stream_list; !found && i; i = i->next) { + GstDiscovererStreamInfo *stream = + GST_DISCOVERER_STREAM_INFO(i->data); + GType stream_type = G_TYPE_FROM_INSTANCE (stream); + + if (stream_type != GST_TYPE_DISCOVERER_AUDIO_INFO) + continue; + + caps = caps_from_audio_stream_info (stream); + + if (match_profile (profile, + caps, + GST_TYPE_ENCODING_AUDIO_PROFILE)) { + found = TRUE; + break; + } + + gst_caps_unref (caps); + } + + gst_discoverer_stream_info_list_free (stream_list); + + return found; +} + +static void +guess_audio_profile (GstDiscovererInfo *info, + gchar **name, + gchar **mime, + GList *profiles) +{ + GList *i; + GUPnPDLNAProfile *profile; + GstEncodingProfile *enc_profile; + + for (i = profiles; i; i = i->next) { + profile = (GUPnPDLNAProfile *)(i->data); + enc_profile = gupnp_dlna_profile_get_encoding_profile (profile); + + gupnp_dlna_debug ("Checking DLNA profile %s", + gupnp_dlna_profile_get_name (profile)); + + if (!check_audio_profile (info, enc_profile)) + gupnp_dlna_debug (" Audio did not match"); + else if (!check_container (info, enc_profile)) + gupnp_dlna_debug (" Container did not match"); + else { + *name = g_strdup + (gupnp_dlna_profile_get_name (profile)); + *mime = g_strdup + (gupnp_dlna_profile_get_mime (profile)); + break; + } + } +} + +static GstCaps * +caps_from_video_stream_info (GstDiscovererStreamInfo *info) +{ + GstCaps *temp = gst_discoverer_stream_info_get_caps (info); + GstCaps *caps = gst_caps_copy (temp); + const GstDiscovererVideoInfo *video_info = + GST_DISCOVERER_VIDEO_INFO (info); + const GstTagList *stream_tag_list; + guint n, d, data; + gboolean value; + + gst_caps_unref (temp); + + data = gst_discoverer_video_info_get_height (video_info); + if (data) + gst_caps_set_simple (caps, "height", G_TYPE_INT, data, NULL); + + data = gst_discoverer_video_info_get_width (video_info); + if (data) + gst_caps_set_simple (caps, "width", G_TYPE_INT, data, NULL); + + data = gst_discoverer_video_info_get_depth (video_info); + if (data) + gst_caps_set_simple (caps, "depth", G_TYPE_INT, data, NULL); + + n = gst_discoverer_video_info_get_framerate_num (video_info); + d = gst_discoverer_video_info_get_framerate_denom (video_info); + if (n && d) + gst_caps_set_simple (caps, + "framerate", + GST_TYPE_FRACTION, n, d, + NULL); + + n = gst_discoverer_video_info_get_par_num (video_info); + d = gst_discoverer_video_info_get_par_denom (video_info); + if (n && d) + gst_caps_set_simple (caps, + "pixel-aspect-ratio", + GST_TYPE_FRACTION, n, d, + NULL); + + value = gst_discoverer_video_info_is_interlaced (video_info); + if (value) + gst_caps_set_simple + (caps, "interlaced", G_TYPE_BOOLEAN, value, NULL); + + stream_tag_list = gst_discoverer_stream_info_get_tags (info); + if (stream_tag_list) { + guint bitrate; + if (gst_tag_list_get_uint (stream_tag_list, "bitrate", &bitrate)) + gst_caps_set_simple + (caps, "bitrate", G_TYPE_INT, (int) bitrate, NULL); + + if (gst_tag_list_get_uint (stream_tag_list, + "maximum-bitrate", + &bitrate)) + gst_caps_set_simple (caps, + "maximum-bitrate", + G_TYPE_INT, + (int) bitrate, + NULL); + } + + return caps; +} + +static gboolean +check_video_profile (GstDiscovererInfo *info, + GstEncodingProfile *profile) +{ + GList *i, *stream_list; + gboolean found_video = FALSE, found_audio = FALSE;; + + stream_list = gst_discoverer_info_get_stream_list (info); + + /* Check video and audio restrictions */ + for (i = stream_list; + i && !(found_video && found_audio); + i = i->next) { + GstDiscovererStreamInfo *stream; + GType stream_type; + GstCaps *caps = NULL; + + stream = GST_DISCOVERER_STREAM_INFO(i->data); + stream_type = G_TYPE_FROM_INSTANCE (stream); + + if (!found_video && + stream_type == GST_TYPE_DISCOVERER_VIDEO_INFO) { + caps = caps_from_video_stream_info (stream); + if (match_profile (profile, + caps, + GST_TYPE_ENCODING_VIDEO_PROFILE)) + found_video = TRUE; + else + gupnp_dlna_debug (" Video did not match"); + } else if (!found_audio && + stream_type == GST_TYPE_DISCOVERER_AUDIO_INFO) { + caps = caps_from_audio_stream_info (stream); + if (match_profile (profile, + caps, + GST_TYPE_ENCODING_AUDIO_PROFILE)) + found_audio = TRUE; + else + gupnp_dlna_debug (" Audio did not match"); + } + + if (caps) + gst_caps_unref (caps); + } + + gst_discoverer_stream_info_list_free (stream_list); + + if (!found_video || !found_audio) + return FALSE; + + /* Check container restrictions */ + if (!check_container (info, profile)) { + gupnp_dlna_debug (" Container did not match"); + return FALSE; + } + + return TRUE; +} + +static void +guess_video_profile (GstDiscovererInfo *info, + gchar **name, + gchar **mime, + GList *profiles) +{ + GUPnPDLNAProfile *profile = NULL; + GstEncodingProfile *enc_profile; + GList *i; + + for (i = profiles; i; i = i->next) { + profile = (GUPnPDLNAProfile *)(i->data); + enc_profile = gupnp_dlna_profile_get_encoding_profile (profile); + + gupnp_dlna_debug ("Checking DLNA profile %s", + gupnp_dlna_profile_get_name (profile)); + if (check_video_profile (info, enc_profile)) { + *name = g_strdup (gupnp_dlna_profile_get_name (profile)); + *mime = g_strdup (gupnp_dlna_profile_get_mime (profile)); + break; + } + } +} + +static void +guess_image_profile (GstDiscovererStreamInfo *info, + gchar **name, + gchar **mime, + GList *profiles) +{ + GstCaps *caps; + GList *i; + gboolean found = FALSE; + GUPnPDLNAProfile *profile; + GstEncodingProfile *enc_profile; + const GstDiscovererVideoInfo *video_info = + GST_DISCOVERER_VIDEO_INFO (info); + + if (!info || !gst_discoverer_video_info_is_image (video_info)) + return; + + caps = caps_from_video_stream_info (info); + + for (i = profiles; !found && i; i = i->next) { + profile = (GUPnPDLNAProfile *)(i->data); + enc_profile = gupnp_dlna_profile_get_encoding_profile (profile); + + /* Optimisation TODO: this can be pre-computed */ + if (!is_video_profile (enc_profile)) + continue; + + if (match_profile (enc_profile, + caps, + GST_TYPE_ENCODING_VIDEO_PROFILE)) { + /* Found a match */ + *name = g_strdup (gupnp_dlna_profile_get_name (profile)); + *mime = g_strdup (gupnp_dlna_profile_get_mime (profile)); + break; + } + } + + gst_caps_unref (caps); +} + +GUPnPDLNAInformation * +gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInfo *info, + GList *profiles) +{ + GUPnPDLNAInformation *dlna; + GList *video_list, *audio_list; + gchar *name = NULL, *mime = NULL; + + video_list = gst_discoverer_info_get_video_streams (info); + audio_list = gst_discoverer_info_get_audio_streams (info); + if (video_list) { + if ((g_list_length (video_list) ==1 ) && + gst_discoverer_video_info_is_image + (GST_DISCOVERER_VIDEO_INFO + (video_list->data))) { + GstDiscovererStreamInfo *stream; + stream = (GstDiscovererStreamInfo *) video_list->data; + guess_image_profile (stream, &name, &mime, profiles); + } else + guess_video_profile (info, &name, &mime, profiles); + } else if (audio_list) + guess_audio_profile (info, &name, &mime, profiles); + + gst_discoverer_stream_info_list_free (audio_list); + gst_discoverer_stream_info_list_free (video_list); + + dlna = gupnp_dlna_information_new (name, mime, info); + + + g_free (name); + g_free (mime); + + return dlna; +} diff --git a/libgupnp-dlna/profile-loading.c b/libgupnp-dlna/profile-loading.c new file mode 100644 index 0000000..874fe44 --- /dev/null +++ b/libgupnp-dlna/profile-loading.c @@ -0,0 +1,921 @@ +/* + * Copyright (C) 2010 Nokia Corporation. + * + * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <glib.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <libxml/xmlreader.h> +#include <libxml/relaxng.h> +#include <gst/pbutils/pbutils.h> +#include "profile-loading.h" +#include "gupnp-dlna-profile.h" +#include "gupnp-dlna-profile-private.h" + +#define GST_CAPS_NULL_NAME "NULL" +#define DLNA_DATA_DIR DATA_DIR \ + G_DIR_SEPARATOR_S "dlna-profiles" G_DIR_SEPARATOR_S + +static gboolean +copy_func (GQuark field_id, const GValue *value, gpointer data) +{ + GstStructure *st2 = (GstStructure *)data; + + if (!gst_structure_has_field (st2, g_quark_to_string (field_id))) + gst_structure_id_set_value (st2, field_id, value); + + return TRUE; +} + +/* Note: It is assumed that caps1 and caps2 have only 1 structure each */ +static GstCaps * +merge_caps (GstCaps *caps1, GstCaps *caps2) +{ + GstStructure *st1, *st2; + GstCaps *ret; + gboolean any = FALSE; + + /* If one of the caps GST_CAPS_ANY, gst_caps_merge will result in a + * GST_CAPS_ANY, which might not be correct for us */ + if (!gst_caps_is_any (caps1) && !gst_caps_is_any (caps2)) { + any = TRUE; + gst_caps_merge (caps1, gst_caps_copy (caps2)); + gst_caps_do_simplify (caps1); + } + + ret = gst_caps_make_writable (caps1); + st1 = gst_caps_get_structure (ret, 0); + if (gst_caps_get_size (caps1) == 2) + /* Non-merged fields were copied to a second structure in caps + * at gst_merge_caps() time */ + st2 = gst_caps_get_structure (ret, 1); + else + /* Either one of the caps was GST_CAPS_ANY, or there were no + * unmerged fields */ + st2 = gst_caps_get_structure (caps2, 0); + + /* If caps1 has a name, we retain it. If not, and caps2 does, caps1 + * gets caps2's name. */ + if ((g_strcmp0 (GST_CAPS_NULL_NAME, + gst_structure_get_name (st1)) == 0) && + (g_strcmp0 (GST_CAPS_NULL_NAME, + gst_structure_get_name (st2)) != 0)) { + gst_structure_set_name (st1, gst_structure_get_name (st2)); + } + + /* We now walk over the structures and append any fields that are in + * caps2 but not in caps1. */ + if (any || gst_caps_get_size (caps1) == 2) + gst_structure_foreach (st2, copy_func, st1); + + if (gst_caps_get_size (caps1) == 2) + gst_caps_remove_structure (ret, 1); + + return ret; +} + +static xmlChar * +get_value (xmlTextReaderPtr reader) +{ + xmlChar *value = NULL, *curr; + int ret = 1; + + curr = xmlTextReaderName (reader); + + /* This function may be called with reader pointing to a <field> or + * the element just below a <field>. In the former case, we move the + * cursor forward and then continue processing. */ + if (xmlStrEqual (curr, BAD_CAST ("field"))) + ret = xmlTextReaderRead (reader); + xmlFree (curr); + + while (ret == 1) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + if (xmlTextReaderNodeType (reader) == 1 && + xmlStrEqual (tag, BAD_CAST ("value"))) { + /* <value> */ + + /* Note: This assumes you won't have a comment in the + * middle of your text */ + do { + ret = xmlTextReaderRead (reader); + } while (ret == 1 && + xmlTextReaderNodeType (reader) != 3 && + xmlTextReaderNodeType (reader) != 15); + + /* We're now at the real text between a <value> and a + * </value> */ + + if (xmlTextReaderNodeType (reader) == 3) + value = xmlTextReaderValue (reader); + } + + if (xmlTextReaderNodeType (reader) == 15 && + xmlStrEqual (tag, BAD_CAST ("value"))) { + /* </value> */ + xmlFree (tag); + + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } + + if (!value) + g_warning ("Empty <value>s are illegal"); + + return value; +} + +static void +xml_str_free (xmlChar *str, gpointer unused) +{ + xmlFree (str); +} + +static void +free_restrictions_struct (gpointer data, gpointer user_data) +{ + GUPnPDLNARestrictions *restr = (GUPnPDLNARestrictions *)data; + if (restr) { + if (restr->caps) + gst_caps_unref (restr->caps); + + g_free (restr); + } +} + +static void +process_range (xmlTextReaderPtr reader, GString *caps_str) +{ + xmlChar *min, *max; + + min = xmlTextReaderGetAttribute (reader, BAD_CAST ("min")); + max = xmlTextReaderGetAttribute (reader, BAD_CAST ("max")); + + g_string_append_printf (caps_str, "[ %s, %s ]", min, max); + + xmlFree (min); + xmlFree (max); +} + +static int +process_field (xmlTextReaderPtr reader, + GString *caps_str, + gboolean relaxed_mode, + gboolean extended_mode) +{ + int ret; + xmlChar *name; + xmlChar *type; + xmlChar *used; + GList *values = NULL; + gboolean done = FALSE, skip = FALSE; + + /* + * Parse the 'used' attribute and figure out the mode we + * need to follow. + */ + used = xmlTextReaderGetAttribute (reader, BAD_CAST ("used")); + if (used) { + if ((relaxed_mode == FALSE) && + xmlStrEqual (used, BAD_CAST ("in-relaxed"))) { + skip = TRUE; + } else if ((relaxed_mode == TRUE) && + (xmlStrEqual (used, BAD_CAST ("in-strict")))) { + skip = TRUE; + } + + xmlFree (used); + } + + name = xmlTextReaderGetAttribute (reader, BAD_CAST ("name")); + type = xmlTextReaderGetAttribute (reader, BAD_CAST ("type")); + + /* + * This function reads a <field> and appends it to caps_str in the + * GstCaps-as-a-string format: + * + * Single value: field = (type) value + * Multiple values: field = (type) { value1, value2, value3 } + * Range: field = (type) [ min, max ] + */ + + /* Fields are comma-separeted. The leading comma is okay for the first + * field - we will be prepending the restriction name to this string */ + if (!skip) + g_string_append_printf (caps_str, ", %s = (%s) ", name, type); + + xmlFree (name); + xmlFree (type); + + ret = xmlTextReaderRead (reader); + while (ret == 1 && !done) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + switch (xmlTextReaderNodeType (reader)) { + case 1: + if (skip) + break; + + if (xmlStrEqual (tag, BAD_CAST ("range"))) { + /* <range> */ + process_range (reader, caps_str); + } else if (xmlStrEqual (tag, BAD_CAST ("value"))) { + /* <value> */ + xmlChar *value; + + value = get_value (reader); + + if (value) + values = g_list_append (values, value); + } + + break; + + case 15: + if (xmlStrEqual (tag, BAD_CAST ("field"))) + /* </field> */ + done = TRUE; + + break; + + default: + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } + + if (skip) + return ret; + + if (g_list_length (values) == 1) + /* Single value */ + g_string_append_printf (caps_str, + "%s", + (xmlChar *) values->data); + else if (g_list_length (values) > 1) { + /* Multiple values */ + GList *tmp = values->next; + g_string_append_printf (caps_str, + "{ %s", + (xmlChar *) values->data); + + do { + g_string_append_printf (caps_str, + ", %s", + (xmlChar *) tmp->data); + } while ((tmp = tmp->next) != NULL); + + g_string_append_printf (caps_str, " }"); + } + + if (values) { + g_list_foreach (values, (GFunc) xml_str_free, NULL); + g_list_free (values); + } + + return ret; +} + +static GUPnPDLNARestrictions * +process_parent (xmlTextReaderPtr reader, GUPnPDLNALoadState *data) +{ + xmlChar *parent; + xmlChar *used; + GUPnPDLNARestrictions *restr = NULL; + + /* + * Check to see if we need to follow any relaxed/strict mode + * restrictions. + */ + used = xmlTextReaderGetAttribute (reader, BAD_CAST ("used")); + if (used) { + if ((data->relaxed_mode == FALSE) && + xmlStrEqual (used, BAD_CAST ("in-relaxed"))) { + xmlFree (used); + return NULL; + } else if ((data->relaxed_mode == TRUE) && + (xmlStrEqual (used, BAD_CAST ("in-strict")))) { + xmlFree (used); + return NULL; + } + } + + parent = xmlTextReaderGetAttribute (reader, BAD_CAST ("name")); + restr = g_hash_table_lookup (data->restrictions, parent); + + if (!restr) { + g_warning ("Could not find parent restriction: %s", parent); + return NULL; + } + + xmlFree (parent); + xmlFree (used); + + return restr; +} + +static GUPnPDLNARestrictions * +process_restriction (xmlTextReaderPtr reader, GUPnPDLNALoadState *data) +{ + GUPnPDLNARestrictions *restr = NULL; + GType type; + GstCaps *caps = NULL; + GString *caps_str = g_string_sized_new (100); + GList *parents = NULL, *tmp; + xmlChar *id, *name = NULL, *restr_type, *used; + int ret; + gboolean done = FALSE, skip = FALSE; + + /* + * First we parse the 'used' attribute and figure out + * the mode we need to comply to. + */ + used = xmlTextReaderGetAttribute (reader, BAD_CAST ("used")); + if (used) { + if ((data->relaxed_mode == FALSE) && + xmlStrEqual (used, BAD_CAST ("in-relaxed"))) { + skip = TRUE; + } else if ((data->relaxed_mode == TRUE) && + (xmlStrEqual (used, BAD_CAST ("in-strict")))) { + skip = TRUE; + } + } + + /* We then walk through the fields in this restriction, and make a + * string that can be parsed by gst_caps_from_string (). We then make + * a GstCaps from this string */ + + id = xmlTextReaderGetAttribute (reader, BAD_CAST ("id")); + restr_type = xmlTextReaderGetAttribute (reader, BAD_CAST ("type")); + + ret = xmlTextReaderRead (reader); + while (ret == 1 && !done) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + switch (xmlTextReaderNodeType (reader)) { + case 1: + if (skip) + break; + + if (xmlStrEqual (tag, BAD_CAST ("field"))) { + /* <field> */ + xmlChar *field; + + field = xmlTextReaderGetAttribute + (reader, BAD_CAST ("name")); + + /* We handle the "name" field specially - if + * present, it is the caps name */ + if (xmlStrEqual (field, BAD_CAST ("name"))) + name = get_value (reader); + else + process_field (reader, + caps_str, + data->relaxed_mode, + data->extended_mode); + + xmlFree (field); + } else if (xmlStrEqual (tag, BAD_CAST ("parent"))) { + /* <parent> */ + GUPnPDLNARestrictions *restr = + process_parent (reader, data); + + if (restr && restr->caps) + /* Collect parents in a list - we'll + * coalesce them later */ + parents = g_list_append (parents, + gst_caps_copy + (restr->caps)); + } + + break; + + case 15: + if (xmlStrEqual (tag, BAD_CAST ("restriction"))) + /* </restriction> */ + done = TRUE; + + break; + + default: + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } + + if (skip) + goto out; + + /* If the restriction doesn't have a name, we make it up */ + if (!name) + name = BAD_CAST (g_strdup (GST_CAPS_NULL_NAME)); + g_string_prepend (caps_str, (gchar *) name); + xmlFree (name); + + if (xmlStrEqual (restr_type, BAD_CAST ("container"))) + type = GST_TYPE_ENCODING_CONTAINER_PROFILE; + else if (xmlStrEqual (restr_type, BAD_CAST ("audio"))) + type = GST_TYPE_ENCODING_AUDIO_PROFILE; + else if (xmlStrEqual (restr_type, BAD_CAST ("video"))) + type = GST_TYPE_ENCODING_VIDEO_PROFILE; + else if (xmlStrEqual (restr_type, BAD_CAST ("image"))) + type = GST_TYPE_ENCODING_VIDEO_PROFILE; + else { + g_warning ("Support for '%s' restrictions not yet implemented", + restr_type); + goto out; + } + + caps = gst_caps_from_string (caps_str->str); + g_string_free (caps_str, TRUE); + g_return_val_if_fail (caps != NULL, NULL); + + tmp = parents; + while (tmp) { + /* Merge all the parent caps. The child overrides parent + * attributes */ + GstCaps *tmp_caps = (GstCaps *)tmp->data; + caps = merge_caps (caps, tmp_caps); + gst_caps_unref (tmp_caps); + tmp = tmp->next; + } + + restr = g_new0 (GUPnPDLNARestrictions, 1); + + restr->caps = gst_caps_copy (caps); + restr->type = type; + + if (id) + g_hash_table_insert (data->restrictions, id, restr); + +out: + xmlFree (restr_type); + if (used) + xmlFree (used); + if (caps) + gst_caps_unref (caps); + if (parents) + g_list_free (parents); + + return restr; +} + +static void +process_restrictions (xmlTextReaderPtr reader, GUPnPDLNALoadState *data) +{ + int ret = xmlTextReaderRead (reader); + + while (ret == 1) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + switch (xmlTextReaderNodeType (reader)) { + case 1: + if (xmlStrEqual (tag, BAD_CAST ("restriction"))) { + /* <restriction> */ + process_restriction (reader, data); + } + + break; + + case 15: + if (xmlStrEqual (tag, BAD_CAST ("restrictions"))) { + /* </restrictions> */ + xmlFree (tag); + return; + } + + default: + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } +} + +static void +process_dlna_profile (xmlTextReaderPtr reader, + GList **profiles, + GUPnPDLNALoadState *data) +{ + guint ret; + GUPnPDLNAProfile *profile = NULL; + GUPnPDLNAProfile *base = NULL; + GUPnPDLNARestrictions *restr = NULL; + GstCaps *temp_audio = NULL, *temp_video = NULL, *temp_container = NULL; + xmlChar *name, *mime, *id, *base_profile, *extended; + gboolean done = FALSE, is_extended = FALSE; + + name = xmlTextReaderGetAttribute (reader, BAD_CAST ("name")); + mime = xmlTextReaderGetAttribute (reader, BAD_CAST ("mime")); + extended = xmlTextReaderGetAttribute (reader, BAD_CAST ("extended")); + id = xmlTextReaderGetAttribute (reader, BAD_CAST ("id")); + base_profile = xmlTextReaderGetAttribute (reader, + BAD_CAST ("base-profile")); + + /* Create temporary place-holders for caps */ + temp_container = gst_caps_new_empty (); + temp_video = gst_caps_new_empty (); + temp_audio = gst_caps_new_empty (); + + if (!name) { + g_assert (mime == NULL); + + /* We need a non-NULL string to not trigger asserts in the + * places these are used. Profiles without names are used + * only for inheritance, not for actual matching. */ + name = xmlStrdup (BAD_CAST ("")); + mime = xmlStrdup (BAD_CAST ("")); + } + + if (extended && xmlStrEqual (extended, BAD_CAST ("true"))) { + /* If we're not in extended mode, skip this profile */ + if (!data->extended_mode) + goto out; + + is_extended = TRUE; + } + + ret = xmlTextReaderRead (reader); + while (ret == 1 && !done) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + switch (xmlTextReaderNodeType (reader)) { + case 1: + if (xmlStrEqual (tag, BAD_CAST ("restriction"))) + restr = process_restriction (reader, data); + else if (xmlStrEqual (tag, BAD_CAST ("parent"))) + restr = process_parent (reader, data); + + if (!restr) + break; + + if (restr->type == GST_TYPE_ENCODING_CONTAINER_PROFILE) + gst_caps_merge (temp_container, + gst_caps_copy (restr->caps)); + else if (restr->type == GST_TYPE_ENCODING_VIDEO_PROFILE) + gst_caps_merge (temp_video, + gst_caps_copy (restr->caps)); + else if (restr->type == GST_TYPE_ENCODING_AUDIO_PROFILE) + gst_caps_merge (temp_audio, + gst_caps_copy (restr->caps)); + else + g_assert_not_reached (); + + break; + + case 15: + if (xmlStrEqual (tag, BAD_CAST ("dlna-profile"))) + done = TRUE; + + default: + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } + + if (base_profile) { + base = g_hash_table_lookup (data->profile_ids, base_profile); + if (!base) + g_warning ("Invalid base-profile reference"); + } + + + /* create a new GUPnPDLNAProfile */ + profile = gupnp_dlna_profile_new ((gchar *)name, + (gchar *)mime, + GST_CAPS_NONE, + GST_CAPS_NONE, + GST_CAPS_NONE, + is_extended); + + /* Inherit from base profile, if it exists*/ + if (base) { + const GstCaps *video_caps = + gupnp_dlna_profile_get_video_caps (base); + const GstCaps *audio_caps = + gupnp_dlna_profile_get_audio_caps (base); + const GstCaps *container_caps = + gupnp_dlna_profile_get_container_caps (base); + + if (GST_IS_CAPS (video_caps)) + gst_caps_merge (temp_video, + gst_caps_copy (video_caps)); + if (GST_IS_CAPS (audio_caps)) + gst_caps_merge (temp_audio, + gst_caps_copy (audio_caps)); + if (GST_IS_CAPS (container_caps)) + gst_caps_merge (temp_container, + gst_caps_copy (container_caps)); + + } + + + /* The merged caps will be our new GUPnPDLNAProfile */ + + if (GST_IS_CAPS (temp_container) && !gst_caps_is_empty (temp_container)) + gupnp_dlna_profile_set_container_caps (profile, temp_container); + if (GST_IS_CAPS (temp_video) && !gst_caps_is_empty (temp_video)) + gupnp_dlna_profile_set_video_caps (profile, temp_video); + if (GST_IS_CAPS (temp_audio) && !gst_caps_is_empty (temp_audio)) + gupnp_dlna_profile_set_audio_caps (profile, temp_audio); + + *profiles = g_list_append (*profiles, profile); + + if (id) { + /* id is freed when the hash table is destroyed */ + g_object_ref (profile); + g_hash_table_insert (data->profile_ids, id, profile); + } + +out: + + if (temp_container) + gst_caps_unref (temp_container); + if (temp_audio) + gst_caps_unref (temp_audio); + if (temp_video) + gst_caps_unref (temp_video); + + xmlFree (mime); + xmlFree (name); + if (extended) + xmlFree (extended); + if (base_profile) + xmlFree (base_profile); +} + +static GList * +process_include (xmlTextReaderPtr reader, GUPnPDLNALoadState *data) +{ + xmlChar *path; + GList *ret; + + path = xmlTextReaderGetAttribute (reader, BAD_CAST ("ref")); + + if (!g_path_is_absolute ((gchar *) path)) { + gchar *tmp = g_strconcat (DLNA_DATA_DIR, + G_DIR_SEPARATOR_S, + path, + NULL); + xmlFree (path); + path = BAD_CAST (tmp); + } + + ret = gupnp_dlna_load_profiles_from_file ((gchar *) path, + data); + xmlFree (path); + + return ret; +} + +/* This can go away once we have a glib function to canonicalize paths (see + * https://bugzilla.gnome.org/show_bug.cgi?id=111848 + * + * The implementationis not generic enough, but sufficient for our use. The + * idea is taken from Tristan Van Berkom's comment in the bug mentioned above: + * + * 1. cd dirname(path) + * 2. absdir = $CWD + * 3. cd $OLDPWD + * 4. abspath = absdir + basename(path) + */ +static gchar * +canonicalize_path_name (const char *path) +{ + gchar *dir_name = NULL, *file_name = NULL, *abs_dir = NULL, + *old_dir = NULL, *ret = NULL; + + if (g_path_is_absolute (path)) + return g_strdup (path); + + old_dir = g_get_current_dir (); + dir_name = g_path_get_dirname (path); + + if (g_chdir (dir_name) < 0) { + ret = g_strdup (path); + goto out; + } + + abs_dir = g_get_current_dir (); + g_chdir (old_dir); + + file_name = g_path_get_basename (path); + ret = g_build_filename (abs_dir, file_name, NULL); + +out: + g_free (dir_name); + g_free (file_name); + g_free (abs_dir); + g_free (old_dir); + + return ret; +} + +GList * +gupnp_dlna_load_profiles_from_file (const char *file_name, + GUPnPDLNALoadState *data) +{ + GList *profiles = NULL; + gchar *path = NULL; + xmlTextReaderPtr reader; + xmlRelaxNGParserCtxtPtr rngp; + xmlRelaxNGPtr rngs; + int ret; + + path = canonicalize_path_name (file_name); + if (g_hash_table_lookup_extended (data->files_hash, path, NULL, NULL)) + goto out; + else + g_hash_table_insert (data->files_hash, g_strdup (path), NULL); + + reader = xmlNewTextReaderFilename (path); + if (!reader) + goto out; + + /* Load the schema for validation */ + rngp = xmlRelaxNGNewParserCtxt (DLNA_DATA_DIR "dlna-profiles.rng"); + rngs = xmlRelaxNGParse (rngp); + xmlTextReaderRelaxNGSetSchema (reader, rngs); + + ret = xmlTextReaderRead (reader); + while (ret == 1) { + xmlChar *tag; + + tag = xmlTextReaderName (reader); + + switch (xmlTextReaderNodeType (reader)) { + /* Start tag */ + case 1: + if (xmlStrEqual (tag, BAD_CAST ("include"))) { + /* <include> */ + GList *include = + process_include (reader, + data); + profiles = g_list_concat (profiles, + include); + } else if (xmlStrEqual (tag, + BAD_CAST ("restrictions"))) { + /* <restrictions> */ + process_restrictions (reader, + data); + } else if (xmlStrEqual (tag, + BAD_CAST ("dlna-profile"))) { + /* <dlna-profile> */ + process_dlna_profile (reader, + &profiles, + data); + + } + + break; + + default: + break; + } + + xmlFree (tag); + ret = xmlTextReaderRead (reader); + } + + xmlFreeTextReader (reader); + xmlRelaxNGFree (rngs); + xmlRelaxNGFreeParserCtxt (rngp); + +out: + g_free (path); + + return profiles; +} + +GList * +gupnp_dlna_load_profiles_from_dir (gchar *profile_dir, GUPnPDLNALoadState *data) +{ + GDir *dir; + + data->restrictions = + g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) xmlFree, + (GDestroyNotify) + free_restrictions_struct); + data->profile_ids = + g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) xmlFree, + (GDestroyNotify) + g_object_unref); + + GList *profiles = NULL; + + if ((dir = g_dir_open (profile_dir, 0, NULL))) { + const gchar *entry; + + while ((entry = g_dir_read_name (dir))) { + gchar *path = g_strconcat (profile_dir, + G_DIR_SEPARATOR_S, + entry, + NULL); + + if (g_str_has_suffix (entry, ".xml") && + g_file_test (path, G_FILE_TEST_IS_REGULAR)) { + profiles = g_list_concat (profiles, + gupnp_dlna_load_profiles_from_file ( + path, + data)); + } + + g_free (path); + } + + g_dir_close (dir); + } + + g_hash_table_unref (data->restrictions); + g_hash_table_unref (data->profile_ids); + + return profiles; +} + +GList * +gupnp_dlna_load_profiles_from_disk (gboolean relaxed_mode, + gboolean extended_mode) +{ + GUPnPDLNALoadState *load_data; + GList *ret, *i; + + load_data = g_new0 (GUPnPDLNALoadState, 1); + + load_data->files_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + load_data->relaxed_mode = relaxed_mode; + load_data->extended_mode = extended_mode; + + ret = gupnp_dlna_load_profiles_from_dir (DLNA_DATA_DIR, + load_data); + + /* Now that we're done loading profiles, remove all profiles with no + * name which are only used for inheritance and not matching. */ + i = ret; + while (i) { + const gchar *name; + GUPnPDLNAProfile *profile = i->data; + GstEncodingProfile *enc_profile = + gupnp_dlna_profile_get_encoding_profile + (profile); + GList *tmp = g_list_next (i); + + name = gst_encoding_profile_get_name (enc_profile); + if (name[0] == '\0') { + ret = g_list_delete_link (ret, i); + g_object_unref (profile); + } + + i = tmp; + } + + g_hash_table_unref (load_data->files_hash); + g_free (load_data); + load_data = NULL; + + return ret; +} diff --git a/libgupnp-dlna/profile-loading.h b/libgupnp-dlna/profile-loading.h new file mode 100644 index 0000000..fa1f966 --- /dev/null +++ b/libgupnp-dlna/profile-loading.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Nokia Corporation. + * + * Authors: Zeeshan Ali <zeeshanak@gnome.org> + * <zeeshan.ali@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 __GUPNP_DLNA_LOAD_H__ +#define __GUPNP_DLNA_LOAD_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct { + GHashTable *restrictions; + GHashTable *profile_ids; + GHashTable *files_hash; + gboolean relaxed_mode; + gboolean extended_mode; +} GUPnPDLNALoadState; + +typedef struct { + GstCaps *caps; + GType type; +} GUPnPDLNARestrictions; + +GList * +gupnp_dlna_load_profiles_from_file (const gchar *file_name, + GUPnPDLNALoadState *data); +GList * +gupnp_dlna_load_profiles_from_dir (gchar *profile_dir, + GUPnPDLNALoadState *data); + +GList * +gupnp_dlna_load_profiles_from_disk (gboolean relaxed_mode, + gboolean extended_mode); + +G_END_DECLS + +#endif /* __GUPNP_DLNA_LOAD_H__ */ |