diff options
Diffstat (limited to 'gst')
56 files changed, 25267 insertions, 0 deletions
diff --git a/gst/Makefile.am b/gst/Makefile.am new file mode 100644 index 0000000..db96293 --- /dev/null +++ b/gst/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS=$(GST_PLUGINS_SELECTED) +DIST_SUBDIRS=$(GST_PLUGINS_ALL) + +include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/gst/Makefile.in b/gst/Makefile.in new file mode 100644 index 0000000..6cd649a --- /dev/null +++ b/gst/Makefile.in @@ -0,0 +1,794 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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@ + +# include this at the end of $MODULE/ext/Makefile.am to force make to +# build subdirectories in parallel when make -jN is used. We will end up +# descending into all subdirectories a second time, but only after the first +# (parallel) run has finished, so it should go right through the second time. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +DIST_COMMON = $(top_srcdir)/common/parallel-subdirs.mak \ + $(srcdir)/Makefile.in $(srcdir)/Makefile.am +subdir = gst +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = $(GST_PLUGINS_SELECTED) +DIST_SUBDIRS = $(GST_PLUGINS_ALL) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/common/parallel-subdirs.mak $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gst/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/common/parallel-subdirs.mak: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + 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-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + + +.PHONY: independent-subdirs $(SUBDIRS) + +independent-subdirs: $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ + +all-recursive: independent-subdirs + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/asfdemux/Makefile.am b/gst/asfdemux/Makefile.am new file mode 100644 index 0000000..93fa1e8 --- /dev/null +++ b/gst/asfdemux/Makefile.am @@ -0,0 +1,27 @@ +plugin_LTLIBRARIES = libgstasf.la + +libgstasf_la_SOURCES = gstasfdemux.c gstasf.c asfheaders.c asfpacket.c gstrtpasfdepay.c gstrtspwms.c +libgstasf_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstasf_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstriff-@GST_API_VERSION@ -lgstrtsp-@GST_API_VERSION@ -lgstsdp-@GST_API_VERSION@ \ + -lgstrtp-@GST_API_VERSION@ -lgstaudio-@GST_API_VERSION@ -lgsttag-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) \ + $(WIN32_LIBS) +libgstasf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstasf_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstasfdemux.h asfheaders.h asfpacket.h gstrtpasfdepay.h gstrtspwms.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstasf -:SHARED libgstasf \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstasf_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstasf_la_CFLAGS) \ + -:LDFLAGS $(libgstasf_la_LDFLAGS) \ + $(libgstasf_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/asfdemux/Makefile.in b/gst/asfdemux/Makefile.in new file mode 100644 index 0000000..b64afd9 --- /dev/null +++ b/gst/asfdemux/Makefile.in @@ -0,0 +1,888 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +subdir = gst/asfdemux +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) README +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstasf_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libgstasf_la_OBJECTS = libgstasf_la-gstasfdemux.lo \ + libgstasf_la-gstasf.lo libgstasf_la-asfheaders.lo \ + libgstasf_la-asfpacket.lo libgstasf_la-gstrtpasfdepay.lo \ + libgstasf_la-gstrtspwms.lo +libgstasf_la_OBJECTS = $(am_libgstasf_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstasf_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstasf_la_CFLAGS) $(CFLAGS) \ + $(libgstasf_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +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_CCLD_1 = +SOURCES = $(libgstasf_la_SOURCES) +DIST_SOURCES = $(libgstasf_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstasf.la +libgstasf_la_SOURCES = gstasfdemux.c gstasf.c asfheaders.c asfpacket.c gstrtpasfdepay.c gstrtspwms.c +libgstasf_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstasf_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstriff-@GST_API_VERSION@ -lgstrtsp-@GST_API_VERSION@ -lgstsdp-@GST_API_VERSION@ \ + -lgstrtp-@GST_API_VERSION@ -lgstaudio-@GST_API_VERSION@ -lgsttag-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) \ + $(WIN32_LIBS) + +libgstasf_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstasf_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = gstasfdemux.h asfheaders.h asfpacket.h gstrtpasfdepay.h gstrtspwms.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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 gst/asfdemux/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/asfdemux/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstasf.la: $(libgstasf_la_OBJECTS) $(libgstasf_la_DEPENDENCIES) $(EXTRA_libgstasf_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstasf_la_LINK) -rpath $(plugindir) $(libgstasf_la_OBJECTS) $(libgstasf_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-asfheaders.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-asfpacket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-gstasf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-gstasfdemux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-gstrtpasfdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstasf_la-gstrtspwms.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +libgstasf_la-gstasfdemux.lo: gstasfdemux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-gstasfdemux.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-gstasfdemux.Tpo -c -o libgstasf_la-gstasfdemux.lo `test -f 'gstasfdemux.c' || echo '$(srcdir)/'`gstasfdemux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-gstasfdemux.Tpo $(DEPDIR)/libgstasf_la-gstasfdemux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstasfdemux.c' object='libgstasf_la-gstasfdemux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-gstasfdemux.lo `test -f 'gstasfdemux.c' || echo '$(srcdir)/'`gstasfdemux.c + +libgstasf_la-gstasf.lo: gstasf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-gstasf.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-gstasf.Tpo -c -o libgstasf_la-gstasf.lo `test -f 'gstasf.c' || echo '$(srcdir)/'`gstasf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-gstasf.Tpo $(DEPDIR)/libgstasf_la-gstasf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstasf.c' object='libgstasf_la-gstasf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-gstasf.lo `test -f 'gstasf.c' || echo '$(srcdir)/'`gstasf.c + +libgstasf_la-asfheaders.lo: asfheaders.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-asfheaders.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-asfheaders.Tpo -c -o libgstasf_la-asfheaders.lo `test -f 'asfheaders.c' || echo '$(srcdir)/'`asfheaders.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-asfheaders.Tpo $(DEPDIR)/libgstasf_la-asfheaders.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asfheaders.c' object='libgstasf_la-asfheaders.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-asfheaders.lo `test -f 'asfheaders.c' || echo '$(srcdir)/'`asfheaders.c + +libgstasf_la-asfpacket.lo: asfpacket.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-asfpacket.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-asfpacket.Tpo -c -o libgstasf_la-asfpacket.lo `test -f 'asfpacket.c' || echo '$(srcdir)/'`asfpacket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-asfpacket.Tpo $(DEPDIR)/libgstasf_la-asfpacket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asfpacket.c' object='libgstasf_la-asfpacket.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-asfpacket.lo `test -f 'asfpacket.c' || echo '$(srcdir)/'`asfpacket.c + +libgstasf_la-gstrtpasfdepay.lo: gstrtpasfdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-gstrtpasfdepay.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-gstrtpasfdepay.Tpo -c -o libgstasf_la-gstrtpasfdepay.lo `test -f 'gstrtpasfdepay.c' || echo '$(srcdir)/'`gstrtpasfdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-gstrtpasfdepay.Tpo $(DEPDIR)/libgstasf_la-gstrtpasfdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpasfdepay.c' object='libgstasf_la-gstrtpasfdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-gstrtpasfdepay.lo `test -f 'gstrtpasfdepay.c' || echo '$(srcdir)/'`gstrtpasfdepay.c + +libgstasf_la-gstrtspwms.lo: gstrtspwms.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -MT libgstasf_la-gstrtspwms.lo -MD -MP -MF $(DEPDIR)/libgstasf_la-gstrtspwms.Tpo -c -o libgstasf_la-gstrtspwms.lo `test -f 'gstrtspwms.c' || echo '$(srcdir)/'`gstrtspwms.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstasf_la-gstrtspwms.Tpo $(DEPDIR)/libgstasf_la-gstrtspwms.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtspwms.c' object='libgstasf_la-gstrtspwms.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstasf_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstasf_la_CFLAGS) $(CFLAGS) -c -o libgstasf_la-gstrtspwms.lo `test -f 'gstrtspwms.c' || echo '$(srcdir)/'`gstrtspwms.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstasf -:SHARED libgstasf \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstasf_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstasf_la_CFLAGS) \ + -:LDFLAGS $(libgstasf_la_LDFLAGS) \ + $(libgstasf_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/asfdemux/README b/gst/asfdemux/README new file mode 100644 index 0000000..d864eb0 --- /dev/null +++ b/gst/asfdemux/README @@ -0,0 +1,88 @@ +ASF Demuxer Plugin +================== + +Overview +-------- + +This plugin is a demuxer for Microsoft's ASF Advanced Streaming Format +or ASF [1]. This demuxer only supports ASF v1.0 since the vast +majority of existing ASF files use that version. The specification +has been derived from a third party source [2] without reference to +the original. + +Design +------ + +The ASF format can carry any combination of audio, video or +'ASF_Command_Media' streams. For simplicity it is assumed that each +file can carry up to 16 audio streams and 16 video streams. These are +implemented as dynamic pads and appear as appropriate once the file +headers have been parsed. + + (-------------------------) + ! asfdemux ! + ! (video/raw0)--- + ! (video/raw1)--- + ! (video/raw... + --- src ! + ! (audio/raw0)--- + ! (audio/raw1)--- + ! (audio/raw... + ! ! + (-------------------------) + + +Known stream fourccs are: + +Type Tags MIME type +------------------------------------------ +H263 H263 I263 video/x-h263 +MJPEG MJPG image/jpeg +MPEG4 DIVX divx DX50 video/mpeg + XVID xvid mp4s + MP4S M4S2 m4s2 + 0x04000000 +MSMPEG4V1 MPG4 video/mpeg +MSMPEG4V2 MP42 video/mpeg +MSMPEG4V3 MP43 DIV3 video/mpeg +WMV1 WMV1 video/x-wmv, wmvversion = (int) 1 +WMV2 WMV2 video/x-wmv, wmvversion = (int) 2 +WMV3 WMV3 video/x-wmv, wmvversion = (int) 3 +WMA1 WMA1 audio/x-wma, wmaversion = (int) 1 +WMA2 WMA2 audio/x-wma, wmaversion = (int) 2 + audio/x-wma, wmaversion = (int) 3 + +These video stream headers is very similar to that used in the AVI +format as are the audio stream headers. In addition the content types +are basically the same also so, for compatibility with existing +plugins the src pads are set up as video/x-msvideo. This enables +compatibility with the ffmpeg plugin. + +The demuxing process begins with the loop function gst_asf_demux_loop +and parses the file in a recursive tree as follows: + + gst_asf_demux_loop() + +-> gst_asf_demux_process_object() <---- + +-> gst_asf_demux_process_stream() \ + |-> gst_asf_demux_process_file() | + |-> gst_asf_demux_process_header() --+ + |-> gst_asf_demux_process_data() + +-> gst_asf_demux_process_segment() + +-> gst_asf_demux_process_chunk() + +Todo +---- + +- Support for ASF v2.0 +- Support for command media streams + + + +References +---------- + +[1] Microsoft. ASF Specification - Windows Media Technologies. +http://www.microsoft.com/windows/windowsmedia/format/asfspec.aspx (v01.20.01e, September 2003) + +[2] divx at euro.ru. ASF format version 1.0, +reconstruction. http://avifile.sourceforge.net/asf-1.0.htm diff --git a/gst/asfdemux/asfheaders.c b/gst/asfdemux/asfheaders.c new file mode 100644 index 0000000..b8e8a3c --- /dev/null +++ b/gst/asfdemux/asfheaders.c @@ -0,0 +1,212 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <gst/gst.h> + +#include "asfheaders.h" + +const ASFGuidHash asf_payload_ext_guids[] = { + {ASF_PAYLOAD_EXTENSION_DURATION, "ASF_PAYLOAD_EXTENSION_DURATION", + {0xC6BD9450, 0x4907867F, 0x79C7A383, 0xAD33B721} + }, + {ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT, "ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT", + {0xD590DC20, 0x436C07BC, 0xBBF3f79C, 0xDCA4F1FB}}, + {ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO, + "ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO", + {0x1b1ee554, 0x4bc8f9ea, 0x6b371a82, 0xb8c4e474}}, + {ASF_PAYLOAD_EXTENSION_TIMING, "ASF_PAYLOAD_EXTENSION_TIMING", + {0XFD3CC02A, 0X4CFA06DB, 0X12721C80, 0XE44587D3}}, + {ASF_PAYLOAD_EXTENSION_UNDEFINED, "ASF_PAYLOAD_EXTENSION_UNDEFINED", + {0, 0, 0, 0} + } +}; + +const ASFGuidHash asf_correction_guids[] = { + {ASF_CORRECTION_ON, "ASF_CORRECTION_ON", + {0xBFC3CD50, 0x11CF618F, 0xAA00B28B, 0x20E2B400} + }, + {ASF_CORRECTION_OFF, "ASF_CORRECTION_OFF", + {0x20FB5700, 0x11CF5B55, 0x8000FDA8, 0x2B445C5F} + }, + /* CHECKME: where does this 49F1A440... GUID come from? (tpm) */ + {ASF_CORRECTION_OFF, "ASF_CORRECTION_OFF", + {0x49F1A440, 0x11D04ECE, 0xA000ACA3, 0xF64803C9} + }, + {ASF_CORRECTION_UNDEFINED, "ASF_CORRECTION_UNDEFINED", + {0, 0, 0, 0} + } +}; + +const ASFGuidHash asf_stream_guids[] = { + {ASF_STREAM_VIDEO, "ASF_STREAM_VIDEO", + {0xBC19EFC0, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F} + }, + {ASF_STREAM_AUDIO, "ASF_STREAM_AUDIO", + {0xF8699E40, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F} + }, + {ASF_STREAM_EXT_EMBED_HEADER, "ASF_STREAM_EXT_EMBED_HEADER", + {0X3AFB65E2, 0X40F247EF, 0XA9702CAC, 0X43D3710D}}, + {ASF_STREAM_UNDEFINED, "ASF_STREAM_UNDEFINED", + {0, 0, 0, 0} + } +}; + +const ASFGuidHash asf_ext_stream_guids[] = { + {ASF_EXT_STREAM_AUDIO, "ASF_EXT_STREAM_AUDIO", + {0X31178C9D, 0X452803E1, 0XF93D82B5, 0X03F522DB} + }, + {ASF_EXT_STREAM_UNDEFINED, "ASF_EXT_STREAM_UNDEFINED", + {0, 0, 0, 0} + } +}; + +const ASFGuidHash asf_object_guids[] = { + {ASF_OBJ_STREAM, "ASF_OBJ_STREAM", + {0xB7DC0791, 0x11CFA9B7, 0xC000E68E, 0x6553200C} + }, + {ASF_OBJ_DATA, "ASF_OBJ_DATA", + {0x75b22636, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200} + }, + {ASF_OBJ_FILE, "ASF_OBJ_FILE", + {0x8CABDCA1, 0x11CFA947, 0xC000E48E, 0x6553200C} + }, + {ASF_OBJ_HEADER, "ASF_OBJ_HEADER", + {0x75B22630, 0x11CF668E, 0xAA00D9A6, 0x6CCE6200} + }, + {ASF_OBJ_CONCEAL_NONE, "ASF_OBJ_CONCEAL_NONE", + {0x20fb5700, 0x11cf5b55, 0x8000FDa8, 0x2B445C5f} + }, + {ASF_OBJ_COMMENT, "ASF_OBJ_COMMENT", + {0x75b22633, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200} + }, + {ASF_OBJ_CODEC_COMMENT, "ASF_OBJ_CODEC_COMMENT", + {0x86D15240, 0x11D0311D, 0xA000A4A3, 0xF64803C9} + }, + {ASF_OBJ_CODEC_COMMENT1, "ASF_OBJ_CODEC_COMMENT1", + {0x86d15241, 0x11d0311d, 0xA000A4a3, 0xF64803c9} + }, + {ASF_OBJ_SIMPLE_INDEX, "ASF_OBJ_SIMPLE_INDEX", + {0x33000890, 0x11cfe5b1, 0xA000F489, 0xCB4903c9} + }, + {ASF_OBJ_INDEX, "ASF_OBJ_INDEX", + {0xd6e229d3, 0x11d135da, 0xa0003490, 0xbe4903c9} + }, + {ASF_OBJ_HEAD1, "ASF_OBJ_HEAD1", + {0x5fbf03b5, 0x11cfa92e, 0xC000E38e, 0x6553200c} + }, + {ASF_OBJ_HEAD2, "ASF_OBJ_HEAD2", + {0xabd3d211, 0x11cfa9ba, 0xC000E68e, 0x6553200c} + }, + {ASF_OBJ_PADDING, "ASF_OBJ_PADDING", + {0x1806D474, 0x4509CADF, 0xAB9ABAA4, 0xE8AA96CB} + }, + {ASF_OBJ_BITRATE_PROPS, "ASF_OBJ_BITRATE_PROPS", + {0x7bf875ce, 0x11d1468d, 0x6000828d, 0xb2a2c997} + }, + {ASF_OBJ_EXT_CONTENT_DESC, "ASF_OBJ_EXT_CONTENT_DESC", + {0xd2d0a440, 0x11d2e307, 0xa000f097, 0x50a85ec9} + }, + {ASF_OBJ_BITRATE_MUTEX, "ASF_OBJ_BITRATE_MUTEX", + {0xd6e229dc, 0x11d135da, 0xa0003490, 0xbe4903c9} + }, + {ASF_OBJ_LANGUAGE_LIST, "ASF_OBJ_LANGUAGE_LIST", + {0x7c4346a9, 0x4bfcefe0, 0x3e3929b2, 0x855c41de} + }, + {ASF_OBJ_METADATA_OBJECT, "ASF_OBJ_METADATA_OBJECT", + {0xc5f8cbea, 0x48775baf, 0x8caa6784, 0xca4cfa44} + }, + {ASF_OBJ_EXTENDED_STREAM_PROPS, "ASF_OBJ_EXTENDED_STREAM_PROPS", + {0x14e6a5cb, 0x4332c672, 0x69a99983, 0x5a5b0652} + }, + {ASF_OBJ_COMPATIBILITY, "ASF_OBJ_COMPATIBILITY", + {0x26f18b5d, 0x47ec4584, 0x650e5f9f, 0xc952041f} + }, + {ASF_OBJ_INDEX_PLACEHOLDER, "ASF_OBJ_INDEX_PLACEHOLDER", + {0xd9aade20, 0x4f9c7c17, 0x558528bc, 0xa2e298dd} + }, + {ASF_OBJ_INDEX_PARAMETERS, "ASF_OBJ_INDEX_PARAMETERS", + {0xd6e229df, 0x11d135da, 0xa0003490, 0xbe4903c9} + }, + {ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION, "ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION", + {0xa08649cf, 0x46704775, 0x356e168a, 0xcd667535} + }, + {ASF_OBJ_STREAM_PRIORITIZATION, "ASF_OBJ_STREAM_PRIORITIZATION", + {0xd4fed15b, 0x454f88d3, 0x5cedf081, 0x249e9945} + }, + {ASF_OBJ_CONTENT_ENCRYPTION, "ASF_OBJ_CONTENT_ENCRYPTION", + {0x2211b3fb, 0x11d2bd23, 0xa000b7b4, 0x6efc55c9} + }, + {ASF_OBJ_EXT_CONTENT_ENCRYPTION, "ASF_OBJ_EXT_CONTENT_ENCRYPTION", + {0x298ae614, 0x4c172622, 0xe0da35b9, 0x9c28e97e} + }, + {ASF_OBJ_DIGITAL_SIGNATURE_OBJECT, "ASF_OBJ_DIGITAL_SIGNATURE_OBJECT", + {0x2211b3fc, 0x11d2bd23, 0xa000b7b4, 0x6efc55c9} + }, + {ASF_OBJ_SCRIPT_COMMAND, "ASF_OBJ_SCRIPT_COMMAND", + {0x1efb1a30, 0x11d00b62, 0xa0009ba3, 0xf64803c9} + }, + {ASF_OBJ_MARKER, "ASF_OBJ_MARKER", + {0xf487cd01, 0x11cfa951, 0xc000e68e, 0x6553200c} + }, + /* This guid is definitely used for encryption (mentioned in MS smooth + * streaming docs) in new PlayReady (c) (tm) (wtf) system, but I haven't + * found a proper name for it. + * (Edward Jan 11 2011).*/ + {ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT, "ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT", + {0x9a04f079, 0x42869840, 0x5be692ab, 0x955f88e0} + }, + {ASF_OBJ_METADATA_LIBRARY_OBJECT, "ASF_OBJ_METADATA_LIBRARY_OBJECT", + {0x44231c94, 0x49d19498, 0x131d41a1, 0x5470454e} + }, + {ASF_OBJ_UNDEFINED, "ASF_OBJ_UNDEFINED", + {0, 0, 0, 0} + } +}; + +guint32 +gst_asf_identify_guid (const ASFGuidHash * guids, ASFGuid * guid) +{ + gint i; + + for (i = 0; guids[i].obj_id != ASF_OBJ_UNDEFINED; ++i) { + if (guids[i].guid.v1 == guid->v1 && + guids[i].guid.v2 == guid->v2 && + guids[i].guid.v3 == guid->v3 && guids[i].guid.v4 == guid->v4) { + return guids[i].obj_id; + } + } + + /* The base case if none is found */ + return ASF_OBJ_UNDEFINED; +} + +const gchar * +gst_asf_get_guid_nick (const ASFGuidHash * guids, guint32 obj_id) +{ + gint i; + + for (i = 0; guids[i].obj_id != ASF_OBJ_UNDEFINED; ++i) { + if (guids[i].obj_id == obj_id) { + return guids[i].obj_id_str; + } + } + + /* The base case if none is found */ + return "ASF_OBJ_UNDEFINED"; +} diff --git a/gst/asfdemux/asfheaders.h b/gst/asfdemux/asfheaders.h new file mode 100644 index 0000000..9e8d972 --- /dev/null +++ b/gst/asfdemux/asfheaders.h @@ -0,0 +1,188 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __ASFHEADERS_H__ +#define __ASFHEADERS_H__ + +G_BEGIN_DECLS + +typedef struct { + guint32 v1; + guint32 v2; + guint32 v3; + guint32 v4; +} ASFGuid; + + + +typedef struct { + guint8 obj_id; + const gchar *obj_id_str; + ASFGuid guid; +} ASFGuidHash; + +typedef enum { + ASF_OBJ_UNDEFINED = 0, + ASF_OBJ_STREAM, + ASF_OBJ_DATA, + ASF_OBJ_FILE, + ASF_OBJ_HEADER, + ASF_OBJ_CONCEAL_NONE, + ASF_OBJ_COMMENT, + ASF_OBJ_CODEC_COMMENT, + ASF_OBJ_CODEC_COMMENT1, + ASF_OBJ_SIMPLE_INDEX, + ASF_OBJ_INDEX, + ASF_OBJ_HEAD1, + ASF_OBJ_HEAD2, + ASF_OBJ_PADDING, + ASF_OBJ_BITRATE_PROPS, + ASF_OBJ_EXT_CONTENT_DESC, + ASF_OBJ_BITRATE_MUTEX, + ASF_OBJ_LANGUAGE_LIST, + ASF_OBJ_METADATA_OBJECT, + ASF_OBJ_EXTENDED_STREAM_PROPS, + ASF_OBJ_COMPATIBILITY, + ASF_OBJ_INDEX_PLACEHOLDER, + ASF_OBJ_INDEX_PARAMETERS, + ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION, + ASF_OBJ_STREAM_PRIORITIZATION, + ASF_OBJ_CONTENT_ENCRYPTION, + ASF_OBJ_EXT_CONTENT_ENCRYPTION, + ASF_OBJ_DIGITAL_SIGNATURE_OBJECT, + ASF_OBJ_SCRIPT_COMMAND, + ASF_OBJ_MARKER, + ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT, + ASF_OBJ_METADATA_LIBRARY_OBJECT, +} AsfObjectID; + +typedef enum { + ASF_STREAM_UNDEFINED = 0, + ASF_STREAM_VIDEO, + ASF_STREAM_AUDIO, + ASF_STREAM_EXT_EMBED_HEADER +} AsfStreamType; + +typedef enum { + ASF_EXT_STREAM_UNDEFINED = 0, + ASF_EXT_STREAM_AUDIO +} AsfExtStreamType; + +typedef enum { + ASF_CORRECTION_UNDEFINED = 0, + ASF_CORRECTION_ON, + ASF_CORRECTION_OFF +} AsfCorrectionType; + +typedef enum { + ASF_PAYLOAD_EXTENSION_UNDEFINED = 0, + ASF_PAYLOAD_EXTENSION_DURATION, + ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT, + ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO, + ASF_PAYLOAD_EXTENSION_TIMING +} AsfPayloadExtensionID; + +extern const ASFGuidHash asf_payload_ext_guids[]; + +extern const ASFGuidHash asf_correction_guids[]; + +extern const ASFGuidHash asf_stream_guids[]; + +extern const ASFGuidHash asf_ext_stream_guids[]; + +extern const ASFGuidHash asf_object_guids[]; + +/* GUID utilities */ +guint32 gst_asf_identify_guid (const ASFGuidHash * guids, + ASFGuid * guid); + +const gchar *gst_asf_get_guid_nick (const ASFGuidHash * guids, + guint32 obj_id); + +struct _asf_stream_audio { + guint16 codec_tag; + guint16 channels; + guint32 sample_rate; + guint32 byte_rate; + guint16 block_align; + guint16 word_size; + guint16 size; +}; + +typedef struct _asf_stream_audio asf_stream_audio; + +struct _asf_stream_video { + guint32 width; + guint32 height; + guint8 unknown; + guint16 size; +}; + +typedef struct _asf_stream_video asf_stream_video; + +struct _asf_stream_video_format { + guint32 size; + guint32 width; + guint32 height; + guint16 planes; + guint16 depth; + guint32 tag; + guint32 image_size; + guint32 xpels_meter; + guint32 ypels_meter; + guint32 num_colors; + guint32 imp_colors; +}; + +typedef struct _asf_stream_video_format asf_stream_video_format; + +struct _asf_obj_data_correction { + guint8 type; + guint8 cycle; +}; + +typedef struct _asf_obj_data_correction asf_obj_data_correction; + +struct _asf_packet_info { + guint32 padsize; + guint8 replicsizetype; + guint8 fragoffsettype; + guint8 seqtype; + guint8 segsizetype; + gboolean multiple; + guint32 size_left; +}; + +typedef struct _asf_packet_info asf_packet_info; + +struct _asf_segment_info { + guint8 stream_number; + guint32 chunk_size; + guint32 frag_offset; + guint32 segment_size; + guint32 sequence; + guint32 frag_timestamp; + gboolean compressed; +}; + +typedef struct _asf_segment_info asf_segment_info; + +G_END_DECLS + +#endif /* __ASFHEADERS_H__ */ diff --git a/gst/asfdemux/asfpacket.c b/gst/asfdemux/asfpacket.c new file mode 100755 index 0000000..7379d86 --- /dev/null +++ b/gst/asfdemux/asfpacket.c @@ -0,0 +1,658 @@ +/* GStreamer ASF/WMV/WMA demuxer + * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* FIXME: + * file:///home/tpm/samples/video/asf//336370-regis-velo862.wmv + * file:///home/tpm/samples/video/asf//336370-eichhoer.wmv + * throw errors (not always necessarily) in this code path + * (looks like they carry broken payloads/packets though) */ + +#include "asfpacket.h" + +#include <gst/gstutils.h> +#include <gst/gstinfo.h> +#include <string.h> + +/* we are unlikely to deal with lengths > 2GB here any time soon, so just + * return a signed int and use that for error reporting */ +static inline gint +asf_packet_read_varlen_int (guint lentype_flags, guint lentype_bit_offset, + const guint8 ** p_data, guint * p_size) +{ + static const guint lens[4] = { 0, 1, 2, 4 }; + guint len, val; + + len = lens[(lentype_flags >> lentype_bit_offset) & 0x03]; + + /* will make caller bail out with a short read if there's not enough data */ + if (G_UNLIKELY (*p_size < len)) { + GST_WARNING ("need %u bytes, but only %u bytes available", len, *p_size); + return -1; + } + + switch (len) { + case 0: + val = 0; + break; + case 1: + val = GST_READ_UINT8 (*p_data); + break; + case 2: + val = GST_READ_UINT16_LE (*p_data); + break; + case 4: + val = GST_READ_UINT32_LE (*p_data); + break; + default: + val = 0; + g_assert_not_reached (); + } + + *p_data += len; + *p_size -= len; + + return (gint) val; +} + +static GstBuffer * +asf_packet_create_payload_buffer (AsfPacket * packet, const guint8 ** p_data, + guint * p_size, guint payload_len) +{ + guint off; + + g_assert (payload_len <= *p_size); + + off = (guint) (*p_data - packet->bdata); + g_assert (off < gst_buffer_get_size (packet->buf)); + + *p_data += payload_len; + *p_size -= payload_len; + + return gst_buffer_copy_region (packet->buf, GST_BUFFER_COPY_ALL, off, + payload_len); +} + +static AsfPayload * +asf_payload_find_previous_fragment (AsfPayload * payload, AsfStream * stream) +{ + AsfPayload *ret; + + if (G_UNLIKELY (stream->payloads->len == 0)) { + GST_DEBUG ("No previous fragments to merge with for stream %u", stream->id); + return NULL; + } + + ret = + &g_array_index (stream->payloads, AsfPayload, stream->payloads->len - 1); + + if (G_UNLIKELY (ret->mo_size != payload->mo_size || + ret->mo_number != payload->mo_number || ret->mo_offset != 0)) { + if (payload->mo_size != 0) { + GST_WARNING ("Previous fragment does not match continued fragment"); + return NULL; + } else { + /* Warn about this case, but accept it anyway: files in the wild sometimes + * have continued packets where the subsequent fragments say that they're + * zero-sized. */ + GST_WARNING ("Previous fragment found, but current fragment has " + "zero size, accepting anyway"); + } + } +#if 0 + if (this_fragment->mo_offset + this_payload_len > first_fragment->mo_size) { + GST_WARNING ("Merged fragments would be bigger than the media object"); + return FALSE; + } +#endif + + return ret; +} + +/* TODO: if we have another payload already queued for this stream and that + * payload doesn't have a duration, maybe we can calculate a duration for it + * (if the previous timestamp is smaller etc. etc.) */ +static void +gst_asf_payload_queue_for_stream (GstASFDemux * demux, AsfPayload * payload, + AsfStream * stream) +{ + GST_DEBUG_OBJECT (demux, "Got payload for stream %d ts:%" GST_TIME_FORMAT, + stream->id, GST_TIME_ARGS (payload->ts)); + + /* make timestamps start from 0; first_ts will be determined during activation (once we have enough data), + which will also update ts of all packets queued before we knew first_ts; */ + if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (demux->first_ts) + && GST_CLOCK_TIME_IS_VALID (payload->ts))) { + if (payload->ts > demux->first_ts) + payload->ts -= demux->first_ts; + else + payload->ts = 0; + } + + /* remove any incomplete payloads that will never be completed */ + while (stream->payloads->len > 0) { + AsfPayload *prev; + guint idx_last; + + idx_last = stream->payloads->len - 1; + prev = &g_array_index (stream->payloads, AsfPayload, idx_last); + + if (G_UNLIKELY (gst_asf_payload_is_complete (prev))) + break; + + GST_DEBUG_OBJECT (demux, "Dropping incomplete fragmented media object " + "queued for stream %u", stream->id); + + gst_buffer_replace (&prev->buf, NULL); + g_array_remove_index (stream->payloads, idx_last); + + /* there's data missing, so there's a discontinuity now */ + GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); + } + + /* If we're about to queue a key frame that is before the segment start, we + * can ditch any previously queued payloads (which would also be before the + * segment start). This makes sure the decoder doesn't decode more than + * absolutely necessary after a seek (we don't push out payloads that are + * before the segment start until we have at least one that falls within the + * segment) */ + if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) && + payload->ts < demux->segment.start && payload->keyframe)) { + GST_DEBUG_OBJECT (demux, "Queueing keyframe before segment start, removing" + " %u previously-queued payloads, which would be out of segment too and" + " hence don't have to be decoded", stream->payloads->len); + while (stream->payloads->len > 0) { + AsfPayload *last; + guint idx_last; + + idx_last = stream->payloads->len - 1; + last = &g_array_index (stream->payloads, AsfPayload, idx_last); + gst_buffer_replace (&last->buf, NULL); + g_array_remove_index (stream->payloads, idx_last); + } + + /* Mark discontinuity (should be done via stream->discont anyway though) */ + GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); + } + + g_array_append_vals (stream->payloads, payload, 1); +} + +static void +asf_payload_parse_replicated_data_extensions (AsfStream * stream, + AsfPayload * payload) +{ + AsfPayloadExtension *ext; + guint off; + guint16 ext_len; + + if (!stream->ext_props.valid || stream->ext_props.payload_extensions == NULL) + return; + + off = 8; + for (ext = stream->ext_props.payload_extensions; ext->len > 0; ++ext) { + ext_len = ext->len; + if (ext_len == 0xFFFF) { /* extension length is determined by first two bytes in replicated data */ + ext_len = GST_READ_UINT16_LE (payload->rep_data + off); + off += 2; + } + if (G_UNLIKELY (off + ext_len > payload->rep_data_len)) { + GST_WARNING ("not enough replicated data for defined extensions"); + return; + } + switch (ext->id) { + case ASF_PAYLOAD_EXTENSION_DURATION: + if (G_LIKELY (ext_len == 2)) { + guint16 tdur = GST_READ_UINT16_LE (payload->rep_data + off); + /* packet durations of 1ms are mostly invalid */ + if (tdur != 1) + payload->duration = tdur * GST_MSECOND; + } else { + GST_WARNING ("unexpected DURATION extensions len %u", ext_len); + } + break; + case ASF_PAYLOAD_EXTENSION_SYSTEM_CONTENT: + if (G_LIKELY (ext_len == 1)) { + guint8 data = payload->rep_data[off]; + + payload->interlaced = data & 0x1; + payload->rff = data & 0x8; + payload->tff = (data & 0x2) || !(data & 0x4); + GST_DEBUG ("SYSTEM_CONTENT: interlaced:%d, rff:%d, tff:%d", + payload->interlaced, payload->rff, payload->tff); + } else { + GST_WARNING ("unexpected SYSTEM_CONTE extensions len %u", ext_len); + } + break; + case ASF_PAYLOAD_EXTENSION_SYSTEM_PIXEL_ASPECT_RATIO: + if (G_LIKELY (ext_len == 2)) { + payload->par_x = payload->rep_data[off]; + payload->par_y = payload->rep_data[off + 1]; + GST_DEBUG ("PAR %d / %d", payload->par_x, payload->par_y); + } else { + GST_WARNING ("unexpected SYSTEM_PIXEL_ASPECT_RATIO extensions len %u", + ext_len); + } + break; + case ASF_PAYLOAD_EXTENSION_TIMING: + { + /* dvr-ms timing - this will override packet timestamp */ + guint64 time = GST_READ_UINT64_LE (payload->rep_data + off + 8); + if (time != 0xFFFFFFFFFFFFFFFF) + payload->ts = time * 100; + else + payload->ts = GST_CLOCK_TIME_NONE; + } + break; + default: + GST_LOG ("UNKNOWN PAYLOAD EXTENSION!"); + break; + } + off += ext_len; + } +} + +static gboolean +gst_asf_demux_parse_payload (GstASFDemux * demux, AsfPacket * packet, + gint lentype, const guint8 ** p_data, guint * p_size) +{ + AsfPayload payload = { 0, }; + AsfStream *stream; + gboolean is_compressed; + guint payload_len; + guint stream_num; + + if (G_UNLIKELY (*p_size < 1)) { + GST_WARNING_OBJECT (demux, "Short packet!"); + return FALSE; + } + + stream_num = GST_READ_UINT8 (*p_data) & 0x7f; + payload.keyframe = ((GST_READ_UINT8 (*p_data) & 0x80) != 0); + + *p_data += 1; + *p_size -= 1; + + payload.ts = GST_CLOCK_TIME_NONE; + payload.duration = GST_CLOCK_TIME_NONE; + payload.par_x = 0; + payload.par_y = 0; + payload.interlaced = FALSE; + payload.tff = FALSE; + payload.rff = FALSE; + + payload.mo_number = + asf_packet_read_varlen_int (packet->prop_flags, 4, p_data, p_size); + payload.mo_offset = + asf_packet_read_varlen_int (packet->prop_flags, 2, p_data, p_size); + payload.rep_data_len = + asf_packet_read_varlen_int (packet->prop_flags, 0, p_data, p_size); + + is_compressed = (payload.rep_data_len == 1); + + GST_LOG_OBJECT (demux, "payload for stream %u", stream_num); + GST_LOG_OBJECT (demux, "keyframe : %s", (payload.keyframe) ? "yes" : "no"); + GST_LOG_OBJECT (demux, "compressed : %s", (is_compressed) ? "yes" : "no"); + + if (G_UNLIKELY (*p_size < payload.rep_data_len)) { + GST_WARNING_OBJECT (demux, "Short packet! rep_data_len=%u, size=%u", + payload.rep_data_len, *p_size); + return FALSE; + } + + memcpy (payload.rep_data, *p_data, + MIN (sizeof (payload.rep_data), payload.rep_data_len)); + + *p_data += payload.rep_data_len; + *p_size -= payload.rep_data_len; + + if (G_UNLIKELY (*p_size == 0)) { + GST_WARNING_OBJECT (demux, "payload without data!?"); + return FALSE; + } + + /* we use -1 as lentype for a single payload that's the size of the packet */ + if (G_UNLIKELY ((lentype >= 0 && lentype <= 3))) { + payload_len = asf_packet_read_varlen_int (lentype, 0, p_data, p_size); + if (*p_size < payload_len) { + GST_WARNING_OBJECT (demux, "Short packet! payload_len=%u, size=%u", + payload_len, *p_size); + return FALSE; + } + } else { + payload_len = *p_size; + } + + GST_LOG_OBJECT (demux, "payload length: %u", payload_len); + + stream = gst_asf_demux_get_stream (demux, stream_num); + + if (G_UNLIKELY (stream == NULL)) { + if (gst_asf_demux_is_unknown_stream (demux, stream_num)) { + GST_WARNING_OBJECT (demux, "Payload for unknown stream %u, skipping", + stream_num); + } + if (*p_size < payload_len) { + *p_data += *p_size; + *p_size = 0; + } else { + *p_data += payload_len; + *p_size -= payload_len; + } + return TRUE; + } + + if (G_UNLIKELY (!is_compressed)) { + GST_LOG_OBJECT (demux, "replicated data length: %u", payload.rep_data_len); + + if (payload.rep_data_len >= 8) { + payload.mo_size = GST_READ_UINT32_LE (payload.rep_data); + payload.ts = GST_READ_UINT32_LE (payload.rep_data + 4) * GST_MSECOND; + if (G_UNLIKELY (payload.ts < demux->preroll)) + payload.ts = 0; + else + payload.ts -= demux->preroll; + asf_payload_parse_replicated_data_extensions (stream, &payload); + + GST_LOG_OBJECT (demux, "media object size : %u", payload.mo_size); + GST_LOG_OBJECT (demux, "media object ts : %" GST_TIME_FORMAT, + GST_TIME_ARGS (payload.ts)); + GST_LOG_OBJECT (demux, "media object dur : %" GST_TIME_FORMAT, + GST_TIME_ARGS (payload.duration)); + } else if (payload.rep_data_len == 0) { + payload.mo_size = 0; + } else if (payload.rep_data_len != 0) { + GST_WARNING_OBJECT (demux, "invalid replicated data length, very bad"); + *p_data += payload_len; + *p_size -= payload_len; + return FALSE; + } + + GST_LOG_OBJECT (demux, "media object offset : %u", payload.mo_offset); + + GST_LOG_OBJECT (demux, "payload length: %u", payload_len); + + if (payload_len == 0) { + GST_DEBUG_OBJECT (demux, "skipping empty payload"); + } else if (payload.mo_offset == 0 && payload.mo_size == payload_len) { + /* if the media object is not fragmented, just create a sub-buffer */ + GST_LOG_OBJECT (demux, "unfragmented media object size %u", payload_len); + payload.buf = asf_packet_create_payload_buffer (packet, p_data, p_size, + payload_len); + payload.buf_filled = payload_len; + gst_asf_payload_queue_for_stream (demux, &payload, stream); + } else { + const guint8 *payload_data = *p_data; + + g_assert (payload_len <= *p_size); + + *p_data += payload_len; + *p_size -= payload_len; + + /* n-th fragment of a fragmented media object? */ + if (payload.mo_offset != 0) { + AsfPayload *prev; + + if ((prev = asf_payload_find_previous_fragment (&payload, stream))) { + if (prev->buf == NULL || (payload.mo_size > 0 + && payload.mo_size != prev->mo_size) + || payload.mo_offset >= gst_buffer_get_size (prev->buf) + || payload.mo_offset + payload_len > + gst_buffer_get_size (prev->buf)) { + GST_WARNING_OBJECT (demux, "Offset doesn't match previous data?!"); + } else { + /* we assume fragments are payloaded with increasing mo_offset */ + if (payload.mo_offset != prev->buf_filled) { + GST_WARNING_OBJECT (demux, "media object payload discontinuity: " + "offset=%u vs buf_filled=%u", payload.mo_offset, + prev->buf_filled); + } + gst_buffer_fill (prev->buf, payload.mo_offset, + payload_data, payload_len); + prev->buf_filled = + MAX (prev->buf_filled, payload.mo_offset + payload_len); + GST_LOG_OBJECT (demux, "Merged media object fragments, size now %u", + prev->buf_filled); + } + } else { + GST_DEBUG_OBJECT (demux, "n-th payload fragment, but don't have " + "any previous fragment, ignoring payload"); + } + } else { + GST_LOG_OBJECT (demux, "allocating buffer of size %u for fragmented " + "media object", payload.mo_size); + payload.buf = gst_buffer_new_allocate (NULL, payload.mo_size, NULL); + gst_buffer_fill (payload.buf, 0, payload_data, payload_len); + payload.buf_filled = payload_len; + + gst_asf_payload_queue_for_stream (demux, &payload, stream); + } + } + } else { + const guint8 *payload_data; + GstClockTime ts, ts_delta; + guint num; + + GST_LOG_OBJECT (demux, "Compressed payload, length=%u", payload_len); + + payload_data = *p_data; + + *p_data += payload_len; + *p_size -= payload_len; + + ts = payload.mo_offset * GST_MSECOND; + if (G_UNLIKELY (ts < demux->preroll)) + ts = 0; + else + ts -= demux->preroll; + ts_delta = payload.rep_data[0] * GST_MSECOND; + + for (num = 0; payload_len > 0; ++num) { + guint sub_payload_len; + + sub_payload_len = GST_READ_UINT8 (payload_data); + + GST_LOG_OBJECT (demux, "subpayload #%u: len=%u, ts=%" GST_TIME_FORMAT, + num, sub_payload_len, GST_TIME_ARGS (ts)); + + ++payload_data; + --payload_len; + + if (G_UNLIKELY (payload_len < sub_payload_len)) { + GST_WARNING_OBJECT (demux, "Short payload! %u bytes left", payload_len); + return FALSE; + } + + if (G_LIKELY (sub_payload_len > 0)) { + payload.buf = asf_packet_create_payload_buffer (packet, + &payload_data, &payload_len, sub_payload_len); + payload.buf_filled = sub_payload_len; + + payload.ts = ts; + if (G_LIKELY (ts_delta)) + payload.duration = ts_delta; + else + payload.duration = GST_CLOCK_TIME_NONE; + + gst_asf_payload_queue_for_stream (demux, &payload, stream); + } + + ts += ts_delta; + } + } + + return TRUE; +} + +GstAsfDemuxParsePacketError +gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf) +{ + AsfPacket packet = { 0, }; + GstMapInfo map; + const guint8 *data; + gboolean has_multiple_payloads; + GstAsfDemuxParsePacketError ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE; + guint8 ec_flags, flags1; + guint size; + + gst_buffer_map (buf, &map, GST_MAP_READ); + data = map.data; + size = map.size; + GST_LOG_OBJECT (demux, "Buffer size: %u", size); + + /* need at least two payload flag bytes, send time, and duration */ + if (G_UNLIKELY (size < 2 + 4 + 2)) { + GST_WARNING_OBJECT (demux, "Packet size is < 8"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; + goto done; + } + + packet.buf = buf; + /* evidently transient */ + packet.bdata = data; + + ec_flags = GST_READ_UINT8 (data); + + /* skip optional error correction stuff */ + if ((ec_flags & 0x80) != 0) { + guint ec_len_type, ec_len; + + ec_len_type = (ec_flags & 0x60) >> 5; + if (ec_len_type == 0) { + ec_len = ec_flags & 0x0f; + } else { + GST_WARNING_OBJECT (demux, "unexpected error correction length type %u", + ec_len_type); + ec_len = 2; + } + GST_LOG_OBJECT (demux, "packet has error correction (%u bytes)", ec_len); + + /* still need at least two payload flag bytes, send time, and duration */ + if (size <= (1 + ec_len) + 2 + 4 + 2) { + GST_WARNING_OBJECT (demux, "Packet size is < 8 with Error Correction"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; + goto done; + } + + data += 1 + ec_len; + size -= 1 + ec_len; + } + + /* parse payload info */ + flags1 = GST_READ_UINT8 (data); + packet.prop_flags = GST_READ_UINT8 (data + 1); + + data += 2; + size -= 2; + + has_multiple_payloads = (flags1 & 0x01) != 0; + + packet.length = asf_packet_read_varlen_int (flags1, 5, &data, &size); + + packet.sequence = asf_packet_read_varlen_int (flags1, 1, &data, &size); + + packet.padding = asf_packet_read_varlen_int (flags1, 3, &data, &size); + + if (G_UNLIKELY (size < 6)) { + GST_WARNING_OBJECT (demux, "Packet size is < 6"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; + goto done; + } + + packet.send_time = GST_READ_UINT32_LE (data) * GST_MSECOND; + packet.duration = GST_READ_UINT16_LE (data + 4) * GST_MSECOND; + + data += 4 + 2; + size -= 4 + 2; + + GST_LOG_OBJECT (demux, "flags : 0x%x", flags1); + GST_LOG_OBJECT (demux, "multiple payloads: %u", has_multiple_payloads); + GST_LOG_OBJECT (demux, "packet length : %u", packet.length); + GST_LOG_OBJECT (demux, "sequence : %u", packet.sequence); + GST_LOG_OBJECT (demux, "padding : %u", packet.padding); + GST_LOG_OBJECT (demux, "send time : %" GST_TIME_FORMAT, + GST_TIME_ARGS (packet.send_time)); + GST_LOG_OBJECT (demux, "duration : %" GST_TIME_FORMAT, + GST_TIME_ARGS (packet.duration)); + + if (G_UNLIKELY (packet.padding == (guint) - 1 || size < packet.padding)) { + GST_WARNING_OBJECT (demux, "No padding, or padding bigger than buffer"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; + goto done; + } + + size -= packet.padding; + + /* adjust available size for parsing if there's less actual packet data for + * parsing than there is data in bytes (for sample see bug 431318) */ + if (G_UNLIKELY (packet.length != 0 && packet.padding == 0 + && packet.length < demux->packet_size)) { + GST_LOG_OBJECT (demux, "shortened packet with implicit padding, " + "adjusting available data size"); + if (size < demux->packet_size - packet.length) { + /* the buffer is smaller than the implicit padding */ + GST_WARNING_OBJECT (demux, "Buffer is smaller than the implicit padding"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; + goto done; + } else { + /* subtract the implicit padding */ + size -= (demux->packet_size - packet.length); + } + } + + if (has_multiple_payloads) { + guint i, num, lentype; + + if (G_UNLIKELY (size < 1)) { + GST_WARNING_OBJECT (demux, "No room more in buffer"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; + goto done; + } + + num = (GST_READ_UINT8 (data) & 0x3F) >> 0; + lentype = (GST_READ_UINT8 (data) & 0xC0) >> 6; + + ++data; + --size; + + GST_LOG_OBJECT (demux, "num payloads : %u", num); + + for (i = 0; i < num; ++i) { + GST_LOG_OBJECT (demux, "Parsing payload %u/%u, size left: %u", i + 1, num, + size); + + if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, lentype, + &data, &size))) { + GST_WARNING_OBJECT (demux, "Failed to parse payload %u/%u", i + 1, num); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL; + break; + } + } + } else { + GST_LOG_OBJECT (demux, "Parsing single payload"); + if (G_UNLIKELY (!gst_asf_demux_parse_payload (demux, &packet, -1, &data, + &size))) { + GST_WARNING_OBJECT (demux, "Failed to parse payload"); + ret = GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE; + } + } + +done: + gst_buffer_unmap (buf, &map); + return ret; +} diff --git a/gst/asfdemux/asfpacket.h b/gst/asfdemux/asfpacket.h new file mode 100644 index 0000000..a812e74 --- /dev/null +++ b/gst/asfdemux/asfpacket.h @@ -0,0 +1,74 @@ +/* GStreamer ASF/WMV/WMA demuxer + * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __ASF_PACKET_H__ +#define __ASF_PACKET_H__ + +#include <gst/gstbuffer.h> +#include <gst/gstclock.h> + +#include "gstasfdemux.h" + +G_BEGIN_DECLS + +typedef struct { + gboolean keyframe; /* buffer flags might not survive merge.. */ + guint mo_number; /* media object number (unused) */ + guint mo_offset; /* offset (timestamp for compressed data) */ + guint mo_size; /* size of media-object-to-be, or 0 */ + guint buf_filled; /* how much of the mo data we got so far */ + GstBuffer *buf; /* buffer to assemble media-object or NULL*/ + guint rep_data_len; /* should never be more than 256, since */ + guint8 rep_data[256]; /* the length should be stored in a byte */ + GstClockTime ts; + GstClockTime duration; /* is not always available */ + guint8 par_x; /* not always available (0:deactivated) */ + guint8 par_y; /* not always available (0:deactivated) */ + gboolean interlaced; /* default: FALSE */ + gboolean tff; + gboolean rff; +} AsfPayload; + +typedef struct { + GstBuffer *buf; + const guint8 *bdata; + guint length; /* packet length (unused) */ + guint padding; /* length of padding at end of packet */ + guint sequence; /* sequence (unused) */ + GstClockTime send_time; + GstClockTime duration; + + guint8 prop_flags; /* payload length types */ +} AsfPacket; + +typedef enum { + GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE, + GST_ASF_DEMUX_PARSE_PACKET_ERROR_RECOVERABLE, + GST_ASF_DEMUX_PARSE_PACKET_ERROR_FATAL +} GstAsfDemuxParsePacketError; + +GstAsfDemuxParsePacketError gst_asf_demux_parse_packet (GstASFDemux * demux, GstBuffer * buf); + +#define gst_asf_payload_is_complete(payload) \ + ((payload)->buf_filled >= (payload)->mo_size) + +G_END_DECLS + +#endif /* __ASF_PACKET_H__ */ + diff --git a/gst/asfdemux/gstasf.c b/gst/asfdemux/gstasf.c new file mode 100644 index 0000000..01d289f --- /dev/null +++ b/gst/asfdemux/gstasf.c @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/riff/riff-read.h> +#include "gst/gst-i18n-plugin.h" + +#include "gstasfdemux.h" +#include "gstrtspwms.h" +#include "gstrtpasfdepay.h" + +/* #include "gstasfmux.h" */ + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (asfdemux_dbg, "asfdemux", 0, "asf demuxer element"); + +#ifdef ENABLE_NLS + GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, + LOCALEDIR); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif /* ENABLE_NLS */ + + gst_riff_init (); + + if (!gst_element_register (plugin, "asfdemux", GST_RANK_SECONDARY, + GST_TYPE_ASF_DEMUX)) { + return FALSE; + } + if (!gst_element_register (plugin, "rtspwms", GST_RANK_SECONDARY, + GST_TYPE_RTSP_WMS)) { + return FALSE; + } + if (!gst_element_register (plugin, "rtpasfdepay", GST_RANK_MARGINAL, + GST_TYPE_RTP_ASF_DEPAY)) { + return FALSE; + } +/* + if (!gst_element_register (plugin, "asfmux", GST_RANK_NONE, GST_TYPE_ASFMUX)) + return FALSE; +*/ + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + asf, + "Demuxes and muxes audio and video in Microsofts ASF format", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/asfdemux/gstasfdemux.c b/gst/asfdemux/gstasfdemux.c new file mode 100644 index 0000000..1794380 --- /dev/null +++ b/gst/asfdemux/gstasfdemux.c @@ -0,0 +1,4367 @@ +/* GStreamer ASF/WMV/WMA demuxer + * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) 2006-2009 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* TODO: + * + * - _loop(): + * stop if at end of segment if != end of file, ie. demux->segment.stop + * + * - fix packet parsing: + * there's something wrong with timestamps for packets with keyframes, + * and durations too. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gstutils.h> +#include <gst/base/gstbytereader.h> +#include <gst/base/gsttypefindhelper.h> +#include <gst/riff/riff-media.h> +#include <gst/tag/tag.h> +#include <gst/gst-i18n-plugin.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gstasfdemux.h" +#include "asfheaders.h" +#include "asfpacket.h" + +static GstStaticPadTemplate gst_asf_demux_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf") + ); + +static GstStaticPadTemplate audio_src_template = +GST_STATIC_PAD_TEMPLATE ("audio_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate video_src_template = +GST_STATIC_PAD_TEMPLATE ("video_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +/* size of an ASF object header, ie. GUID (16 bytes) + object size (8 bytes) */ +#define ASF_OBJECT_HEADER_SIZE (16+8) + +/* FIXME: get rid of this */ +/* abuse this GstFlowReturn enum for internal usage */ +#define ASF_FLOW_NEED_MORE_DATA 99 + +#define gst_asf_get_flow_name(flow) \ + (flow == ASF_FLOW_NEED_MORE_DATA) ? \ + "need-more-data" : gst_flow_get_name (flow) + +GST_DEBUG_CATEGORY (asfdemux_dbg); + +static GstStateChangeReturn gst_asf_demux_change_state (GstElement * element, + GstStateChange transition); +static gboolean gst_asf_demux_element_send_event (GstElement * element, + GstEvent * event); +static gboolean gst_asf_demux_send_event_unlocked (GstASFDemux * demux, + GstEvent * event); +static gboolean gst_asf_demux_handle_src_query (GstPad * pad, + GstObject * parent, GstQuery * query); +static GstFlowReturn gst_asf_demux_chain (GstPad * pad, GstObject * parent, + GstBuffer * buf); +static gboolean gst_asf_demux_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static GstFlowReturn gst_asf_demux_process_object (GstASFDemux * demux, + guint8 ** p_data, guint64 * p_size); +static gboolean gst_asf_demux_activate (GstPad * sinkpad, GstObject * parent); +static gboolean gst_asf_demux_activate_mode (GstPad * sinkpad, + GstObject * parent, GstPadMode mode, gboolean active); +static void gst_asf_demux_loop (GstASFDemux * demux); +static void +gst_asf_demux_process_queued_extended_stream_objects (GstASFDemux * demux); +static gboolean gst_asf_demux_pull_headers (GstASFDemux * demux); +static void gst_asf_demux_pull_indices (GstASFDemux * demux); +static void gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * asf); +static gboolean +gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data); +static void gst_asf_demux_descramble_buffer (GstASFDemux * demux, + AsfStream * stream, GstBuffer ** p_buffer); +static void gst_asf_demux_activate_stream (GstASFDemux * demux, + AsfStream * stream); +static GstStructure *gst_asf_demux_get_metadata_for_stream (GstASFDemux * d, + guint stream_num); +static GstFlowReturn gst_asf_demux_push_complete_payloads (GstASFDemux * demux, + gboolean force); + +#define gst_asf_demux_parent_class parent_class +G_DEFINE_TYPE (GstASFDemux, gst_asf_demux, GST_TYPE_ELEMENT); + +static void +gst_asf_demux_class_init (GstASFDemuxClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass *) klass; + + gst_element_class_set_static_metadata (gstelement_class, "ASF Demuxer", + "Codec/Demuxer", + "Demultiplexes ASF Streams", "Owen Fraser-Green <owen@discobabe.net>"); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&audio_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&video_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_asf_demux_sink_template)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_asf_demux_change_state); + gstelement_class->send_event = + GST_DEBUG_FUNCPTR (gst_asf_demux_element_send_event); +} + +static void +gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream) +{ + gst_caps_replace (&stream->caps, NULL); + if (stream->pending_tags) { + gst_tag_list_unref (stream->pending_tags); + stream->pending_tags = NULL; + } + if (stream->pad) { + if (stream->active) { + gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad); + gst_flow_combiner_remove_pad (demux->flowcombiner, stream->pad); + } else + gst_object_unref (stream->pad); + stream->pad = NULL; + } + + if (stream->payloads) { + while (stream->payloads->len > 0) { + AsfPayload *payload; + guint last; + + last = stream->payloads->len - 1; + payload = &g_array_index (stream->payloads, AsfPayload, last); + gst_buffer_replace (&payload->buf, NULL); + g_array_remove_index (stream->payloads, last); + } + g_array_free (stream->payloads, TRUE); + stream->payloads = NULL; + } + if (stream->ext_props.valid) { + g_free (stream->ext_props.payload_extensions); + stream->ext_props.payload_extensions = NULL; + } +} + +static void +gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset) +{ + GST_LOG_OBJECT (demux, "resetting"); + + gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); + demux->segment_running = FALSE; + if (demux->adapter && !chain_reset) { + gst_adapter_clear (demux->adapter); + g_object_unref (demux->adapter); + demux->adapter = NULL; + } + if (demux->taglist) { + gst_tag_list_unref (demux->taglist); + demux->taglist = NULL; + } + if (demux->metadata) { + gst_caps_unref (demux->metadata); + demux->metadata = NULL; + } + if (demux->global_metadata) { + gst_structure_free (demux->global_metadata); + demux->global_metadata = NULL; + } + + demux->state = GST_ASF_DEMUX_STATE_HEADER; + g_free (demux->objpath); + demux->objpath = NULL; + g_strfreev (demux->languages); + demux->languages = NULL; + demux->num_languages = 0; + g_slist_foreach (demux->ext_stream_props, (GFunc) gst_mini_object_unref, + NULL); + g_slist_free (demux->ext_stream_props); + demux->ext_stream_props = NULL; + + while (demux->old_num_streams > 0) { + gst_asf_demux_free_stream (demux, + &demux->old_stream[demux->old_num_streams - 1]); + --demux->old_num_streams; + } + memset (demux->old_stream, 0, sizeof (demux->old_stream)); + demux->old_num_streams = 0; + + /* when resetting for a new chained asf, we don't want to remove the pads + * before adding the new ones */ + if (chain_reset) { + memcpy (demux->old_stream, demux->stream, sizeof (demux->stream)); + demux->old_num_streams = demux->num_streams; + demux->num_streams = 0; + } + + while (demux->num_streams > 0) { + gst_asf_demux_free_stream (demux, &demux->stream[demux->num_streams - 1]); + --demux->num_streams; + } + memset (demux->stream, 0, sizeof (demux->stream)); + if (!chain_reset) { + /* do not remove those for not adding pads with same name */ + demux->num_audio_streams = 0; + demux->num_video_streams = 0; + demux->have_group_id = FALSE; + demux->group_id = G_MAXUINT; + } + demux->num_streams = 0; + demux->activated_streams = FALSE; + demux->first_ts = GST_CLOCK_TIME_NONE; + demux->segment_ts = GST_CLOCK_TIME_NONE; + demux->in_gap = 0; + if (!chain_reset) + gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED); + demux->state = GST_ASF_DEMUX_STATE_HEADER; + demux->seekable = FALSE; + demux->broadcast = FALSE; + demux->sidx_interval = 0; + demux->sidx_num_entries = 0; + g_free (demux->sidx_entries); + demux->sidx_entries = NULL; + + demux->speed_packets = 1; + + if (chain_reset) { + GST_LOG_OBJECT (demux, "Restarting"); + gst_segment_init (&demux->segment, GST_FORMAT_TIME); + demux->need_newsegment = TRUE; + demux->segment_seqnum = 0; + demux->segment_running = FALSE; + demux->accurate = FALSE; + demux->metadata = gst_caps_new_empty (); + demux->global_metadata = gst_structure_new_empty ("metadata"); + demux->data_size = 0; + demux->data_offset = 0; + demux->index_offset = 0; + } else { + demux->base_offset = 0; + } + + g_slist_free (demux->other_streams); + demux->other_streams = NULL; +} + +static void +gst_asf_demux_init (GstASFDemux * demux) +{ + demux->sinkpad = + gst_pad_new_from_static_template (&gst_asf_demux_sink_template, "sink"); + gst_pad_set_chain_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_asf_demux_chain)); + gst_pad_set_event_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_asf_demux_sink_event)); + gst_pad_set_activate_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_asf_demux_activate)); + gst_pad_set_activatemode_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_asf_demux_activate_mode)); + gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); + + /* set initial state */ + gst_asf_demux_reset (demux, FALSE); +} + +static gboolean +gst_asf_demux_activate (GstPad * sinkpad, GstObject * parent) +{ + GstQuery *query; + gboolean pull_mode; + + query = gst_query_new_scheduling (); + + if (!gst_pad_peer_query (sinkpad, query)) { + gst_query_unref (query); + goto activate_push; + } + + pull_mode = gst_query_has_scheduling_mode_with_flags (query, + GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); + gst_query_unref (query); + + if (!pull_mode) + goto activate_push; + + GST_DEBUG_OBJECT (sinkpad, "activating pull"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); + +activate_push: + { + GST_DEBUG_OBJECT (sinkpad, "activating push"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } +} + +static gboolean +gst_asf_demux_activate_mode (GstPad * sinkpad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean res; + GstASFDemux *demux; + + demux = GST_ASF_DEMUX (parent); + + switch (mode) { + case GST_PAD_MODE_PUSH: + demux->state = GST_ASF_DEMUX_STATE_HEADER; + demux->streaming = TRUE; + res = TRUE; + break; + case GST_PAD_MODE_PULL: + if (active) { + demux->state = GST_ASF_DEMUX_STATE_HEADER; + demux->streaming = FALSE; + + res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_asf_demux_loop, + demux, NULL); + } else { + res = gst_pad_stop_task (sinkpad); + } + break; + default: + res = FALSE; + break; + } + return res; +} + +static gboolean +gst_asf_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstASFDemux *demux; + gboolean ret = TRUE; + + demux = GST_ASF_DEMUX (parent); + + GST_LOG_OBJECT (demux, "handling %s event", GST_EVENT_TYPE_NAME (event)); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT:{ + const GstSegment *segment; + + gst_event_parse_segment (event, &segment); + + if (segment->format == GST_FORMAT_BYTES) { + if (demux->packet_size && segment->start > demux->data_offset) + demux->packet = (segment->start - demux->data_offset) / + demux->packet_size; + else + demux->packet = 0; + } else if (segment->format == GST_FORMAT_TIME) { + /* do not know packet position, not really a problem */ + demux->packet = -1; + } else { + GST_WARNING_OBJECT (demux, "unsupported newsegment format, ignoring"); + gst_event_unref (event); + break; + } + + /* record upstream segment for interpolation */ + if (segment->format != demux->in_segment.format) + gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED); + gst_segment_copy_into (segment, &demux->in_segment); + + /* in either case, clear some state and generate newsegment later on */ + GST_OBJECT_LOCK (demux); + demux->segment_ts = GST_CLOCK_TIME_NONE; + demux->in_gap = GST_CLOCK_TIME_NONE; + demux->need_newsegment = TRUE; + demux->segment_seqnum = gst_event_get_seqnum (event); + gst_asf_demux_reset_stream_state_after_discont (demux); + GST_OBJECT_UNLOCK (demux); + + gst_event_unref (event); + break; + } + case GST_EVENT_EOS:{ + GstFlowReturn flow; + + if (demux->state == GST_ASF_DEMUX_STATE_HEADER) { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, + (_("This stream contains no data.")), + ("got eos and didn't receive a complete header object")); + break; + } + flow = gst_asf_demux_push_complete_payloads (demux, TRUE); + if (flow < GST_FLOW_EOS || flow == GST_FLOW_NOT_LINKED) { + GST_ELEMENT_ERROR (demux, STREAM, FAILED, + (_("Internal data stream error.")), + ("streaming stopped, reason %s", gst_flow_get_name (flow))); + break; + } + + GST_OBJECT_LOCK (demux); + gst_adapter_clear (demux->adapter); + GST_OBJECT_UNLOCK (demux); + gst_asf_demux_send_event_unlocked (demux, event); + break; + } + + case GST_EVENT_FLUSH_STOP: + GST_OBJECT_LOCK (demux); + gst_asf_demux_reset_stream_state_after_discont (demux); + GST_OBJECT_UNLOCK (demux); + gst_asf_demux_send_event_unlocked (demux, event); + /* upon activation, latency is no longer introduced, e.g. after seek */ + if (demux->activated_streams) + demux->latency = 0; + break; + + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static gboolean +gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet, + GstClockTime seek_time, GstClockTime * p_idx_time, guint * speed, + gboolean next, gboolean * eos) +{ + GstClockTime idx_time; + guint idx; + + if (eos) + *eos = FALSE; + + if (G_UNLIKELY (demux->sidx_num_entries == 0 || demux->sidx_interval == 0)) + return FALSE; + + idx = (guint) ((seek_time + demux->preroll) / demux->sidx_interval); + + if (next) { + /* if we want the next keyframe, we have to go forward till we find + a different packet number */ + guint idx2 = idx; + if (idx >= demux->sidx_num_entries - 1) { + /* If we get here, we're asking for next keyframe after the last one. There isn't one. */ + if (eos) + *eos = TRUE; + return FALSE; + } + for (idx2 = idx + 1; idx2 < demux->sidx_num_entries; ++idx2) { + if (demux->sidx_entries[idx].packet != demux->sidx_entries[idx2].packet) { + idx = idx2; + break; + } + } + } + + if (G_UNLIKELY (idx >= demux->sidx_num_entries)) { + if (eos) + *eos = TRUE; + return FALSE; + } + + *packet = demux->sidx_entries[idx].packet; + if (speed) + *speed = demux->sidx_entries[idx].count; + + /* so we get closer to the actual time of the packet ... actually, let's not + * do this, since we throw away superfluous payloads before the seek position + * anyway; this way, our key unit seek 'snap resolution' is a bit better + * (ie. same as index resolution) */ + /* + while (idx > 0 && demux->sidx_entries[idx-1] == demux->sidx_entries[idx]) + --idx; + */ + + idx_time = demux->sidx_interval * idx; + if (G_LIKELY (idx_time >= demux->preroll)) + idx_time -= demux->preroll; + + GST_DEBUG_OBJECT (demux, "%" GST_TIME_FORMAT " => packet %u at %" + GST_TIME_FORMAT, GST_TIME_ARGS (seek_time), *packet, + GST_TIME_ARGS (idx_time)); + + if (G_LIKELY (p_idx_time)) + *p_idx_time = idx_time; + + return TRUE; +} + +static void +gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * demux) +{ + guint n; + + gst_adapter_clear (demux->adapter); + + GST_DEBUG_OBJECT (demux, "reset stream state"); + + for (n = 0; n < demux->num_streams; n++) { + demux->stream[n].discont = TRUE; + + while (demux->stream[n].payloads->len > 0) { + AsfPayload *payload; + guint last; + + last = demux->stream[n].payloads->len - 1; + payload = &g_array_index (demux->stream[n].payloads, AsfPayload, last); + gst_buffer_replace (&payload->buf, NULL); + g_array_remove_index (demux->stream[n].payloads, last); + } + } +} + +static void +gst_asf_demux_mark_discont (GstASFDemux * demux) +{ + guint n; + + GST_DEBUG_OBJECT (demux, "Mark stream discont"); + + for (n = 0; n < demux->num_streams; n++) + demux->stream[n].discont = TRUE; +} + +/* do a seek in push based mode */ +static gboolean +gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event) +{ + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + guint packet; + gboolean res; + GstEvent *byte_event; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + stop_type = GST_SEEK_TYPE_NONE; + stop = -1; + + GST_DEBUG_OBJECT (demux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (cur)); + + /* determine packet, by index or by estimation */ + if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL, NULL, FALSE, + NULL)) { + packet = + (guint) gst_util_uint64_scale (demux->num_packets, cur, + demux->play_time); + } + + if (packet > demux->num_packets) { + GST_DEBUG_OBJECT (demux, "could not determine packet to seek to, " + "seek aborted."); + return FALSE; + } + + GST_DEBUG_OBJECT (demux, "seeking to packet %d", packet); + + cur = demux->data_offset + (packet * demux->packet_size); + + GST_DEBUG_OBJECT (demux, "Pushing BYTE seek rate %g, " + "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, cur, stop); + /* BYTE seek event */ + byte_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, + cur, stop_type, stop); + gst_event_set_seqnum (byte_event, gst_event_get_seqnum (event)); + res = gst_pad_push_event (demux->sinkpad, byte_event); + + return res; +} + +static gboolean +gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event) +{ + GstClockTime idx_time; + GstSegment segment; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + GstFormat format; + gboolean only_need_update; + gboolean keyunit_sync, after, before, next; + gboolean flush; + gdouble rate; + gint64 cur, stop; + gint64 seek_time; + guint packet, speed_count = 1; + gboolean eos; + guint32 seqnum; + GstEvent *fevent; + + if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 || + demux->num_packets == 0 || demux->play_time == 0)) { + GST_LOG_OBJECT (demux, "stream is not seekable"); + return FALSE; + } + + if (G_UNLIKELY (!demux->activated_streams)) { + GST_LOG_OBJECT (demux, "streams not yet activated, ignoring seek"); + return FALSE; + } + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + seqnum = gst_event_get_seqnum (event); + + if (G_UNLIKELY (format != GST_FORMAT_TIME)) { + GST_LOG_OBJECT (demux, "seeking is only supported in TIME format"); + return FALSE; + } + + if (G_UNLIKELY (rate <= 0.0)) { + GST_LOG_OBJECT (demux, "backward playback is not supported yet"); + return FALSE; + } + + flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); + demux->accurate = + ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE); + keyunit_sync = ((flags & GST_SEEK_FLAG_KEY_UNIT) == GST_SEEK_FLAG_KEY_UNIT); + after = ((flags & GST_SEEK_FLAG_SNAP_AFTER) == GST_SEEK_FLAG_SNAP_AFTER); + before = ((flags & GST_SEEK_FLAG_SNAP_BEFORE) == GST_SEEK_FLAG_SNAP_BEFORE); + next = after && !before; + + if (G_UNLIKELY (demux->streaming)) { + /* support it safely needs more segment handling, e.g. closing etc */ + if (!flush) { + GST_LOG_OBJECT (demux, "streaming; non-flushing seek not supported"); + return FALSE; + } + /* we can (re)construct the start later on, but not the end */ + if (stop_type != GST_SEEK_TYPE_NONE && + (stop_type != GST_SEEK_TYPE_SET || GST_CLOCK_TIME_IS_VALID (stop))) { + GST_LOG_OBJECT (demux, "streaming; end position must be NONE"); + return FALSE; + } + gst_event_ref (event); + /* upstream might handle TIME seek, e.g. mms or rtsp, + * or not, e.g. http, then we give it a hand */ + if (!gst_pad_push_event (demux->sinkpad, event)) + return gst_asf_demux_handle_seek_push (demux, event); + else + return TRUE; + } + + /* unlock the streaming thread */ + if (G_LIKELY (flush)) { + fevent = gst_event_new_flush_start (); + + gst_event_set_seqnum (fevent, seqnum); + gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent)); + gst_asf_demux_send_event_unlocked (demux, fevent); + } else { + gst_pad_pause_task (demux->sinkpad); + } + + /* grab the stream lock so that streaming cannot continue, for + * non flushing seeks when the element is in PAUSED this could block + * forever */ + GST_PAD_STREAM_LOCK (demux->sinkpad); + + /* we now can stop flushing, since we have the stream lock now */ + fevent = gst_event_new_flush_stop (TRUE); + gst_event_set_seqnum (fevent, seqnum); + gst_pad_push_event (demux->sinkpad, gst_event_ref (fevent)); + + if (G_LIKELY (flush)) + gst_asf_demux_send_event_unlocked (demux, fevent); + else + gst_event_unref (fevent); + + /* operating on copy of segment until we know the seek worked */ + segment = demux->segment; + + if (G_UNLIKELY (demux->segment_running && !flush)) { + GstSegment newsegment; + GstEvent *newseg; + + /* create the segment event to close the current segment */ + gst_segment_copy_into (&segment, &newsegment); + newseg = gst_event_new_segment (&newsegment); + gst_event_set_seqnum (newseg, seqnum); + + gst_asf_demux_send_event_unlocked (demux, newseg); + } + + gst_segment_do_seek (&segment, rate, format, flags, cur_type, + cur, stop_type, stop, &only_need_update); + + GST_DEBUG_OBJECT (demux, "seeking to time %" GST_TIME_FORMAT ", segment: " + "%" GST_SEGMENT_FORMAT, GST_TIME_ARGS (segment.start), &segment); + + if (cur_type != GST_SEEK_TYPE_SET) + seek_time = segment.start; + else + seek_time = cur; + + /* FIXME: should check the KEY_UNIT flag; need to adjust position to + * real start of data and segment_start to indexed time for key unit seek*/ + if (G_UNLIKELY (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time, + &idx_time, &speed_count, next, &eos))) { + gint64 offset; + + if (eos) { + demux->packet = demux->num_packets; + goto skip; + } + + /* First try to query our source to see if it can convert for us. This is + the case when our source is an mms stream, notice that in this case + gstmms will do a time based seek to get the byte offset, this is not a + problem as the seek to this offset needs to happen anway. */ + if (gst_pad_peer_query_convert (demux->sinkpad, GST_FORMAT_TIME, seek_time, + GST_FORMAT_BYTES, &offset)) { + packet = (offset - demux->data_offset) / demux->packet_size; + GST_LOG_OBJECT (demux, "convert %" GST_TIME_FORMAT + " to bytes query result: %" G_GINT64_FORMAT ", data_ofset: %" + G_GINT64_FORMAT ", packet_size: %u," " resulting packet: %u\n", + GST_TIME_ARGS (seek_time), offset, demux->data_offset, + demux->packet_size, packet); + } else { + /* FIXME: For streams containing video, seek to an earlier position in + * the hope of hitting a keyframe and let the sinks throw away the stuff + * before the segment start. For audio-only this is unnecessary as every + * frame is 'key'. */ + if (flush && (demux->accurate || (keyunit_sync && !next)) + && demux->num_video_streams > 0) { + seek_time -= 5 * GST_SECOND; + if (seek_time < 0) + seek_time = 0; + } + + packet = (guint) gst_util_uint64_scale (demux->num_packets, + seek_time, demux->play_time); + + if (packet > demux->num_packets) + packet = demux->num_packets; + } + } else { + if (G_LIKELY (keyunit_sync)) { + GST_DEBUG_OBJECT (demux, "key unit seek, adjust seek_time = %" + GST_TIME_FORMAT " to index_time = %" GST_TIME_FORMAT, + GST_TIME_ARGS (seek_time), GST_TIME_ARGS (idx_time)); + segment.start = idx_time; + segment.position = idx_time; + segment.time = idx_time; + } + } + + GST_DEBUG_OBJECT (demux, "seeking to packet %u (%d)", packet, speed_count); + + GST_OBJECT_LOCK (demux); + demux->segment = segment; + demux->packet = packet; + demux->need_newsegment = TRUE; + demux->segment_seqnum = seqnum; + demux->speed_packets = speed_count; + gst_asf_demux_reset_stream_state_after_discont (demux); + GST_OBJECT_UNLOCK (demux); + +skip: + /* restart our task since it might have been stopped when we did the flush */ + gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_asf_demux_loop, + demux, NULL); + + /* streaming can continue now */ + GST_PAD_STREAM_UNLOCK (demux->sinkpad); + + return TRUE; +} + +static gboolean +gst_asf_demux_handle_src_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstASFDemux *demux; + gboolean ret; + + demux = GST_ASF_DEMUX (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + GST_LOG_OBJECT (pad, "seek event"); + ret = gst_asf_demux_handle_seek_event (demux, event); + gst_event_unref (event); + break; + case GST_EVENT_QOS: + case GST_EVENT_NAVIGATION: + /* just drop these two silently */ + gst_event_unref (event); + ret = FALSE; + break; + default: + GST_LOG_OBJECT (pad, "%s event", GST_EVENT_TYPE_NAME (event)); + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static inline guint32 +gst_asf_demux_identify_guid (const ASFGuidHash * guids, ASFGuid * guid) +{ + guint32 ret; + + ret = gst_asf_identify_guid (guids, guid); + + GST_LOG ("%s 0x%08x-0x%08x-0x%08x-0x%08x", + gst_asf_get_guid_nick (guids, ret), + guid->v1, guid->v2, guid->v3, guid->v4); + + return ret; +} + +typedef struct +{ + AsfObjectID id; + guint64 size; +} AsfObject; + + +/* expect is true when the user is expeting an object, + * when false, it will give no warnings if the object + * is not identified + */ +static gboolean +asf_demux_peek_object (GstASFDemux * demux, const guint8 * data, + guint data_len, AsfObject * object, gboolean expect) +{ + ASFGuid guid; + + if (data_len < ASF_OBJECT_HEADER_SIZE) + return FALSE; + + guid.v1 = GST_READ_UINT32_LE (data + 0); + guid.v2 = GST_READ_UINT32_LE (data + 4); + guid.v3 = GST_READ_UINT32_LE (data + 8); + guid.v4 = GST_READ_UINT32_LE (data + 12); + + object->size = GST_READ_UINT64_LE (data + 16); + + /* FIXME: make asf_demux_identify_object_guid() */ + object->id = gst_asf_demux_identify_guid (asf_object_guids, &guid); + if (object->id == ASF_OBJ_UNDEFINED && expect) { + GST_WARNING_OBJECT (demux, "Unknown object %08x-%08x-%08x-%08x", + guid.v1, guid.v2, guid.v3, guid.v4); + } + + return TRUE; +} + +static void +gst_asf_demux_release_old_pads (GstASFDemux * demux) +{ + GST_DEBUG_OBJECT (demux, "Releasing old pads"); + + while (demux->old_num_streams > 0) { + gst_pad_push_event (demux->old_stream[demux->old_num_streams - 1].pad, + gst_event_new_eos ()); + gst_asf_demux_free_stream (demux, + &demux->old_stream[demux->old_num_streams - 1]); + --demux->old_num_streams; + } + memset (demux->old_stream, 0, sizeof (demux->old_stream)); + demux->old_num_streams = 0; +} + +static GstFlowReturn +gst_asf_demux_chain_headers (GstASFDemux * demux) +{ + GstFlowReturn flow; + AsfObject obj; + guint8 *header_data, *data = NULL; + const guint8 *cdata = NULL; + guint64 header_size; + + cdata = (guint8 *) gst_adapter_map (demux->adapter, ASF_OBJECT_HEADER_SIZE); + if (cdata == NULL) + goto need_more_data; + + asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, TRUE); + if (obj.id != ASF_OBJ_HEADER) + goto wrong_type; + + GST_LOG_OBJECT (demux, "header size = %u", (guint) obj.size); + + /* + 50 for non-packet data at beginning of ASF_OBJ_DATA */ + if (gst_adapter_available (demux->adapter) < obj.size + 50) + goto need_more_data; + + data = gst_adapter_take (demux->adapter, obj.size + 50); + + header_data = data; + header_size = obj.size; + flow = gst_asf_demux_process_object (demux, &header_data, &header_size); + if (flow != GST_FLOW_OK) + goto parse_failed; + + /* calculate where the packet data starts */ + demux->data_offset = obj.size + 50; + + /* now parse the beginning of the ASF_OBJ_DATA object */ + if (!gst_asf_demux_parse_data_object_start (demux, data + obj.size)) + goto wrong_type; + + if (demux->num_streams == 0) + goto no_streams; + + g_free (data); + return GST_FLOW_OK; + +/* NON-FATAL */ +need_more_data: + { + GST_LOG_OBJECT (demux, "not enough data in adapter yet"); + return GST_FLOW_OK; + } + +/* ERRORS */ +wrong_type: + { + GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), + ("This doesn't seem to be an ASF file")); + g_free (data); + return GST_FLOW_ERROR; + } +no_streams: +parse_failed: + { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("header parsing failed, or no streams found, flow = %s", + gst_flow_get_name (flow))); + g_free (data); + return GST_FLOW_ERROR; + } +} + +static gboolean +gst_asf_demux_pull_data (GstASFDemux * demux, guint64 offset, guint size, + GstBuffer ** p_buf, GstFlowReturn * p_flow) +{ + gsize buffer_size; + GstFlowReturn flow; + + GST_LOG_OBJECT (demux, "pulling buffer at %" G_GUINT64_FORMAT "+%u", + offset, size); + + flow = gst_pad_pull_range (demux->sinkpad, offset, size, p_buf); + + if (G_LIKELY (p_flow)) + *p_flow = flow; + + if (G_UNLIKELY (flow != GST_FLOW_OK)) { + GST_DEBUG_OBJECT (demux, "flow %s pulling buffer at %" G_GUINT64_FORMAT + "+%u", gst_flow_get_name (flow), offset, size); + *p_buf = NULL; + return FALSE; + } + + g_assert (*p_buf != NULL); + + buffer_size = gst_buffer_get_size (*p_buf); + if (G_UNLIKELY (buffer_size < size)) { + GST_DEBUG_OBJECT (demux, "short read pulling buffer at %" G_GUINT64_FORMAT + "+%u (got only %" G_GSIZE_FORMAT " bytes)", offset, size, buffer_size); + gst_buffer_unref (*p_buf); + if (G_LIKELY (p_flow)) + *p_flow = GST_FLOW_EOS; + *p_buf = NULL; + return FALSE; + } + + return TRUE; +} + +static void +gst_asf_demux_pull_indices (GstASFDemux * demux) +{ + GstBuffer *buf = NULL; + guint64 offset; + guint num_read = 0; + + offset = demux->index_offset; + + if (G_UNLIKELY (offset == 0)) { + GST_DEBUG_OBJECT (demux, "can't read indices, don't know index offset"); + return; + } + + while (gst_asf_demux_pull_data (demux, offset, 16 + 8, &buf, NULL)) { + GstFlowReturn flow; + AsfObject obj; + GstMapInfo map; + guint8 *bufdata; + + gst_buffer_map (buf, &map, GST_MAP_READ); + g_assert (map.size >= 16 + 8); + asf_demux_peek_object (demux, map.data, 16 + 8, &obj, TRUE); + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + + /* check for sanity */ + if (G_UNLIKELY (obj.size > (5 * 1024 * 1024))) { + GST_DEBUG_OBJECT (demux, "implausible index object size, bailing out"); + break; + } + + if (G_UNLIKELY (!gst_asf_demux_pull_data (demux, offset, obj.size, &buf, + NULL))) + break; + + GST_LOG_OBJECT (demux, "index object at offset 0x%" G_GINT64_MODIFIER "X" + ", size %u", offset, (guint) obj.size); + + offset += obj.size; /* increase before _process_object changes it */ + + gst_buffer_map (buf, &map, GST_MAP_READ); + g_assert (map.size >= obj.size); + bufdata = (guint8 *) map.data; + flow = gst_asf_demux_process_object (demux, &bufdata, &obj.size); + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + + if (G_UNLIKELY (flow != GST_FLOW_OK)) + break; + + ++num_read; + } + GST_DEBUG_OBJECT (demux, "read %u index objects", num_read); +} + +static gboolean +gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data) +{ + AsfObject obj; + + asf_demux_peek_object (demux, data, 50, &obj, TRUE); + if (obj.id != ASF_OBJ_DATA) { + GST_WARNING_OBJECT (demux, "headers not followed by a DATA object"); + return FALSE; + } + + demux->state = GST_ASF_DEMUX_STATE_DATA; + + if (!demux->broadcast && obj.size > 50) { + demux->data_size = obj.size - 50; + /* CHECKME: for at least one file this is off by +158 bytes?! */ + demux->index_offset = demux->data_offset + demux->data_size; + } else { + demux->data_size = 0; + demux->index_offset = 0; + } + + demux->packet = 0; + + if (!demux->broadcast) { + /* skip object header (24 bytes) and file GUID (16 bytes) */ + demux->num_packets = GST_READ_UINT64_LE (data + (16 + 8) + 16); + } else { + demux->num_packets = 0; + } + + if (demux->num_packets == 0) + demux->seekable = FALSE; + + /* fallback in the unlikely case that headers are inconsistent, can't hurt */ + if (demux->data_size == 0 && demux->num_packets > 0) { + demux->data_size = demux->num_packets * demux->packet_size; + demux->index_offset = demux->data_offset + demux->data_size; + } + + /* process pending stream objects and create pads for those */ + gst_asf_demux_process_queued_extended_stream_objects (demux); + + GST_INFO_OBJECT (demux, "Stream has %" G_GUINT64_FORMAT " packets, " + "data_offset=%" G_GINT64_FORMAT ", data_size=%" G_GINT64_FORMAT + ", index_offset=%" G_GUINT64_FORMAT, demux->num_packets, + demux->data_offset, demux->data_size, demux->index_offset); + + return TRUE; +} + +static gboolean +gst_asf_demux_pull_headers (GstASFDemux * demux) +{ + GstFlowReturn flow; + AsfObject obj; + GstBuffer *buf = NULL; + guint64 size; + GstMapInfo map; + guint8 *bufdata; + + GST_LOG_OBJECT (demux, "reading headers"); + + /* pull HEADER object header, so we know its size */ + if (!gst_asf_demux_pull_data (demux, demux->base_offset, 16 + 8, &buf, NULL)) + goto read_failed; + + gst_buffer_map (buf, &map, GST_MAP_READ); + g_assert (map.size >= 16 + 8); + asf_demux_peek_object (demux, map.data, 16 + 8, &obj, TRUE); + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + + if (obj.id != ASF_OBJ_HEADER) + goto wrong_type; + + GST_LOG_OBJECT (demux, "header size = %u", (guint) obj.size); + + /* pull HEADER object */ + if (!gst_asf_demux_pull_data (demux, demux->base_offset, obj.size, &buf, + NULL)) + goto read_failed; + + size = obj.size; /* don't want obj.size changed */ + gst_buffer_map (buf, &map, GST_MAP_READ); + g_assert (map.size >= size); + bufdata = (guint8 *) map.data; + flow = gst_asf_demux_process_object (demux, &bufdata, &size); + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + + if (flow != GST_FLOW_OK) { + GST_WARNING_OBJECT (demux, "process_object: %s", gst_flow_get_name (flow)); + goto parse_failed; + } + + /* calculate where the packet data starts */ + demux->data_offset = demux->base_offset + obj.size + 50; + + /* now pull beginning of DATA object before packet data */ + if (!gst_asf_demux_pull_data (demux, demux->base_offset + obj.size, 50, &buf, + NULL)) + goto read_failed; + + gst_buffer_map (buf, &map, GST_MAP_READ); + g_assert (map.size >= size); + bufdata = (guint8 *) map.data; + if (!gst_asf_demux_parse_data_object_start (demux, bufdata)) + goto wrong_type; + + if (demux->num_streams == 0) + goto no_streams; + + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + + return TRUE; + +/* ERRORS */ +wrong_type: + { + if (buf != NULL) { + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + } + GST_ELEMENT_ERROR (demux, STREAM, WRONG_TYPE, (NULL), + ("This doesn't seem to be an ASF file")); + return FALSE; + } + +no_streams: +read_failed: +parse_failed: + { + if (buf) + gst_buffer_unmap (buf, &map); + gst_buffer_replace (&buf, NULL); + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), (NULL)); + return FALSE; + } +} + +static gboolean +all_streams_prerolled (GstASFDemux * demux) +{ + GstClockTime preroll_time; + guint i, num_no_data = 0; + + /* Allow at least 500ms of preroll_time */ + preroll_time = MAX (demux->preroll, 500 * GST_MSECOND); + + /* returns TRUE as long as there isn't a stream which (a) has data queued + * and (b) the timestamp of last piece of data queued is < demux->preroll + * AND there is at least one other stream with data queued */ + for (i = 0; i < demux->num_streams; ++i) { + AsfPayload *last_payload = NULL; + AsfStream *stream; + gint last_idx; + + stream = &demux->stream[i]; + if (G_UNLIKELY (stream->payloads->len == 0)) { + ++num_no_data; + GST_LOG_OBJECT (stream->pad, "no data queued"); + continue; + } + + /* find last payload with timestamp */ + for (last_idx = stream->payloads->len - 1; + last_idx >= 0 && (last_payload == NULL + || !GST_CLOCK_TIME_IS_VALID (last_payload->ts)); --last_idx) { + last_payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + } + + GST_LOG_OBJECT (stream->pad, "checking if %" GST_TIME_FORMAT " > %" + GST_TIME_FORMAT, GST_TIME_ARGS (last_payload->ts), + GST_TIME_ARGS (preroll_time)); + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (last_payload->ts) + || last_payload->ts <= preroll_time)) { + GST_LOG_OBJECT (stream->pad, "not beyond preroll point yet"); + return FALSE; + } + } + + if (G_UNLIKELY (num_no_data > 0)) + return FALSE; + + return TRUE; +} + +#if 0 +static gboolean +gst_asf_demux_have_mutually_exclusive_active_stream (GstASFDemux * demux, + AsfStream * stream) +{ + GSList *l; + + for (l = demux->mut_ex_streams; l != NULL; l = l->next) { + guint8 *mes; + + /* check for each mutual exclusion group whether it affects this stream */ + for (mes = (guint8 *) l->data; mes != NULL && *mes != 0xff; ++mes) { + if (*mes == stream->id) { + /* we are in this group; let's check if we've already activated streams + * that are in the same group (and hence mutually exclusive to this + * one) */ + for (mes = (guint8 *) l->data; mes != NULL && *mes != 0xff; ++mes) { + guint i; + + for (i = 0; i < demux->num_streams; ++i) { + if (demux->stream[i].id == *mes && demux->stream[i].active) { + GST_LOG_OBJECT (demux, "stream with ID %d is mutually exclusive " + "to already active stream with ID %d", stream->id, + demux->stream[i].id); + return TRUE; + } + } + } + /* we can only be in this group once, let's break out and move on to + * the next mutual exclusion group */ + break; + } + } + } + + return FALSE; +} +#endif + +static void +gst_asf_demux_check_segment_ts (GstASFDemux * demux, GstClockTime payload_ts) +{ + /* remember the first queued timestamp for the segment */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->segment_ts) && + GST_CLOCK_TIME_IS_VALID (demux->first_ts))) { + GST_DEBUG_OBJECT (demux, "segment ts: %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->first_ts)); + demux->segment_ts = payload_ts; + /* always note, but only determines segment when streaming */ + if (demux->streaming) + gst_segment_do_seek (&demux->segment, demux->in_segment.rate, + GST_FORMAT_TIME, (GstSeekFlags) demux->segment.flags, + GST_SEEK_TYPE_SET, demux->segment_ts, GST_SEEK_TYPE_NONE, 0, NULL); + } +} + +static gboolean +gst_asf_demux_check_first_ts (GstASFDemux * demux, gboolean force) +{ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (demux->first_ts))) { + GstClockTime first_ts = GST_CLOCK_TIME_NONE; + int i; + + /* go trhough each stream, find smallest timestamp */ + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + int j; + GstClockTime stream_min_ts = GST_CLOCK_TIME_NONE; + GstClockTime stream_min_ts2 = GST_CLOCK_TIME_NONE; /* second smallest timestamp */ + stream = &demux->stream[i]; + + for (j = 0; j < stream->payloads->len; ++j) { + AsfPayload *payload = &g_array_index (stream->payloads, AsfPayload, j); + if (GST_CLOCK_TIME_IS_VALID (payload->ts) && + (!GST_CLOCK_TIME_IS_VALID (stream_min_ts) + || stream_min_ts > payload->ts)) { + stream_min_ts = payload->ts; + } + if (GST_CLOCK_TIME_IS_VALID (payload->ts) && + payload->ts > stream_min_ts && + (!GST_CLOCK_TIME_IS_VALID (stream_min_ts2) + || stream_min_ts2 > payload->ts)) { + stream_min_ts2 = payload->ts; + } + } + + /* there are some DVR ms files where first packet has TS of 0 (instead of -1) while subsequent packets have + regular (singificantly larger) timestamps. If we don't deal with it, we may end up with huge gap in timestamps + which makes playback stuck. The 0 timestamp may also be valid though, if the second packet timestamp continues + from it. I havent found a better way to distinguish between these two, except to set an arbitrary boundary + and disregard the first 0 timestamp if the second timestamp is bigger than the boundary) */ + + if (stream_min_ts == 0 && stream_min_ts2 == GST_CLOCK_TIME_NONE && !force) /* still waiting for the second timestamp */ + return FALSE; + + if (stream_min_ts == 0 && stream_min_ts2 > GST_SECOND) /* first timestamp is 0 and second is significantly larger, disregard the 0 */ + stream_min_ts = stream_min_ts2; + + /* if we don't have timestamp for this stream, wait for more data */ + if (!GST_CLOCK_TIME_IS_VALID (stream_min_ts) && !force) + return FALSE; + + if (GST_CLOCK_TIME_IS_VALID (stream_min_ts) && + (!GST_CLOCK_TIME_IS_VALID (first_ts) || first_ts > stream_min_ts)) + first_ts = stream_min_ts; + } + + if (!GST_CLOCK_TIME_IS_VALID (first_ts)) /* can happen with force = TRUE */ + first_ts = 0; + + demux->first_ts = first_ts; + + /* update packets queued before we knew first timestamp */ + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + int j; + stream = &demux->stream[i]; + + for (j = 0; j < stream->payloads->len; ++j) { + AsfPayload *payload = &g_array_index (stream->payloads, AsfPayload, j); + if (GST_CLOCK_TIME_IS_VALID (payload->ts)) { + if (payload->ts > first_ts) + payload->ts -= first_ts; + else + payload->ts = 0; + } + } + } + } + + gst_asf_demux_check_segment_ts (demux, 0); + + return TRUE; +} + +static gboolean +gst_asf_demux_update_caps_from_payload (GstASFDemux * demux, AsfStream * stream) +{ + /* try to determine whether the stream is AC-3 or MPEG; In dvr-ms the codecTag is unreliable + and often set wrong, inspecting the data is the only way that seem to be working */ + GstTypeFindProbability prob = GST_TYPE_FIND_NONE; + GstCaps *caps = NULL; + int i; + GstAdapter *adapter = gst_adapter_new (); + + for (i = 0; i < stream->payloads->len && prob < GST_TYPE_FIND_LIKELY; ++i) { + const guint8 *data; + AsfPayload *payload; + int len; + + payload = &g_array_index (stream->payloads, AsfPayload, i); + gst_adapter_push (adapter, gst_buffer_ref (payload->buf)); + len = gst_adapter_available (adapter); + data = gst_adapter_map (adapter, len); + + again: + +#define MIN_LENGTH 128 + + /* look for the sync points */ + while (TRUE) { + if (len < MIN_LENGTH || /* give typefind something to work on */ + (data[0] == 0x0b && data[1] == 0x77) || /* AC-3 sync point */ + (data[0] == 0xFF && ((data[1] & 0xF0) >> 4) == 0xF)) /* MPEG sync point */ + break; + ++data; + --len; + } + + gst_caps_take (&caps, gst_type_find_helper_for_data (GST_OBJECT (demux), + data, len, &prob)); + + if (prob < GST_TYPE_FIND_LIKELY) { + ++data; + --len; + if (len > MIN_LENGTH) + /* this wasn't it, look for another sync point */ + goto again; + } + + gst_adapter_unmap (adapter); + } + + gst_object_unref (adapter); + + if (caps) { + gst_caps_take (&stream->caps, caps); + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +gst_asf_demux_check_activate_streams (GstASFDemux * demux, gboolean force) +{ + guint i; + + if (demux->activated_streams) + return TRUE; + + if (G_UNLIKELY (!gst_asf_demux_check_first_ts (demux, force))) + return FALSE; + + if (!all_streams_prerolled (demux) && !force) { + GST_DEBUG_OBJECT (demux, "not all streams with data beyond preroll yet"); + return FALSE; + } + + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream = &demux->stream[i]; + + if (stream->payloads->len > 0) { + + if (stream->inspect_payload && /* dvr-ms required payload inspection */ + !stream->active && /* do not inspect active streams (caps were already set) */ + !gst_asf_demux_update_caps_from_payload (demux, stream) && /* failed to determine caps */ + stream->payloads->len < 20) { /* if we couldn't determine the caps from 20 packets then just give up and use whatever was in codecTag */ + /* try to gather some more data */ + return FALSE; + } + /* we don't check mutual exclusion stuff here; either we have data for + * a stream, then we active it, or we don't, then we'll ignore it */ + GST_LOG_OBJECT (stream->pad, "is prerolled - activate!"); + gst_asf_demux_activate_stream (demux, stream); + } else { + GST_LOG_OBJECT (stream->pad, "no data, ignoring stream"); + } + } + + gst_asf_demux_release_old_pads (demux); + + demux->activated_streams = TRUE; + GST_LOG_OBJECT (demux, "signalling no more pads"); + gst_element_no_more_pads (GST_ELEMENT (demux)); + return TRUE; +} + +/* returns the stream that has a complete payload with the lowest timestamp + * queued, or NULL (we push things by timestamp because during the internal + * prerolling we might accumulate more data then the external queues can take, + * so we'd lock up if we pushed all accumulated data for stream N in one go) */ +static AsfStream * +gst_asf_demux_find_stream_with_complete_payload (GstASFDemux * demux) +{ + AsfPayload *best_payload = NULL; + AsfStream *best_stream = NULL; + guint i; + + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + int j; + + stream = &demux->stream[i]; + + /* Don't push any data until we have at least one payload that falls within + * the current segment. This way we can remove out-of-segment payloads that + * don't need to be decoded after a seek, sending only data from the + * keyframe directly before our segment start */ + if (stream->payloads->len > 0) { + AsfPayload *payload = NULL; + gint last_idx; + + /* find last payload with timestamp */ + for (last_idx = stream->payloads->len - 1; + last_idx >= 0 && (payload == NULL + || !GST_CLOCK_TIME_IS_VALID (payload->ts)); --last_idx) { + payload = &g_array_index (stream->payloads, AsfPayload, last_idx); + } + + /* if this is first payload after seek we might need to update the segment */ + if (GST_CLOCK_TIME_IS_VALID (payload->ts)) + gst_asf_demux_check_segment_ts (demux, payload->ts); + + if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) && + (payload->ts < demux->segment.start))) { + if (G_UNLIKELY ((!demux->accurate) && payload->keyframe)) { + GST_DEBUG_OBJECT (stream->pad, + "Found keyframe, updating segment start to %" GST_TIME_FORMAT, + GST_TIME_ARGS (payload->ts)); + demux->segment.start = payload->ts; + demux->segment.time = payload->ts; + } else { + GST_DEBUG_OBJECT (stream->pad, "Last queued payload has timestamp %" + GST_TIME_FORMAT " which is before our segment start %" + GST_TIME_FORMAT ", not pushing yet", GST_TIME_ARGS (payload->ts), + GST_TIME_ARGS (demux->segment.start)); + continue; + } + } + + /* Now see if there's a complete payload queued for this stream */ + + payload = NULL; + /* find first complete payload with timestamp */ + for (j = 0; + j < stream->payloads->len && (payload == NULL + || !GST_CLOCK_TIME_IS_VALID (payload->ts)); ++j) { + payload = &g_array_index (stream->payloads, AsfPayload, j); + } + + if (!gst_asf_payload_is_complete (payload)) + continue; + + /* ... and whether its timestamp is lower than the current best */ + if (best_stream == NULL || best_payload->ts > payload->ts) { + best_stream = stream; + best_payload = payload; + } + } + } + + return best_stream; +} + +static GstFlowReturn +gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force) +{ + AsfStream *stream; + GstFlowReturn ret = GST_FLOW_OK; + + if (G_UNLIKELY (!demux->activated_streams)) { + if (!gst_asf_demux_check_activate_streams (demux, force)) + return GST_FLOW_OK; + /* streams are now activated */ + } + + while ((stream = gst_asf_demux_find_stream_with_complete_payload (demux))) { + AsfPayload *payload; + + /* wait until we had a chance to "lock on" some payload's timestamp */ + if (G_UNLIKELY (demux->need_newsegment + && !GST_CLOCK_TIME_IS_VALID (demux->segment_ts))) + return GST_FLOW_OK; + + payload = &g_array_index (stream->payloads, AsfPayload, 0); + + /* do we need to send a newsegment event */ + if ((G_UNLIKELY (demux->need_newsegment))) { + GstEvent *segment_event; + + /* safe default if insufficient upstream info */ + if (!GST_CLOCK_TIME_IS_VALID (demux->in_gap)) + demux->in_gap = 0; + + if (demux->segment.stop == GST_CLOCK_TIME_NONE && + demux->segment.duration > 0) { + /* slight HACK; prevent clipping of last bit */ + demux->segment.stop = demux->segment.duration + demux->in_gap; + } + + /* FIXME : only if ACCURATE ! */ + if (G_LIKELY (!demux->accurate + && (GST_CLOCK_TIME_IS_VALID (payload->ts)))) { + GST_DEBUG ("Adjusting newsegment start to %" GST_TIME_FORMAT, + GST_TIME_ARGS (payload->ts)); + demux->segment.start = payload->ts; + demux->segment.time = payload->ts; + } + + GST_DEBUG_OBJECT (demux, "sending new-segment event %" GST_SEGMENT_FORMAT, + &demux->segment); + + /* note: we fix up all timestamps to start from 0, so this should be ok */ + segment_event = gst_event_new_segment (&demux->segment); + if (demux->segment_seqnum) + gst_event_set_seqnum (segment_event, demux->segment_seqnum); + gst_asf_demux_send_event_unlocked (demux, segment_event); + + /* now post any global tags we may have found */ + if (demux->taglist == NULL) { + demux->taglist = gst_tag_list_new_empty (); + gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL); + } + + gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_CONTAINER_FORMAT, "ASF", NULL); + + GST_DEBUG_OBJECT (demux, "global tags: %" GST_PTR_FORMAT, demux->taglist); + gst_asf_demux_send_event_unlocked (demux, + gst_event_new_tag (demux->taglist)); + demux->taglist = NULL; + + demux->need_newsegment = FALSE; + demux->segment_seqnum = 0; + demux->segment_running = TRUE; + } + + /* Do we have tags pending for this stream? */ + if (G_UNLIKELY (stream->pending_tags)) { + GST_LOG_OBJECT (stream->pad, "%" GST_PTR_FORMAT, stream->pending_tags); + gst_pad_push_event (stream->pad, + gst_event_new_tag (stream->pending_tags)); + stream->pending_tags = NULL; + } + + /* We have the whole packet now so we should push the packet to + * the src pad now. First though we should check if we need to do + * descrambling */ + if (G_UNLIKELY (stream->span > 1)) { + gst_asf_demux_descramble_buffer (demux, stream, &payload->buf); + } + + payload->buf = gst_buffer_make_writable (payload->buf); + + if (G_LIKELY (!payload->keyframe)) { + GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DELTA_UNIT); + } + + if (G_UNLIKELY (stream->discont)) { + GST_DEBUG_OBJECT (stream->pad, "marking DISCONT on stream"); + GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + + if (G_UNLIKELY (stream->is_video && payload->par_x && payload->par_y && + (payload->par_x != stream->par_x) && + (payload->par_y != stream->par_y))) { + GST_DEBUG ("Updating PAR (%d/%d => %d/%d)", + stream->par_x, stream->par_y, payload->par_x, payload->par_y); + stream->par_x = payload->par_x; + stream->par_y = payload->par_y; + stream->caps = gst_caps_make_writable (stream->caps); + gst_caps_set_simple (stream->caps, "pixel-aspect-ratio", + GST_TYPE_FRACTION, stream->par_x, stream->par_y, NULL); + gst_pad_set_caps (stream->pad, stream->caps); + } + + if (G_UNLIKELY (stream->interlaced != payload->interlaced)) { + GST_DEBUG ("Updating interlaced status (%d => %d)", stream->interlaced, + payload->interlaced); + stream->interlaced = payload->interlaced; + stream->caps = gst_caps_make_writable (stream->caps); + gst_caps_set_simple (stream->caps, "interlace-mode", G_TYPE_BOOLEAN, + (stream->interlaced ? "mixed" : "progressive"), NULL); + gst_pad_set_caps (stream->pad, stream->caps); + } + + /* (sort of) interpolate timestamps using upstream "frame of reference", + * typically useful for live src, but might (unavoidably) mess with + * position reporting if a live src is playing not so live content + * (e.g. rtspsrc taking some time to fall back to tcp) */ + GST_BUFFER_PTS (payload->buf) = payload->ts; + if (GST_BUFFER_PTS_IS_VALID (payload->buf)) { + GST_BUFFER_PTS (payload->buf) += demux->in_gap; + } + if (payload->duration == GST_CLOCK_TIME_NONE + && stream->ext_props.avg_time_per_frame != 0) + GST_BUFFER_DURATION (payload->buf) = + stream->ext_props.avg_time_per_frame * 100; + else + GST_BUFFER_DURATION (payload->buf) = payload->duration; + + /* FIXME: we should really set durations on buffers if we can */ + + GST_LOG_OBJECT (stream->pad, "pushing buffer, ts=%" GST_TIME_FORMAT + ", dur=%" GST_TIME_FORMAT " size=%" G_GSIZE_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (payload->buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (payload->buf)), + gst_buffer_get_size (payload->buf)); + + if (stream->active) { + ret = gst_pad_push (stream->pad, payload->buf); + ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); + } else { + gst_buffer_unref (payload->buf); + ret = GST_FLOW_OK; + } + payload->buf = NULL; + g_array_remove_index (stream->payloads, 0); + + /* Break out as soon as we have an issue */ + if (G_UNLIKELY (ret != GST_FLOW_OK)) + break; + } + + return ret; +} + +static gboolean +gst_asf_demux_check_buffer_is_header (GstASFDemux * demux, GstBuffer * buf) +{ + AsfObject obj; + GstMapInfo map; + g_assert (buf != NULL); + + GST_LOG_OBJECT (demux, "Checking if buffer is a header"); + + gst_buffer_map (buf, &map, GST_MAP_READ); + + /* we return false on buffer too small */ + if (map.size < ASF_OBJECT_HEADER_SIZE) { + gst_buffer_unmap (buf, &map); + return FALSE; + } + + /* check if it is a header */ + asf_demux_peek_object (demux, map.data, ASF_OBJECT_HEADER_SIZE, &obj, TRUE); + gst_buffer_unmap (buf, &map); + if (obj.id == ASF_OBJ_HEADER) { + return TRUE; + } + return FALSE; +} + +static gboolean +gst_asf_demux_check_chained_asf (GstASFDemux * demux) +{ + guint64 off = demux->data_offset + (demux->packet * demux->packet_size); + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buf = NULL; + gboolean header = FALSE; + + /* TODO maybe we should skip index objects after the data and look + * further for a new header */ + if (gst_asf_demux_pull_data (demux, off, ASF_OBJECT_HEADER_SIZE, &buf, &ret)) { + g_assert (buf != NULL); + /* check if it is a header */ + if (gst_asf_demux_check_buffer_is_header (demux, buf)) { + GST_DEBUG_OBJECT (demux, "new base offset: %" G_GUINT64_FORMAT, off); + demux->base_offset = off; + header = TRUE; + } + + gst_buffer_unref (buf); + } + + return header; +} + +static void +gst_asf_demux_loop (GstASFDemux * demux) +{ + GstFlowReturn flow = GST_FLOW_OK; + GstBuffer *buf = NULL; + guint64 off; + gboolean sent_eos = FALSE; + + if (G_UNLIKELY (demux->state == GST_ASF_DEMUX_STATE_HEADER)) { + if (!gst_asf_demux_pull_headers (demux)) { + flow = GST_FLOW_ERROR; + goto pause; + } + + gst_asf_demux_pull_indices (demux); + } + + g_assert (demux->state == GST_ASF_DEMUX_STATE_DATA); + + if (G_UNLIKELY (demux->num_packets != 0 + && demux->packet >= demux->num_packets)) + goto eos; + + GST_LOG_OBJECT (demux, "packet %u/%u", (guint) demux->packet + 1, + (guint) demux->num_packets); + + off = demux->data_offset + (demux->packet * demux->packet_size); + + if (G_UNLIKELY (!gst_asf_demux_pull_data (demux, off, + demux->packet_size * demux->speed_packets, &buf, &flow))) { + GST_DEBUG_OBJECT (demux, "got flow %s", gst_flow_get_name (flow)); + if (flow == GST_FLOW_EOS) + goto eos; + else if (flow == GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (demux, "Not fatal"); + goto pause; + } else + goto read_failed; + } + + if (G_LIKELY (demux->speed_packets == 1)) { + GstAsfDemuxParsePacketError err; + err = gst_asf_demux_parse_packet (demux, buf); + if (G_UNLIKELY (err != GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE)) { + /* when we don't know when the data object ends, we should check + * for a chained asf */ + if (demux->num_packets == 0) { + if (gst_asf_demux_check_buffer_is_header (demux, buf)) { + GST_INFO_OBJECT (demux, "Chained asf found"); + demux->base_offset = off; + gst_asf_demux_reset (demux, TRUE); + gst_buffer_unref (buf); + return; + } + } + /* FIXME: We should tally up fatal errors and error out only + * after a few broken packets in a row? */ + + GST_INFO_OBJECT (demux, "Ignoring recoverable parse error"); + gst_buffer_unref (buf); + ++demux->packet; + return; + } + + flow = gst_asf_demux_push_complete_payloads (demux, FALSE); + + ++demux->packet; + + } else { + guint n; + for (n = 0; n < demux->speed_packets; n++) { + GstBuffer *sub; + GstAsfDemuxParsePacketError err; + + sub = + gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, + n * demux->packet_size, demux->packet_size); + err = gst_asf_demux_parse_packet (demux, sub); + if (G_UNLIKELY (err != GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE)) { + /* when we don't know when the data object ends, we should check + * for a chained asf */ + if (demux->num_packets == 0) { + if (gst_asf_demux_check_buffer_is_header (demux, sub)) { + GST_INFO_OBJECT (demux, "Chained asf found"); + demux->base_offset = off + n * demux->packet_size; + gst_asf_demux_reset (demux, TRUE); + gst_buffer_unref (sub); + gst_buffer_unref (buf); + return; + } + } + /* FIXME: We should tally up fatal errors and error out only + * after a few broken packets in a row? */ + + GST_INFO_OBJECT (demux, "Ignoring recoverable parse error"); + flow = GST_FLOW_OK; + } + + gst_buffer_unref (sub); + + if (err == GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE) + flow = gst_asf_demux_push_complete_payloads (demux, FALSE); + + ++demux->packet; + + } + + /* reset speed pull */ + demux->speed_packets = 1; + } + + gst_buffer_unref (buf); + + if (G_UNLIKELY (demux->num_packets > 0 + && demux->packet >= demux->num_packets)) { + GST_LOG_OBJECT (demux, "reached EOS"); + goto eos; + } + + if (G_UNLIKELY (flow != GST_FLOW_OK)) { + GST_DEBUG_OBJECT (demux, "pushing complete payloads failed"); + goto pause; + } + + /* check if we're at the end of the configured segment */ + /* FIXME: check if segment end reached etc. */ + + return; + +eos: + { + /* if we haven't activated our streams yet, this might be because we have + * less data queued than required for preroll; force stream activation and + * send any pending payloads before sending EOS */ + if (!demux->activated_streams) + gst_asf_demux_push_complete_payloads (demux, TRUE); + + /* we want to push an eos or post a segment-done in any case */ + if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gint64 stop; + + /* for segment playback we need to post when (in stream time) + * we stopped, this is either stop (when set) or the duration. */ + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + + GST_INFO_OBJECT (demux, "Posting segment-done, at end of segment"); + gst_element_post_message (GST_ELEMENT_CAST (demux), + gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, + stop)); + gst_asf_demux_send_event_unlocked (demux, + gst_event_new_segment_done (GST_FORMAT_TIME, stop)); + } else if (flow != GST_FLOW_EOS) { + /* check if we have a chained asf, in case, we don't eos yet */ + if (gst_asf_demux_check_chained_asf (demux)) { + GST_INFO_OBJECT (demux, "Chained ASF starting"); + gst_asf_demux_reset (demux, TRUE); + return; + } + } + /* normal playback, send EOS to all linked pads */ + GST_INFO_OBJECT (demux, "Sending EOS, at end of stream"); + gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ()); + sent_eos = TRUE; + /* ... and fall through to pause */ + } +pause: + { + GST_DEBUG_OBJECT (demux, "pausing task, flow return: %s", + gst_flow_get_name (flow)); + demux->segment_running = FALSE; + gst_pad_pause_task (demux->sinkpad); + + /* For the error cases (not EOS) */ + if (!sent_eos) { + if (flow == GST_FLOW_EOS) + gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ()); + else if (flow < GST_FLOW_EOS || flow == GST_FLOW_NOT_LINKED) { + /* Post an error. Hopefully something else already has, but if not... */ + GST_ELEMENT_ERROR (demux, STREAM, FAILED, + (_("Internal data stream error.")), + ("streaming stopped, reason %s", gst_flow_get_name (flow))); + } + } + return; + } + +/* ERRORS */ +read_failed: + { + GST_DEBUG_OBJECT (demux, "Read failed, doh"); + gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ()); + flow = GST_FLOW_EOS; + goto pause; + } +#if 0 + /* See FIXMEs above */ +parse_error: + { + gst_buffer_unref (buf); + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("Error parsing ASF packet %u", (guint) demux->packet)); + gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ()); + flow = GST_FLOW_ERROR; + goto pause; + } +#endif +} + +#define GST_ASF_DEMUX_CHECK_HEADER_YES 0 +#define GST_ASF_DEMUX_CHECK_HEADER_NO 1 +#define GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA 2 + +static gint +gst_asf_demux_check_header (GstASFDemux * demux) +{ + AsfObject obj; + guint8 *cdata = (guint8 *) gst_adapter_map (demux->adapter, + ASF_OBJECT_HEADER_SIZE); + if (cdata == NULL) /* need more data */ + return GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA; + + asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, FALSE); + if (obj.id != ASF_OBJ_HEADER) { + return GST_ASF_DEMUX_CHECK_HEADER_NO; + } else { + return GST_ASF_DEMUX_CHECK_HEADER_YES; + } +} + +static GstFlowReturn +gst_asf_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstASFDemux *demux; + + demux = GST_ASF_DEMUX (parent); + + GST_LOG_OBJECT (demux, + "buffer: size=%" G_GSIZE_FORMAT ", offset=%" G_GINT64_FORMAT ", time=%" + GST_TIME_FORMAT, gst_buffer_get_size (buf), GST_BUFFER_OFFSET (buf), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf))) { + GST_DEBUG_OBJECT (demux, "received DISCONT"); + gst_asf_demux_mark_discont (demux); + } + + if (G_UNLIKELY ((!GST_CLOCK_TIME_IS_VALID (demux->in_gap) && + GST_BUFFER_TIMESTAMP_IS_VALID (buf)))) { + demux->in_gap = GST_BUFFER_TIMESTAMP (buf) - demux->in_segment.start; + GST_DEBUG_OBJECT (demux, "upstream segment start %" GST_TIME_FORMAT + ", interpolation gap: %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->in_segment.start), GST_TIME_ARGS (demux->in_gap)); + } + + gst_adapter_push (demux->adapter, buf); + + switch (demux->state) { + case GST_ASF_DEMUX_STATE_INDEX:{ + gint result = gst_asf_demux_check_header (demux); + if (result == GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA) /* need more data */ + break; + + if (result == GST_ASF_DEMUX_CHECK_HEADER_NO) { + /* we don't care about this, probably an index */ + /* TODO maybe would be smarter to skip all the indices + * until we got a new header or EOS to decide */ + GST_LOG_OBJECT (demux, "Received index object, its EOS"); + goto eos; + } else { + GST_INFO_OBJECT (demux, "Chained asf starting"); + /* cleanup and get ready for a chained asf */ + gst_asf_demux_reset (demux, TRUE); + /* fall through */ + } + } + case GST_ASF_DEMUX_STATE_HEADER:{ + ret = gst_asf_demux_chain_headers (demux); + if (demux->state != GST_ASF_DEMUX_STATE_DATA) + break; + /* otherwise fall through */ + } + case GST_ASF_DEMUX_STATE_DATA: + { + guint64 data_size; + + data_size = demux->packet_size; + + while (gst_adapter_available (demux->adapter) >= data_size) { + GstBuffer *buf; + GstAsfDemuxParsePacketError err; + + /* we don't know the length of the stream + * check for a chained asf everytime */ + if (demux->num_packets == 0) { + gint result = gst_asf_demux_check_header (demux); + + if (result == GST_ASF_DEMUX_CHECK_HEADER_YES) { + GST_INFO_OBJECT (demux, "Chained asf starting"); + /* cleanup and get ready for a chained asf */ + gst_asf_demux_reset (demux, TRUE); + break; + } + } else if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0 + && demux->packet >= demux->num_packets)) { + /* do not overshoot data section when streaming */ + break; + } + + buf = gst_adapter_take_buffer (demux->adapter, data_size); + + /* FIXME: We should tally up fatal errors and error out only + * after a few broken packets in a row? */ + err = gst_asf_demux_parse_packet (demux, buf); + + gst_buffer_unref (buf); + + if (G_LIKELY (err == GST_ASF_DEMUX_PARSE_PACKET_ERROR_NONE)) + ret = gst_asf_demux_push_complete_payloads (demux, FALSE); + else + GST_WARNING_OBJECT (demux, "Parse error"); + + if (demux->packet >= 0) + ++demux->packet; + } + if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0 + && demux->packet >= demux->num_packets)) { + demux->state = GST_ASF_DEMUX_STATE_INDEX; + } + break; + } + default: + g_assert_not_reached (); + } + +done: + if (ret != GST_FLOW_OK) + GST_DEBUG_OBJECT (demux, "flow: %s", gst_flow_get_name (ret)); + + return ret; + +eos: + { + GST_DEBUG_OBJECT (demux, "Handled last packet, setting EOS"); + ret = GST_FLOW_EOS; + goto done; + } +} + +static inline gboolean +gst_asf_demux_skip_bytes (guint num_bytes, guint8 ** p_data, guint64 * p_size) +{ + if (*p_size < num_bytes) + return FALSE; + + *p_data += num_bytes; + *p_size -= num_bytes; + return TRUE; +} + +static inline guint8 +gst_asf_demux_get_uint8 (guint8 ** p_data, guint64 * p_size) +{ + guint8 ret; + + g_assert (*p_size >= 1); + ret = GST_READ_UINT8 (*p_data); + *p_data += sizeof (guint8); + *p_size -= sizeof (guint8); + return ret; +} + +static inline guint16 +gst_asf_demux_get_uint16 (guint8 ** p_data, guint64 * p_size) +{ + guint16 ret; + + g_assert (*p_size >= 2); + ret = GST_READ_UINT16_LE (*p_data); + *p_data += sizeof (guint16); + *p_size -= sizeof (guint16); + return ret; +} + +static inline guint32 +gst_asf_demux_get_uint32 (guint8 ** p_data, guint64 * p_size) +{ + guint32 ret; + + g_assert (*p_size >= 4); + ret = GST_READ_UINT32_LE (*p_data); + *p_data += sizeof (guint32); + *p_size -= sizeof (guint32); + return ret; +} + +static inline guint64 +gst_asf_demux_get_uint64 (guint8 ** p_data, guint64 * p_size) +{ + guint64 ret; + + g_assert (*p_size >= 8); + ret = GST_READ_UINT64_LE (*p_data); + *p_data += sizeof (guint64); + *p_size -= sizeof (guint64); + return ret; +} + +static gboolean +gst_asf_demux_get_buffer (GstBuffer ** p_buf, guint num_bytes_to_read, + guint8 ** p_data, guint64 * p_size) +{ + *p_buf = NULL; + + if (*p_size < num_bytes_to_read) + return FALSE; + + *p_buf = gst_buffer_new_and_alloc (num_bytes_to_read); + gst_buffer_fill (*p_buf, 0, *p_data, num_bytes_to_read); + + *p_data += num_bytes_to_read; + *p_size -= num_bytes_to_read; + + return TRUE; +} + +static gboolean +gst_asf_demux_get_bytes (guint8 ** p_buf, guint num_bytes_to_read, + guint8 ** p_data, guint64 * p_size) +{ + *p_buf = NULL; + + if (*p_size < num_bytes_to_read) + return FALSE; + + *p_buf = g_memdup (*p_data, num_bytes_to_read); + *p_data += num_bytes_to_read; + *p_size -= num_bytes_to_read; + return TRUE; +} + +static gboolean +gst_asf_demux_get_string (gchar ** p_str, guint16 * p_strlen, + guint8 ** p_data, guint64 * p_size) +{ + guint16 s_length; + guint8 *s; + + *p_str = NULL; + + if (*p_size < 2) + return FALSE; + + s_length = gst_asf_demux_get_uint16 (p_data, p_size); + + if (p_strlen) + *p_strlen = s_length; + + if (s_length == 0) { + GST_WARNING ("zero-length string"); + *p_str = g_strdup (""); + return TRUE; + } + + if (!gst_asf_demux_get_bytes (&s, s_length, p_data, p_size)) + return FALSE; + + g_assert (s != NULL); + + /* just because They don't exist doesn't + * mean They are not out to get you ... */ + if (s[s_length - 1] != '\0') { + s = g_realloc (s, s_length + 1); + s[s_length] = '\0'; + } + + *p_str = (gchar *) s; + return TRUE; +} + + +static void +gst_asf_demux_get_guid (ASFGuid * guid, guint8 ** p_data, guint64 * p_size) +{ + g_assert (*p_size >= 4 * sizeof (guint32)); + + guid->v1 = gst_asf_demux_get_uint32 (p_data, p_size); + guid->v2 = gst_asf_demux_get_uint32 (p_data, p_size); + guid->v3 = gst_asf_demux_get_uint32 (p_data, p_size); + guid->v4 = gst_asf_demux_get_uint32 (p_data, p_size); +} + +static gboolean +gst_asf_demux_get_stream_audio (asf_stream_audio * audio, guint8 ** p_data, + guint64 * p_size) +{ + if (*p_size < (2 + 2 + 4 + 4 + 2 + 2 + 2)) + return FALSE; + + /* WAVEFORMATEX Structure */ + audio->codec_tag = gst_asf_demux_get_uint16 (p_data, p_size); + audio->channels = gst_asf_demux_get_uint16 (p_data, p_size); + audio->sample_rate = gst_asf_demux_get_uint32 (p_data, p_size); + audio->byte_rate = gst_asf_demux_get_uint32 (p_data, p_size); + audio->block_align = gst_asf_demux_get_uint16 (p_data, p_size); + audio->word_size = gst_asf_demux_get_uint16 (p_data, p_size); + /* Codec specific data size */ + audio->size = gst_asf_demux_get_uint16 (p_data, p_size); + return TRUE; +} + +static gboolean +gst_asf_demux_get_stream_video (asf_stream_video * video, guint8 ** p_data, + guint64 * p_size) +{ + if (*p_size < (4 + 4 + 1 + 2)) + return FALSE; + + video->width = gst_asf_demux_get_uint32 (p_data, p_size); + video->height = gst_asf_demux_get_uint32 (p_data, p_size); + video->unknown = gst_asf_demux_get_uint8 (p_data, p_size); + video->size = gst_asf_demux_get_uint16 (p_data, p_size); + return TRUE; +} + +static gboolean +gst_asf_demux_get_stream_video_format (asf_stream_video_format * fmt, + guint8 ** p_data, guint64 * p_size) +{ + if (*p_size < (4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 4)) + return FALSE; + + fmt->size = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->width = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->height = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->planes = gst_asf_demux_get_uint16 (p_data, p_size); + fmt->depth = gst_asf_demux_get_uint16 (p_data, p_size); + fmt->tag = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->image_size = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->xpels_meter = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->ypels_meter = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->num_colors = gst_asf_demux_get_uint32 (p_data, p_size); + fmt->imp_colors = gst_asf_demux_get_uint32 (p_data, p_size); + return TRUE; +} + +AsfStream * +gst_asf_demux_get_stream (GstASFDemux * demux, guint16 id) +{ + guint i; + + for (i = 0; i < demux->num_streams; i++) { + if (demux->stream[i].id == id) + return &demux->stream[i]; + } + + if (gst_asf_demux_is_unknown_stream (demux, id)) + GST_WARNING ("Segment found for undefined stream: (%d)", id); + return NULL; +} + +static AsfStream * +gst_asf_demux_setup_pad (GstASFDemux * demux, GstPad * src_pad, + GstCaps * caps, guint16 id, gboolean is_video, GstTagList * tags) +{ + AsfStream *stream; + + gst_pad_use_fixed_caps (src_pad); + gst_pad_set_caps (src_pad, caps); + + gst_pad_set_event_function (src_pad, + GST_DEBUG_FUNCPTR (gst_asf_demux_handle_src_event)); + gst_pad_set_query_function (src_pad, + GST_DEBUG_FUNCPTR (gst_asf_demux_handle_src_query)); + + stream = &demux->stream[demux->num_streams]; + stream->caps = caps; + stream->pad = src_pad; + stream->id = id; + stream->fps_known = !is_video; /* bit hacky for audio */ + stream->is_video = is_video; + stream->pending_tags = tags; + stream->discont = TRUE; + if (is_video) { + GstStructure *st; + gint par_x, par_y; + st = gst_caps_get_structure (caps, 0); + if (gst_structure_get_fraction (st, "pixel-aspect-ratio", &par_x, &par_y) && + par_x > 0 && par_y > 0) { + GST_DEBUG ("PAR %d/%d", par_x, par_y); + stream->par_x = par_x; + stream->par_y = par_y; + } + } + + stream->payloads = g_array_new (FALSE, FALSE, sizeof (AsfPayload)); + + GST_INFO ("Created pad %s for stream %u with caps %" GST_PTR_FORMAT, + GST_PAD_NAME (src_pad), demux->num_streams, caps); + + ++demux->num_streams; + + stream->active = FALSE; + + return stream; +} + +static AsfStream * +gst_asf_demux_add_audio_stream (GstASFDemux * demux, + asf_stream_audio * audio, guint16 id, guint8 ** p_data, guint64 * p_size) +{ + GstTagList *tags = NULL; + GstBuffer *extradata = NULL; + GstPad *src_pad; + GstCaps *caps; + guint16 size_left = 0; + gchar *codec_name = NULL; + gchar *name = NULL; + + size_left = audio->size; + + /* Create the audio pad */ + name = g_strdup_printf ("audio_%u", demux->num_audio_streams); + + src_pad = gst_pad_new_from_static_template (&audio_src_template, name); + g_free (name); + + /* Swallow up any left over data and set up the + * standard properties from the header info */ + if (size_left) { + GST_INFO_OBJECT (demux, "Audio header contains %d bytes of " + "codec specific data", size_left); + + g_assert (size_left <= *p_size); + gst_asf_demux_get_buffer (&extradata, size_left, p_data, p_size); + } + + /* asf_stream_audio is the same as gst_riff_strf_auds, but with an + * additional two bytes indicating extradata. */ + /* FIXME: Handle the channel reorder map here */ + caps = gst_riff_create_audio_caps (audio->codec_tag, NULL, + (gst_riff_strf_auds *) audio, extradata, NULL, &codec_name, NULL); + + if (caps == NULL) { + caps = gst_caps_new_simple ("audio/x-asf-unknown", "codec_id", + G_TYPE_INT, (gint) audio->codec_tag, NULL); + } + + /* Informing about that audio format we just added */ + if (codec_name) { + tags = gst_tag_list_new (GST_TAG_AUDIO_CODEC, codec_name, NULL); + g_free (codec_name); + } + + if (extradata) + gst_buffer_unref (extradata); + + GST_INFO ("Adding audio stream #%u, id %u codec %u (0x%04x), tags=%" + GST_PTR_FORMAT, demux->num_audio_streams, id, audio->codec_tag, + audio->codec_tag, tags); + + ++demux->num_audio_streams; + + return gst_asf_demux_setup_pad (demux, src_pad, caps, id, FALSE, tags); +} + +static AsfStream * +gst_asf_demux_add_video_stream (GstASFDemux * demux, + asf_stream_video_format * video, guint16 id, + guint8 ** p_data, guint64 * p_size) +{ + GstTagList *tags = NULL; + GstStructure *caps_s; + GstBuffer *extradata = NULL; + GstPad *src_pad; + GstCaps *caps; + gchar *str; + gchar *name = NULL; + gchar *codec_name = NULL; + gint size_left = video->size - 40; + + /* Create the video pad */ + name = g_strdup_printf ("video_%u", demux->num_video_streams); + src_pad = gst_pad_new_from_static_template (&video_src_template, name); + g_free (name); + + /* Now try some gstreamer formatted MIME types (from gst_avi_demux_strf_vids) */ + if (size_left) { + GST_LOG ("Video header has %d bytes of codec specific data", size_left); + g_assert (size_left <= *p_size); + gst_asf_demux_get_buffer (&extradata, size_left, p_data, p_size); + } + + GST_DEBUG ("video codec %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (video->tag)); + + /* yes, asf_stream_video_format and gst_riff_strf_vids are the same */ + caps = gst_riff_create_video_caps (video->tag, NULL, + (gst_riff_strf_vids *) video, extradata, NULL, &codec_name); + + if (caps == NULL) { + caps = gst_caps_new_simple ("video/x-asf-unknown", "fourcc", + G_TYPE_UINT, video->tag, NULL); + } else { + GstStructure *s; + gint ax, ay; + + s = gst_asf_demux_get_metadata_for_stream (demux, id); + if (gst_structure_get_int (s, "AspectRatioX", &ax) && + gst_structure_get_int (s, "AspectRatioY", &ay) && (ax > 0 && ay > 0)) { + gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, + ax, ay, NULL); + + } else { + guint ax, ay; + /* retry with the global metadata */ + GST_DEBUG ("Retrying with global metadata %" GST_PTR_FORMAT, + demux->global_metadata); + s = demux->global_metadata; + if (gst_structure_get_uint (s, "AspectRatioX", &ax) && + gst_structure_get_uint (s, "AspectRatioY", &ay)) { + GST_DEBUG ("ax:%d, ay:%d", ax, ay); + if (ax > 0 && ay > 0) + gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, + ax, ay, NULL); + } + } + s = gst_caps_get_structure (caps, 0); + gst_structure_remove_field (s, "framerate"); + } + + caps_s = gst_caps_get_structure (caps, 0); + + /* add format field with fourcc to WMV/VC1 caps to differentiate variants */ + if (gst_structure_has_name (caps_s, "video/x-wmv")) { + str = g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (video->tag)); + gst_caps_set_simple (caps, "format", G_TYPE_STRING, str, NULL); + g_free (str); + } + + if (codec_name) { + tags = gst_tag_list_new (GST_TAG_VIDEO_CODEC, codec_name, NULL); + g_free (codec_name); + } + + if (extradata) + gst_buffer_unref (extradata); + + GST_INFO ("Adding video stream #%u, id %u, codec %" + GST_FOURCC_FORMAT " (0x%08x)", demux->num_video_streams, id, + GST_FOURCC_ARGS (video->tag), video->tag); + + ++demux->num_video_streams; + + return gst_asf_demux_setup_pad (demux, src_pad, caps, id, TRUE, tags); +} + +static void +gst_asf_demux_activate_stream (GstASFDemux * demux, AsfStream * stream) +{ + if (!stream->active) { + GstEvent *event; + gchar *stream_id; + + GST_INFO_OBJECT (demux, "Activating stream %2u, pad %s, caps %" + GST_PTR_FORMAT, stream->id, GST_PAD_NAME (stream->pad), stream->caps); + gst_pad_set_active (stream->pad, TRUE); + + stream_id = + gst_pad_create_stream_id_printf (stream->pad, GST_ELEMENT_CAST (demux), + "%03u", stream->id); + + event = + gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0); + if (event) { + if (gst_event_parse_group_id (event, &demux->group_id)) + demux->have_group_id = TRUE; + else + demux->have_group_id = FALSE; + gst_event_unref (event); + } else if (!demux->have_group_id) { + demux->have_group_id = TRUE; + demux->group_id = gst_util_group_id_next (); + } + + event = gst_event_new_stream_start (stream_id); + if (demux->have_group_id) + gst_event_set_group_id (event, demux->group_id); + + gst_pad_push_event (stream->pad, event); + g_free (stream_id); + gst_pad_set_caps (stream->pad, stream->caps); + + gst_element_add_pad (GST_ELEMENT_CAST (demux), stream->pad); + gst_flow_combiner_add_pad (demux->flowcombiner, stream->pad); + stream->active = TRUE; + } +} + +static AsfStream * +gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + AsfCorrectionType correction_type; + AsfStreamType stream_type; + GstClockTime time_offset; + gboolean is_encrypted G_GNUC_UNUSED; + guint16 stream_id; + guint16 flags; + ASFGuid guid; + guint stream_specific_size; + guint type_specific_size G_GNUC_UNUSED; + guint unknown G_GNUC_UNUSED; + gboolean inspect_payload = FALSE; + AsfStream *stream = NULL; + + /* Get the rest of the header's header */ + if (size < (16 + 16 + 8 + 4 + 4 + 2 + 4)) + goto not_enough_data; + + gst_asf_demux_get_guid (&guid, &data, &size); + stream_type = gst_asf_demux_identify_guid (asf_stream_guids, &guid); + + gst_asf_demux_get_guid (&guid, &data, &size); + correction_type = gst_asf_demux_identify_guid (asf_correction_guids, &guid); + + time_offset = gst_asf_demux_get_uint64 (&data, &size) * 100; + + type_specific_size = gst_asf_demux_get_uint32 (&data, &size); + stream_specific_size = gst_asf_demux_get_uint32 (&data, &size); + + flags = gst_asf_demux_get_uint16 (&data, &size); + stream_id = flags & 0x7f; + is_encrypted = ! !((flags & 0x8000) << 15); + unknown = gst_asf_demux_get_uint32 (&data, &size); + + GST_DEBUG_OBJECT (demux, "Found stream %u, time_offset=%" GST_TIME_FORMAT, + stream_id, GST_TIME_ARGS (time_offset)); + + /* dvr-ms has audio stream declared in stream specific data */ + if (stream_type == ASF_STREAM_EXT_EMBED_HEADER) { + AsfExtStreamType ext_stream_type; + gst_asf_demux_get_guid (&guid, &data, &size); + ext_stream_type = gst_asf_demux_identify_guid (asf_ext_stream_guids, &guid); + + if (ext_stream_type == ASF_EXT_STREAM_AUDIO) { + inspect_payload = TRUE; + + gst_asf_demux_get_guid (&guid, &data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_uint32 (&data, &size); + gst_asf_demux_get_guid (&guid, &data, &size); + gst_asf_demux_get_uint32 (&data, &size); + stream_type = ASF_STREAM_AUDIO; + } + } + + switch (stream_type) { + case ASF_STREAM_AUDIO:{ + asf_stream_audio audio_object; + + if (!gst_asf_demux_get_stream_audio (&audio_object, &data, &size)) + goto not_enough_data; + + GST_INFO ("Object is an audio stream with %u bytes of additional data", + audio_object.size); + + stream = gst_asf_demux_add_audio_stream (demux, &audio_object, stream_id, + &data, &size); + + switch (correction_type) { + case ASF_CORRECTION_ON:{ + guint span, packet_size, chunk_size, data_size, silence_data; + + GST_INFO ("Using error correction"); + + if (size < (1 + 2 + 2 + 2 + 1)) + goto not_enough_data; + + span = gst_asf_demux_get_uint8 (&data, &size); + packet_size = gst_asf_demux_get_uint16 (&data, &size); + chunk_size = gst_asf_demux_get_uint16 (&data, &size); + data_size = gst_asf_demux_get_uint16 (&data, &size); + silence_data = gst_asf_demux_get_uint8 (&data, &size); + + stream->span = span; + + GST_DEBUG_OBJECT (demux, "Descrambling ps:%u cs:%u ds:%u s:%u sd:%u", + packet_size, chunk_size, data_size, span, silence_data); + + if (stream->span > 1) { + if (chunk_size == 0 || ((packet_size / chunk_size) <= 1)) { + /* Disable descrambling */ + stream->span = 0; + } else { + /* FIXME: this else branch was added for + * weird_al_yankovic - the saga begins.asf */ + stream->ds_packet_size = packet_size; + stream->ds_chunk_size = chunk_size; + } + } else { + /* Descambling is enabled */ + stream->ds_packet_size = packet_size; + stream->ds_chunk_size = chunk_size; + } +#if 0 + /* Now skip the rest of the silence data */ + if (data_size > 1) + gst_bytestream_flush (demux->bs, data_size - 1); +#else + /* FIXME: CHECKME. And why -1? */ + if (data_size > 1) { + if (!gst_asf_demux_skip_bytes (data_size - 1, &data, &size)) { + goto not_enough_data; + } + } +#endif + break; + } + case ASF_CORRECTION_OFF:{ + GST_INFO ("Error correction off"); + if (!gst_asf_demux_skip_bytes (stream_specific_size, &data, &size)) + goto not_enough_data; + break; + } + default: + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("Audio stream using unknown error correction")); + return NULL; + } + + break; + } + + case ASF_STREAM_VIDEO:{ + asf_stream_video_format video_format_object; + asf_stream_video video_object; + guint16 vsize; + + if (!gst_asf_demux_get_stream_video (&video_object, &data, &size)) + goto not_enough_data; + + vsize = video_object.size - 40; /* Byte order gets offset by single byte */ + + GST_INFO ("object is a video stream with %u bytes of " + "additional data", vsize); + + if (!gst_asf_demux_get_stream_video_format (&video_format_object, + &data, &size)) { + goto not_enough_data; + } + + stream = gst_asf_demux_add_video_stream (demux, &video_format_object, + stream_id, &data, &size); + + break; + } + + default: + GST_WARNING_OBJECT (demux, "Unknown stream type for stream %u", + stream_id); + demux->other_streams = + g_slist_append (demux->other_streams, GINT_TO_POINTER (stream_id)); + break; + } + + if (stream) + stream->inspect_payload = inspect_payload; + return stream; + +not_enough_data: + { + GST_WARNING_OBJECT (demux, "Unexpected end of data parsing stream object"); + /* we'll error out later if we found no streams */ + return NULL; + } +} + +static const gchar * +gst_asf_demux_get_gst_tag_from_tag_name (const gchar * name_utf8) +{ + const struct + { + const gchar *asf_name; + const gchar *gst_name; + } tags[] = { + { + "WM/Genre", GST_TAG_GENRE}, { + "WM/AlbumTitle", GST_TAG_ALBUM}, { + "WM/AlbumArtist", GST_TAG_ARTIST}, { + "WM/Picture", GST_TAG_IMAGE}, { + "WM/Track", GST_TAG_TRACK_NUMBER}, { + "WM/TrackNumber", GST_TAG_TRACK_NUMBER}, { + "WM/Year", GST_TAG_DATE_TIME} + /* { "WM/Composer", GST_TAG_COMPOSER } */ + }; + gsize out; + guint i; + + if (name_utf8 == NULL) { + GST_WARNING ("Failed to convert name to UTF8, skipping"); + return NULL; + } + + out = strlen (name_utf8); + + for (i = 0; i < G_N_ELEMENTS (tags); ++i) { + if (strncmp (tags[i].asf_name, name_utf8, out) == 0) { + GST_LOG ("map tagname '%s' -> '%s'", name_utf8, tags[i].gst_name); + return tags[i].gst_name; + } + } + + return NULL; +} + +/* gst_asf_demux_add_global_tags() takes ownership of taglist! */ +static void +gst_asf_demux_add_global_tags (GstASFDemux * demux, GstTagList * taglist) +{ + GstTagList *t; + + GST_DEBUG_OBJECT (demux, "adding global tags: %" GST_PTR_FORMAT, taglist); + + if (taglist == NULL) + return; + + if (gst_tag_list_is_empty (taglist)) { + gst_tag_list_unref (taglist); + return; + } + + t = gst_tag_list_merge (demux->taglist, taglist, GST_TAG_MERGE_APPEND); + gst_tag_list_set_scope (t, GST_TAG_SCOPE_GLOBAL); + if (demux->taglist) + gst_tag_list_unref (demux->taglist); + gst_tag_list_unref (taglist); + demux->taglist = t; + GST_LOG_OBJECT (demux, "global tags now: %" GST_PTR_FORMAT, demux->taglist); +} + +#define ASF_DEMUX_DATA_TYPE_UTF16LE_STRING 0 +#define ASF_DEMUX_DATA_TYPE_BYTE_ARRAY 1 +#define ASF_DEMUX_DATA_TYPE_DWORD 3 + +static void +asf_demux_parse_picture_tag (GstTagList * tags, const guint8 * tag_data, + guint tag_data_len) +{ + GstByteReader r; + const guint8 *img_data = NULL; + guint32 img_data_len = 0; + guint8 pic_type = 0; + + gst_byte_reader_init (&r, tag_data, tag_data_len); + + /* skip mime type string (we don't trust it and do our own typefinding), + * and also skip the description string, since we don't use it */ + if (!gst_byte_reader_get_uint8 (&r, &pic_type) || + !gst_byte_reader_get_uint32_le (&r, &img_data_len) || + !gst_byte_reader_skip_string_utf16 (&r) || + !gst_byte_reader_skip_string_utf16 (&r) || + !gst_byte_reader_get_data (&r, img_data_len, &img_data)) { + goto not_enough_data; + } + + + if (!gst_tag_list_add_id3_image (tags, img_data, img_data_len, pic_type)) + GST_DEBUG ("failed to add image extracted from WM/Picture tag to taglist"); + + return; + +not_enough_data: + { + GST_DEBUG ("Failed to read WM/Picture tag: not enough data"); + GST_MEMDUMP ("WM/Picture data", tag_data, tag_data_len); + return; + } +} + +/* Extended Content Description Object */ +static GstFlowReturn +gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + /* Other known (and unused) 'text/unicode' metadata available : + * + * WM/Lyrics = + * WM/MediaPrimaryClassID = {D1607DBC-E323-4BE2-86A1-48A42A28441E} + * WMFSDKVersion = 9.00.00.2980 + * WMFSDKNeeded = 0.0.0.0000 + * WM/UniqueFileIdentifier = AMGa_id=R 15334;AMGp_id=P 5149;AMGt_id=T 2324984 + * WM/Publisher = 4AD + * WM/Provider = AMG + * WM/ProviderRating = 8 + * WM/ProviderStyle = Rock (similar to WM/Genre) + * WM/GenreID (similar to WM/Genre) + * WM/TrackNumber (same as WM/Track but as a string) + * + * Other known (and unused) 'non-text' metadata available : + * + * WM/EncodingTime + * WM/MCDI + * IsVBR + * + * We might want to read WM/TrackNumber and use atoi() if we don't have + * WM/Track + */ + + GstTagList *taglist; + guint16 blockcount, i; + + GST_INFO_OBJECT (demux, "object is an extended content description"); + + taglist = gst_tag_list_new_empty (); + + /* Content Descriptor Count */ + if (size < 2) + goto not_enough_data; + + blockcount = gst_asf_demux_get_uint16 (&data, &size); + + for (i = 1; i <= blockcount; ++i) { + const gchar *gst_tag_name; + guint16 datatype; + guint16 value_len; + guint16 name_len; + GValue tag_value = { 0, }; + gsize in, out; + gchar *name; + gchar *name_utf8 = NULL; + gchar *value; + + /* Descriptor */ + if (!gst_asf_demux_get_string (&name, &name_len, &data, &size)) + goto not_enough_data; + + if (size < 2) { + g_free (name); + goto not_enough_data; + } + /* Descriptor Value Data Type */ + datatype = gst_asf_demux_get_uint16 (&data, &size); + + /* Descriptor Value (not really a string, but same thing reading-wise) */ + if (!gst_asf_demux_get_string (&value, &value_len, &data, &size)) { + g_free (name); + goto not_enough_data; + } + + name_utf8 = + g_convert (name, name_len, "UTF-8", "UTF-16LE", &in, &out, NULL); + + if (name_utf8 != NULL) { + GST_DEBUG ("Found tag/metadata %s", name_utf8); + + gst_tag_name = gst_asf_demux_get_gst_tag_from_tag_name (name_utf8); + GST_DEBUG ("gst_tag_name %s", GST_STR_NULL (gst_tag_name)); + + switch (datatype) { + case ASF_DEMUX_DATA_TYPE_UTF16LE_STRING:{ + gchar *value_utf8; + + value_utf8 = g_convert (value, value_len, "UTF-8", "UTF-16LE", + &in, &out, NULL); + + /* get rid of tags with empty value */ + if (value_utf8 != NULL && *value_utf8 != '\0') { + GST_DEBUG ("string value %s", value_utf8); + + value_utf8[out] = '\0'; + + if (gst_tag_name != NULL) { + if (strcmp (gst_tag_name, GST_TAG_DATE_TIME) == 0) { + guint year = atoi (value_utf8); + + if (year > 0) { + g_value_init (&tag_value, GST_TYPE_DATE_TIME); + g_value_take_boxed (&tag_value, gst_date_time_new_y (year)); + } + } else if (strcmp (gst_tag_name, GST_TAG_GENRE) == 0) { + guint id3v1_genre_id; + const gchar *genre_str; + + if (sscanf (value_utf8, "(%u)", &id3v1_genre_id) == 1 && + ((genre_str = gst_tag_id3_genre_get (id3v1_genre_id)))) { + GST_DEBUG ("Genre: %s -> %s", value_utf8, genre_str); + g_free (value_utf8); + value_utf8 = g_strdup (genre_str); + } + } else { + GType tag_type; + + /* convert tag from string to other type if required */ + tag_type = gst_tag_get_type (gst_tag_name); + g_value_init (&tag_value, tag_type); + if (!gst_value_deserialize (&tag_value, value_utf8)) { + GValue from_val = { 0, }; + + g_value_init (&from_val, G_TYPE_STRING); + g_value_set_string (&from_val, value_utf8); + if (!g_value_transform (&from_val, &tag_value)) { + GST_WARNING_OBJECT (demux, + "Could not transform string tag to " "%s tag type %s", + gst_tag_name, g_type_name (tag_type)); + g_value_unset (&tag_value); + } + g_value_unset (&from_val); + } + } + } else { + /* metadata ! */ + GST_DEBUG ("Setting metadata"); + g_value_init (&tag_value, G_TYPE_STRING); + g_value_set_string (&tag_value, value_utf8); + } + } else if (value_utf8 == NULL) { + GST_WARNING ("Failed to convert string value to UTF8, skipping"); + } else { + GST_DEBUG ("Skipping empty string value for %s", + GST_STR_NULL (gst_tag_name)); + } + g_free (value_utf8); + break; + } + case ASF_DEMUX_DATA_TYPE_BYTE_ARRAY:{ + if (gst_tag_name) { + if (!g_str_equal (gst_tag_name, GST_TAG_IMAGE)) { + GST_FIXME ("Unhandled byte array tag %s", + GST_STR_NULL (gst_tag_name)); + break; + } else { + asf_demux_parse_picture_tag (taglist, (guint8 *) value, + value_len); + } + } + break; + } + case ASF_DEMUX_DATA_TYPE_DWORD:{ + guint uint_val = GST_READ_UINT32_LE (value); + + /* this is the track number */ + g_value_init (&tag_value, G_TYPE_UINT); + + /* WM/Track counts from 0 */ + if (!strcmp (name_utf8, "WM/Track")) + ++uint_val; + + g_value_set_uint (&tag_value, uint_val); + break; + } + default:{ + GST_DEBUG ("Skipping tag %s of type %d", gst_tag_name, datatype); + break; + } + } + + if (G_IS_VALUE (&tag_value)) { + if (gst_tag_name) { + GstTagMergeMode merge_mode = GST_TAG_MERGE_APPEND; + + /* WM/TrackNumber is more reliable than WM/Track, since the latter + * is supposed to have a 0 base but is often wrongly written to start + * from 1 as well, so prefer WM/TrackNumber when we have it: either + * replace the value added earlier from WM/Track or put it first in + * the list, so that it will get picked up by _get_uint() */ + if (strcmp (name_utf8, "WM/TrackNumber") == 0) + merge_mode = GST_TAG_MERGE_REPLACE; + + gst_tag_list_add_values (taglist, merge_mode, gst_tag_name, + &tag_value, NULL); + } else { + GST_DEBUG ("Setting global metadata %s", name_utf8); + gst_structure_set_value (demux->global_metadata, name_utf8, + &tag_value); + } + + g_value_unset (&tag_value); + } + } + + g_free (name); + g_free (value); + g_free (name_utf8); + } + + gst_asf_demux_add_global_tags (demux, taglist); + + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_WARNING ("Unexpected end of data parsing ext content desc object"); + gst_tag_list_unref (taglist); + return GST_FLOW_OK; /* not really fatal */ + } +} + +static GstStructure * +gst_asf_demux_get_metadata_for_stream (GstASFDemux * demux, guint stream_num) +{ + gchar sname[32]; + guint i; + + g_snprintf (sname, sizeof (sname), "stream-%u", stream_num); + + for (i = 0; i < gst_caps_get_size (demux->metadata); ++i) { + GstStructure *s; + + s = gst_caps_get_structure (demux->metadata, i); + if (gst_structure_has_name (s, sname)) + return s; + } + + gst_caps_append_structure (demux->metadata, gst_structure_new_empty (sname)); + + /* try lookup again; demux->metadata took ownership of the structure, so we + * can't really make any assumptions about what happened to it, so we can't + * just return it directly after appending it */ + return gst_asf_demux_get_metadata_for_stream (demux, stream_num); +} + +static GstFlowReturn +gst_asf_demux_process_metadata (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + guint16 blockcount, i; + + GST_INFO_OBJECT (demux, "object is a metadata object"); + + /* Content Descriptor Count */ + if (size < 2) + goto not_enough_data; + + blockcount = gst_asf_demux_get_uint16 (&data, &size); + + for (i = 0; i < blockcount; ++i) { + GstStructure *s; + guint16 stream_num, name_len, data_type, lang_idx G_GNUC_UNUSED; + guint32 data_len, ival; + gchar *name_utf8; + + if (size < (2 + 2 + 2 + 2 + 4)) + goto not_enough_data; + + lang_idx = gst_asf_demux_get_uint16 (&data, &size); + stream_num = gst_asf_demux_get_uint16 (&data, &size); + name_len = gst_asf_demux_get_uint16 (&data, &size); + data_type = gst_asf_demux_get_uint16 (&data, &size); + data_len = gst_asf_demux_get_uint32 (&data, &size); + + if (size < name_len + data_len) + goto not_enough_data; + + /* convert name to UTF-8 */ + name_utf8 = g_convert ((gchar *) data, name_len, "UTF-8", "UTF-16LE", + NULL, NULL, NULL); + gst_asf_demux_skip_bytes (name_len, &data, &size); + + if (name_utf8 == NULL) { + GST_WARNING ("Failed to convert value name to UTF8, skipping"); + gst_asf_demux_skip_bytes (data_len, &data, &size); + continue; + } + + if (data_type != ASF_DEMUX_DATA_TYPE_DWORD) { + gst_asf_demux_skip_bytes (data_len, &data, &size); + g_free (name_utf8); + continue; + } + + /* read DWORD */ + if (size < 4) { + g_free (name_utf8); + goto not_enough_data; + } + + ival = gst_asf_demux_get_uint32 (&data, &size); + + /* skip anything else there may be, just in case */ + gst_asf_demux_skip_bytes (data_len - 4, &data, &size); + + s = gst_asf_demux_get_metadata_for_stream (demux, stream_num); + gst_structure_set (s, name_utf8, G_TYPE_INT, ival, NULL); + g_free (name_utf8); + } + + GST_INFO_OBJECT (demux, "metadata = %" GST_PTR_FORMAT, demux->metadata); + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_WARNING ("Unexpected end of data parsing metadata object"); + return GST_FLOW_OK; /* not really fatal */ + } +} + +static GstFlowReturn +gst_asf_demux_process_header (GstASFDemux * demux, guint8 * data, guint64 size) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint32 i, num_objects; + guint8 unknown G_GNUC_UNUSED; + + /* Get the rest of the header's header */ + if (size < (4 + 1 + 1)) + goto not_enough_data; + + num_objects = gst_asf_demux_get_uint32 (&data, &size); + unknown = gst_asf_demux_get_uint8 (&data, &size); + unknown = gst_asf_demux_get_uint8 (&data, &size); + + GST_INFO_OBJECT (demux, "object is a header with %u parts", num_objects); + + /* Loop through the header's objects, processing those */ + for (i = 0; i < num_objects; ++i) { + GST_INFO_OBJECT (demux, "reading header part %u", i); + ret = gst_asf_demux_process_object (demux, &data, &size); + if (ret != GST_FLOW_OK) { + GST_WARNING ("process_object returned %s", gst_asf_get_flow_name (ret)); + break; + } + } + + return ret; + +not_enough_data: + { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("short read parsing HEADER object")); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_asf_demux_process_file (GstASFDemux * demux, guint8 * data, guint64 size) +{ + guint64 creation_time G_GNUC_UNUSED; + guint64 file_size G_GNUC_UNUSED; + guint64 send_time G_GNUC_UNUSED; + guint64 packets_count, play_time, preroll; + guint32 flags, min_pktsize, max_pktsize, min_bitrate G_GNUC_UNUSED; + + if (size < (16 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4)) + goto not_enough_data; + + gst_asf_demux_skip_bytes (16, &data, &size); /* skip GUID */ + file_size = gst_asf_demux_get_uint64 (&data, &size); + creation_time = gst_asf_demux_get_uint64 (&data, &size); + packets_count = gst_asf_demux_get_uint64 (&data, &size); + play_time = gst_asf_demux_get_uint64 (&data, &size); + send_time = gst_asf_demux_get_uint64 (&data, &size); + preroll = gst_asf_demux_get_uint64 (&data, &size); + flags = gst_asf_demux_get_uint32 (&data, &size); + min_pktsize = gst_asf_demux_get_uint32 (&data, &size); + max_pktsize = gst_asf_demux_get_uint32 (&data, &size); + min_bitrate = gst_asf_demux_get_uint32 (&data, &size); + + demux->broadcast = ! !(flags & 0x01); + demux->seekable = ! !(flags & 0x02); + + GST_DEBUG_OBJECT (demux, "min_pktsize = %u", min_pktsize); + GST_DEBUG_OBJECT (demux, "flags::broadcast = %d", demux->broadcast); + GST_DEBUG_OBJECT (demux, "flags::seekable = %d", demux->seekable); + + if (demux->broadcast) { + /* these fields are invalid if the broadcast flag is set */ + play_time = 0; + file_size = 0; + } + + if (min_pktsize != max_pktsize) + goto non_fixed_packet_size; + + demux->packet_size = max_pktsize; + + /* FIXME: do we need send_time as well? what is it? */ + if ((play_time * 100) >= (preroll * GST_MSECOND)) + demux->play_time = (play_time * 100) - (preroll * GST_MSECOND); + else + demux->play_time = 0; + + demux->preroll = preroll * GST_MSECOND; + + /* initial latency */ + demux->latency = demux->preroll; + + if (demux->play_time == 0) + demux->seekable = FALSE; + + GST_DEBUG_OBJECT (demux, "play_time %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->play_time)); + GST_DEBUG_OBJECT (demux, "preroll %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->preroll)); + + if (demux->play_time > 0) { + demux->segment.duration = demux->play_time; + } + + GST_INFO ("object is a file with %" G_GUINT64_FORMAT " data packets", + packets_count); + GST_INFO ("preroll = %" G_GUINT64_FORMAT, demux->preroll); + + return GST_FLOW_OK; + +/* ERRORS */ +non_fixed_packet_size: + { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("packet size must be fixed")); + return GST_FLOW_ERROR; + } +not_enough_data: + { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("short read parsing FILE object")); + return GST_FLOW_ERROR; + } +} + +/* Content Description Object */ +static GstFlowReturn +gst_asf_demux_process_comment (GstASFDemux * demux, guint8 * data, guint64 size) +{ + struct + { + const gchar *gst_tag; + guint16 val_length; + gchar *val_utf8; + } tags[5] = { + { + GST_TAG_TITLE, 0, NULL}, { + GST_TAG_ARTIST, 0, NULL}, { + GST_TAG_COPYRIGHT, 0, NULL}, { + GST_TAG_DESCRIPTION, 0, NULL}, { + GST_TAG_COMMENT, 0, NULL} + }; + GstTagList *taglist; + GValue value = { 0 }; + gsize in, out; + gint i = -1; + + GST_INFO_OBJECT (demux, "object is a comment"); + + if (size < (2 + 2 + 2 + 2 + 2)) + goto not_enough_data; + + tags[0].val_length = gst_asf_demux_get_uint16 (&data, &size); + tags[1].val_length = gst_asf_demux_get_uint16 (&data, &size); + tags[2].val_length = gst_asf_demux_get_uint16 (&data, &size); + tags[3].val_length = gst_asf_demux_get_uint16 (&data, &size); + tags[4].val_length = gst_asf_demux_get_uint16 (&data, &size); + + GST_DEBUG_OBJECT (demux, "Comment lengths: title=%d author=%d copyright=%d " + "description=%d rating=%d", tags[0].val_length, tags[1].val_length, + tags[2].val_length, tags[3].val_length, tags[4].val_length); + + for (i = 0; i < G_N_ELEMENTS (tags); ++i) { + if (size < tags[i].val_length) + goto not_enough_data; + + /* might be just '/0', '/0'... */ + if (tags[i].val_length > 2 && tags[i].val_length % 2 == 0) { + /* convert to UTF-8 */ + tags[i].val_utf8 = g_convert ((gchar *) data, tags[i].val_length, + "UTF-8", "UTF-16LE", &in, &out, NULL); + } + gst_asf_demux_skip_bytes (tags[i].val_length, &data, &size); + } + + /* parse metadata into taglist */ + taglist = gst_tag_list_new_empty (); + g_value_init (&value, G_TYPE_STRING); + for (i = 0; i < G_N_ELEMENTS (tags); ++i) { + if (tags[i].val_utf8 && strlen (tags[i].val_utf8) > 0 && tags[i].gst_tag) { + g_value_set_string (&value, tags[i].val_utf8); + gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, + tags[i].gst_tag, &value, NULL); + } + } + g_value_unset (&value); + + gst_asf_demux_add_global_tags (demux, taglist); + + for (i = 0; i < G_N_ELEMENTS (tags); ++i) + g_free (tags[i].val_utf8); + + return GST_FLOW_OK; + +not_enough_data: + { + GST_WARNING_OBJECT (demux, "unexpectedly short of data while processing " + "comment tag section %d, skipping comment object", i); + for (i = 0; i < G_N_ELEMENTS (tags); i++) + g_free (tags[i].val_utf8); + return GST_FLOW_OK; /* not really fatal */ + } +} + +static GstFlowReturn +gst_asf_demux_process_bitrate_props_object (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + guint16 num_streams, i; + AsfStream *stream; + + if (size < 2) + goto not_enough_data; + + num_streams = gst_asf_demux_get_uint16 (&data, &size); + + GST_INFO ("object is a bitrate properties object with %u streams", + num_streams); + + if (size < (num_streams * (2 + 4))) + goto not_enough_data; + + for (i = 0; i < num_streams; ++i) { + guint32 bitrate; + guint16 stream_id; + + stream_id = gst_asf_demux_get_uint16 (&data, &size); + bitrate = gst_asf_demux_get_uint32 (&data, &size); + + if (stream_id < GST_ASF_DEMUX_NUM_STREAM_IDS) { + GST_DEBUG_OBJECT (demux, "bitrate of stream %u = %u", stream_id, bitrate); + stream = gst_asf_demux_get_stream (demux, stream_id); + if (stream) { + if (stream->pending_tags == NULL) { + stream->pending_tags = + gst_tag_list_new (GST_TAG_BITRATE, bitrate, NULL); + } + } else { + GST_WARNING_OBJECT (demux, "Stream id %u wasn't found", stream_id); + } + } else { + GST_WARNING ("stream id %u is too large", stream_id); + } + } + + return GST_FLOW_OK; + +not_enough_data: + { + GST_WARNING_OBJECT (demux, "short read parsing bitrate props object!"); + return GST_FLOW_OK; /* not really fatal */ + } +} + +static GstFlowReturn +gst_asf_demux_process_header_ext (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint64 hdr_size; + + /* Get the rest of the header's header */ + if (size < (16 + 2 + 4)) + goto not_enough_data; + + /* skip GUID and two other bytes */ + gst_asf_demux_skip_bytes (16 + 2, &data, &size); + hdr_size = gst_asf_demux_get_uint32 (&data, &size); + + GST_INFO ("extended header object with a size of %u bytes", (guint) size); + + /* FIXME: does data_size include the rest of the header that we have read? */ + if (hdr_size > size) + goto not_enough_data; + + while (hdr_size > 0) { + ret = gst_asf_demux_process_object (demux, &data, &hdr_size); + if (ret != GST_FLOW_OK) + break; + } + + return ret; + +not_enough_data: + { + GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), + ("short read parsing extended header object")); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_asf_demux_process_language_list (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + guint i; + + if (size < 2) + goto not_enough_data; + + if (demux->languages) { + GST_WARNING ("More than one LANGUAGE_LIST object in stream"); + g_strfreev (demux->languages); + demux->languages = NULL; + demux->num_languages = 0; + } + + demux->num_languages = gst_asf_demux_get_uint16 (&data, &size); + GST_LOG ("%u languages:", demux->num_languages); + + demux->languages = g_new0 (gchar *, demux->num_languages + 1); + for (i = 0; i < demux->num_languages; ++i) { + guint8 len, *lang_data = NULL; + + if (size < 1) + goto not_enough_data; + len = gst_asf_demux_get_uint8 (&data, &size); + if (gst_asf_demux_get_bytes (&lang_data, len, &data, &size)) { + gchar *utf8; + + utf8 = g_convert ((gchar *) lang_data, len, "UTF-8", "UTF-16LE", NULL, + NULL, NULL); + + /* truncate "en-us" etc. to just "en" */ + if (utf8 && strlen (utf8) >= 5 && (utf8[2] == '-' || utf8[2] == '_')) { + utf8[2] = '\0'; + } + GST_DEBUG ("[%u] %s", i, GST_STR_NULL (utf8)); + demux->languages[i] = utf8; + g_free (lang_data); + } else { + goto not_enough_data; + } + } + + return GST_FLOW_OK; + +not_enough_data: + { + GST_WARNING_OBJECT (demux, "short read parsing language list object!"); + g_free (demux->languages); + demux->languages = NULL; + return GST_FLOW_OK; /* not fatal */ + } +} + +static GstFlowReturn +gst_asf_demux_process_simple_index (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + GstClockTime interval; + guint32 count, i; + + if (size < (16 + 8 + 4 + 4)) + goto not_enough_data; + + /* skip file id */ + gst_asf_demux_skip_bytes (16, &data, &size); + interval = gst_asf_demux_get_uint64 (&data, &size) * (GstClockTime) 100; + gst_asf_demux_skip_bytes (4, &data, &size); + count = gst_asf_demux_get_uint32 (&data, &size); + if (count > 0) { + demux->sidx_interval = interval; + demux->sidx_num_entries = count; + g_free (demux->sidx_entries); + demux->sidx_entries = g_new0 (AsfSimpleIndexEntry, count); + + for (i = 0; i < count; ++i) { + if (G_UNLIKELY (size < 6)) { + /* adjust for broken files, to avoid having entries at the end + * of the parsed index that point to time=0. Resulting in seeking to + * the end of the file leading back to the beginning */ + demux->sidx_num_entries -= (count - i); + break; + } + demux->sidx_entries[i].packet = gst_asf_demux_get_uint32 (&data, &size); + demux->sidx_entries[i].count = gst_asf_demux_get_uint16 (&data, &size); + GST_LOG_OBJECT (demux, "%" GST_TIME_FORMAT " = packet %4u count : %2d", + GST_TIME_ARGS (i * interval), demux->sidx_entries[i].packet, + demux->sidx_entries[i].count); + } + } else { + GST_DEBUG_OBJECT (demux, "simple index object with 0 entries"); + } + + return GST_FLOW_OK; + +not_enough_data: + { + GST_WARNING_OBJECT (demux, "short read parsing simple index object!"); + return GST_FLOW_OK; /* not fatal */ + } +} + +static GstFlowReturn +gst_asf_demux_process_advanced_mutual_exclusion (GstASFDemux * demux, + guint8 * data, guint64 size) +{ + ASFGuid guid; + guint16 num, i; + guint8 *mes; + + if (size < 16 + 2 + (2 * 2)) + goto not_enough_data; + + gst_asf_demux_get_guid (&guid, &data, &size); + num = gst_asf_demux_get_uint16 (&data, &size); + + if (num < 2) { + GST_WARNING_OBJECT (demux, "nonsensical mutually exclusive streams count"); + return GST_FLOW_OK; + } + + if (size < (num * sizeof (guint16))) + goto not_enough_data; + + /* read mutually exclusive stream numbers */ + mes = g_new (guint8, num + 1); + for (i = 0; i < num; ++i) { + mes[i] = gst_asf_demux_get_uint16 (&data, &size) & 0x7f; + GST_LOG_OBJECT (demux, "mutually exclusive: stream #%d", mes[i]); + } + + /* add terminator so we can easily get the count or know when to stop */ + mes[i] = (guint8) - 1; + + demux->mut_ex_streams = g_slist_append (demux->mut_ex_streams, mes); + + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_WARNING_OBJECT (demux, "short read parsing advanced mutual exclusion"); + return GST_FLOW_OK; /* not absolutely fatal */ + } +} + +gboolean +gst_asf_demux_is_unknown_stream (GstASFDemux * demux, guint stream_num) +{ + return g_slist_find (demux->other_streams, + GINT_TO_POINTER (stream_num)) == NULL; +} + +static GstFlowReturn +gst_asf_demux_process_ext_stream_props (GstASFDemux * demux, guint8 * data, + guint64 size) +{ + AsfStreamExtProps esp; + AsfStream *stream = NULL; + AsfObject stream_obj; + guint16 stream_name_count; + guint16 num_payload_ext; + guint64 len; + guint8 *stream_obj_data = NULL; + guint8 *data_start; + guint obj_size; + guint i, stream_num; + + data_start = data; + obj_size = (guint) size; + + if (size < 64) + goto not_enough_data; + + esp.valid = TRUE; + esp.start_time = gst_asf_demux_get_uint64 (&data, &size) * GST_MSECOND; + esp.end_time = gst_asf_demux_get_uint64 (&data, &size) * GST_MSECOND; + esp.data_bitrate = gst_asf_demux_get_uint32 (&data, &size); + esp.buffer_size = gst_asf_demux_get_uint32 (&data, &size); + esp.intial_buf_fullness = gst_asf_demux_get_uint32 (&data, &size); + esp.data_bitrate2 = gst_asf_demux_get_uint32 (&data, &size); + esp.buffer_size2 = gst_asf_demux_get_uint32 (&data, &size); + esp.intial_buf_fullness2 = gst_asf_demux_get_uint32 (&data, &size); + esp.max_obj_size = gst_asf_demux_get_uint32 (&data, &size); + esp.flags = gst_asf_demux_get_uint32 (&data, &size); + stream_num = gst_asf_demux_get_uint16 (&data, &size); + esp.lang_idx = gst_asf_demux_get_uint16 (&data, &size); + esp.avg_time_per_frame = gst_asf_demux_get_uint64 (&data, &size); + stream_name_count = gst_asf_demux_get_uint16 (&data, &size); + num_payload_ext = gst_asf_demux_get_uint16 (&data, &size); + + GST_INFO ("start_time = %" GST_TIME_FORMAT, + GST_TIME_ARGS (esp.start_time)); + GST_INFO ("end_time = %" GST_TIME_FORMAT, + GST_TIME_ARGS (esp.end_time)); + GST_INFO ("flags = %08x", esp.flags); + GST_INFO ("average time per frame = %" GST_TIME_FORMAT, + GST_TIME_ARGS (esp.avg_time_per_frame * 100)); + GST_INFO ("stream number = %u", stream_num); + GST_INFO ("stream language ID idx = %u (%s)", esp.lang_idx, + (esp.lang_idx < demux->num_languages) ? + GST_STR_NULL (demux->languages[esp.lang_idx]) : "??"); + GST_INFO ("stream name count = %u", stream_name_count); + + /* read stream names */ + for (i = 0; i < stream_name_count; ++i) { + guint16 stream_lang_idx G_GNUC_UNUSED; + gchar *stream_name = NULL; + + if (size < 2) + goto not_enough_data; + stream_lang_idx = gst_asf_demux_get_uint16 (&data, &size); + if (!gst_asf_demux_get_string (&stream_name, NULL, &data, &size)) + goto not_enough_data; + GST_INFO ("stream name %d: %s", i, GST_STR_NULL (stream_name)); + g_free (stream_name); /* TODO: store names in struct */ + } + + /* read payload extension systems stuff */ + GST_LOG ("payload extension systems count = %u", num_payload_ext); + + if (num_payload_ext > 0) + esp.payload_extensions = g_new0 (AsfPayloadExtension, num_payload_ext + 1); + else + esp.payload_extensions = NULL; + + for (i = 0; i < num_payload_ext; ++i) { + AsfPayloadExtension ext; + ASFGuid ext_guid; + guint32 sys_info_len; + + if (size < 16 + 2 + 4) + goto not_enough_data; + + gst_asf_demux_get_guid (&ext_guid, &data, &size); + ext.id = gst_asf_demux_identify_guid (asf_payload_ext_guids, &ext_guid); + ext.len = gst_asf_demux_get_uint16 (&data, &size); + + sys_info_len = gst_asf_demux_get_uint32 (&data, &size); + GST_LOG ("payload systems info len = %u", sys_info_len); + if (!gst_asf_demux_skip_bytes (sys_info_len, &data, &size)) + goto not_enough_data; + + esp.payload_extensions[i] = ext; + } + + GST_LOG ("bytes read: %u/%u", (guint) (data - data_start), obj_size); + + /* there might be an optional STREAM_INFO object here now; if not, we + * should have parsed the corresponding stream info object already (since + * we are parsing the extended stream properties objects delayed) */ + if (size == 0) { + stream = gst_asf_demux_get_stream (demux, stream_num); + goto done; + } + + /* get size of the stream object */ + if (!asf_demux_peek_object (demux, data, size, &stream_obj, TRUE)) + goto not_enough_data; + + if (stream_obj.id != ASF_OBJ_STREAM) + goto expected_stream_object; + + if (stream_obj.size < ASF_OBJECT_HEADER_SIZE || + stream_obj.size > (10 * 1024 * 1024)) + goto not_enough_data; + + gst_asf_demux_skip_bytes (ASF_OBJECT_HEADER_SIZE, &data, &size); + + /* process this stream object later after all the other 'normal' ones + * have been processed (since the others are more important/non-hidden) */ + len = stream_obj.size - ASF_OBJECT_HEADER_SIZE; + if (!gst_asf_demux_get_bytes (&stream_obj_data, len, &data, &size)) + goto not_enough_data; + + /* parse stream object */ + stream = gst_asf_demux_parse_stream_object (demux, stream_obj_data, len); + g_free (stream_obj_data); + +done: + + if (stream) { + stream->ext_props = esp; + + /* try to set the framerate */ + if (stream->is_video && stream->caps) { + GValue framerate = { 0 }; + GstStructure *s; + gint num, denom; + + g_value_init (&framerate, GST_TYPE_FRACTION); + + num = GST_SECOND / 100; + denom = esp.avg_time_per_frame; + if (denom == 0) { + /* avoid division by 0, assume 25/1 framerate */ + denom = GST_SECOND / 2500; + } + + gst_value_set_fraction (&framerate, num, denom); + + stream->caps = gst_caps_make_writable (stream->caps); + s = gst_caps_get_structure (stream->caps, 0); + gst_structure_set_value (s, "framerate", &framerate); + g_value_unset (&framerate); + GST_DEBUG_OBJECT (demux, "setting framerate of %d/%d = %f", + num, denom, ((gdouble) num) / denom); + } + + /* add language info now if we have it */ + if (stream->ext_props.lang_idx < demux->num_languages) { + if (stream->pending_tags == NULL) + stream->pending_tags = gst_tag_list_new_empty (); + GST_LOG_OBJECT (demux, "stream %u has language '%s'", stream->id, + demux->languages[stream->ext_props.lang_idx]); + gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_APPEND, + GST_TAG_LANGUAGE_CODE, demux->languages[stream->ext_props.lang_idx], + NULL); + } + } else if (gst_asf_demux_is_unknown_stream (demux, stream_num)) { + GST_WARNING_OBJECT (demux, "Ext. stream properties for unknown stream"); + } + + return GST_FLOW_OK; + + /* Errors */ +not_enough_data: + { + GST_WARNING_OBJECT (demux, "short read parsing ext stream props object!"); + return GST_FLOW_OK; /* not absolutely fatal */ + } +expected_stream_object: + { + GST_WARNING_OBJECT (demux, "error parsing extended stream properties " + "object: expected embedded stream object, but got %s object instead!", + gst_asf_get_guid_nick (asf_object_guids, stream_obj.id)); + return GST_FLOW_OK; /* not absolutely fatal */ + } +} + +static const gchar * +gst_asf_demux_push_obj (GstASFDemux * demux, guint32 obj_id) +{ + const gchar *nick; + + nick = gst_asf_get_guid_nick (asf_object_guids, obj_id); + if (g_str_has_prefix (nick, "ASF_OBJ_")) + nick += strlen ("ASF_OBJ_"); + + if (demux->objpath == NULL) { + demux->objpath = g_strdup (nick); + } else { + gchar *newpath; + + newpath = g_strdup_printf ("%s/%s", demux->objpath, nick); + g_free (demux->objpath); + demux->objpath = newpath; + } + + return (const gchar *) demux->objpath; +} + +static void +gst_asf_demux_pop_obj (GstASFDemux * demux) +{ + gchar *s; + + if ((s = g_strrstr (demux->objpath, "/"))) { + *s = '\0'; + } else { + g_free (demux->objpath); + demux->objpath = NULL; + } +} + +static void +gst_asf_demux_process_queued_extended_stream_objects (GstASFDemux * demux) +{ + GSList *l; + guint i; + + /* Parse the queued extended stream property objects and add the info + * to the existing streams or add the new embedded streams, but without + * activating them yet */ + GST_LOG_OBJECT (demux, "%u queued extended stream properties objects", + g_slist_length (demux->ext_stream_props)); + + for (l = demux->ext_stream_props, i = 0; l != NULL; l = l->next, ++i) { + GstBuffer *buf = GST_BUFFER (l->data); + GstMapInfo map; + + gst_buffer_map (buf, &map, GST_MAP_READ); + + GST_LOG_OBJECT (demux, "parsing ext. stream properties object #%u", i); + gst_asf_demux_process_ext_stream_props (demux, map.data, map.size); + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + } + g_slist_free (demux->ext_stream_props); + demux->ext_stream_props = NULL; +} + +#if 0 +static void +gst_asf_demux_activate_ext_props_streams (GstASFDemux * demux) +{ + guint i, j; + + for (i = 0; i < demux->num_streams; ++i) { + AsfStream *stream; + gboolean is_hidden; + GSList *x; + + stream = &demux->stream[i]; + + GST_LOG_OBJECT (demux, "checking stream %2u", stream->id); + + if (stream->active) { + GST_LOG_OBJECT (demux, "stream %2u is already activated", stream->id); + continue; + } + + is_hidden = FALSE; + for (x = demux->mut_ex_streams; x != NULL; x = x->next) { + guint8 *mes; + + /* check for each mutual exclusion whether it affects this stream */ + for (mes = (guint8 *) x->data; mes != NULL && *mes != 0xff; ++mes) { + if (*mes == stream->id) { + /* if yes, check if we've already added streams that are mutually + * exclusive with the stream we're about to add */ + for (mes = (guint8 *) x->data; mes != NULL && *mes != 0xff; ++mes) { + for (j = 0; j < demux->num_streams; ++j) { + /* if the broadcast flag is set, assume the hidden streams aren't + * actually streamed and hide them (or playbin won't work right), + * otherwise assume their data is available */ + if (demux->stream[j].id == *mes && demux->broadcast) { + is_hidden = TRUE; + GST_LOG_OBJECT (demux, "broadcast stream ID %d to be added is " + "mutually exclusive with already existing stream ID %d, " + "hiding stream", stream->id, demux->stream[j].id); + goto next; + } + } + } + break; + } + } + } + + next: + + /* FIXME: we should do stream activation based on preroll data in + * streaming mode too */ + if (demux->streaming && !is_hidden) + gst_asf_demux_activate_stream (demux, stream); + } +} +#endif + +static GstFlowReturn +gst_asf_demux_process_object (GstASFDemux * demux, guint8 ** p_data, + guint64 * p_size) +{ + GstFlowReturn ret = GST_FLOW_OK; + AsfObject obj; + guint64 obj_data_size; + + if (*p_size < ASF_OBJECT_HEADER_SIZE) + return ASF_FLOW_NEED_MORE_DATA; + + asf_demux_peek_object (demux, *p_data, ASF_OBJECT_HEADER_SIZE, &obj, TRUE); + gst_asf_demux_skip_bytes (ASF_OBJECT_HEADER_SIZE, p_data, p_size); + + obj_data_size = obj.size - ASF_OBJECT_HEADER_SIZE; + + if (*p_size < obj_data_size) + return ASF_FLOW_NEED_MORE_DATA; + + gst_asf_demux_push_obj (demux, obj.id); + + GST_INFO ("%s: size %" G_GUINT64_FORMAT, demux->objpath, obj.size); + + switch (obj.id) { + case ASF_OBJ_STREAM: + gst_asf_demux_parse_stream_object (demux, *p_data, obj_data_size); + ret = GST_FLOW_OK; + break; + case ASF_OBJ_FILE: + ret = gst_asf_demux_process_file (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_HEADER: + ret = gst_asf_demux_process_header (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_COMMENT: + ret = gst_asf_demux_process_comment (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_HEAD1: + ret = gst_asf_demux_process_header_ext (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_BITRATE_PROPS: + ret = + gst_asf_demux_process_bitrate_props_object (demux, *p_data, + obj_data_size); + break; + case ASF_OBJ_EXT_CONTENT_DESC: + ret = + gst_asf_demux_process_ext_content_desc (demux, *p_data, + obj_data_size); + break; + case ASF_OBJ_METADATA_OBJECT: + ret = gst_asf_demux_process_metadata (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_EXTENDED_STREAM_PROPS:{ + GstBuffer *buf; + + /* process these later, we might not have parsed the corresponding + * stream object yet */ + GST_LOG ("%s: queued for later parsing", demux->objpath); + buf = gst_buffer_new_and_alloc (obj_data_size); + gst_buffer_fill (buf, 0, *p_data, obj_data_size); + demux->ext_stream_props = g_slist_append (demux->ext_stream_props, buf); + ret = GST_FLOW_OK; + break; + } + case ASF_OBJ_LANGUAGE_LIST: + ret = gst_asf_demux_process_language_list (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_ADVANCED_MUTUAL_EXCLUSION: + ret = gst_asf_demux_process_advanced_mutual_exclusion (demux, *p_data, + obj_data_size); + break; + case ASF_OBJ_SIMPLE_INDEX: + ret = gst_asf_demux_process_simple_index (demux, *p_data, obj_data_size); + break; + case ASF_OBJ_CONTENT_ENCRYPTION: + case ASF_OBJ_EXT_CONTENT_ENCRYPTION: + case ASF_OBJ_DIGITAL_SIGNATURE_OBJECT: + case ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT: + goto error_encrypted; + case ASF_OBJ_CONCEAL_NONE: + case ASF_OBJ_HEAD2: + case ASF_OBJ_UNDEFINED: + case ASF_OBJ_CODEC_COMMENT: + case ASF_OBJ_INDEX: + case ASF_OBJ_PADDING: + case ASF_OBJ_BITRATE_MUTEX: + case ASF_OBJ_COMPATIBILITY: + case ASF_OBJ_INDEX_PLACEHOLDER: + case ASF_OBJ_INDEX_PARAMETERS: + case ASF_OBJ_STREAM_PRIORITIZATION: + case ASF_OBJ_SCRIPT_COMMAND: + case ASF_OBJ_METADATA_LIBRARY_OBJECT: + default: + /* Unknown/unhandled object, skip it and hope for the best */ + GST_INFO ("%s: skipping object", demux->objpath); + ret = GST_FLOW_OK; + break; + } + + /* this can't fail, we checked the number of bytes available before */ + gst_asf_demux_skip_bytes (obj_data_size, p_data, p_size); + + GST_LOG ("%s: ret = %s", demux->objpath, gst_asf_get_flow_name (ret)); + + gst_asf_demux_pop_obj (demux); + + return ret; + +/* ERRORS */ +error_encrypted: + { + GST_ELEMENT_ERROR (demux, STREAM, DECRYPT, (NULL), (NULL)); + return GST_FLOW_ERROR; + } +} + +static void +gst_asf_demux_descramble_buffer (GstASFDemux * demux, AsfStream * stream, + GstBuffer ** p_buffer) +{ + GstBuffer *descrambled_buffer; + GstBuffer *scrambled_buffer; + GstBuffer *sub_buffer; + guint offset; + guint off; + guint row; + guint col; + guint idx; + + /* descrambled_buffer is initialised in the first iteration */ + descrambled_buffer = NULL; + scrambled_buffer = *p_buffer; + + if (gst_buffer_get_size (scrambled_buffer) < + stream->ds_packet_size * stream->span) + return; + + for (offset = 0; offset < gst_buffer_get_size (scrambled_buffer); + offset += stream->ds_chunk_size) { + off = offset / stream->ds_chunk_size; + row = off / stream->span; + col = off % stream->span; + idx = row + col * stream->ds_packet_size / stream->ds_chunk_size; + GST_DEBUG ("idx=%u, row=%u, col=%u, off=%u, ds_chunk_size=%u", idx, row, + col, off, stream->ds_chunk_size); + GST_DEBUG ("scrambled buffer size=%" G_GSIZE_FORMAT + ", span=%u, packet_size=%u", gst_buffer_get_size (scrambled_buffer), + stream->span, stream->ds_packet_size); + GST_DEBUG ("gst_buffer_get_size (scrambled_buffer) = %" G_GSIZE_FORMAT, + gst_buffer_get_size (scrambled_buffer)); + sub_buffer = + gst_buffer_copy_region (scrambled_buffer, GST_BUFFER_COPY_MEMORY, + idx * stream->ds_chunk_size, stream->ds_chunk_size); + if (!offset) { + descrambled_buffer = sub_buffer; + } else { + descrambled_buffer = gst_buffer_append (descrambled_buffer, sub_buffer); + } + } + + GST_BUFFER_TIMESTAMP (descrambled_buffer) = + GST_BUFFER_TIMESTAMP (scrambled_buffer); + GST_BUFFER_DURATION (descrambled_buffer) = + GST_BUFFER_DURATION (scrambled_buffer); + GST_BUFFER_OFFSET (descrambled_buffer) = GST_BUFFER_OFFSET (scrambled_buffer); + GST_BUFFER_OFFSET_END (descrambled_buffer) = + GST_BUFFER_OFFSET_END (scrambled_buffer); + + /* FIXME/CHECK: do we need to transfer buffer flags here too? */ + + gst_buffer_unref (scrambled_buffer); + *p_buffer = descrambled_buffer; +} + +static gboolean +gst_asf_demux_element_send_event (GstElement * element, GstEvent * event) +{ + GstASFDemux *demux = GST_ASF_DEMUX (element); + gint i; + + GST_DEBUG ("handling element event of type %s", GST_EVENT_TYPE_NAME (event)); + + for (i = 0; i < demux->num_streams; ++i) { + gst_event_ref (event); + if (gst_asf_demux_handle_src_event (demux->stream[i].pad, + GST_OBJECT_CAST (element), event)) { + gst_event_unref (event); + return TRUE; + } + } + + gst_event_unref (event); + return FALSE; +} + +/* takes ownership of the passed event */ +static gboolean +gst_asf_demux_send_event_unlocked (GstASFDemux * demux, GstEvent * event) +{ + gboolean ret = TRUE; + gint i; + + GST_DEBUG_OBJECT (demux, "sending %s event to all source pads", + GST_EVENT_TYPE_NAME (event)); + + for (i = 0; i < demux->num_streams; ++i) { + gst_event_ref (event); + ret &= gst_pad_push_event (demux->stream[i].pad, event); + } + gst_event_unref (event); + return ret; +} + +static gboolean +gst_asf_demux_handle_src_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstASFDemux *demux; + gboolean res = FALSE; + + demux = GST_ASF_DEMUX (parent); + + GST_DEBUG ("handling %s query", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + + if (format != GST_FORMAT_TIME) { + GST_LOG ("only support duration queries in TIME format"); + break; + } + + res = gst_pad_query_default (pad, parent, query); + if (!res) { + GST_OBJECT_LOCK (demux); + + if (demux->segment.duration != GST_CLOCK_TIME_NONE) { + GST_LOG ("returning duration: %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->segment.duration)); + + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->segment.duration); + + res = TRUE; + } else { + GST_LOG ("duration not known yet"); + } + + GST_OBJECT_UNLOCK (demux); + } + break; + } + + case GST_QUERY_POSITION:{ + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + if (format != GST_FORMAT_TIME) { + GST_LOG ("only support position queries in TIME format"); + break; + } + + GST_OBJECT_LOCK (demux); + + if (demux->segment.position != GST_CLOCK_TIME_NONE) { + GST_LOG ("returning position: %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->segment.position)); + + gst_query_set_position (query, GST_FORMAT_TIME, + demux->segment.position); + + res = TRUE; + } else { + GST_LOG ("position not known yet"); + } + + GST_OBJECT_UNLOCK (demux); + break; + } + + case GST_QUERY_SEEKING:{ + GstFormat format; + + gst_query_parse_seeking (query, &format, NULL, NULL, NULL); + if (format == GST_FORMAT_TIME) { + gint64 duration; + + GST_OBJECT_LOCK (demux); + duration = demux->segment.duration; + GST_OBJECT_UNLOCK (demux); + + if (!demux->streaming || !demux->seekable) { + gst_query_set_seeking (query, GST_FORMAT_TIME, demux->seekable, 0, + duration); + res = TRUE; + } else { + GstFormat fmt; + gboolean seekable; + + /* try downstream first in TIME */ + res = gst_pad_query_default (pad, parent, query); + + gst_query_parse_seeking (query, &fmt, &seekable, NULL, NULL); + GST_LOG_OBJECT (demux, "upstream %s seekable %d", + GST_STR_NULL (gst_format_get_name (fmt)), seekable); + /* if no luck, maybe in BYTES */ + if (!seekable || fmt != GST_FORMAT_TIME) { + GstQuery *q; + + q = gst_query_new_seeking (GST_FORMAT_BYTES); + if ((res = gst_pad_peer_query (demux->sinkpad, q))) { + gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL); + GST_LOG_OBJECT (demux, "upstream %s seekable %d", + GST_STR_NULL (gst_format_get_name (fmt)), seekable); + if (fmt != GST_FORMAT_BYTES) + seekable = FALSE; + } + gst_query_unref (q); + gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, + duration); + res = TRUE; + } + } + } else + GST_LOG_OBJECT (demux, "only support seeking in TIME format"); + break; + } + + case GST_QUERY_LATENCY: + { + gboolean live; + GstClockTime min, max; + + /* preroll delay does not matter in non-live pipeline, + * but we might end up in a live (rtsp) one ... */ + + /* first forward */ + res = gst_pad_query_default (pad, parent, query); + if (!res) + break; + + gst_query_parse_latency (query, &live, &min, &max); + + GST_DEBUG_OBJECT (demux, "Peer latency: live %d, min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, live, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + GST_OBJECT_LOCK (demux); + if (min != -1) + min += demux->latency; + if (max != -1) + max += demux->latency; + GST_OBJECT_UNLOCK (demux); + + gst_query_set_latency (query, live, min, max); + break; + } + case GST_QUERY_SEGMENT: + { + GstFormat format; + gint64 start, stop; + + format = demux->segment.format; + + start = + gst_segment_to_stream_time (&demux->segment, format, + demux->segment.start); + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + else + stop = gst_segment_to_stream_time (&demux->segment, format, stop); + + gst_query_set_segment (query, demux->segment.rate, format, start, stop); + res = TRUE; + break; + } + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + + return res; +} + +static GstStateChangeReturn +gst_asf_demux_change_state (GstElement * element, GstStateChange transition) +{ + GstASFDemux *demux = GST_ASF_DEMUX (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY:{ + gst_segment_init (&demux->segment, GST_FORMAT_TIME); + demux->need_newsegment = TRUE; + demux->segment_running = FALSE; + demux->accurate = FALSE; + demux->adapter = gst_adapter_new (); + demux->metadata = gst_caps_new_empty (); + demux->global_metadata = gst_structure_new_empty ("metadata"); + demux->data_size = 0; + demux->data_offset = 0; + demux->index_offset = 0; + demux->base_offset = 0; + demux->flowcombiner = gst_flow_combiner_new (); + break; + } + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_asf_demux_reset (demux, FALSE); + break; + + case GST_STATE_CHANGE_READY_TO_NULL: + gst_asf_demux_reset (demux, FALSE); + gst_flow_combiner_free (demux->flowcombiner); + demux->flowcombiner = NULL; + break; + default: + break; + } + + return ret; +} diff --git a/gst/asfdemux/gstasfdemux.h b/gst/asfdemux/gstasfdemux.h new file mode 100644 index 0000000..46e1e13 --- /dev/null +++ b/gst/asfdemux/gstasfdemux.h @@ -0,0 +1,223 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __ASF_DEMUX_H__ +#define __ASF_DEMUX_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/base/gstflowcombiner.h> + +#include "asfheaders.h" + +G_BEGIN_DECLS + +#define GST_TYPE_ASF_DEMUX \ + (gst_asf_demux_get_type()) +#define GST_ASF_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_DEMUX,GstASFDemux)) +#define GST_ASF_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_DEMUX,GstASFDemuxClass)) +#define GST_IS_ASF_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_DEMUX)) +#define GST_IS_ASF_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_DEMUX)) + +GST_DEBUG_CATEGORY_EXTERN (asfdemux_dbg); +#define GST_CAT_DEFAULT asfdemux_dbg + +typedef struct _GstASFDemux GstASFDemux; +typedef struct _GstASFDemuxClass GstASFDemuxClass; + +typedef struct { + guint32 packet; + guint16 count; +} AsfSimpleIndexEntry; + +typedef struct { + AsfPayloadExtensionID id : 16; /* extension ID; the :16 makes sure the + * struct gets packed into 4 bytes */ + guint16 len; /* save this so we can skip unknown IDs */ +} AsfPayloadExtension; + +typedef struct +{ + gboolean valid; /* TRUE if structure is valid/filled */ + + GstClockTime start_time; + GstClockTime end_time; + GstClockTime avg_time_per_frame; + guint32 data_bitrate; + guint32 buffer_size; + guint32 intial_buf_fullness; + guint32 data_bitrate2; + guint32 buffer_size2; + guint32 intial_buf_fullness2; + guint32 max_obj_size; + guint32 flags; + guint16 lang_idx; + + /* may be NULL if there are no extensions; otherwise, terminated by + * an AsfPayloadExtension record with len 0 */ + AsfPayloadExtension *payload_extensions; + + /* missing: stream names */ +} AsfStreamExtProps; + +typedef struct +{ + AsfStreamType type; + + gboolean active; /* if the stream has been activated (pad added) */ + + GstPad *pad; + guint16 id; + + /* video-only */ + gboolean is_video; + gboolean fps_known; + + GstCaps *caps; + + GstTagList *pending_tags; + + gboolean discont; + + /* Descrambler settings */ + guint8 span; + guint16 ds_packet_size; + guint16 ds_chunk_size; + guint16 ds_data_size; + + /* for new parsing code */ + GArray *payloads; /* pending payloads */ + + /* Video stream PAR & interlacing */ + guint8 par_x; + guint8 par_y; + gboolean interlaced; + + /* extended stream properties (optional) */ + AsfStreamExtProps ext_props; + + gboolean inspect_payload; +} AsfStream; + +typedef enum { + GST_ASF_DEMUX_STATE_HEADER, + GST_ASF_DEMUX_STATE_DATA, + GST_ASF_DEMUX_STATE_INDEX +} GstASFDemuxState; + +#define GST_ASF_DEMUX_NUM_VIDEO_PADS 16 +#define GST_ASF_DEMUX_NUM_AUDIO_PADS 32 +#define GST_ASF_DEMUX_NUM_STREAMS 32 +#define GST_ASF_DEMUX_NUM_STREAM_IDS 127 + +struct _GstASFDemux { + GstElement element; + + GstPad *sinkpad; + + gboolean have_group_id; + guint group_id; + + GstAdapter *adapter; + GstTagList *taglist; + GstASFDemuxState state; + + /* byte offset where the asf starts, which might not be zero on chained + * asfs, index_offset and data_offset already are 'offseted' by base_offset */ + guint64 base_offset; + + guint64 index_offset; /* byte offset where index might be, or 0 */ + guint64 data_offset; /* byte offset where packets start */ + guint64 data_size; /* total size of packet data in bytes, or 0 */ + guint64 num_packets; /* total number of data packets, or 0 */ + gint64 packet; /* current packet */ + guint speed_packets; /* Known number of packets to get in one go*/ + + gchar **languages; + guint num_languages; + + GstCaps *metadata; /* metadata, for delayed parsing; one + * structure ('stream-N') per stream */ + GstStructure *global_metadata; /* metadata which isn't specific to one stream */ + GSList *ext_stream_props; /* for delayed processing (buffers) */ + GSList *mut_ex_streams; /* mutually exclusive streams */ + + guint32 num_audio_streams; + guint32 num_video_streams; + guint32 num_streams; + AsfStream stream[GST_ASF_DEMUX_NUM_STREAMS]; + gboolean activated_streams; + GstFlowCombiner *flowcombiner; + + /* for chained asf handling, we need to hold the old asf streams until + * we detect the new ones */ + AsfStream old_stream[GST_ASF_DEMUX_NUM_STREAMS]; + gboolean old_num_streams; + + GstClockTime first_ts; /* smallest timestamp found */ + + guint32 packet_size; + guint64 play_time; + + guint64 preroll; + + gboolean seekable; + gboolean broadcast; + + GstSegment segment; /* configured play segment */ + gboolean accurate; + + gboolean need_newsegment; /* do we need to send a new-segment event? */ + guint32 segment_seqnum; /* if the new segment must have this seqnum */ + GstClockTime segment_ts; /* streaming; timestamp for segment start */ + GstSegment in_segment; /* streaming; upstream segment info */ + GstClockTime in_gap; /* streaming; upstream initial segment gap for interpolation */ + gboolean segment_running; /* if we've started the current segment */ + gboolean streaming; /* TRUE if we are operating chain-based */ + GstClockTime latency; + + /* for debugging only */ + gchar *objpath; + + /* simple index, if available */ + GstClockTime sidx_interval; /* interval between entries in ns */ + guint sidx_num_entries; /* number of index entries */ + AsfSimpleIndexEntry *sidx_entries; /* packet number for each entry */ + + GSList *other_streams; /* remember streams that are in header but have unknown type */ +}; + +struct _GstASFDemuxClass { + GstElementClass parent_class; +}; + +GType gst_asf_demux_get_type (void); + +AsfStream * gst_asf_demux_get_stream (GstASFDemux * demux, guint16 id); + +gboolean gst_asf_demux_is_unknown_stream(GstASFDemux *demux, guint stream_num); + +G_END_DECLS + +#endif /* __ASF_DEMUX_H__ */ diff --git a/gst/asfdemux/gstrtpasfdepay.c b/gst/asfdemux/gstrtpasfdepay.c new file mode 100644 index 0000000..1ba5d02 --- /dev/null +++ b/gst/asfdemux/gstrtpasfdepay.c @@ -0,0 +1,545 @@ +/* GStreamer RTP ASF depayloader + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * 2009 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstrtpasfdepay.h" +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include <stdlib.h> + +GST_DEBUG_CATEGORY_STATIC (rtpasfdepayload_debug); +#define GST_CAT_DEFAULT rtpasfdepayload_debug + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf") + ); + +/* Other parameters: config, maxps */ +#define SINK_CAPS \ + "application/x-rtp, " \ + "media = (string) { \"application\", \"video\", \"audio\" }, " \ + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " \ + "clock-rate = (int) [1, MAX ], " \ + "encoding-name = (string) \"X-ASF-PF\"" + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS) + ); + +#define gst_rtp_asf_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpAsfDepay, gst_rtp_asf_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_asf_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_asf_depay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_asf_depay_setcaps (GstRTPBaseDepayload * depay, + GstCaps * caps); +static GstBuffer *gst_rtp_asf_depay_process (GstRTPBaseDepayload * basedepay, + GstBuffer * buf); + +static void +gst_rtp_asf_depay_class_init (GstRtpAsfDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_factory)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP ASF packet depayloader", "Codec/Depayloader/Network", + "Extracts ASF streams from RTP", + "Tim-Philipp Müller <tim centricular net>, " + "Wim Taymans <wim.taymans@gmail.com>"); + + gobject_class->finalize = gst_rtp_asf_depay_finalize; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_change_state); + + gstrtpbasedepayload_class->set_caps = + GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_setcaps); + gstrtpbasedepayload_class->process = + GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_process); + + GST_DEBUG_CATEGORY_INIT (rtpasfdepayload_debug, "rtpasfdepayload", 0, + "RTP asf depayloader element"); +} + +static void +gst_rtp_asf_depay_init (GstRtpAsfDepay * depay) +{ + depay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_asf_depay_finalize (GObject * object) +{ + GstRtpAsfDepay *depay; + + depay = GST_RTP_ASF_DEPAY (object); + + g_object_unref (depay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const guint8 asf_marker[16] = { 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, + 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c +}; + +static gboolean +gst_rtp_asf_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstRtpAsfDepay *depay; + GstStructure *s; + const gchar *config_str, *ps_string; + GstBuffer *buf; + GstCaps *src_caps; + guint8 *headers; + gsize headers_len; + gint clock_rate; + + depay = GST_RTP_ASF_DEPAY (depayload); + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "clock-rate", &clock_rate) || clock_rate < 0) + clock_rate = 1000; + depayload->clock_rate = clock_rate; + + /* config contains the asf headers in base64 coding */ + config_str = gst_structure_get_string (s, "config"); + if (config_str == NULL || *config_str == '\0') + goto no_config; + + ps_string = gst_structure_get_string (s, "maxps"); + if (ps_string == NULL || *ps_string == '\0') + goto no_packetsize; + + if (depay->packet_size) { + /* header sent again following seek; + * discard to avoid confusing upstream */ + if (depay->packet_size == atoi (ps_string)) { + goto duplicate_header; + } else { + /* since we should fiddle with downstream state to handle this */ + goto refuse_renegotiation; + } + } else + depay->packet_size = atoi (ps_string); + if (depay->packet_size <= 16) + goto invalid_packetsize; + + headers = (guint8 *) g_base64_decode (config_str, &headers_len); + + if (headers == NULL || headers_len < 16 + || memcmp (headers, asf_marker, 16) != 0) + goto invalid_headers; + + src_caps = gst_caps_new_empty_simple ("video/x-ms-asf"); + gst_pad_set_caps (depayload->srcpad, src_caps); + gst_caps_unref (src_caps); + + buf = gst_buffer_new (); + gst_buffer_append_memory (buf, + gst_memory_new_wrapped (0, headers, headers_len, 0, headers_len, headers, + g_free)); + + gst_rtp_base_depayload_push (depayload, buf); + + return TRUE; + + /* ERRORS */ +no_config: + { + GST_WARNING_OBJECT (depay, "caps without 'config' field with asf headers"); + return FALSE; + } +no_packetsize: + { + GST_WARNING_OBJECT (depay, "caps without 'maxps' (packet size) field"); + return FALSE; + } +invalid_packetsize: + { + GST_WARNING_OBJECT (depay, "packet size %u invalid", depay->packet_size); + return FALSE; + } +invalid_headers: + { + GST_WARNING_OBJECT (depay, "headers don't look like valid ASF headers"); + g_free (headers); + return FALSE; + } +duplicate_header: + { + GST_DEBUG_OBJECT (depayload, "discarding duplicate header"); + return TRUE; + } +refuse_renegotiation: + { + GST_WARNING_OBJECT (depayload, "cannot renegotiate to different header"); + return FALSE; + } +} + +static gint +field_size (guint8 field) +{ + switch (field) { + /* DWORD - 32 bits */ + case 3: + return 4; + + /* WORD - 16 bits */ + case 2: + return 2; + + /* BYTE - 8 bits */ + case 1: + return 1; + + /* non-exitent */ + case 0: + default: + return 0; + } +} + +/* Set the padding field to te correct value as the spec + * says it should be se to 0 in the rtp packets + */ +static GstBuffer * +gst_rtp_asf_depay_update_padding (GstRtpAsfDepay * depayload, GstBuffer * buf) +{ + GstBuffer *result; + GstMapInfo map; + guint8 *data; + gint offset = 0; + guint8 aux; + guint8 seq_type; + guint8 pad_type; + guint8 pkt_type; + gsize plen, padding; + + plen = gst_buffer_get_size (buf); + if (plen == depayload->packet_size) + return buf; + + padding = depayload->packet_size - plen; + + GST_LOG_OBJECT (depayload, + "padding buffer size %" G_GSIZE_FORMAT " to packet size %d", plen, + depayload->packet_size); + + result = gst_buffer_new_and_alloc (depayload->packet_size); + + gst_buffer_map (result, &map, GST_MAP_READ); + data = map.data; + memset (data + plen, 0, padding); + + gst_buffer_extract (buf, 0, data, plen); + gst_buffer_unref (buf); + + aux = data[offset++]; + if (aux & 0x80) { + guint8 err_len = 0; + if (aux & 0x60) { + GST_WARNING_OBJECT (depayload, "Error correction length type should be " + "set to 0"); + /* this packet doesn't follow the spec */ + gst_buffer_unmap (result, &map); + return result; + } + err_len = aux & 0x0F; + offset += err_len; + + aux = data[offset++]; + } + seq_type = (aux >> 1) & 0x3; + pad_type = (aux >> 3) & 0x3; + pkt_type = (aux >> 5) & 0x3; + + offset += 1; /* skip property flags */ + offset += field_size (pkt_type); /* skip packet length */ + offset += field_size (seq_type); /* skip sequence field */ + + /* write padding */ + switch (pad_type) { + /* DWORD */ + case 3: + GST_WRITE_UINT32_LE (&(data[offset]), padding); + break; + + /* WORD */ + case 2: + GST_WRITE_UINT16_LE (&(data[offset]), padding); + break; + + /* BYTE */ + case 1: + data[offset] = (guint8) padding; + break; + + /* non-existent */ + case 0: + default: + break; + } + gst_buffer_unmap (result, &map); + + return result; +} + +/* Docs: 'RTSP Protocol PDF' document from http://sdp.ppona.com/ (page 8) */ + +static GstBuffer * +gst_rtp_asf_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpAsfDepay *depay; + const guint8 *payload; + GstBuffer *outbuf; + gboolean S, L, R, D, I; + guint payload_len, hdr_len, offset; + guint len_offs; + GstClockTime timestamp; + GstRTPBuffer rtpbuf = { NULL }; + + depay = GST_RTP_ASF_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_LOG_OBJECT (depay, "got DISCONT"); + gst_adapter_clear (depay->adapter); + depay->discont = TRUE; + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf); + timestamp = GST_BUFFER_TIMESTAMP (buf); + + payload_len = gst_rtp_buffer_get_payload_len (&rtpbuf); + payload = gst_rtp_buffer_get_payload (&rtpbuf); + offset = 0; + + GST_LOG_OBJECT (depay, "got payload len of %u", payload_len); + + do { + guint packet_len; + + /* packet header is at least 4 bytes */ + if (payload_len < 4) + goto too_small; + + /* 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S|L|R|D|I|RES | Length/Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Relative Timestamp (optional) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Duration (optional) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | LocationId (optional) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * S: packet contains a keyframe. + * L: If 1, Length/Offset contains length, else contains the byte offset + * of the fragment's first byte counted from the beginning of the + * complete ASF data packet. + * R: relative timestamp present + * D: duration present + * I: locationid present + */ + + S = ((payload[0] & 0x80) != 0); + L = ((payload[0] & 0x40) != 0); + R = ((payload[0] & 0x20) != 0); + D = ((payload[0] & 0x10) != 0); + I = ((payload[0] & 0x08) != 0); + + hdr_len = 4; + + len_offs = (payload[1] << 16) | (payload[2] << 8) | payload[3]; + + if (R) { + GST_DEBUG ("Relative timestamp field present : %u", + GST_READ_UINT32_BE (payload + hdr_len)); + hdr_len += 4; + } + if (D) { + GST_DEBUG ("Duration field present : %u", + GST_READ_UINT32_BE (payload + hdr_len)); + hdr_len += 4; + } + if (I) { + GST_DEBUG ("LocationId field present : %u", + GST_READ_UINT32_BE (payload + hdr_len)); + hdr_len += 4; + } + + GST_LOG_OBJECT (depay, "S %d, L %d, R %d, D %d, I %d", S, L, R, D, I); + GST_LOG_OBJECT (depay, "payload_len:%d, hdr_len:%d, len_offs:%d", + payload_len, hdr_len, len_offs); + + if (payload_len < hdr_len) + goto too_small; + + /* skip headers */ + payload_len -= hdr_len; + payload += hdr_len; + offset += hdr_len; + + if (L) { + /* L bit set, len contains the length of the packet */ + packet_len = len_offs; + } else { + /* else it contains an offset which we don't handle yet */ + GST_LOG_OBJECT (depay, "We have a fragmented packet"); + packet_len = payload_len; + } + + if (packet_len > payload_len) + packet_len = payload_len; + + GST_LOG_OBJECT (depay, "packet len %u, payload len %u, packet_size:%u", + packet_len, payload_len, depay->packet_size); + + if (!L) { + guint available; + GstBuffer *sub; + + /* Fragmented packet handling */ + outbuf = NULL; + + if (len_offs == (available = gst_adapter_available (depay->adapter))) { + /* fragment aligns with what we have, add it */ + GST_LOG_OBJECT (depay, "collecting fragment"); + sub = + gst_rtp_buffer_get_payload_subbuffer (&rtpbuf, offset, packet_len); + gst_adapter_push (depay->adapter, sub); + /* RTP marker bit M is set if this is last fragment */ + if (gst_rtp_buffer_get_marker (&rtpbuf)) { + GST_LOG_OBJECT (depay, "last fragment, assembling packet"); + outbuf = + gst_adapter_take_buffer (depay->adapter, available + packet_len); + } + } else { + if (available) { + GST_WARNING_OBJECT (depay, "Offset doesn't match previous data?!"); + GST_DEBUG_OBJECT (depay, "clearing for re-sync"); + gst_adapter_clear (depay->adapter); + } else + GST_DEBUG_OBJECT (depay, "waiting for start of packet"); + } + } else { + GST_LOG_OBJECT (depay, "collecting packet"); + outbuf = + gst_rtp_buffer_get_payload_subbuffer (&rtpbuf, offset, packet_len); + } + + /* If we haven't completed a full ASF packet, return */ + if (!outbuf) + return NULL; + + outbuf = gst_rtp_asf_depay_update_padding (depay, outbuf); + + if (!S) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + + if (depay->discont) { + GST_LOG_OBJECT (depay, "setting DISCONT"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + depay->discont = FALSE; + } + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + gst_rtp_base_depayload_push (depayload, outbuf); + + /* only apply the timestamp to the first buffer of this packet */ + timestamp = -1; + + /* skip packet data */ + payload += packet_len; + offset += packet_len; + payload_len -= packet_len; + } while (payload_len > 0); + + gst_rtp_buffer_unmap (&rtpbuf); + + return NULL; + +/* ERRORS */ +too_small: + { + gst_rtp_buffer_unmap (&rtpbuf); + GST_WARNING_OBJECT (depayload, "Payload too small, expected at least 4 " + "bytes for header, but got only %d bytes", payload_len); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_asf_depay_change_state (GstElement * element, GstStateChange trans) +{ + GstStateChangeReturn ret; + GstRtpAsfDepay *depay; + + depay = GST_RTP_ASF_DEPAY (element); + + switch (trans) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (depay->adapter); + depay->discont = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans); + + switch (trans) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_adapter_clear (depay->adapter); + break; + default: + break; + } + + return ret; +} diff --git a/gst/asfdemux/gstrtpasfdepay.h b/gst/asfdemux/gstrtpasfdepay.h new file mode 100644 index 0000000..8388c8a --- /dev/null +++ b/gst/asfdemux/gstrtpasfdepay.h @@ -0,0 +1,64 @@ +/* GStreamer RTP ASF depayloader + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * 2009 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_ASF_DEPAY_H__ +#define __GST_RTP_ASF_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_ASF_DEPAY \ + (gst_rtp_asf_depay_get_type()) +#define GST_RTP_ASF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ASF_DEPAY,GstRtpAsfDepay)) +#define GST_RTP_ASF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ASF_DEPAY,GstRtpAsfDepayClass)) +#define GST_IS_RTP_ASF_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ASF_DEPAY)) +#define GST_IS_RTP_ASF_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ASF_DEPAY)) + +typedef struct _GstRtpAsfDepay GstRtpAsfDepay; +typedef struct _GstRtpAsfDepayClass GstRtpAsfDepayClass; + +struct _GstRtpAsfDepay +{ + GstRTPBaseDepayload depayload; + + guint packet_size; + + GstAdapter *adapter; + gboolean discont; +}; + +struct _GstRtpAsfDepayClass +{ + GstRTPBaseDepayloadClass depayload_class; +}; + +GType gst_rtp_asf_depay_get_type (void); + +G_END_DECLS + +#endif /* __GST_RTP_ASF_DEPAY_H__ */ diff --git a/gst/asfdemux/gstrtspwms.c b/gst/asfdemux/gstrtspwms.c new file mode 100644 index 0000000..c864287 --- /dev/null +++ b/gst/asfdemux/gstrtspwms.c @@ -0,0 +1,236 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* Element-Checklist-Version: 5 */ + +/** + * SECTION:element-rtspwms + * + * A WMS RTSP extension + */ + +#include <string.h> + +#include <gst/rtsp/gstrtspextension.h> + +#include "gstrtspwms.h" + +GST_DEBUG_CATEGORY_STATIC (rtspwms_debug); +#define GST_CAT_DEFAULT (rtspwms_debug) + +#define SERVER_PREFIX "WMServer/" +#define HEADER_PREFIX "data:application/vnd.ms.wms-hdr.asfv1;base64," +#define EXTENSION_CMD "application/x-wms-extension-cmd" + +static GstRTSPResult +gst_rtsp_wms_before_send (GstRTSPExtension * ext, GstRTSPMessage * request) +{ + GstRTSPWMS *ctx = (GstRTSPWMS *) ext; + + GST_DEBUG_OBJECT (ext, "before send"); + + switch (request->type_data.request.method) { + case GST_RTSP_OPTIONS: + { + /* activate ourselves with the first request */ + ctx->active = TRUE; + break; + } + default: + break; + } + return GST_RTSP_OK; +} + +static GstRTSPResult +gst_rtsp_wms_after_send (GstRTSPExtension * ext, GstRTSPMessage * req, + GstRTSPMessage * resp) +{ + GstRTSPWMS *ctx = (GstRTSPWMS *) ext; + + GST_DEBUG_OBJECT (ext, "after send"); + + switch (req->type_data.request.method) { + case GST_RTSP_OPTIONS: + { + gchar *server = NULL; + + gst_rtsp_message_get_header (resp, GST_RTSP_HDR_SERVER, &server, 0); + if (server && g_str_has_prefix (server, SERVER_PREFIX)) + ctx->active = TRUE; + else + ctx->active = FALSE; + break; + } + default: + break; + } + return GST_RTSP_OK; +} + + +static GstRTSPResult +gst_rtsp_wms_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, + GstStructure * props) +{ + const gchar *config, *maxps; + gint i; + GstRTSPWMS *ctx = (GstRTSPWMS *) ext; + + if (!ctx->active) + return GST_RTSP_OK; + + for (i = 0; (config = gst_sdp_message_get_attribute_val_n (sdp, "pgmpu", i)); + i++) { + if (g_str_has_prefix (config, HEADER_PREFIX)) { + config += strlen (HEADER_PREFIX); + gst_structure_set (props, "config", G_TYPE_STRING, config, NULL); + break; + } + } + if (config == NULL) + goto no_config; + + gst_structure_set (props, "config", G_TYPE_STRING, config, NULL); + + maxps = gst_sdp_message_get_attribute_val (sdp, "maxps"); + if (maxps) + gst_structure_set (props, "maxps", G_TYPE_STRING, maxps, NULL); + + gst_structure_set (props, "encoding-name", G_TYPE_STRING, "X-ASF-PF", NULL); + gst_structure_set (props, "media", G_TYPE_STRING, "application", NULL); + + return GST_RTSP_OK; + + /* ERRORS */ +no_config: + { + GST_DEBUG_OBJECT (ctx, "Could not find config SDP field, deactivating."); + ctx->active = FALSE; + return GST_RTSP_OK; + } +} + +static gboolean +gst_rtsp_wms_configure_stream (GstRTSPExtension * ext, GstCaps * caps) +{ + GstRTSPWMS *ctx; + GstStructure *s; + const gchar *encoding; + + ctx = (GstRTSPWMS *) ext; + s = gst_caps_get_structure (caps, 0); + encoding = gst_structure_get_string (s, "encoding-name"); + + if (!encoding) + return TRUE; + + GST_DEBUG_OBJECT (ctx, "%" GST_PTR_FORMAT " encoding-name: %s", caps, + encoding); + + /* rtx streams do not need to be configured */ + if (!strcmp (encoding, "X-WMS-RTX")) + return FALSE; + + return TRUE; +} + +static GstRTSPResult +gst_rtsp_wms_receive_request (GstRTSPExtension * ext, GstRTSPMessage * request) +{ + GstRTSPWMS *ctx; + GstRTSPResult res = GST_RTSP_ENOTIMPL; + GstRTSPMessage response = { 0 }; + + ctx = (GstRTSPWMS *) ext; + + GST_DEBUG_OBJECT (ext, "before send"); + + switch (request->type_data.request.method) { + case GST_RTSP_SET_PARAMETER: + { + gchar *content_type = NULL; + + gst_rtsp_message_get_header (request, GST_RTSP_HDR_CONTENT_TYPE, + &content_type, 0); + + if (content_type && !g_ascii_strcasecmp (content_type, EXTENSION_CMD)) { + /* parse the command */ + + /* default implementation, send OK */ + res = gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, "OK", + request); + if (res < 0) + goto send_error; + + GST_DEBUG_OBJECT (ctx, "replying with OK"); + + /* send reply */ + if ((res = gst_rtsp_extension_send (ext, request, &response)) < 0) + goto send_error; + + res = GST_RTSP_EEOF; + } + break; + } + default: + break; + } + return res; + +send_error: + { + return res; + } +} + +static void gst_rtsp_wms_extension_init (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (GstRTSPWMS, gst_rtsp_wms, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_RTSP_EXTENSION, + gst_rtsp_wms_extension_init)); + +static void +gst_rtsp_wms_class_init (GstRTSPWMSClass * g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + GST_DEBUG_CATEGORY_INIT (rtspwms_debug, "rtspwms", 0, "WMS RTSP extension"); + + gst_element_class_set_static_metadata (element_class, "WMS RTSP Extension", + "Network/Extension/Protocol", + "Extends RTSP so that it can handle WMS setup", + "Wim Taymans <wim.taymans@gmail.com>"); +} + +static void +gst_rtsp_wms_init (GstRTSPWMS * rtspwms) +{ +} + +static void +gst_rtsp_wms_extension_init (gpointer g_iface, gpointer iface_data) +{ + GstRTSPExtensionInterface *iface = (GstRTSPExtensionInterface *) g_iface; + + iface->parse_sdp = gst_rtsp_wms_parse_sdp; + iface->before_send = gst_rtsp_wms_before_send; + iface->after_send = gst_rtsp_wms_after_send; + iface->configure_stream = gst_rtsp_wms_configure_stream; + iface->receive_request = gst_rtsp_wms_receive_request; +} diff --git a/gst/asfdemux/gstrtspwms.h b/gst/asfdemux/gstrtspwms.h new file mode 100644 index 0000000..feb8c43 --- /dev/null +++ b/gst/asfdemux/gstrtspwms.h @@ -0,0 +1,50 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTSP_WMS_H__ +#define __GST_RTSP_WMS_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTSP_WMS (gst_rtsp_wms_get_type()) +#define GST_IS_RTSP_WMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSP_WMS)) +#define GST_IS_RTSP_WMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSP_WMS)) +#define GST_RTSP_WMS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSP_WMS, GstRTSPWMS)) +#define GST_RTSP_WMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSP_WMS, GstRTSPWMSClass)) + +typedef struct _GstRTSPWMS GstRTSPWMS; +typedef struct _GstRTSPWMSClass GstRTSPWMSClass; + +struct _GstRTSPWMS { + GstElement element; + + gboolean active; +}; + +struct _GstRTSPWMSClass { + GstElementClass parent_class; +}; + +GType gst_rtsp_wms_get_type(void); + +G_END_DECLS + +#endif /* __GST_RTSP_WMS_H__ */ diff --git a/gst/dvdlpcmdec/Makefile.am b/gst/dvdlpcmdec/Makefile.am new file mode 100644 index 0000000..fcee1e4 --- /dev/null +++ b/gst/dvdlpcmdec/Makefile.am @@ -0,0 +1,24 @@ + +plugin_LTLIBRARIES = libgstdvdlpcmdec.la + +libgstdvdlpcmdec_la_SOURCES = gstdvdlpcmdec.c +libgstdvdlpcmdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstdvdlpcmdec_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ $(GST_LIBS) +libgstdvdlpcmdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstdvdlpcmdec_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstdvdlpcmdec.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstdvdlpcmdec -:SHARED libgstdvdlpcmdec \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstdvdlpcmdec_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdvdlpcmdec_la_CFLAGS) \ + -:LDFLAGS $(libgstdvdlpcmdec_la_LDFLAGS) \ + $(libgstdvdlpcmdec_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/dvdlpcmdec/Makefile.in b/gst/dvdlpcmdec/Makefile.in new file mode 100644 index 0000000..d20e1fd --- /dev/null +++ b/gst/dvdlpcmdec/Makefile.in @@ -0,0 +1,839 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +subdir = gst/dvdlpcmdec +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstdvdlpcmdec_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libgstdvdlpcmdec_la_OBJECTS = libgstdvdlpcmdec_la-gstdvdlpcmdec.lo +libgstdvdlpcmdec_la_OBJECTS = $(am_libgstdvdlpcmdec_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstdvdlpcmdec_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstdvdlpcmdec_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(libgstdvdlpcmdec_la_CFLAGS) $(CFLAGS) \ + $(libgstdvdlpcmdec_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +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_CCLD_1 = +SOURCES = $(libgstdvdlpcmdec_la_SOURCES) +DIST_SOURCES = $(libgstdvdlpcmdec_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstdvdlpcmdec.la +libgstdvdlpcmdec_la_SOURCES = gstdvdlpcmdec.c +libgstdvdlpcmdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstdvdlpcmdec_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ $(GST_LIBS) +libgstdvdlpcmdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstdvdlpcmdec_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = gstdvdlpcmdec.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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 gst/dvdlpcmdec/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/dvdlpcmdec/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstdvdlpcmdec.la: $(libgstdvdlpcmdec_la_OBJECTS) $(libgstdvdlpcmdec_la_DEPENDENCIES) $(EXTRA_libgstdvdlpcmdec_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstdvdlpcmdec_la_LINK) -rpath $(plugindir) $(libgstdvdlpcmdec_la_OBJECTS) $(libgstdvdlpcmdec_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdvdlpcmdec_la-gstdvdlpcmdec.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +libgstdvdlpcmdec_la-gstdvdlpcmdec.lo: gstdvdlpcmdec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdlpcmdec_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdlpcmdec_la_CFLAGS) $(CFLAGS) -MT libgstdvdlpcmdec_la-gstdvdlpcmdec.lo -MD -MP -MF $(DEPDIR)/libgstdvdlpcmdec_la-gstdvdlpcmdec.Tpo -c -o libgstdvdlpcmdec_la-gstdvdlpcmdec.lo `test -f 'gstdvdlpcmdec.c' || echo '$(srcdir)/'`gstdvdlpcmdec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstdvdlpcmdec_la-gstdvdlpcmdec.Tpo $(DEPDIR)/libgstdvdlpcmdec_la-gstdvdlpcmdec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstdvdlpcmdec.c' object='libgstdvdlpcmdec_la-gstdvdlpcmdec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdlpcmdec_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdlpcmdec_la_CFLAGS) $(CFLAGS) -c -o libgstdvdlpcmdec_la-gstdvdlpcmdec.lo `test -f 'gstdvdlpcmdec.c' || echo '$(srcdir)/'`gstdvdlpcmdec.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstdvdlpcmdec -:SHARED libgstdvdlpcmdec \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstdvdlpcmdec_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdvdlpcmdec_la_CFLAGS) \ + -:LDFLAGS $(libgstdvdlpcmdec_la_LDFLAGS) \ + $(libgstdvdlpcmdec_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/dvdlpcmdec/gstdvdlpcmdec.c b/gst/dvdlpcmdec/gstdvdlpcmdec.c new file mode 100644 index 0000000..03024b7 --- /dev/null +++ b/gst/dvdlpcmdec/gstdvdlpcmdec.c @@ -0,0 +1,863 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Jan Schmidt <jan@noraisin.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* Element-Checklist-Version: TODO */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdlib.h> +#include <string.h> + +#include "gstdvdlpcmdec.h" +#include <gst/audio/audio.h> + +GST_DEBUG_CATEGORY_STATIC (dvdlpcm_debug); +#define GST_CAT_DEFAULT dvdlpcm_debug + +static GstStaticPadTemplate gst_dvdlpcmdec_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-private1-lpcm; " + "audio/x-lpcm, " + "width = (int) { 16, 20, 24 }, " + "rate = (int) { 32000, 44100, 48000, 96000 }, " + "channels = (int) [ 1, 8 ], " + "dynamic_range = (int) [ 0, 255 ], " + "emphasis = (boolean) { TRUE, FALSE }, " + "mute = (boolean) { TRUE, FALSE } ") + ); + +static GstStaticPadTemplate gst_dvdlpcmdec_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) { S16BE, S24BE }, " + "layout = (string) interleaved, " + "rate = (int) { 32000, 44100, 48000, 96000 }, " + "channels = (int) [ 1, 8 ]") + ); + +/* DvdLpcmDec signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0 + /* FILL ME */ +}; + +static void gst_dvdlpcmdec_base_init (gpointer g_class); +static void gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass); +static void gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec); + +static GstFlowReturn gst_dvdlpcmdec_chain_raw (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static GstFlowReturn gst_dvdlpcmdec_chain_dvd (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static gboolean gst_dvdlpcmdec_setcaps (GstPad * pad, GstCaps * caps); +static gboolean dvdlpcmdec_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); + +static GstStateChangeReturn gst_dvdlpcmdec_change_state (GstElement * element, + GstStateChange transition); + +static GstElementClass *parent_class = NULL; + +GType +gst_dvdlpcmdec_get_type (void) +{ + static GType dvdlpcmdec_type = 0; + + if (!dvdlpcmdec_type) { + static const GTypeInfo dvdlpcmdec_info = { + sizeof (GstDvdLpcmDecClass), + gst_dvdlpcmdec_base_init, + NULL, + (GClassInitFunc) gst_dvdlpcmdec_class_init, + NULL, + NULL, + sizeof (GstDvdLpcmDec), + 0, + (GInstanceInitFunc) gst_dvdlpcmdec_init, + }; + + dvdlpcmdec_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstDvdLpcmDec", + &dvdlpcmdec_info, 0); + } + return dvdlpcmdec_type; +} + +static void +gst_dvdlpcmdec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_dvdlpcmdec_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_dvdlpcmdec_src_template)); + gst_element_class_set_static_metadata (element_class, + "DVD LPCM Audio decoder", "Codec/Decoder/Audio", + "Decode DVD LPCM frames into standard PCM audio", + "Jan Schmidt <jan@noraisin.net>, Michael Smith <msmith@fluendo.com>"); +} + +static void +gst_dvdlpcmdec_class_init (GstDvdLpcmDecClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gstelement_class->change_state = gst_dvdlpcmdec_change_state; +} + +static void +gst_dvdlpcm_reset (GstDvdLpcmDec * dvdlpcmdec) +{ + gst_audio_info_init (&dvdlpcmdec->info); + dvdlpcmdec->dynamic_range = 0; + dvdlpcmdec->emphasis = FALSE; + dvdlpcmdec->mute = FALSE; + dvdlpcmdec->timestamp = GST_CLOCK_TIME_NONE; + + dvdlpcmdec->header = 0; + + gst_segment_init (&dvdlpcmdec->segment, GST_FORMAT_UNDEFINED); +} + +static void +gst_dvdlpcmdec_init (GstDvdLpcmDec * dvdlpcmdec) +{ + dvdlpcmdec->sinkpad = + gst_pad_new_from_static_template (&gst_dvdlpcmdec_sink_template, "sink"); + gst_pad_set_event_function (dvdlpcmdec->sinkpad, dvdlpcmdec_sink_event); + gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->sinkpad); + + dvdlpcmdec->srcpad = + gst_pad_new_from_static_template (&gst_dvdlpcmdec_src_template, "src"); + gst_pad_use_fixed_caps (dvdlpcmdec->srcpad); + gst_element_add_pad (GST_ELEMENT (dvdlpcmdec), dvdlpcmdec->srcpad); + + gst_dvdlpcm_reset (dvdlpcmdec); +} + +static const GstAudioChannelPosition channel_positions[][8] = { + {GST_AUDIO_CHANNEL_POSITION_MONO}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_INVALID}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_INVALID} +}; + +static void +gst_dvdlpcmdec_send_tags (GstDvdLpcmDec * dvdlpcmdec) +{ + GstTagList *taglist; + guint bitrate; + gint bpf, rate; + + bpf = GST_AUDIO_INFO_BPF (&dvdlpcmdec->info); + rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info); + + bitrate = bpf * 8 * rate; + + taglist = gst_tag_list_new (GST_TAG_AUDIO_CODEC, "LPCM Audio", + GST_TAG_BITRATE, bitrate, NULL); + + gst_pad_push_event (dvdlpcmdec->srcpad, gst_event_new_tag (taglist)); +} + +static gboolean +gst_dvdlpcmdec_set_outcaps (GstDvdLpcmDec * dvdlpcmdec) +{ + gboolean res = TRUE; + GstCaps *src_caps; + + /* Build caps to set on the src pad, which we know from the incoming caps */ + src_caps = gst_audio_info_to_caps (&dvdlpcmdec->info); + + res = gst_pad_set_caps (dvdlpcmdec->srcpad, src_caps); + if (res) { + GST_DEBUG_OBJECT (dvdlpcmdec, "Successfully set output caps: %" + GST_PTR_FORMAT, src_caps); + + gst_dvdlpcmdec_send_tags (dvdlpcmdec); + } else { + GST_DEBUG_OBJECT (dvdlpcmdec, "Failed to set output caps: %" + GST_PTR_FORMAT, src_caps); + } + + gst_caps_unref (src_caps); + + return res; +} + +static gboolean +gst_dvdlpcmdec_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + gboolean res = TRUE; + GstDvdLpcmDec *dvdlpcmdec; + GstAudioFormat format; + gint rate, channels, width; + const GstAudioChannelPosition *position; + + g_return_val_if_fail (caps != NULL, FALSE); + g_return_val_if_fail (pad != NULL, FALSE); + + dvdlpcmdec = GST_DVDLPCMDEC (gst_pad_get_parent (pad)); + + structure = gst_caps_get_structure (caps, 0); + + /* If we have the DVD structured LPCM (including header) then we wait + * for incoming data before creating the output pad caps */ + if (gst_structure_has_name (structure, "audio/x-private1-lpcm")) { + gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_dvd); + goto done; + } + + gst_pad_set_chain_function (dvdlpcmdec->sinkpad, gst_dvdlpcmdec_chain_raw); + + res &= gst_structure_get_int (structure, "rate", &rate); + res &= gst_structure_get_int (structure, "channels", &channels); + res &= gst_structure_get_int (structure, "width", &width); + res &= gst_structure_get_int (structure, "dynamic_range", + &dvdlpcmdec->dynamic_range); + res &= gst_structure_get_boolean (structure, "emphasis", + &dvdlpcmdec->emphasis); + res &= gst_structure_get_boolean (structure, "mute", &dvdlpcmdec->mute); + + if (!res) + goto caps_parse_error; + + switch (width) { + case 24: + case 20: + format = GST_AUDIO_FORMAT_S24BE; + break; + case 16: + format = GST_AUDIO_FORMAT_S16BE; + break; + default: + format = GST_AUDIO_FORMAT_UNKNOWN; + break; + } + + gst_audio_info_set_format (&dvdlpcmdec->info, format, rate, channels, NULL); + if (channels < 9 + && channel_positions[channels - 1][0] != + GST_AUDIO_CHANNEL_POSITION_INVALID) { + dvdlpcmdec->info.flags &= ~GST_AUDIO_FLAG_UNPOSITIONED; + position = channel_positions[channels - 1]; + dvdlpcmdec->lpcm_layout = position; + memcpy (dvdlpcmdec->info.position, position, + sizeof (GstAudioChannelPosition) * channels); + gst_audio_channel_positions_to_valid_order (dvdlpcmdec->info.position, + channels); + } + + dvdlpcmdec->width = width; + + res = gst_dvdlpcmdec_set_outcaps (dvdlpcmdec); + +done: + gst_object_unref (dvdlpcmdec); + return res; + + /* ERRORS */ +caps_parse_error: + { + GST_DEBUG_OBJECT (dvdlpcmdec, "Couldn't get parameters; missing caps?"); + gst_object_unref (dvdlpcmdec); + return FALSE; + } +} + +static void +update_timestamps (GstDvdLpcmDec * dvdlpcmdec, GstBuffer * buf, int samples) +{ + gboolean take_buf_ts = FALSE; + gint rate; + + rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info); + + GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (samples, GST_SECOND, rate); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + if (GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp)) { + GstClockTimeDiff one_sample = GST_SECOND / rate; + GstClockTimeDiff diff = GST_CLOCK_DIFF (GST_BUFFER_TIMESTAMP (buf), + dvdlpcmdec->timestamp); + + if (diff > one_sample || diff < -one_sample) + take_buf_ts = TRUE; + } else { + take_buf_ts = TRUE; + } + } else if (!GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp)) { + dvdlpcmdec->timestamp = 0; + } + + if (take_buf_ts) { + /* Take buffer timestamp */ + dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf); + } else { + GST_BUFFER_TIMESTAMP (buf) = dvdlpcmdec->timestamp; + } + + dvdlpcmdec->timestamp += GST_BUFFER_DURATION (buf); + + GST_LOG_OBJECT (dvdlpcmdec, "Updated timestamp to %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); +} + +static void +parse_header (GstDvdLpcmDec * dec, guint32 header) +{ + GstAudioFormat format; + gint rate, channels, width; + + /* We don't actually use 'dynamic range', 'mute', or 'emphasis' currently, + * but parse them out */ + dec->dynamic_range = header & 0xff; + + dec->mute = (header & 0x400000) != 0; + dec->emphasis = (header & 0x800000) != 0; + + /* These two bits tell us the bit depth */ + switch (header & 0xC000) { + case 0x8000: + /* 24 bits in 3 bytes */ + format = GST_AUDIO_FORMAT_S24BE; + width = 24; + break; + case 0x4000: + /* 20 bits in 3 bytes */ + format = GST_AUDIO_FORMAT_S24BE; + width = 20; + break; + default: + format = GST_AUDIO_FORMAT_S16BE; + width = 16; + break; + } + + dec->width = width; + + /* Only four sample rates supported */ + switch (header & 0x3000) { + case 0x0000: + rate = 48000; + break; + case 0x1000: + rate = 96000; + break; + case 0x2000: + rate = 44100; + break; + case 0x3000: + rate = 32000; + break; + default: + rate = 0; + break; + } + + /* And, of course, the number of channels (up to 8) */ + channels = ((header >> 8) & 0x7) + 1; + + gst_audio_info_set_format (&dec->info, format, rate, channels, NULL); + if (channels < 9 + && channel_positions[channels - 1][0] != + GST_AUDIO_CHANNEL_POSITION_INVALID) { + const GstAudioChannelPosition *position; + + dec->info.flags &= ~GST_AUDIO_FLAG_UNPOSITIONED; + position = channel_positions[channels - 1]; + dec->lpcm_layout = position; + memcpy (dec->info.position, position, + sizeof (GstAudioChannelPosition) * channels); + gst_audio_channel_positions_to_valid_order (dec->info.position, channels); + } +} + +static GstFlowReturn +gst_dvdlpcmdec_chain_dvd (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstDvdLpcmDec *dvdlpcmdec; + GstMapInfo map; + guint8 *data; + gsize size; + guint first_access; + guint32 header; + GstBuffer *subbuf; + GstFlowReturn ret = GST_FLOW_OK; + gint off, len; + gint rate, channels; + + dvdlpcmdec = GST_DVDLPCMDEC (parent); + + gst_buffer_map (buf, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + if (size < 5) + goto too_small; + + /* We have a 5 byte header, now. + * The first two bytes are a (big endian) 16 bit offset into our buffer. + * The buffer timestamp refers to this offset. + * + * The other three bytes are a (big endian) number in which the header is + * encoded. + */ + first_access = (data[0] << 8) | data[1]; + if (first_access > size) + goto invalid_data; + + /* Don't keep the 'frame number' low 5 bits of the first byte */ + header = ((data[2] & 0xC0) << 16) | (data[3] << 8) | data[4]; + + /* see if we have a new header */ + if (header != dvdlpcmdec->header) { + parse_header (dvdlpcmdec, header); + + if (!gst_dvdlpcmdec_set_outcaps (dvdlpcmdec)) + goto negotiation_failed; + + dvdlpcmdec->header = header; + } + + GST_LOG_OBJECT (dvdlpcmdec, "first_access %d, buffer length %" G_GSIZE_FORMAT, + first_access, size); + + rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info); + channels = GST_AUDIO_INFO_CHANNELS (&dvdlpcmdec->info); + + /* After first_access, we have an additional 3 bytes of data we've parsed and + * don't want to handle; this is included within the value of first_access. + * So a first_access value of between 1 and 3 is just broken, we treat that + * the same as zero. first_access == 4 means we only need to create a single + * sub-buffer, greater than that we need to create two. */ + + /* skip access unit bytes and info */ + off = 5; + + if (first_access > 4) { + guint samples = 0; + GstClockTime ts; + + /* length of first buffer */ + len = first_access - 4; + + GST_LOG_OBJECT (dvdlpcmdec, "Creating first sub-buffer off %d, len %d", + off, len); + + /* see if we need a subbuffer without timestamp */ + if (off + len > size) + goto bad_first_access; + + subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, len); + + /* If we don't have a stored timestamp from the last packet, + * (it's straight after a new-segment, but we have one on the + * first access buffer, then calculate the timestamp to align + * this buffer to just before the first_access buffer */ + if (!GST_CLOCK_TIME_IS_VALID (dvdlpcmdec->timestamp) && + GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + switch (dvdlpcmdec->width) { + case 16: + samples = len / channels / 2; + break; + case 20: + samples = (len / channels) * 2 / 5; + break; + case 24: + samples = len / channels / 3; + break; + } + } + if (samples != 0) { + ts = gst_util_uint64_scale (samples, GST_SECOND, rate); + if (ts < GST_BUFFER_TIMESTAMP (buf)) + GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf) - ts; + else + GST_BUFFER_TIMESTAMP (subbuf) = 0; + } else { + GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE; + } + + ret = gst_dvdlpcmdec_chain_raw (pad, parent, subbuf); + if (ret != GST_FLOW_OK) + goto done; + + /* then the buffer with new timestamp */ + off += len; + len = size - off; + + GST_LOG_OBJECT (dvdlpcmdec, "Creating next sub-buffer off %d, len %d", off, + len); + + if (len > 0) { + subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, len); + GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); + + ret = gst_dvdlpcmdec_chain_raw (pad, parent, subbuf); + } + } else { + GST_LOG_OBJECT (dvdlpcmdec, + "Creating single sub-buffer off %d, len %" G_GSIZE_FORMAT, off, + size - off); + subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, off, size - off); + GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); + ret = gst_dvdlpcmdec_chain_raw (pad, parent, subbuf); + } + +done: + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + + return ret; + + /* ERRORS */ +too_small: + { + /* Buffer is too small */ + GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE, + ("Invalid data found parsing LPCM packet"), + ("LPCM packet was too small. Dropping")); + ret = GST_FLOW_OK; + goto done; + } +invalid_data: + { + GST_ELEMENT_WARNING (dvdlpcmdec, STREAM, DECODE, + ("Invalid data found parsing LPCM packet"), + ("LPCM packet contained invalid first access. Dropping")); + ret = GST_FLOW_OK; + goto done; + } +negotiation_failed: + { + GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL), + ("Failed to configure output format")); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } +bad_first_access: + { + GST_WARNING_OBJECT (pad, "Bad first_access parameter in buffer"); + GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, DECODE, + (NULL), + ("first_access parameter out of range: bad buffer from demuxer")); + ret = GST_FLOW_ERROR; + goto done; + } +} + +static GstFlowReturn +gst_dvdlpcmdec_chain_raw (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstDvdLpcmDec *dvdlpcmdec; + gsize size; + GstFlowReturn ret; + guint samples = 0; + gint rate, channels; + + dvdlpcmdec = GST_DVDLPCMDEC (parent); + + size = gst_buffer_get_size (buf); + + GST_LOG_OBJECT (dvdlpcmdec, + "got buffer %p of size %" G_GSIZE_FORMAT " with ts %" GST_TIME_FORMAT, + buf, size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + rate = GST_AUDIO_INFO_RATE (&dvdlpcmdec->info); + channels = GST_AUDIO_INFO_CHANNELS (&dvdlpcmdec->info); + + if (rate == 0) + goto not_negotiated; + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) + dvdlpcmdec->timestamp = GST_BUFFER_TIMESTAMP (buf); + + /* We don't currently do anything at all regarding emphasis, mute or + * dynamic_range - I'm not sure what they're for */ + switch (dvdlpcmdec->width) { + case 16: + { + /* We can just pass 16-bits straight through intact, once we set + * appropriate things on the buffer */ + samples = size / channels / 2; + if (samples < 1) + goto drop; + buf = gst_buffer_make_writable (buf); + break; + } + case 20: + { + /* Allocate a new buffer and copy 20-bit width to 24-bit */ + gint64 samples = size * 8 / 20; + gint64 count = size / 10; + gint64 i; + GstMapInfo srcmap, destmap; + guint8 *src; + guint8 *dest; + GstBuffer *outbuf; + + if (samples < 1) + goto drop; + + outbuf = gst_buffer_new_allocate (NULL, samples * 3, NULL); + gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1); + + /* adjust samples so we can calc the new timestamp */ + samples = samples / channels; + + gst_buffer_map (buf, &srcmap, GST_MAP_READ); + gst_buffer_map (outbuf, &destmap, GST_MAP_WRITE); + src = srcmap.data; + dest = destmap.data; + + /* Copy 20-bit LPCM format to 24-bit buffers, with 0x00 in the lowest + * nibble. Note that the first 2 bytes are already correct */ + for (i = 0; i < count; i++) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[8] & 0xf0; + dest[3] = src[2]; + dest[4] = src[3]; + dest[5] = (src[8] & 0x0f) << 4; + dest[6] = src[4]; + dest[7] = src[5]; + dest[8] = src[9] & 0x0f; + dest[9] = src[6]; + dest[10] = src[7]; + dest[11] = (src[9] & 0x0f) << 4; + + src += 10; + dest += 12; + } + gst_buffer_unmap (outbuf, &destmap); + gst_buffer_unmap (buf, &srcmap); + gst_buffer_unref (buf); + buf = outbuf; + break; + } + case 24: + { + /* Rearrange 24-bit LPCM format in-place. Note that the first 2 + * and last byte are already correct */ + guint count = size / 12; + gint i; + GstMapInfo map; + guint8 *ptr; + + samples = size / channels / 3; + + if (samples < 1) + goto drop; + + /* Ensure our output buffer is writable */ + buf = gst_buffer_make_writable (buf); + + gst_buffer_map (buf, &map, GST_MAP_READWRITE); + ptr = map.data; + + for (i = 0; i < count; i++) { + guint8 tmp; + + tmp = ptr[10]; + ptr[10] = ptr[7]; + ptr[7] = ptr[5]; + ptr[5] = ptr[9]; + ptr[9] = ptr[6]; + ptr[6] = ptr[4]; + ptr[4] = ptr[3]; + ptr[3] = ptr[2]; + ptr[2] = ptr[8]; + ptr[8] = tmp; + + ptr += 12; + } + gst_buffer_unmap (buf, &map); + break; + } + default: + goto invalid_width; + } + + update_timestamps (dvdlpcmdec, buf, samples); + + if (dvdlpcmdec->lpcm_layout) + gst_audio_buffer_reorder_channels (buf, dvdlpcmdec->info.finfo->format, + dvdlpcmdec->info.channels, dvdlpcmdec->lpcm_layout, + dvdlpcmdec->info.position); + + ret = gst_pad_push (dvdlpcmdec->srcpad, buf); + +done: + return ret; + + /* ERRORS */ +drop: + { + GST_DEBUG_OBJECT (dvdlpcmdec, + "Buffer of size %" G_GSIZE_FORMAT " is too small. Dropping", size); + gst_buffer_unref (buf); + ret = GST_FLOW_OK; + goto done; + } +not_negotiated: + { + GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, FORMAT, (NULL), + ("Buffer pushed before negotiation")); + gst_buffer_unref (buf); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } +invalid_width: + { + GST_ELEMENT_ERROR (dvdlpcmdec, STREAM, WRONG_TYPE, (NULL), + ("Invalid sample width configured")); + gst_buffer_unref (buf); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } +} + +static gboolean +dvdlpcmdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstDvdLpcmDec *dvdlpcmdec; + gboolean res; + + dvdlpcmdec = GST_DVDLPCMDEC (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_dvdlpcmdec_setcaps (pad, caps); + gst_event_unref (event); + break; + } + case GST_EVENT_SEGMENT: + { + GstSegment seg; + + gst_event_copy_segment (event, &seg); + + GST_DEBUG_OBJECT (dvdlpcmdec, "segment %" GST_SEGMENT_FORMAT, &seg); + + dvdlpcmdec->segment = seg; + + if (seg.format == GST_FORMAT_TIME) { + dvdlpcmdec->timestamp = GST_CLOCK_TIME_NONE; + } else { + dvdlpcmdec->timestamp = 0; + } + res = gst_pad_push_event (dvdlpcmdec->srcpad, event); + break; + } + case GST_EVENT_FLUSH_STOP: + gst_segment_init (&dvdlpcmdec->segment, GST_FORMAT_UNDEFINED); + res = gst_pad_push_event (dvdlpcmdec->srcpad, event); + break; + default: + res = gst_pad_push_event (dvdlpcmdec->srcpad, event); + break; + } + + return res; +} + +static GstStateChangeReturn +gst_dvdlpcmdec_change_state (GstElement * element, GstStateChange transition) +{ + GstDvdLpcmDec *dvdlpcmdec = GST_DVDLPCMDEC (element); + GstStateChangeReturn res; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_dvdlpcm_reset (dvdlpcmdec); + break; + default: + break; + } + + res = parent_class->change_state (element, transition); + + switch (transition) { + default: + break; + } + + return res; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (dvdlpcm_debug, "dvdlpcmdec", 0, "DVD LPCM Decoder"); + + if (!gst_element_register (plugin, "dvdlpcmdec", GST_RANK_PRIMARY, + GST_TYPE_DVDLPCMDEC)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + dvdlpcmdec, + "Decode DVD LPCM frames into standard PCM", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/dvdlpcmdec/gstdvdlpcmdec.h b/gst/dvdlpcmdec/gstdvdlpcmdec.h new file mode 100644 index 0000000..955f60c --- /dev/null +++ b/gst/dvdlpcmdec/gstdvdlpcmdec.h @@ -0,0 +1,69 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Jan Schmidt <jan@noraisin.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_DVDLPCMDEC_H__ +#define __GST_DVDLPCMDEC_H__ + +#include <gst/gst.h> +#include <gst/audio/audio.h> + +G_BEGIN_DECLS + +#define GST_TYPE_DVDLPCMDEC \ + (gst_dvdlpcmdec_get_type()) +#define GST_DVDLPCMDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVDLPCMDEC,GstDvdLpcmDec)) +#define GST_DVDLPCMDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVDLPCMDEC,GstDvdLpcmDecClass)) +#define GST_IS_DVDLPCMDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVDLPCMDEC)) +#define GST_IS_DVDLPCMDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVDLPCMDEC)) + +typedef struct _GstDvdLpcmDec GstDvdLpcmDec; +typedef struct _GstDvdLpcmDecClass GstDvdLpcmDecClass; + +struct _GstDvdLpcmDec { + GstElement element; + + GstPad *sinkpad,*srcpad; + + guint32 header; + + GstAudioInfo info; + const GstAudioChannelPosition *lpcm_layout; + gint width; + gint dynamic_range; + gint emphasis; + gint mute; + + GstClockTime timestamp; + GstSegment segment; +}; + +struct _GstDvdLpcmDecClass { + GstElementClass parent_class; +}; + +GType gst_dvdlpcmdec_get_type (void); + +G_END_DECLS + +#endif /* __GST_DVDLPCMDEC_H__ */ diff --git a/gst/dvdsub/Makefile.am b/gst/dvdsub/Makefile.am new file mode 100644 index 0000000..5272ac5 --- /dev/null +++ b/gst/dvdsub/Makefile.am @@ -0,0 +1,25 @@ +plugin_LTLIBRARIES = libgstdvdsub.la + +libgstdvdsub_la_SOURCES = gstdvdsubdec.c gstdvdsubparse.c +libgstdvdsub_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstdvdsub_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) \ + $(GST_BASE_LIBS) $(GST_LIBS) +libgstdvdsub_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstdvdsub_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstdvdsubdec.h gstdvdsubparse.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstdvdsub -:SHARED libgstdvdsub \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstdvdsub_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdvdsub_la_CFLAGS) \ + -:LDFLAGS $(libgstdvdsub_la_LDFLAGS) \ + $(libgstdvdsub_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/dvdsub/Makefile.in b/gst/dvdsub/Makefile.in new file mode 100644 index 0000000..9fd3f77 --- /dev/null +++ b/gst/dvdsub/Makefile.in @@ -0,0 +1,852 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +subdir = gst/dvdsub +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstdvdsub_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgstdvdsub_la_OBJECTS = libgstdvdsub_la-gstdvdsubdec.lo \ + libgstdvdsub_la-gstdvdsubparse.lo +libgstdvdsub_la_OBJECTS = $(am_libgstdvdsub_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstdvdsub_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstdvdsub_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstdvdsub_la_CFLAGS) $(CFLAGS) \ + $(libgstdvdsub_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +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_CCLD_1 = +SOURCES = $(libgstdvdsub_la_SOURCES) +DIST_SOURCES = $(libgstdvdsub_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstdvdsub.la +libgstdvdsub_la_SOURCES = gstdvdsubdec.c gstdvdsubparse.c +libgstdvdsub_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) + +libgstdvdsub_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) \ + $(GST_BASE_LIBS) $(GST_LIBS) + +libgstdvdsub_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstdvdsub_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = gstdvdsubdec.h gstdvdsubparse.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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 gst/dvdsub/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/dvdsub/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstdvdsub.la: $(libgstdvdsub_la_OBJECTS) $(libgstdvdsub_la_DEPENDENCIES) $(EXTRA_libgstdvdsub_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstdvdsub_la_LINK) -rpath $(plugindir) $(libgstdvdsub_la_OBJECTS) $(libgstdvdsub_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdvdsub_la-gstdvdsubdec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstdvdsub_la-gstdvdsubparse.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +libgstdvdsub_la-gstdvdsubdec.lo: gstdvdsubdec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdsub_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdsub_la_CFLAGS) $(CFLAGS) -MT libgstdvdsub_la-gstdvdsubdec.lo -MD -MP -MF $(DEPDIR)/libgstdvdsub_la-gstdvdsubdec.Tpo -c -o libgstdvdsub_la-gstdvdsubdec.lo `test -f 'gstdvdsubdec.c' || echo '$(srcdir)/'`gstdvdsubdec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstdvdsub_la-gstdvdsubdec.Tpo $(DEPDIR)/libgstdvdsub_la-gstdvdsubdec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstdvdsubdec.c' object='libgstdvdsub_la-gstdvdsubdec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdsub_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdsub_la_CFLAGS) $(CFLAGS) -c -o libgstdvdsub_la-gstdvdsubdec.lo `test -f 'gstdvdsubdec.c' || echo '$(srcdir)/'`gstdvdsubdec.c + +libgstdvdsub_la-gstdvdsubparse.lo: gstdvdsubparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdsub_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdsub_la_CFLAGS) $(CFLAGS) -MT libgstdvdsub_la-gstdvdsubparse.lo -MD -MP -MF $(DEPDIR)/libgstdvdsub_la-gstdvdsubparse.Tpo -c -o libgstdvdsub_la-gstdvdsubparse.lo `test -f 'gstdvdsubparse.c' || echo '$(srcdir)/'`gstdvdsubparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstdvdsub_la-gstdvdsubparse.Tpo $(DEPDIR)/libgstdvdsub_la-gstdvdsubparse.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstdvdsubparse.c' object='libgstdvdsub_la-gstdvdsubparse.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstdvdsub_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstdvdsub_la_CFLAGS) $(CFLAGS) -c -o libgstdvdsub_la-gstdvdsubparse.lo `test -f 'gstdvdsubparse.c' || echo '$(srcdir)/'`gstdvdsubparse.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstdvdsub -:SHARED libgstdvdsub \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstdvdsub_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdvdsub_la_CFLAGS) \ + -:LDFLAGS $(libgstdvdsub_la_LDFLAGS) \ + $(libgstdvdsub_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/dvdsub/gstdvdsubdec.c b/gst/dvdsub/gstdvdsubdec.c new file mode 100644 index 0000000..2589ee6 --- /dev/null +++ b/gst/dvdsub/gstdvdsubdec.c @@ -0,0 +1,1160 @@ +/* GStreamer + * Copyright (C) <2005> Jan Schmidt <jan@fluendo.com> + * Copyright (C) <2002> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdvdsubdec.h" +#include "gstdvdsubparse.h" +#include <string.h> + +#define gst_dvd_sub_dec_parent_class parent_class +G_DEFINE_TYPE (GstDvdSubDec, gst_dvd_sub_dec, GST_TYPE_ELEMENT); + +static gboolean gst_dvd_sub_dec_src_event (GstPad * srcpad, GstObject * parent, + GstEvent * event); +static GstFlowReturn gst_dvd_sub_dec_chain (GstPad * pad, GstObject * parent, + GstBuffer * buf); + +static gboolean gst_dvd_sub_dec_handle_dvd_event (GstDvdSubDec * dec, + GstEvent * event); +static void gst_dvd_sub_dec_finalize (GObject * gobject); +static void gst_setup_palette (GstDvdSubDec * dec); +static void gst_dvd_sub_dec_merge_title (GstDvdSubDec * dec, + GstVideoFrame * frame); +static GstClockTime gst_dvd_sub_dec_get_event_delay (GstDvdSubDec * dec); +static gboolean gst_dvd_sub_dec_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_dvd_sub_dec_sink_setcaps (GstPad * pad, GstCaps * caps); + +static GstFlowReturn gst_send_subtitle_frame (GstDvdSubDec * dec, + GstClockTime end_ts); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw, format = (string) { AYUV, ARGB }," + "width = (int) 720, height = (int) 576, framerate = (fraction) 0/1") + ); + +static GstStaticPadTemplate subtitle_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("subpicture/x-dvd") + ); + +GST_DEBUG_CATEGORY_STATIC (gst_dvd_sub_dec_debug); +#define GST_CAT_DEFAULT (gst_dvd_sub_dec_debug) + +enum +{ + SPU_FORCE_DISPLAY = 0x00, + SPU_SHOW = 0x01, + SPU_HIDE = 0x02, + SPU_SET_PALETTE = 0x03, + SPU_SET_ALPHA = 0x04, + SPU_SET_SIZE = 0x05, + SPU_SET_OFFSETS = 0x06, + SPU_WIPE = 0x07, + SPU_END = 0xff +}; + +static const guint32 default_clut[16] = { + 0xb48080, 0x248080, 0x628080, 0xd78080, + 0x808080, 0x808080, 0x808080, 0x808080, + 0x808080, 0x808080, 0x808080, 0x808080, + 0x808080, 0x808080, 0x808080, 0x808080 +}; + +typedef struct RLE_state +{ + gint id; + gint aligned; + gint offset[2]; + gint hl_left; + gint hl_right; + + guchar *target; + + guchar next; +} +RLE_state; + +static void +gst_dvd_sub_dec_class_init (GstDvdSubDecClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_dvd_sub_dec_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&subtitle_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "DVD subtitle decoder", "Codec/Decoder/Video", + "Decodes DVD subtitles into AYUV video frames", + "Wim Taymans <wim.taymans@gmail.com>, " + "Jan Schmidt <thaytan@mad.scientist.com>"); +} + +static void +gst_dvd_sub_dec_init (GstDvdSubDec * dec) +{ + GstPadTemplate *tmpl; + + dec->sinkpad = gst_pad_new_from_static_template (&subtitle_template, "sink"); + gst_pad_set_chain_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_chain)); + gst_pad_set_event_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_sink_event)); + gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); + + tmpl = gst_static_pad_template_get (&src_template); + dec->srcpad = gst_pad_new_from_template (tmpl, "src"); + gst_pad_set_event_function (dec->srcpad, + GST_DEBUG_FUNCPTR (gst_dvd_sub_dec_src_event)); + gst_pad_use_fixed_caps (dec->srcpad); + gst_object_unref (tmpl); + gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); + + /* FIXME: aren't there more possible sizes? (tpm) */ + dec->in_width = 720; + dec->in_height = 576; + + dec->partialbuf = NULL; + dec->have_title = FALSE; + dec->parse_pos = NULL; + dec->forced_display = FALSE; + dec->visible = FALSE; + + memcpy (dec->current_clut, default_clut, sizeof (guint32) * 16); + + gst_setup_palette (dec); + + dec->next_ts = 0; + dec->next_event_ts = GST_CLOCK_TIME_NONE; + + dec->buf_dirty = TRUE; + dec->use_ARGB = FALSE; +} + +static void +gst_dvd_sub_dec_finalize (GObject * gobject) +{ + GstDvdSubDec *dec = GST_DVD_SUB_DEC (gobject); + + if (dec->partialbuf) { + gst_buffer_unmap (dec->partialbuf, &dec->partialmap); + gst_buffer_unref (dec->partialbuf); + dec->partialbuf = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (gobject); +} + +static gboolean +gst_dvd_sub_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + gboolean res = FALSE; + + switch (GST_EVENT_TYPE (event)) { + default: + res = gst_pad_event_default (pad, parent, event); + break; + } + + return res; +} + +static GstClockTime +gst_dvd_sub_dec_get_event_delay (GstDvdSubDec * dec) +{ + guchar *buf; + guint16 ticks; + GstClockTime event_delay; + + /* If starting a new buffer, follow the first DCSQ ptr */ + if (dec->parse_pos == dec->partialmap.data) { + buf = dec->parse_pos + dec->data_size; + } else { + buf = dec->parse_pos; + } + + ticks = GST_READ_UINT16_BE (buf); + event_delay = gst_util_uint64_scale (ticks, 1024 * GST_SECOND, 90000); + + GST_DEBUG_OBJECT (dec, "returning delay %" GST_TIME_FORMAT " from offset %u", + GST_TIME_ARGS (event_delay), (guint) (buf - dec->parse_pos)); + + return event_delay; +} + +/* + * Parse the next event time in the current subpicture buffer, stopping + * when time advances to the next state. + */ +static void +gst_dvd_sub_dec_parse_subpic (GstDvdSubDec * dec) +{ +#define PARSE_BYTES_NEEDED(x) if ((buf+(x)) >= end) \ + { GST_WARNING("Subtitle stream broken parsing %c", *buf); \ + broken = TRUE; break; } + + guchar *start = dec->partialmap.data; + guchar *buf; + guchar *end; + gboolean broken = FALSE; + gboolean last_seq = FALSE; + guchar *next_seq = NULL; + GstClockTime event_time; + + /* nothing to do if we finished this buffer already */ + if (dec->parse_pos == NULL) + return; + + g_return_if_fail (dec->packet_size >= 4); + + end = start + dec->packet_size; + if (dec->parse_pos == start) { + buf = dec->parse_pos + dec->data_size; + } else { + buf = dec->parse_pos; + } + + g_assert (buf >= start && buf < end); + + /* If the next control sequence is at the current offset, this is + * the last one */ + next_seq = start + GST_READ_UINT16_BE (buf + 2); + last_seq = (next_seq == buf); + buf += 4; + + while ((buf < end) && (!broken)) { + switch (*buf) { + case SPU_FORCE_DISPLAY: /* Forced display menu subtitle */ + dec->forced_display = TRUE; + dec->buf_dirty = TRUE; + GST_DEBUG_OBJECT (dec, "SPU FORCE_DISPLAY"); + buf++; + break; + case SPU_SHOW: /* Show the subtitle in this packet */ + dec->visible = TRUE; + dec->buf_dirty = TRUE; + GST_DEBUG_OBJECT (dec, "SPU SHOW at %" GST_TIME_FORMAT, + GST_TIME_ARGS (dec->next_event_ts)); + buf++; + break; + case SPU_HIDE: + /* 02 ff (ff) is the end of the packet, hide the subpicture */ + dec->visible = FALSE; + dec->buf_dirty = TRUE; + + GST_DEBUG_OBJECT (dec, "SPU HIDE at %" GST_TIME_FORMAT, + GST_TIME_ARGS (dec->next_event_ts)); + buf++; + break; + case SPU_SET_PALETTE: /* palette */ + PARSE_BYTES_NEEDED (3); + + GST_DEBUG_OBJECT (dec, "SPU SET_PALETTE"); + + dec->subtitle_index[3] = buf[1] >> 4; + dec->subtitle_index[2] = buf[1] & 0xf; + dec->subtitle_index[1] = buf[2] >> 4; + dec->subtitle_index[0] = buf[2] & 0xf; + gst_setup_palette (dec); + + dec->buf_dirty = TRUE; + buf += 3; + break; + case SPU_SET_ALPHA: /* transparency palette */ + PARSE_BYTES_NEEDED (3); + + GST_DEBUG_OBJECT (dec, "SPU SET_ALPHA"); + + dec->subtitle_alpha[3] = buf[1] >> 4; + dec->subtitle_alpha[2] = buf[1] & 0xf; + dec->subtitle_alpha[1] = buf[2] >> 4; + dec->subtitle_alpha[0] = buf[2] & 0xf; + gst_setup_palette (dec); + + dec->buf_dirty = TRUE; + buf += 3; + break; + case SPU_SET_SIZE: /* image coordinates */ + PARSE_BYTES_NEEDED (7); + + dec->top = ((buf[4] & 0x3f) << 4) | ((buf[5] & 0xe0) >> 4); + dec->left = ((buf[1] & 0x3f) << 4) | ((buf[2] & 0xf0) >> 4); + dec->right = ((buf[2] & 0x03) << 8) | buf[3]; + dec->bottom = ((buf[5] & 0x03) << 8) | buf[6]; + + GST_DEBUG_OBJECT (dec, "SPU SET_SIZE left %d, top %d, right %d, " + "bottom %d", dec->left, dec->top, dec->right, dec->bottom); + + dec->buf_dirty = TRUE; + buf += 7; + break; + case SPU_SET_OFFSETS: /* image 1 / image 2 offsets */ + PARSE_BYTES_NEEDED (5); + + dec->offset[0] = (((guint) buf[1]) << 8) | buf[2]; + dec->offset[1] = (((guint) buf[3]) << 8) | buf[4]; + GST_DEBUG_OBJECT (dec, "Offset1 %d, Offset2 %d", + dec->offset[0], dec->offset[1]); + + dec->buf_dirty = TRUE; + buf += 5; + break; + case SPU_WIPE: + { + guint length; + + PARSE_BYTES_NEEDED (3); + + GST_WARNING_OBJECT (dec, "SPU_WIPE not yet implemented"); + + length = (buf[1] << 8) | (buf[2]); + buf += 1 + length; + + dec->buf_dirty = TRUE; + break; + } + case SPU_END: + buf = (last_seq) ? end : next_seq; + + /* Start a new control sequence */ + if (buf + 4 < end) { + guint16 ticks = GST_READ_UINT16_BE (buf); + + event_time = gst_util_uint64_scale (ticks, 1024 * GST_SECOND, 90000); + + GST_DEBUG_OBJECT (dec, + "Next DCSQ at offset %u, delay %g secs (%d ticks)", + (guint) (buf - start), + gst_util_guint64_to_gdouble (event_time / GST_SECOND), ticks); + + dec->parse_pos = buf; + if (event_time > 0) { + dec->next_event_ts += event_time; + + GST_LOG_OBJECT (dec, "Exiting parse loop with time %g", + gst_guint64_to_gdouble (dec->next_event_ts) / + gst_guint64_to_gdouble (GST_SECOND)); + return; + } + break; + } else { + dec->parse_pos = NULL; + dec->next_event_ts = GST_CLOCK_TIME_NONE; + GST_LOG_OBJECT (dec, "Finished all cmds. Exiting parse loop"); + return; + } + default: + GST_ERROR + ("Invalid sequence in subtitle packet header (%.2x). Skipping", + *buf); + broken = TRUE; + dec->parse_pos = NULL; + break; + } + } +} + +static inline int +gst_get_nibble (guchar * buffer, RLE_state * state) +{ + if (state->aligned) { + state->next = buffer[state->offset[state->id]++]; + state->aligned = 0; + return state->next >> 4; + } else { + state->aligned = 1; + return state->next & 0xf; + } +} + +/* Premultiply the current lookup table into the "target" cache */ +static void +gst_setup_palette (GstDvdSubDec * dec) +{ + gint i; + guint32 col; + Color_val *target_yuv = dec->palette_cache_yuv; + Color_val *target2_yuv = dec->hl_palette_cache_yuv; + Color_val *target_rgb = dec->palette_cache_rgb; + Color_val *target2_rgb = dec->hl_palette_cache_rgb; + + for (i = 0; i < 4; i++, target2_yuv++, target_yuv++) { + col = dec->current_clut[dec->subtitle_index[i]]; + target_yuv->Y_R = (col >> 16) & 0xff; + target_yuv->V_B = (col >> 8) & 0xff; + target_yuv->U_G = col & 0xff; + target_yuv->A = dec->subtitle_alpha[i] * 0xff / 0xf; + + col = dec->current_clut[dec->menu_index[i]]; + target2_yuv->Y_R = (col >> 16) & 0xff; + target2_yuv->V_B = (col >> 8) & 0xff; + target2_yuv->U_G = col & 0xff; + target2_yuv->A = dec->menu_alpha[i] * 0xff / 0xf; + + /* If ARGB flag set, then convert YUV palette to RGB */ + /* Using integer aritmetic */ + if (dec->use_ARGB) { + guchar C = target_yuv->Y_R - 16; + guchar D = target_yuv->U_G - 128; + guchar E = target_yuv->V_B - 128; + + target_rgb->Y_R = CLAMP (((298 * C + 409 * E + 128) >> 8), 0, 255); + target_rgb->U_G = + CLAMP (((298 * C - 100 * D - 128 * E + 128) >> 8), 0, 255); + target_rgb->V_B = CLAMP (((298 * C + 516 * D + 128) >> 8), 0, 255); + target_rgb->A = target_yuv->A; + + C = target2_yuv->Y_R - 16; + D = target2_yuv->U_G - 128; + E = target2_yuv->V_B - 128; + + target2_rgb->Y_R = CLAMP (((298 * C + 409 * E + 128) >> 8), 0, 255); + target2_rgb->U_G = + CLAMP (((298 * C - 100 * D - 128 * E + 128) >> 8), 0, 255); + target2_rgb->V_B = CLAMP (((298 * C + 516 * D + 128) >> 8), 0, 255); + target2_rgb->A = target2_yuv->A; + } + target_rgb++; + target2_rgb++; + } +} + +static inline guint +gst_get_rle_code (guchar * buffer, RLE_state * state) +{ + gint code; + + code = gst_get_nibble (buffer, state); + if (code < 0x4) { /* 4 .. f */ + code = (code << 4) | gst_get_nibble (buffer, state); + if (code < 0x10) { /* 1x .. 3x */ + code = (code << 4) | gst_get_nibble (buffer, state); + if (code < 0x40) { /* 04x .. 0fx */ + code = (code << 4) | gst_get_nibble (buffer, state); + } + } + } + return code; +} + +#define DRAW_RUN(target,len,c) \ +G_STMT_START { \ + gint i = 0; \ + if ((c)->A) { \ + for (i = 0; i < (len); i++) { \ + *(target)++ = (c)->A; \ + *(target)++ = (c)->Y_R; \ + *(target)++ = (c)->U_G; \ + *(target)++ = (c)->V_B; \ + } \ + } else { \ + (target) += 4 * (len); \ + } \ +} G_STMT_END + +/* + * This function steps over each run-length segment, drawing + * into the YUVA/ARGB buffers as it goes. UV are composited and then output + * at half width/height + */ +static void +gst_draw_rle_line (GstDvdSubDec * dec, guchar * buffer, RLE_state * state) +{ + gint length, colourid; + guint code; + gint x, right; + guchar *target; + + target = state->target; + + x = dec->left; + right = dec->right + 1; + + while (x < right) { + gboolean in_hl; + const Color_val *colour_entry; + + code = gst_get_rle_code (buffer, state); + length = code >> 2; + colourid = code & 3; + if (dec->use_ARGB) + colour_entry = dec->palette_cache_rgb + colourid; + else + colour_entry = dec->palette_cache_yuv + colourid; + + /* Length = 0 implies fill to the end of the line */ + /* Restrict the colour run to the end of the line */ + if (length == 0 || x + length > right) + length = right - x; + + /* Check if this run of colour touches the highlight region */ + in_hl = ((x <= state->hl_right) && (x + length) >= state->hl_left); + if (in_hl) { + gint run; + + /* Draw to the left of the highlight */ + if (x <= state->hl_left) { + run = MIN (length, state->hl_left - x + 1); + + DRAW_RUN (target, run, colour_entry); + length -= run; + x += run; + } + + /* Draw across the highlight region */ + if (x <= state->hl_right) { + const Color_val *hl_colour; + if (dec->use_ARGB) + hl_colour = dec->hl_palette_cache_rgb + colourid; + else + hl_colour = dec->hl_palette_cache_yuv + colourid; + + run = MIN (length, state->hl_right - x + 1); + + DRAW_RUN (target, run, hl_colour); + length -= run; + x += run; + } + } + + /* Draw the rest of the run */ + if (length > 0) { + DRAW_RUN (target, length, colour_entry); + x += length; + } + } +} + +/* + * Decode the RLE subtitle image and blend with the current + * frame buffer. + */ +static void +gst_dvd_sub_dec_merge_title (GstDvdSubDec * dec, GstVideoFrame * frame) +{ + gint y; + gint Y_stride; + guchar *buffer = dec->partialmap.data; + gint hl_top, hl_bottom; + gint last_y; + RLE_state state; + guint8 *Y_data;; + + GST_DEBUG_OBJECT (dec, "Merging subtitle on frame"); + + Y_data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); + Y_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0); + + state.id = 0; + state.aligned = 1; + state.next = 0; + state.offset[0] = dec->offset[0]; + state.offset[1] = dec->offset[1]; + + /* center the image when display rectangle exceeds the video width */ + if (dec->in_width <= dec->right) { + gint left, disp_width; + + disp_width = dec->right - dec->left + 1; + left = (dec->in_width - disp_width) / 2; + dec->left = left; + dec->right = left + disp_width - 1; + + /* if it clips to the right, shift it left, but only till zero */ + if (dec->right >= dec->in_width) { + gint shift = dec->right - dec->in_width - 1; + if (shift > dec->left) + shift = dec->left; + dec->left -= shift; + dec->right -= shift; + } + + GST_DEBUG_OBJECT (dec, "clipping width to %d,%d", + dec->left, dec->in_width - 1); + } + + /* for the height, bring it up till it fits as well as it can. We + * assume the picture is in the lower part. We should better check where it + * is and do something more clever. */ + if (dec->in_height <= dec->bottom) { + + /* shift it up, but only till zero */ + gint shift = dec->bottom - dec->in_height - 1; + if (shift > dec->top) + shift = dec->top; + dec->top -= shift; + dec->bottom -= shift; + + /* start on even line */ + if (dec->top & 1) { + dec->top--; + dec->bottom--; + } + + GST_DEBUG_OBJECT (dec, "clipping height to %d,%d", + dec->top, dec->in_height - 1); + } + + if (dec->current_button) { + hl_top = dec->hl_top; + hl_bottom = dec->hl_bottom; + } else { + hl_top = -1; + hl_bottom = -1; + } + last_y = MIN (dec->bottom, dec->in_height); + + y = dec->top; + state.target = Y_data + 4 * dec->left + (y * Y_stride); + + /* Now draw scanlines until we hit last_y or end of RLE data */ + for (; ((state.offset[1] < dec->data_size + 2) && (y <= last_y)); y++) { + /* Set up to draw the highlight if we're in the right scanlines */ + if (y > hl_bottom || y < hl_top) { + state.hl_left = -1; + state.hl_right = -1; + } else { + state.hl_left = dec->hl_left; + state.hl_right = dec->hl_right; + } + gst_draw_rle_line (dec, buffer, &state); + + state.target += Y_stride; + + /* Realign the RLE state for the next line */ + if (!state.aligned) + gst_get_nibble (buffer, &state); + state.id = !state.id; + } +} + +static void +gst_send_empty_fill (GstDvdSubDec * dec, GstClockTime ts) +{ + if (dec->next_ts < ts) { + GST_LOG_OBJECT (dec, "Sending GAP event update to advance time to %" + GST_TIME_FORMAT, GST_TIME_ARGS (ts)); + + gst_pad_push_event (dec->srcpad, + gst_event_new_gap (dec->next_ts, ts - dec->next_ts)); + } + dec->next_ts = ts; +} + +static GstFlowReturn +gst_send_subtitle_frame (GstDvdSubDec * dec, GstClockTime end_ts) +{ + GstFlowReturn flow; + GstBuffer *out_buf; + GstVideoFrame frame; + guint8 *data; + gint x, y; + static GstAllocationParams params = { 0, 3, 0, 0, }; + + g_assert (dec->have_title); + g_assert (dec->next_ts <= end_ts); + + /* Check if we need to redraw the output buffer */ + if (!dec->buf_dirty) { + flow = GST_FLOW_OK; + goto out; + } + + out_buf = + gst_buffer_new_allocate (NULL, GST_VIDEO_INFO_SIZE (&dec->info), + ¶ms); + gst_video_frame_map (&frame, &dec->info, out_buf, GST_MAP_READWRITE); + + data = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0); + + /* Clear the buffer */ + /* FIXME - move this into the buffer rendering code */ + for (y = 0; y < dec->in_height; y++) { + guchar *line = data + 4 * dec->in_width * y; + + for (x = 0; x < dec->in_width; x++) { + line[0] = 0; /* A */ + if (!dec->use_ARGB) { + line[1] = 16; /* Y */ + line[2] = 128; /* U */ + line[3] = 128; /* V */ + } else { + line[1] = 0; /* R */ + line[2] = 0; /* G */ + line[3] = 0; /* B */ + } + + line += 4; + } + } + + /* FIXME: do we really want to honour the forced_display flag + * for subtitles streans? */ + if (dec->visible || dec->forced_display) { + gst_dvd_sub_dec_merge_title (dec, &frame); + } + + gst_video_frame_unmap (&frame); + + dec->buf_dirty = FALSE; + + GST_BUFFER_TIMESTAMP (out_buf) = dec->next_ts; + if (GST_CLOCK_TIME_IS_VALID (dec->next_event_ts)) { + GST_BUFFER_DURATION (out_buf) = GST_CLOCK_DIFF (dec->next_ts, + dec->next_event_ts); + } else { + GST_BUFFER_DURATION (out_buf) = GST_CLOCK_TIME_NONE; + } + + GST_DEBUG_OBJECT (dec, "Sending subtitle buffer with ts %" + GST_TIME_FORMAT ", dur %" G_GINT64_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out_buf)), + GST_BUFFER_DURATION (out_buf)); + + flow = gst_pad_push (dec->srcpad, out_buf); + +out: + dec->next_ts = end_ts; + return flow; +} + +/* Walk time forward, processing any subtitle events as needed. */ +static GstFlowReturn +gst_dvd_sub_dec_advance_time (GstDvdSubDec * dec, GstClockTime new_ts) +{ + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (dec, "Advancing time to %" GST_TIME_FORMAT, + GST_TIME_ARGS (new_ts)); + + if (!dec->have_title) { + gst_send_empty_fill (dec, new_ts); + return ret; + } + + while (dec->next_ts < new_ts) { + GstClockTime next_ts = new_ts; + + if (GST_CLOCK_TIME_IS_VALID (dec->next_event_ts) && + dec->next_event_ts < next_ts) { + /* We might need to process the subtitle cmd queue */ + next_ts = dec->next_event_ts; + } + + /* + * Now, either output a filler or a frame spanning + * dec->next_ts to next_ts + */ + if (dec->visible || dec->forced_display) { + ret = gst_send_subtitle_frame (dec, next_ts); + } else { + gst_send_empty_fill (dec, next_ts); + } + + /* + * and then process some subtitle cmds if we need + */ + if (next_ts == dec->next_event_ts) + gst_dvd_sub_dec_parse_subpic (dec); + } + + return ret; +} + +static GstFlowReturn +gst_dvd_sub_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstDvdSubDec *dec; + guint8 *data; + glong size = 0; + + dec = GST_DVD_SUB_DEC (parent); + + GST_DEBUG_OBJECT (dec, "Have buffer of size %" G_GSIZE_FORMAT ", ts %" + GST_TIME_FORMAT ", dur %" G_GINT64_FORMAT, gst_buffer_get_size (buf), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_DURATION (buf)); + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + if (!GST_CLOCK_TIME_IS_VALID (dec->next_ts)) { + dec->next_ts = GST_BUFFER_TIMESTAMP (buf); + } + + /* Move time forward to the start of the new buffer */ + ret = gst_dvd_sub_dec_advance_time (dec, GST_BUFFER_TIMESTAMP (buf)); + } + + if (dec->have_title) { + gst_buffer_unmap (dec->partialbuf, &dec->partialmap); + gst_buffer_unref (dec->partialbuf); + dec->partialbuf = NULL; + dec->have_title = FALSE; + } + + GST_DEBUG_OBJECT (dec, "Got subtitle buffer, pts %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + /* deal with partial frame from previous buffer */ + if (dec->partialbuf) { + gst_buffer_unmap (dec->partialbuf, &dec->partialmap); + dec->partialbuf = gst_buffer_append (dec->partialbuf, buf); + } else { + dec->partialbuf = buf; + } + + gst_buffer_map (dec->partialbuf, &dec->partialmap, GST_MAP_READ); + + data = dec->partialmap.data; + size = dec->partialmap.size; + + if (size > 4) { + dec->packet_size = GST_READ_UINT16_BE (data); + + if (dec->packet_size == size) { + GST_LOG_OBJECT (dec, "Subtitle packet size %d, current size %ld", + dec->packet_size, size); + + dec->data_size = GST_READ_UINT16_BE (data + 2); + + /* Reset parameters for a new subtitle buffer */ + dec->parse_pos = data; + dec->forced_display = FALSE; + dec->visible = FALSE; + + dec->have_title = TRUE; + dec->next_event_ts = GST_BUFFER_TIMESTAMP (dec->partialbuf); + + if (!GST_CLOCK_TIME_IS_VALID (dec->next_event_ts)) + dec->next_event_ts = dec->next_ts; + + dec->next_event_ts += gst_dvd_sub_dec_get_event_delay (dec); + } + } + + return ret; +} + +static gboolean +gst_dvd_sub_dec_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstDvdSubDec *dec = GST_DVD_SUB_DEC (gst_pad_get_parent (pad)); + gboolean ret = FALSE; + GstCaps *out_caps = NULL, *peer_caps = NULL; + + GST_DEBUG_OBJECT (dec, "setcaps called with %" GST_PTR_FORMAT, caps); + + out_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "AYUV", + "width", G_TYPE_INT, dec->in_width, + "height", G_TYPE_INT, dec->in_height, + "framerate", GST_TYPE_FRACTION, 0, 1, NULL); + + peer_caps = gst_pad_get_allowed_caps (dec->srcpad); + if (G_LIKELY (peer_caps)) { + guint i = 0, n = 0; + + n = gst_caps_get_size (peer_caps); + GST_DEBUG_OBJECT (dec, "peer allowed caps (%u structure(s)) are %" + GST_PTR_FORMAT, n, peer_caps); + + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (peer_caps, i); + /* Check if the peer pad support ARGB format, if yes change caps */ + if (gst_structure_has_name (s, "video/x-raw")) { + gst_caps_unref (out_caps); + GST_DEBUG_OBJECT (dec, "trying with ARGB"); + + out_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "ARGB", + "width", G_TYPE_INT, dec->in_width, + "height", G_TYPE_INT, dec->in_height, + "framerate", GST_TYPE_FRACTION, 0, 1, NULL); + + if (gst_pad_peer_query_accept_caps (dec->srcpad, out_caps)) { + GST_DEBUG_OBJECT (dec, "peer accepted ARGB"); + /* If ARGB format then set the flag */ + dec->use_ARGB = TRUE; + break; + } + } + } + gst_caps_unref (peer_caps); + } + GST_DEBUG_OBJECT (dec, "setting caps downstream to %" GST_PTR_FORMAT, + out_caps); + if (gst_pad_set_caps (dec->srcpad, out_caps)) { + gst_video_info_from_caps (&dec->info, out_caps); + } else { + GST_WARNING_OBJECT (dec, "failed setting downstream caps"); + gst_caps_unref (out_caps); + goto beach; + } + + gst_caps_unref (out_caps); + ret = TRUE; + +beach: + gst_object_unref (dec); + return ret; +} + +static gboolean +gst_dvd_sub_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstDvdSubDec *dec = GST_DVD_SUB_DEC (parent); + gboolean ret = FALSE; + + GST_LOG_OBJECT (dec, "%s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = gst_dvd_sub_dec_sink_setcaps (pad, caps); + gst_event_unref (event); + break; + } + case GST_EVENT_CUSTOM_DOWNSTREAM:{ + GstClockTime ts = GST_EVENT_TIMESTAMP (event); + + if (gst_event_has_name (event, "application/x-gst-dvd")) { + if (GST_CLOCK_TIME_IS_VALID (ts)) + gst_dvd_sub_dec_advance_time (dec, ts); + + if (gst_dvd_sub_dec_handle_dvd_event (dec, event)) { + /* gst_dvd_sub_dec_advance_time (dec, dec->next_ts + GST_SECOND / 30.0); */ + gst_event_unref (event); + ret = TRUE; + break; + } + } + + ret = gst_pad_event_default (pad, parent, event); + break; + } + case GST_EVENT_GAP: + { + GstClockTime start, duration; + + gst_event_parse_gap (event, &start, &duration); + if (GST_CLOCK_TIME_IS_VALID (start)) { + if (GST_CLOCK_TIME_IS_VALID (duration)) + start += duration; + /* we do not expect another buffer until after gap, + * so that is our position now */ + GST_DEBUG_OBJECT (dec, "Got GAP event, advancing time from %" + GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (dec->next_ts), GST_TIME_ARGS (start)); + + gst_dvd_sub_dec_advance_time (dec, start); + } else { + GST_WARNING_OBJECT (dec, "Got GAP event with invalid position"); + } + + gst_event_unref (event); + ret = TRUE; + break; + } + case GST_EVENT_SEGMENT: + { + GstSegment seg; + + gst_event_copy_segment (event, &seg); + + { +#if 0 + /* Turn off forced highlight display */ + dec->forced_display = 0; + dec->current_button = 0; +#endif + if (dec->partialbuf) { + gst_buffer_unmap (dec->partialbuf, &dec->partialmap); + gst_buffer_unref (dec->partialbuf); + dec->partialbuf = NULL; + dec->have_title = FALSE; + } + + if (GST_CLOCK_TIME_IS_VALID (seg.time)) + dec->next_ts = seg.time; + else + dec->next_ts = GST_CLOCK_TIME_NONE; + + GST_DEBUG_OBJECT (dec, "Got newsegment, new time = %" + GST_TIME_FORMAT, GST_TIME_ARGS (dec->next_ts)); + + ret = gst_pad_event_default (pad, parent, event); + } + break; + } + case GST_EVENT_FLUSH_STOP:{ + /* Turn off forced highlight display */ + dec->forced_display = 0; + dec->current_button = 0; + + if (dec->partialbuf) { + gst_buffer_unmap (dec->partialbuf, &dec->partialmap); + gst_buffer_unref (dec->partialbuf); + dec->partialbuf = NULL; + dec->have_title = FALSE; + } + + ret = gst_pad_event_default (pad, parent, event); + break; + } + default:{ + ret = gst_pad_event_default (pad, parent, event); + break; + } + } + return ret; +} + +static gboolean +gst_dvd_sub_dec_handle_dvd_event (GstDvdSubDec * dec, GstEvent * event) +{ + GstStructure *structure; + const gchar *event_name; + + structure = (GstStructure *) gst_event_get_structure (event); + + if (structure == NULL) + goto not_handled; + + event_name = gst_structure_get_string (structure, "event"); + + GST_LOG_OBJECT (dec, + "DVD event %s with timestamp %" G_GINT64_FORMAT " on sub pad", + GST_STR_NULL (event_name), GST_EVENT_TIMESTAMP (event)); + + if (event_name == NULL) + goto not_handled; + + if (strcmp (event_name, "dvd-spu-highlight") == 0) { + gint button; + gint palette, sx, sy, ex, ey; + gint i; + + /* Details for the highlight region to display */ + if (!gst_structure_get_int (structure, "button", &button) || + !gst_structure_get_int (structure, "palette", &palette) || + !gst_structure_get_int (structure, "sx", &sx) || + !gst_structure_get_int (structure, "sy", &sy) || + !gst_structure_get_int (structure, "ex", &ex) || + !gst_structure_get_int (structure, "ey", &ey)) { + GST_ERROR_OBJECT (dec, "Invalid dvd-spu-highlight event received"); + return TRUE; + } + dec->current_button = button; + dec->hl_left = sx; + dec->hl_top = sy; + dec->hl_right = ex; + dec->hl_bottom = ey; + for (i = 0; i < 4; i++) { + dec->menu_alpha[i] = ((guint32) (palette) >> (i * 4)) & 0x0f; + dec->menu_index[i] = ((guint32) (palette) >> (16 + (i * 4))) & 0x0f; + } + + GST_DEBUG_OBJECT (dec, "New button activated highlight=(%d,%d) to (%d,%d) " + "palette 0x%x", sx, sy, ex, ey, palette); + gst_setup_palette (dec); + + dec->buf_dirty = TRUE; + } else if (strcmp (event_name, "dvd-spu-clut-change") == 0) { + /* Take a copy of the colour table */ + gchar name[16]; + int i; + gint value; + + GST_LOG_OBJECT (dec, "New colour table received"); + for (i = 0; i < 16; i++) { + g_snprintf (name, sizeof (name), "clut%02d", i); + if (!gst_structure_get_int (structure, name, &value)) { + GST_ERROR_OBJECT (dec, "dvd-spu-clut-change event did not " + "contain %s field", name); + break; + } + dec->current_clut[i] = (guint32) (value); + } + + gst_setup_palette (dec); + + dec->buf_dirty = TRUE; + } else if (strcmp (event_name, "dvd-spu-stream-change") == 0 + || strcmp (event_name, "dvd-spu-reset-highlight") == 0) { + /* Turn off forced highlight display */ + dec->current_button = 0; + + GST_LOG_OBJECT (dec, "Clearing button state"); + dec->buf_dirty = TRUE; + } else if (strcmp (event_name, "dvd-spu-still-frame") == 0) { + /* Handle a still frame */ + GST_LOG_OBJECT (dec, "Received still frame notification"); + } else { + goto not_handled; + } + + return TRUE; + +not_handled: + { + /* Ignore all other unknown events */ + GST_LOG_OBJECT (dec, "Ignoring other custom event %" GST_PTR_FORMAT, + structure); + return FALSE; + } +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "dvdsubdec", GST_RANK_NONE, + GST_TYPE_DVD_SUB_DEC) || + !gst_element_register (plugin, "dvdsubparse", GST_RANK_NONE, + GST_TYPE_DVD_SUB_PARSE)) { + return FALSE; + } + + GST_DEBUG_CATEGORY_INIT (gst_dvd_sub_dec_debug, "dvdsubdec", 0, + "DVD subtitle decoder"); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + dvdsub, + "DVD subtitle parser and decoder", plugin_init, + VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/dvdsub/gstdvdsubdec.h b/gst/dvdsub/gstdvdsubdec.h new file mode 100644 index 0000000..a75e439 --- /dev/null +++ b/gst/dvdsub/gstdvdsubdec.h @@ -0,0 +1,102 @@ +/* GStreamer + * Copyright (C) <2005> Jan Schmidt <jan@fluendo.com> + * Copyright (C) <2002> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <gst/gst.h> +#include <gst/video/video.h> + +#define GST_TYPE_DVD_SUB_DEC (gst_dvd_sub_dec_get_type()) +#define GST_DVD_SUB_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVD_SUB_DEC,GstDvdSubDec)) +#define GST_DVD_SUB_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVD_SUB_DEC,GstDvdSubDecClass)) +#define GST_IS_DVD_SUB_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVD_SUB_DEC)) +#define GST_IS_DVD_SUB_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVD_SUB_DEC)) + +typedef struct _GstDvdSubDec GstDvdSubDec; +typedef struct _GstDvdSubDecClass GstDvdSubDecClass; + +/* Hold premultimplied colour values */ +typedef struct Color_val +{ + guchar Y_R; + guchar U_G; + guchar V_B; + guchar A; + +} Color_val; + +struct _GstDvdSubDec +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + gint in_width, in_height; + + /* Collect together subtitle buffers until we have a full control sequence */ + GstBuffer *partialbuf; + GstMapInfo partialmap; + gboolean have_title; + + guchar subtitle_index[4]; + guchar menu_index[4]; + guchar subtitle_alpha[4]; + guchar menu_alpha[4]; + + guint32 current_clut[16]; + Color_val palette_cache_yuv[4]; + Color_val hl_palette_cache_yuv[4]; + + Color_val palette_cache_rgb[4]; + Color_val hl_palette_cache_rgb[4]; + + GstVideoInfo info; + gboolean use_ARGB; + GstClockTime next_ts; + + /* + * State info for the current subpicture + * buffer + */ + guchar *parse_pos; + + guint16 packet_size; + guint16 data_size; + + gint offset[2]; + + gboolean forced_display; + gboolean visible; + + gint left, top, right, bottom; + gint hl_left, hl_top, hl_right, hl_bottom; + + gint current_button; + + GstClockTime next_event_ts; + + gboolean buf_dirty; +}; + +struct _GstDvdSubDecClass +{ + GstElementClass parent_class; +}; + +GType gst_dvd_sub_dec_get_type (void); diff --git a/gst/dvdsub/gstdvdsubparse.c b/gst/dvdsub/gstdvdsubparse.c new file mode 100644 index 0000000..ea49e53 --- /dev/null +++ b/gst/dvdsub/gstdvdsubparse.c @@ -0,0 +1,241 @@ +/* GStreamer DVD subtitle parser + * Copyright (C) 2007 Mark Nauwelaerts <mnauw@users.sourceforge.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <gst/gst.h> +#include "gstdvdsubparse.h" + +GST_DEBUG_CATEGORY_STATIC (dvdsubparse_debug); +#define GST_CAT_DEFAULT dvdsubparse_debug + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("subpicture/x-dvd, parsed=(boolean)true") + ); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("subpicture/x-dvd") + ); + +static void gst_dvd_sub_parse_finalize (GObject * object); + +static void gst_dvd_sub_parse_reset (GstDvdSubParse * parse); + +static gboolean gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static GstFlowReturn gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent, + GstBuffer * buf); + +static GstStateChangeReturn gst_dvd_sub_parse_change_state (GstElement * + element, GstStateChange transition); + +#define gst_dvd_sub_parse_parent_class parent_class +G_DEFINE_TYPE (GstDvdSubParse, gst_dvd_sub_parse, GST_TYPE_ELEMENT); + +static void +gst_dvd_sub_parse_class_init (GstDvdSubParseClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_dvd_sub_parse_finalize; + + GST_DEBUG_CATEGORY_INIT (dvdsubparse_debug, "dvdsubparse", 0, + "DVD subtitle parser"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_change_state); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "DVD subtitle parser", "Codec/Parser/Subtitle", + "Parses and packetizes DVD subtitle streams", + "Mark Nauwelaerts <mnauw@users.sourceforge.net>"); +} + +static void +gst_dvd_sub_parse_finalize (GObject * object) +{ + GstDvdSubParse *parse = GST_DVD_SUB_PARSE (object); + + g_object_unref (parse->adapter); + parse->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_dvd_sub_parse_init (GstDvdSubParse * parse) +{ + parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_chain_function (parse->sinkpad, + GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_chain)); + gst_pad_set_event_function (parse->sinkpad, + GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_event)); + gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); + + parse->srcpad = gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_use_fixed_caps (parse->srcpad); + gst_pad_set_caps (parse->srcpad, + gst_static_pad_template_get_caps (&src_template)); + gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad); + + /* remainder */ + parse->adapter = gst_adapter_new (); + gst_dvd_sub_parse_reset (parse); +} + +static void +gst_dvd_sub_parse_reset (GstDvdSubParse * parse) +{ + parse->needed = 0; + parse->stamp = GST_CLOCK_TIME_NONE; + gst_adapter_clear (parse->adapter); +} + +static gboolean +gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstDvdSubParse *parse; + gboolean ret; + + parse = GST_DVD_SUB_PARSE (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_unref (event); + caps = gst_static_pad_template_get_caps (&src_template); + gst_pad_push_event (parse->srcpad, gst_event_new_caps (caps)); + gst_caps_unref (caps); + ret = TRUE; + break; + } + case GST_EVENT_FLUSH_STOP: + gst_dvd_sub_parse_reset (parse); + /* fall-through */ + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + + +static GstFlowReturn +gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstDvdSubParse *parse = GST_DVD_SUB_PARSE (parent); + GstAdapter *adapter; + GstBuffer *outbuf = NULL; + GstFlowReturn ret = GST_FLOW_OK; + + adapter = parse->adapter; + + GST_LOG_OBJECT (parse, "%" G_GSIZE_FORMAT " bytes, ts: %" GST_TIME_FORMAT, + gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + gst_adapter_push (adapter, buf); + + if (!parse->needed) { + guint8 data[2]; + + gst_adapter_copy (adapter, data, 0, 2); + parse->needed = GST_READ_UINT16_BE (data); + } + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + if (GST_CLOCK_TIME_IS_VALID (parse->stamp)) + /* normally, we expect only the first fragment to carry a timestamp */ + GST_WARNING_OBJECT (parse, "Received more timestamps than expected."); + else + parse->stamp = GST_BUFFER_TIMESTAMP (buf); + } + + if (parse->needed) { + guint av; + + av = gst_adapter_available (adapter); + if (av >= parse->needed) { + if (av > parse->needed) { + /* normally, we expect several fragment, boundary aligned */ + GST_WARNING_OBJECT (parse, "Unexpected: needed %d, " + "but more (%d) is available.", parse->needed, av); + } + outbuf = gst_adapter_take_buffer (adapter, parse->needed); + /* decorate buffer */ + GST_BUFFER_TIMESTAMP (outbuf) = parse->stamp; + /* reset state */ + parse->stamp = GST_CLOCK_TIME_NONE; + parse->needed = 0; + /* and send along */ + ret = gst_pad_push (parse->srcpad, outbuf); + } + } + + return ret; +} + +static GstStateChangeReturn +gst_dvd_sub_parse_change_state (GstElement * element, GstStateChange transition) +{ + GstDvdSubParse *parse = GST_DVD_SUB_PARSE (element); + GstStateChangeReturn ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_dvd_sub_parse_reset (parse); + break; + default: + break; + } + + return GST_STATE_CHANGE_SUCCESS; +} diff --git a/gst/dvdsub/gstdvdsubparse.h b/gst/dvdsub/gstdvdsubparse.h new file mode 100644 index 0000000..6149232 --- /dev/null +++ b/gst/dvdsub/gstdvdsubparse.h @@ -0,0 +1,65 @@ +/* GStreamer DVD subtitle parser + * Copyright (C) 2007 Mark Nauwelaerts <mnauw@users.sourceforge.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_DVDSUBPARSE_H__ +#define __GST_DVDSUBPARSE_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_DVD_SUB_PARSE \ + (gst_dvd_sub_parse_get_type()) +#define GST_DVD_SUB_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParse)) +#define GST_DVD_SUB_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParseClass)) +#define GST_DVD_SUB_PARSE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_DVD_SUB_PARSE, GstDvdSubParseClass)) +#define GST_IS_DVD_SUB_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_DVD_SUB_PARSE)) +#define GST_IS_DVD_SUB_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_DVD_SUB_PARSE)) + +typedef struct _GstDvdSubParse GstDvdSubParse; +typedef struct _GstDvdSubParseClass GstDvdSubParseClass; + +struct _GstDvdSubParse { + GstElement element; + + /*< private >*/ + GstPad *srcpad; + GstPad *sinkpad; + + GstAdapter *adapter; /* buffer incoming data */ + GstClockTime stamp; /* timestamp of current packet */ + guint needed; /* size of current packet to be assembled */ +}; + +struct _GstDvdSubParseClass { + GstElementClass parent_class; +}; + +GType gst_dvd_sub_parse_get_type (void); + +G_END_DECLS + +#endif /* __GST_DVDSUBPARSE_H__ */ + diff --git a/gst/realmedia/Makefile.am b/gst/realmedia/Makefile.am new file mode 100644 index 0000000..46cbbc3 --- /dev/null +++ b/gst/realmedia/Makefile.am @@ -0,0 +1,39 @@ +plugin_LTLIBRARIES = libgstrmdemux.la + +libgstrmdemux_la_SOURCES = rademux.c rmdemux.c \ + rmutils.c rdtdepay.c rdtmanager.c \ + rtspreal.c realhash.c asmrules.c \ + rdtjitterbuffer.c gstrdtbuffer.c \ + pnmsrc.c realmedia.c + + +libgstrmdemux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstrmdemux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstrtsp-@GST_API_VERSION@ \ + -lgstsdp-@GST_API_VERSION@ \ + -lgstpbutils-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) +libgstrmdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstrmdemux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = rademux.h rmdemux.h rmutils.h rdtdepay.h rdtmanager.h \ + rdtjitterbuffer.h rtspreal.h realhash.h asmrules.h gstrdtbuffer.h \ + pnmsrc.h + +noinst_PROGRAMS = asmrules +asmrules_CFLAGS = $(GST_CFLAGS) -DTEST +asmrules_LDADD = $(GST_LIBS) $(LIBM) + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstrmdemux -:SHARED libgstrmdemux \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstrmdemux_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstrmdemux_la_CFLAGS) \ + -:LDFLAGS $(libgstrmdemux_la_LDFLAGS) \ + $(libgstrmdemux_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/realmedia/Makefile.in b/gst/realmedia/Makefile.in new file mode 100644 index 0000000..5909e4a --- /dev/null +++ b/gst/realmedia/Makefile.in @@ -0,0 +1,986 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +noinst_PROGRAMS = asmrules$(EXEEXT) +subdir = gst/realmedia +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstrmdemux_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgstrmdemux_la_OBJECTS = libgstrmdemux_la-rademux.lo \ + libgstrmdemux_la-rmdemux.lo libgstrmdemux_la-rmutils.lo \ + libgstrmdemux_la-rdtdepay.lo libgstrmdemux_la-rdtmanager.lo \ + libgstrmdemux_la-rtspreal.lo libgstrmdemux_la-realhash.lo \ + libgstrmdemux_la-asmrules.lo \ + libgstrmdemux_la-rdtjitterbuffer.lo \ + libgstrmdemux_la-gstrdtbuffer.lo libgstrmdemux_la-pnmsrc.lo \ + libgstrmdemux_la-realmedia.lo +libgstrmdemux_la_OBJECTS = $(am_libgstrmdemux_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstrmdemux_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) \ + $(libgstrmdemux_la_LDFLAGS) $(LDFLAGS) -o $@ +PROGRAMS = $(noinst_PROGRAMS) +asmrules_SOURCES = asmrules.c +asmrules_OBJECTS = asmrules-asmrules.$(OBJEXT) +asmrules_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +asmrules_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(asmrules_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +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_CCLD_1 = +SOURCES = $(libgstrmdemux_la_SOURCES) asmrules.c +DIST_SOURCES = $(libgstrmdemux_la_SOURCES) asmrules.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstrmdemux.la +libgstrmdemux_la_SOURCES = rademux.c rmdemux.c \ + rmutils.c rdtdepay.c rdtmanager.c \ + rtspreal.c realhash.c asmrules.c \ + rdtjitterbuffer.c gstrdtbuffer.c \ + pnmsrc.c realmedia.c + +libgstrmdemux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstrmdemux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstrtsp-@GST_API_VERSION@ \ + -lgstsdp-@GST_API_VERSION@ \ + -lgstpbutils-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) + +libgstrmdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstrmdemux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = rademux.h rmdemux.h rmutils.h rdtdepay.h rdtmanager.h \ + rdtjitterbuffer.h rtspreal.h realhash.h asmrules.h gstrdtbuffer.h \ + pnmsrc.h + +asmrules_CFLAGS = $(GST_CFLAGS) -DTEST +asmrules_LDADD = $(GST_LIBS) $(LIBM) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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 gst/realmedia/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/realmedia/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstrmdemux.la: $(libgstrmdemux_la_OBJECTS) $(libgstrmdemux_la_DEPENDENCIES) $(EXTRA_libgstrmdemux_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstrmdemux_la_LINK) -rpath $(plugindir) $(libgstrmdemux_la_OBJECTS) $(libgstrmdemux_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +asmrules$(EXEEXT): $(asmrules_OBJECTS) $(asmrules_DEPENDENCIES) $(EXTRA_asmrules_DEPENDENCIES) + @rm -f asmrules$(EXEEXT) + $(AM_V_CCLD)$(asmrules_LINK) $(asmrules_OBJECTS) $(asmrules_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asmrules-asmrules.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-asmrules.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-gstrdtbuffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-pnmsrc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rademux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rdtdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rdtjitterbuffer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rdtmanager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-realhash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-realmedia.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rmdemux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rmutils.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrmdemux_la-rtspreal.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +libgstrmdemux_la-rademux.lo: rademux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rademux.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rademux.Tpo -c -o libgstrmdemux_la-rademux.lo `test -f 'rademux.c' || echo '$(srcdir)/'`rademux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rademux.Tpo $(DEPDIR)/libgstrmdemux_la-rademux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rademux.c' object='libgstrmdemux_la-rademux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rademux.lo `test -f 'rademux.c' || echo '$(srcdir)/'`rademux.c + +libgstrmdemux_la-rmdemux.lo: rmdemux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rmdemux.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rmdemux.Tpo -c -o libgstrmdemux_la-rmdemux.lo `test -f 'rmdemux.c' || echo '$(srcdir)/'`rmdemux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rmdemux.Tpo $(DEPDIR)/libgstrmdemux_la-rmdemux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmdemux.c' object='libgstrmdemux_la-rmdemux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rmdemux.lo `test -f 'rmdemux.c' || echo '$(srcdir)/'`rmdemux.c + +libgstrmdemux_la-rmutils.lo: rmutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rmutils.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rmutils.Tpo -c -o libgstrmdemux_la-rmutils.lo `test -f 'rmutils.c' || echo '$(srcdir)/'`rmutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rmutils.Tpo $(DEPDIR)/libgstrmdemux_la-rmutils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmutils.c' object='libgstrmdemux_la-rmutils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rmutils.lo `test -f 'rmutils.c' || echo '$(srcdir)/'`rmutils.c + +libgstrmdemux_la-rdtdepay.lo: rdtdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rdtdepay.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rdtdepay.Tpo -c -o libgstrmdemux_la-rdtdepay.lo `test -f 'rdtdepay.c' || echo '$(srcdir)/'`rdtdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rdtdepay.Tpo $(DEPDIR)/libgstrmdemux_la-rdtdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rdtdepay.c' object='libgstrmdemux_la-rdtdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rdtdepay.lo `test -f 'rdtdepay.c' || echo '$(srcdir)/'`rdtdepay.c + +libgstrmdemux_la-rdtmanager.lo: rdtmanager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rdtmanager.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rdtmanager.Tpo -c -o libgstrmdemux_la-rdtmanager.lo `test -f 'rdtmanager.c' || echo '$(srcdir)/'`rdtmanager.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rdtmanager.Tpo $(DEPDIR)/libgstrmdemux_la-rdtmanager.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rdtmanager.c' object='libgstrmdemux_la-rdtmanager.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rdtmanager.lo `test -f 'rdtmanager.c' || echo '$(srcdir)/'`rdtmanager.c + +libgstrmdemux_la-rtspreal.lo: rtspreal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rtspreal.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rtspreal.Tpo -c -o libgstrmdemux_la-rtspreal.lo `test -f 'rtspreal.c' || echo '$(srcdir)/'`rtspreal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rtspreal.Tpo $(DEPDIR)/libgstrmdemux_la-rtspreal.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtspreal.c' object='libgstrmdemux_la-rtspreal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rtspreal.lo `test -f 'rtspreal.c' || echo '$(srcdir)/'`rtspreal.c + +libgstrmdemux_la-realhash.lo: realhash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-realhash.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-realhash.Tpo -c -o libgstrmdemux_la-realhash.lo `test -f 'realhash.c' || echo '$(srcdir)/'`realhash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-realhash.Tpo $(DEPDIR)/libgstrmdemux_la-realhash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='realhash.c' object='libgstrmdemux_la-realhash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-realhash.lo `test -f 'realhash.c' || echo '$(srcdir)/'`realhash.c + +libgstrmdemux_la-asmrules.lo: asmrules.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-asmrules.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-asmrules.Tpo -c -o libgstrmdemux_la-asmrules.lo `test -f 'asmrules.c' || echo '$(srcdir)/'`asmrules.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-asmrules.Tpo $(DEPDIR)/libgstrmdemux_la-asmrules.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asmrules.c' object='libgstrmdemux_la-asmrules.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-asmrules.lo `test -f 'asmrules.c' || echo '$(srcdir)/'`asmrules.c + +libgstrmdemux_la-rdtjitterbuffer.lo: rdtjitterbuffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-rdtjitterbuffer.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-rdtjitterbuffer.Tpo -c -o libgstrmdemux_la-rdtjitterbuffer.lo `test -f 'rdtjitterbuffer.c' || echo '$(srcdir)/'`rdtjitterbuffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-rdtjitterbuffer.Tpo $(DEPDIR)/libgstrmdemux_la-rdtjitterbuffer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rdtjitterbuffer.c' object='libgstrmdemux_la-rdtjitterbuffer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-rdtjitterbuffer.lo `test -f 'rdtjitterbuffer.c' || echo '$(srcdir)/'`rdtjitterbuffer.c + +libgstrmdemux_la-gstrdtbuffer.lo: gstrdtbuffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-gstrdtbuffer.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-gstrdtbuffer.Tpo -c -o libgstrmdemux_la-gstrdtbuffer.lo `test -f 'gstrdtbuffer.c' || echo '$(srcdir)/'`gstrdtbuffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-gstrdtbuffer.Tpo $(DEPDIR)/libgstrmdemux_la-gstrdtbuffer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrdtbuffer.c' object='libgstrmdemux_la-gstrdtbuffer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-gstrdtbuffer.lo `test -f 'gstrdtbuffer.c' || echo '$(srcdir)/'`gstrdtbuffer.c + +libgstrmdemux_la-pnmsrc.lo: pnmsrc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-pnmsrc.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-pnmsrc.Tpo -c -o libgstrmdemux_la-pnmsrc.lo `test -f 'pnmsrc.c' || echo '$(srcdir)/'`pnmsrc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-pnmsrc.Tpo $(DEPDIR)/libgstrmdemux_la-pnmsrc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pnmsrc.c' object='libgstrmdemux_la-pnmsrc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-pnmsrc.lo `test -f 'pnmsrc.c' || echo '$(srcdir)/'`pnmsrc.c + +libgstrmdemux_la-realmedia.lo: realmedia.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -MT libgstrmdemux_la-realmedia.lo -MD -MP -MF $(DEPDIR)/libgstrmdemux_la-realmedia.Tpo -c -o libgstrmdemux_la-realmedia.lo `test -f 'realmedia.c' || echo '$(srcdir)/'`realmedia.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrmdemux_la-realmedia.Tpo $(DEPDIR)/libgstrmdemux_la-realmedia.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='realmedia.c' object='libgstrmdemux_la-realmedia.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrmdemux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrmdemux_la_CFLAGS) $(CFLAGS) -c -o libgstrmdemux_la-realmedia.lo `test -f 'realmedia.c' || echo '$(srcdir)/'`realmedia.c + +asmrules-asmrules.o: asmrules.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(asmrules_CFLAGS) $(CFLAGS) -MT asmrules-asmrules.o -MD -MP -MF $(DEPDIR)/asmrules-asmrules.Tpo -c -o asmrules-asmrules.o `test -f 'asmrules.c' || echo '$(srcdir)/'`asmrules.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asmrules-asmrules.Tpo $(DEPDIR)/asmrules-asmrules.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asmrules.c' object='asmrules-asmrules.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(asmrules_CFLAGS) $(CFLAGS) -c -o asmrules-asmrules.o `test -f 'asmrules.c' || echo '$(srcdir)/'`asmrules.c + +asmrules-asmrules.obj: asmrules.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(asmrules_CFLAGS) $(CFLAGS) -MT asmrules-asmrules.obj -MD -MP -MF $(DEPDIR)/asmrules-asmrules.Tpo -c -o asmrules-asmrules.obj `if test -f 'asmrules.c'; then $(CYGPATH_W) 'asmrules.c'; else $(CYGPATH_W) '$(srcdir)/asmrules.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asmrules-asmrules.Tpo $(DEPDIR)/asmrules-asmrules.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asmrules.c' object='asmrules-asmrules.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(asmrules_CFLAGS) $(CFLAGS) -c -o asmrules-asmrules.obj `if test -f 'asmrules.c'; then $(CYGPATH_W) 'asmrules.c'; else $(CYGPATH_W) '$(srcdir)/asmrules.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + clean-pluginLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS clean-pluginLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pluginLTLIBRARIES install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-pluginLTLIBRARIES + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstrmdemux -:SHARED libgstrmdemux \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstrmdemux_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstrmdemux_la_CFLAGS) \ + -:LDFLAGS $(libgstrmdemux_la_LDFLAGS) \ + $(libgstrmdemux_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/realmedia/asmrules.c b/gst/realmedia/asmrules.c new file mode 100644 index 0000000..1d52568 --- /dev/null +++ b/gst/realmedia/asmrules.c @@ -0,0 +1,711 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <string.h> +#include <stdlib.h> + +#include "asmrules.h" + +#define MAX_RULE_LENGTH 2048 + +/* define to enable some more debug */ +#undef DEBUG + +static GstASMNode * +gst_asm_node_new (void) +{ + GstASMNode *node; + + node = g_new0 (GstASMNode, 1); + node->type = GST_ASM_NODE_UNKNOWN; + + return node; +} + +static void +gst_asm_node_free (GstASMNode * node) +{ + if (node->left) + gst_asm_node_free (node->left); + if (node->right) + gst_asm_node_free (node->right); + if (node->type == GST_ASM_NODE_VARIABLE && node->data.varname) + g_free (node->data.varname); + g_free (node); +} + +static gfloat +gst_asm_operator_eval (GstASMOp optype, gfloat left, gfloat right) +{ + gfloat result = 0.0; + + switch (optype) { + case GST_ASM_OP_GREATER: + result = (gfloat) (left > right); + break; + case GST_ASM_OP_LESS: + result = (gfloat) (left < right); + break; + case GST_ASM_OP_GREATEREQUAL: + result = (gfloat) (left >= right); + break; + case GST_ASM_OP_LESSEQUAL: + result = (gfloat) (left <= right); + break; + case GST_ASM_OP_EQUAL: + result = (gfloat) (left == right); + break; + case GST_ASM_OP_NOTEQUAL: + result = (gfloat) (left != right); + break; + case GST_ASM_OP_AND: + result = (gfloat) (left && right); + break; + case GST_ASM_OP_OR: + result = (gfloat) (left || right); + break; + default: + break; + } + return result; +} + +static gfloat +gst_asm_node_evaluate (GstASMNode * node, GHashTable * vars) +{ + gfloat result = 0.0; + + if (node == NULL) + return 0.0; + + switch (node->type) { + case GST_ASM_NODE_VARIABLE: + { + gchar *val; + + val = g_hash_table_lookup (vars, node->data.varname); + if (val) + result = (gfloat) atof (val); + break; + } + case GST_ASM_NODE_INTEGER: + result = (gfloat) node->data.intval; + break; + case GST_ASM_NODE_FLOAT: + result = node->data.floatval; + break; + case GST_ASM_NODE_OPERATOR: + { + gfloat left, right; + + left = gst_asm_node_evaluate (node->left, vars); + right = gst_asm_node_evaluate (node->right, vars); + + result = gst_asm_operator_eval (node->data.optype, left, right); + break; + } + default: + break; + } + return result; +} + +#define IS_SPACE(p) (((p) == ' ') || ((p) == '\n') || \ + ((p) == '\r') || ((p) == '\t')) +#define IS_RULE_DELIM(p) (((p) == ',') || ((p) == ';') || ((p) == ')')) +#define IS_OPERATOR(p) (((p) == '>') || ((p) == '<') || \ + ((p) == '=') || ((p) == '!') || \ + ((p) == '&') || ((p) == '|')) +#define IS_NUMBER(p) ((((p) >= '0') && ((p) <= '9')) || ((p) == '.')) +#define IS_CHAR(p) (!IS_OPERATOR(ch) && !IS_RULE_DELIM(ch) && (ch != '\0')) + +#define IS_OP_TOKEN(t) (((t) == GST_ASM_TOKEN_AND) || ((t) == GST_ASM_TOKEN_OR)) +#define IS_COND_TOKEN(t) (((t) == GST_ASM_TOKEN_LESS) || ((t) == GST_ASM_TOKEN_LESSEQUAL) || \ + ((t) == GST_ASM_TOKEN_GREATER) || ((t) == GST_ASM_TOKEN_GREATEREQUAL) || \ + ((t) == GST_ASM_TOKEN_EQUAL) || ((t) == GST_ASM_TOKEN_NOTEQUAL)) + +typedef struct +{ + const gchar *buffer; + gint pos; + gchar ch; + + GstASMToken token; + gchar val[MAX_RULE_LENGTH]; +} GstASMScan; + +#define NEXT_CHAR(scan) ((scan)->ch = (scan)->buffer[(scan)->pos++]) +#define THIS_CHAR(scan) ((scan)->ch) + +static GstASMScan * +gst_asm_scan_new (const gchar * buffer) +{ + GstASMScan *scan; + + scan = g_new0 (GstASMScan, 1); + scan->buffer = buffer; + NEXT_CHAR (scan); + + return scan; +} + +static void +gst_asm_scan_free (GstASMScan * scan) +{ + g_free (scan); +} + +static void +gst_asm_scan_string (GstASMScan * scan, gchar delim) +{ + gchar ch; + gint i = 0; + + ch = THIS_CHAR (scan); + while ((ch != delim) && (ch != '\0')) { + if (i < MAX_RULE_LENGTH - 1) + scan->val[i++] = ch; + ch = NEXT_CHAR (scan); + if (ch == '\\') + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + if (ch == delim) + NEXT_CHAR (scan); + + scan->token = GST_ASM_TOKEN_STRING; +} + +static void +gst_asm_scan_number (GstASMScan * scan) +{ + gchar ch; + gint i = 0; + gboolean have_float = FALSE; + + ch = THIS_CHAR (scan); + /* real strips all spaces that are not inside quotes for numbers */ + while ((IS_NUMBER (ch) || IS_SPACE (ch))) { + if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch)) + scan->val[i++] = ch; + if (ch == '.') + have_float = TRUE; + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + if (have_float) + scan->token = GST_ASM_TOKEN_FLOAT; + else + scan->token = GST_ASM_TOKEN_INT; +} + +static void +gst_asm_scan_identifier (GstASMScan * scan) +{ + gchar ch; + gint i = 0; + + ch = THIS_CHAR (scan); + /* real strips all spaces that are not inside quotes for identifiers */ + while ((IS_CHAR (ch) || IS_SPACE (ch))) { + if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch)) + scan->val[i++] = ch; + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + scan->token = GST_ASM_TOKEN_IDENTIFIER; +} + +static void +gst_asm_scan_print_token (GstASMScan * scan) +{ +#ifdef DEBUG + switch (scan->token) { + case GST_ASM_TOKEN_NONE: + g_print ("none\n"); + break; + case GST_ASM_TOKEN_EOF: + g_print ("EOF\n"); + break; + + case GST_ASM_TOKEN_INT: + g_print ("INT %d\n", atoi (scan->val)); + break; + case GST_ASM_TOKEN_FLOAT: + g_print ("FLOAT %f\n", atof (scan->val)); + break; + case GST_ASM_TOKEN_IDENTIFIER: + g_print ("ID %s\n", scan->val); + break; + case GST_ASM_TOKEN_STRING: + g_print ("STRING %s\n", scan->val); + break; + + case GST_ASM_TOKEN_HASH: + g_print ("HASH\n"); + break; + case GST_ASM_TOKEN_SEMICOLON: + g_print ("SEMICOLON\n"); + break; + case GST_ASM_TOKEN_COMMA: + g_print ("COMMA\n"); + break; + case GST_ASM_TOKEN_EQUAL: + g_print ("==\n"); + break; + case GST_ASM_TOKEN_NOTEQUAL: + g_print ("!=\n"); + break; + case GST_ASM_TOKEN_AND: + g_print ("&&\n"); + break; + case GST_ASM_TOKEN_OR: + g_print ("||\n"); + break; + case GST_ASM_TOKEN_LESS: + g_print ("<\n"); + break; + case GST_ASM_TOKEN_LESSEQUAL: + g_print ("<=\n"); + break; + case GST_ASM_TOKEN_GREATER: + g_print (">\n"); + break; + case GST_ASM_TOKEN_GREATEREQUAL: + g_print (">=\n"); + break; + case GST_ASM_TOKEN_DOLLAR: + g_print ("$\n"); + break; + case GST_ASM_TOKEN_LPAREN: + g_print ("(\n"); + break; + case GST_ASM_TOKEN_RPAREN: + g_print (")\n"); + break; + default: + break; + } +#endif +} + +static GstASMToken +gst_asm_scan_next_token (GstASMScan * scan) +{ + gchar ch; + + ch = THIS_CHAR (scan); + + /* skip spaces */ + while (IS_SPACE (ch)) + ch = NEXT_CHAR (scan); + + /* remove \ which is common in front of " */ + while (ch == '\\') + ch = NEXT_CHAR (scan); + + switch (ch) { + case '#': + scan->token = GST_ASM_TOKEN_HASH; + NEXT_CHAR (scan); + break; + case ';': + scan->token = GST_ASM_TOKEN_SEMICOLON; + NEXT_CHAR (scan); + break; + case ',': + scan->token = GST_ASM_TOKEN_COMMA; + NEXT_CHAR (scan); + break; + case '=': + scan->token = GST_ASM_TOKEN_EQUAL; + if (NEXT_CHAR (scan) == '=') + NEXT_CHAR (scan); + break; + case '!': + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_NOTEQUAL; + NEXT_CHAR (scan); + } + break; + case '&': + scan->token = GST_ASM_TOKEN_AND; + if (NEXT_CHAR (scan) == '&') + NEXT_CHAR (scan); + break; + case '|': + scan->token = GST_ASM_TOKEN_OR; + if (NEXT_CHAR (scan) == '|') + NEXT_CHAR (scan); + break; + case '<': + scan->token = GST_ASM_TOKEN_LESS; + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_LESSEQUAL; + NEXT_CHAR (scan); + } + break; + case '>': + scan->token = GST_ASM_TOKEN_GREATER; + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_GREATEREQUAL; + NEXT_CHAR (scan); + } + break; + case '$': + scan->token = GST_ASM_TOKEN_DOLLAR; + NEXT_CHAR (scan); + break; + case '(': + scan->token = GST_ASM_TOKEN_LPAREN; + NEXT_CHAR (scan); + break; + case ')': + scan->token = GST_ASM_TOKEN_RPAREN; + NEXT_CHAR (scan); + break; + case '"': + NEXT_CHAR (scan); + gst_asm_scan_string (scan, '"'); + break; + case '\'': + NEXT_CHAR (scan); + gst_asm_scan_string (scan, '\''); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + gst_asm_scan_number (scan); + break; + case '\0': + scan->token = GST_ASM_TOKEN_EOF; + break; + default: + gst_asm_scan_identifier (scan); + break; + } + gst_asm_scan_print_token (scan); + return scan->token; +} + +static GstASMRule * +gst_asm_rule_new (void) +{ + GstASMRule *rule; + + rule = g_new (GstASMRule, 1); + rule->root = NULL; + rule->props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + return rule; +} + +static void +gst_asm_rule_free (GstASMRule * rule) +{ + g_hash_table_destroy (rule->props); + if (rule->root) + gst_asm_node_free (rule->root); + g_free (rule); +} + +static void +gst_asm_rule_add_property (GstASMRule * rule, gchar * key, gchar * val) +{ + g_hash_table_insert (rule->props, key, val); +} + +static GstASMNode *gst_asm_scan_parse_condition (GstASMScan * scan); + +static GstASMNode * +gst_asm_scan_parse_operand (GstASMScan * scan) +{ + GstASMNode *node; + + switch (scan->token) { + case GST_ASM_TOKEN_DOLLAR: + gst_asm_scan_next_token (scan); + + if (scan->token != GST_ASM_TOKEN_IDENTIFIER) + g_warning ("identifier expected"); + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_VARIABLE; + node->data.varname = g_strdup (scan->val); + break; + case GST_ASM_TOKEN_INT: + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_INTEGER; + node->data.intval = (gfloat) atof (scan->val); + break; + case GST_ASM_TOKEN_FLOAT: + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_FLOAT; + node->data.floatval = atoi (scan->val); + break; + case GST_ASM_TOKEN_LPAREN: + gst_asm_scan_next_token (scan); + node = gst_asm_scan_parse_condition (scan); + if (scan->token != GST_ASM_TOKEN_RPAREN) + g_warning (") expected"); + break; + default: + g_warning ("$ <number> or ) expected"); + node = NULL; + break; + } + gst_asm_scan_next_token (scan); + + return node; +} + +static GstASMNode * +gst_asm_scan_parse_expression (GstASMScan * scan) +{ + GstASMNode *node, *left; + + node = gst_asm_scan_parse_operand (scan); + + while (IS_COND_TOKEN (scan->token)) { + left = node; + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_OPERATOR; + node->data.optype = (GstASMOp) scan->token; + + gst_asm_scan_next_token (scan); + + node->right = gst_asm_scan_parse_operand (scan); + node->left = left; + } + return node; +} + +static GstASMNode * +gst_asm_scan_parse_condition (GstASMScan * scan) +{ + GstASMNode *node, *left; + + node = gst_asm_scan_parse_expression (scan); + + while (IS_OP_TOKEN (scan->token)) { + left = node; + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_OPERATOR; + node->data.optype = (GstASMOp) scan->token; + + gst_asm_scan_next_token (scan); + + node->right = gst_asm_scan_parse_expression (scan); + node->left = left; + } + return node; +} + +static void +gst_asm_scan_parse_property (GstASMRule * rule, GstASMScan * scan) +{ + gchar *key, *val; + + if (scan->token != GST_ASM_TOKEN_IDENTIFIER) { + g_warning ("identifier expected"); + return; + } + key = g_strdup (scan->val); + + gst_asm_scan_next_token (scan); + if (scan->token != GST_ASM_TOKEN_EQUAL) { + g_warning ("= expected"); + return; + } + gst_asm_scan_next_token (scan); + val = g_strdup (scan->val); + + gst_asm_rule_add_property (rule, key, val); + gst_asm_scan_next_token (scan); +} + +static GstASMRule * +gst_asm_scan_parse_rule (GstASMScan * scan) +{ + GstASMRule *rule; + + rule = gst_asm_rule_new (); + + if (scan->token == GST_ASM_TOKEN_HASH) { + gst_asm_scan_next_token (scan); + rule->root = gst_asm_scan_parse_condition (scan); + if (scan->token == GST_ASM_TOKEN_COMMA) + gst_asm_scan_next_token (scan); + } + + if (scan->token != GST_ASM_TOKEN_SEMICOLON) { + gst_asm_scan_parse_property (rule, scan); + while (scan->token == GST_ASM_TOKEN_COMMA) { + gst_asm_scan_next_token (scan); + gst_asm_scan_parse_property (rule, scan); + } + gst_asm_scan_next_token (scan); + } + return rule; +} + +static gboolean +gst_asm_rule_evaluate (GstASMRule * rule, GHashTable * vars) +{ + gboolean res; + + if (rule->root) { + res = (gboolean) gst_asm_node_evaluate (rule->root, vars); + } else + res = TRUE; + + return res; +} + +GstASMRuleBook * +gst_asm_rule_book_new (const gchar * rulebook) +{ + GstASMRuleBook *book; + GstASMRule *rule = NULL; + GstASMScan *scan; + GstASMToken token; + + book = g_new0 (GstASMRuleBook, 1); + book->rulebook = rulebook; + + scan = gst_asm_scan_new (book->rulebook); + gst_asm_scan_next_token (scan); + + do { + rule = gst_asm_scan_parse_rule (scan); + if (rule) { + book->rules = g_list_append (book->rules, rule); + book->n_rules++; + } + token = scan->token; + } while (token != GST_ASM_TOKEN_EOF); + + gst_asm_scan_free (scan); + + return book; +} + +void +gst_asm_rule_book_free (GstASMRuleBook * book) +{ + GList *walk; + + for (walk = book->rules; walk; walk = g_list_next (walk)) { + GstASMRule *rule = (GstASMRule *) walk->data; + + gst_asm_rule_free (rule); + } + g_list_free (book->rules); + g_free (book); +} + +gint +gst_asm_rule_book_match (GstASMRuleBook * book, GHashTable * vars, + gint * rulematches) +{ + GList *walk; + gint i, n = 0; + + for (walk = book->rules, i = 0; walk; walk = g_list_next (walk), i++) { + GstASMRule *rule = (GstASMRule *) walk->data; + + if (gst_asm_rule_evaluate (rule, vars)) { + rulematches[n++] = i; + } + } + return n; +} + +#ifdef TEST +gint +main (gint argc, gchar * argv[]) +{ + GstASMRuleBook *book; + gint rulematch[MAX_RULEMATCHES]; + GHashTable *vars; + gint i, n; + + static const gchar rules1[] = + "#($Bandwidth < 67959),TimestampDelivery=T,DropByN=T," + "priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959)," + "AverageBandwidth=67959,Priority=9;#($Bandwidth >= 67959) && ($Bandwidth" + " < 167959),AverageBandwidth=0,Priority=5,OnDepend=\\\"1\\\";#($Bandwidth >= 167959)" + " && ($Bandwidth < 267959),AverageBandwidth=167959,Priority=9;#($Bandwidth >= 167959)" + " && ($Bandwidth < 267959),AverageBandwidth=0,Priority=5,OnDepend=\\\"3\\\";" + "#($Bandwidth >= 267959),AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959)" + ",AverageBandwidth=0,Priority=5,OnDepend=\\\"5\\\";"; + static const gchar rules2[] = + "AverageBandwidth=32041,Priority=5;AverageBandwidth=0," + "Priority=5,OnDepend=\\\"0\\\", OffDepend=\\\"0\\\";"; + static const gchar rules3[] = + "#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=27500,priority=9,PNMKeyframeRule=T;#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=0,priority=5,PNMNonKeyframeRule=T;#(($Bandwidth < 27500) && ($OldPNMPlayer)),TimestampDelivery=T,DropByN=T,priority=9,PNMThinningRule=T;#($Bandwidth < 13899),TimestampDelivery=T,DropByN=T,priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=13899,Priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=0,Priority=5,OnDepend=\\\"4\\\";#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=19000,Priority=9;#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=0,Priority=5,OnDepend=\\\"6\\\";#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=27500,Priority=9;#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=0,Priority=5,OnDepend=\\\"8\\\";#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=132958,Priority=9;#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"10\\\";#($Bandwidth >= 187958),AverageBandwidth=187958,Priority=9;#($Bandwidth >= 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"12\\\";"; + + vars = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (vars, (gchar *) "Bandwidth", (gchar *) "300000"); + + book = gst_asm_rule_book_new (rules1); + n = gst_asm_rule_book_match (book, vars, rulematch); + gst_asm_rule_book_free (book); + + g_print ("%d rules matched\n", n); + for (i = 0; i < n; i++) { + g_print ("rule %d matched\n", rulematch[i]); + } + + book = gst_asm_rule_book_new (rules2); + n = gst_asm_rule_book_match (book, vars, rulematch); + gst_asm_rule_book_free (book); + + g_print ("%d rules matched\n", n); + for (i = 0; i < n; i++) { + g_print ("rule %d matched\n", rulematch[i]); + } + + book = gst_asm_rule_book_new (rules3); + n = gst_asm_rule_book_match (book, vars, rulematch); + gst_asm_rule_book_free (book); + + + g_print ("%d rules matched\n", n); + for (i = 0; i < n; i++) { + g_print ("rule %d matched\n", rulematch[i]); + } + + g_hash_table_destroy (vars); + + return 0; +} +#endif diff --git a/gst/realmedia/asmrules.h b/gst/realmedia/asmrules.h new file mode 100644 index 0000000..1f84f68 --- /dev/null +++ b/gst/realmedia/asmrules.h @@ -0,0 +1,115 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ASM_RULES_H__ +#define __GST_ASM_RULES_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define MAX_RULEMATCHES 16 + +typedef struct _GstASMNode GstASMNode; +typedef struct _GstASMRule GstASMRule; +typedef struct _GstASMRuleBook GstASMRuleBook; + +typedef enum { + GST_ASM_TOKEN_NONE, + GST_ASM_TOKEN_EOF, + + GST_ASM_TOKEN_INT, + GST_ASM_TOKEN_FLOAT, + GST_ASM_TOKEN_IDENTIFIER, + GST_ASM_TOKEN_STRING, + + GST_ASM_TOKEN_HASH, + GST_ASM_TOKEN_SEMICOLON, + GST_ASM_TOKEN_COMMA, + GST_ASM_TOKEN_DOLLAR, + + GST_ASM_TOKEN_LPAREN, + GST_ASM_TOKEN_RPAREN, + + GST_ASM_TOKEN_GREATER, + GST_ASM_TOKEN_LESS, + GST_ASM_TOKEN_GREATEREQUAL, + GST_ASM_TOKEN_LESSEQUAL, + GST_ASM_TOKEN_EQUAL, + GST_ASM_TOKEN_NOTEQUAL, + + GST_ASM_TOKEN_AND, + GST_ASM_TOKEN_OR +} GstASMToken; + +typedef enum { + GST_ASM_NODE_UNKNOWN, + GST_ASM_NODE_VARIABLE, + GST_ASM_NODE_INTEGER, + GST_ASM_NODE_FLOAT, + GST_ASM_NODE_OPERATOR +} GstASMNodeType; + +typedef enum { + GST_ASM_OP_GREATER = GST_ASM_TOKEN_GREATER, + GST_ASM_OP_LESS = GST_ASM_TOKEN_LESS, + GST_ASM_OP_GREATEREQUAL = GST_ASM_TOKEN_GREATEREQUAL, + GST_ASM_OP_LESSEQUAL = GST_ASM_TOKEN_LESSEQUAL, + GST_ASM_OP_EQUAL = GST_ASM_TOKEN_EQUAL, + GST_ASM_OP_NOTEQUAL = GST_ASM_TOKEN_NOTEQUAL, + + GST_ASM_OP_AND = GST_ASM_TOKEN_AND, + GST_ASM_OP_OR = GST_ASM_TOKEN_OR +} GstASMOp; + +struct _GstASMNode { + GstASMNodeType type; + + union { + gchar *varname; + gint intval; + gfloat floatval; + GstASMOp optype; + } data; + + GstASMNode *left; + GstASMNode *right; +}; + +struct _GstASMRule { + GstASMNode *root; + GHashTable *props; +}; + +struct _GstASMRuleBook { + const gchar *rulebook; + + guint n_rules; + GList *rules; +}; + +G_END_DECLS + +GstASMRuleBook* gst_asm_rule_book_new (const gchar *rulebook); +void gst_asm_rule_book_free (GstASMRuleBook *book); + +gint gst_asm_rule_book_match (GstASMRuleBook *book, GHashTable *vars, + gint *rulematches); + +#endif /* __GST_ASM_RULES_H__ */ diff --git a/gst/realmedia/gstrdtbuffer.c b/gst/realmedia/gstrdtbuffer.c new file mode 100644 index 0000000..50bc7f4 --- /dev/null +++ b/gst/realmedia/gstrdtbuffer.c @@ -0,0 +1,477 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <string.h> + +#include "gstrdtbuffer.h" + +gboolean +gst_rdt_buffer_validate_data (guint8 * data, guint len) +{ + return TRUE; +} + +gboolean +gst_rdt_buffer_validate (GstBuffer * buffer) +{ + return TRUE; +} + +guint +gst_rdt_buffer_get_packet_count (GstBuffer * buffer) +{ + GstRDTPacket packet; + guint count; + + g_return_val_if_fail (GST_IS_BUFFER (buffer), 0); + + count = 0; + if (gst_rdt_buffer_get_first_packet (buffer, &packet)) { + do { + count++; + } while (gst_rdt_packet_move_to_next (&packet)); + } + return count; +} + +static gboolean +read_packet_header (GstRDTPacket * packet) +{ + GstMapInfo map; + guint8 *data; + gsize size; + guint offset; + guint length; + guint length_offset; + + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + offset = packet->offset; + + /* check if we are at the end of the buffer, we add 3 because we also want to + * ensure we can read the type, which is always at offset 1 and 2 bytes long. */ + if (offset + 3 > size) + goto packet_end; + + /* read type */ + packet->type = GST_READ_UINT16_BE (&data[offset + 1]); + + length = -1; + length_offset = -1; + + /* figure out the length of the packet, this depends on the type */ + if (GST_RDT_IS_DATA_TYPE (packet->type)) { + if (data[offset] & 0x80) + /* length is present */ + length_offset = 3; + } else { + switch (packet->type) { + case GST_RDT_TYPE_ASMACTION: + if (data[offset] & 0x80) + length_offset = 5; + break; + case GST_RDT_TYPE_BWREPORT: + if (data[offset] & 0x80) + length_offset = 3; + break; + case GST_RDT_TYPE_ACK: + if (data[offset] & 0x80) + length_offset = 3; + break; + case GST_RDT_TYPE_RTTREQ: + length = 3; + break; + case GST_RDT_TYPE_RTTRESP: + length = 11; + break; + case GST_RDT_TYPE_CONGESTION: + length = 11; + break; + case GST_RDT_TYPE_STREAMEND: + length = 9; + /* total_reliable */ + if (data[offset] & 0x80) + length += 2; + /* stream_id_expansion */ + if ((data[offset] & 0x7c) == 0x7c) + length += 2; + /* ext_flag, FIXME, get string length */ + if ((data[offset] & 0x1) == 0x1) + length += 7; + break; + case GST_RDT_TYPE_REPORT: + if (data[offset] & 0x80) + length_offset = 3; + break; + case GST_RDT_TYPE_LATENCY: + if (data[offset] & 0x80) + length_offset = 3; + break; + case GST_RDT_TYPE_INFOREQ: + length = 3; + /* request_time_ms */ + if (data[offset] & 0x2) + length += 2; + break; + case GST_RDT_TYPE_INFORESP: + length = 3; + /* has_rtt_info */ + if (data[offset] & 0x4) { + length += 4; + /* is_delayed */ + if (data[offset] & 0x2) { + length += 4; + } + } + if (data[offset] & 0x1) { + /* buffer_info_count, FIXME read and skip */ + length += 2; + } + break; + case GST_RDT_TYPE_AUTOBW: + if (data[offset] & 0x80) + length_offset = 3; + break; + case GST_RDT_TYPE_INVALID: + default: + goto unknown_packet; + } + } + + if (length != -1) { + /* we have a fixed length */ + packet->length = length; + } else if (length_offset != -1) { + /* we can read the length from an offset */ + packet->length = GST_READ_UINT16_BE (&data[length_offset]); + } else { + /* length is remainder of packet */ + packet->length = size - offset; + } + gst_buffer_unmap (packet->buffer, &map); + + /* the length should be smaller than the remaining size */ + if (packet->length + offset > size) + goto invalid_length; + + return TRUE; + + /* ERRORS */ +packet_end: + { + gst_buffer_unmap (packet->buffer, &map); + return FALSE; + } +unknown_packet: + { + packet->type = GST_RDT_TYPE_INVALID; + gst_buffer_unmap (packet->buffer, &map); + return FALSE; + } +invalid_length: + { + packet->type = GST_RDT_TYPE_INVALID; + packet->length = 0; + return FALSE; + } +} + +gboolean +gst_rdt_buffer_get_first_packet (GstBuffer * buffer, GstRDTPacket * packet) +{ + g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (packet != NULL, FALSE); + + /* init to 0 */ + packet->buffer = buffer; + packet->offset = 0; + packet->type = GST_RDT_TYPE_INVALID; + memset (&packet->map, 0, sizeof (GstMapInfo)); + + if (!read_packet_header (packet)) + return FALSE; + + return TRUE; +} + +gboolean +gst_rdt_packet_move_to_next (GstRDTPacket * packet) +{ + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, FALSE); + g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE); + + /* if we have an invalid packet, it must be the last, + * return FALSE */ + if (packet->type == GST_RDT_TYPE_INVALID) + goto end; + + /* move to next packet */ + packet->offset += packet->length; + + /* try to read new header */ + if (!read_packet_header (packet)) + goto end; + + return TRUE; + + /* ERRORS */ +end: + { + packet->type = GST_RDT_TYPE_INVALID; + return FALSE; + } +} + +GstRDTType +gst_rdt_packet_get_type (GstRDTPacket * packet) +{ + g_return_val_if_fail (packet != NULL, GST_RDT_TYPE_INVALID); + g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, + GST_RDT_TYPE_INVALID); + + return packet->type; +} + +guint16 +gst_rdt_packet_get_length (GstRDTPacket * packet) +{ + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, 0); + + return packet->length; +} + +GstBuffer * +gst_rdt_packet_to_buffer (GstRDTPacket * packet) +{ + GstBuffer *result; + + g_return_val_if_fail (packet != NULL, NULL); + g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, NULL); + + result = + gst_buffer_copy_region (packet->buffer, GST_BUFFER_COPY_ALL, + packet->offset, packet->length); + /* timestamp applies to all packets in this buffer */ + GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (packet->buffer); + + return result; +} + +gint +gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2) +{ + return (gint16) (seqnum2 - seqnum1); +} + +guint16 +gst_rdt_packet_data_get_seq (GstRDTPacket * packet) +{ + GstMapInfo map; + guint header; + guint16 result; + + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + + /* skip header bits */ + header = packet->offset + 1; + + /* read seq_no */ + result = GST_READ_UINT16_BE (&map.data[header]); + + gst_buffer_unmap (packet->buffer, &map); + + return result; +} + +guint8 * +gst_rdt_packet_data_map (GstRDTPacket * packet, guint * size) +{ + GstMapInfo map; + guint header; + gboolean length_included_flag; + gboolean need_reliable_flag; + guint8 stream_id; + guint8 asm_rule_number; + + g_return_val_if_fail (packet != NULL, NULL); + g_return_val_if_fail (packet->map.data == NULL, NULL); + g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), NULL); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + + header = packet->offset; + + length_included_flag = (map.data[header] & 0x80) == 0x80; + need_reliable_flag = (map.data[header] & 0x40) == 0x40; + stream_id = (map.data[header] & 0x3e) >> 1; + + /* skip seq_no and header bits */ + header += 3; + + if (length_included_flag) { + /* skip length */ + header += 2; + } + asm_rule_number = (map.data[header] & 0x3f); + + /* skip timestamp and asm_rule_number */ + header += 5; + + if (stream_id == 0x1f) { + /* skip stream_id_expansion */ + header += 2; + } + if (need_reliable_flag) { + /* skip total_reliable */ + header += 2; + } + if (asm_rule_number == 63) { + /* skip asm_rule_number_expansion */ + header += 2; + } + + if (size) + *size = packet->length - (header - packet->offset); + + packet->map = map; + + return &map.data[header]; +} + +gboolean +gst_rdt_packet_data_unmap (GstRDTPacket * packet) +{ + g_return_val_if_fail (packet != NULL, FALSE); + g_return_val_if_fail (packet->map.data != NULL, FALSE); + + gst_buffer_unmap (packet->buffer, &packet->map); + packet->map.data = NULL; + + return TRUE; +} + +guint16 +gst_rdt_packet_data_get_stream_id (GstRDTPacket * packet) +{ + GstMapInfo map; + guint16 result; + guint header; + gboolean length_included_flag; + + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + + header = packet->offset; + + length_included_flag = (map.data[header] & 0x80) == 0x80; + result = (map.data[header] & 0x3e) >> 1; + if (result == 31) { + /* skip seq_no and header bits */ + header += 3; + + if (length_included_flag) { + /* skip length */ + header += 2; + } + /* skip asm_rule_number and timestamp */ + header += 5; + + /* stream_id_expansion */ + result = GST_READ_UINT16_BE (&map.data[header]); + } + gst_buffer_unmap (packet->buffer, &map); + + return result; +} + +guint32 +gst_rdt_packet_data_get_timestamp (GstRDTPacket * packet) +{ + GstMapInfo map; + guint header; + gboolean length_included_flag; + guint32 result; + + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + + header = packet->offset; + + length_included_flag = (map.data[header] & 0x80) == 0x80; + + /* skip seq_no and header bits */ + header += 3; + + if (length_included_flag) { + /* skip length */ + header += 2; + } + /* skip asm_rule_number */ + header += 1; + + /* get timestamp */ + result = GST_READ_UINT32_BE (&map.data[header]); + gst_buffer_unmap (packet->buffer, &map); + + return result; +} + +guint8 +gst_rdt_packet_data_get_flags (GstRDTPacket * packet) +{ + GstMapInfo map; + guint8 result; + guint header; + gboolean length_included_flag; + + g_return_val_if_fail (packet != NULL, 0); + g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0); + + gst_buffer_map (packet->buffer, &map, GST_MAP_READ); + + header = packet->offset; + + length_included_flag = (map.data[header] & 0x80) == 0x80; + + /* skip seq_no and header bits */ + header += 3; + + if (length_included_flag) { + /* skip length */ + header += 2; + } + /* get flags */ + result = map.data[header]; + gst_buffer_unmap (packet->buffer, &map); + + return result; +} diff --git a/gst/realmedia/gstrdtbuffer.h b/gst/realmedia/gstrdtbuffer.h new file mode 100644 index 0000000..1ff9c93 --- /dev/null +++ b/gst/realmedia/gstrdtbuffer.h @@ -0,0 +1,122 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * gstrdtbuffer.h: various helper functions to manipulate buffers + * with RDT payload. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RDTBUFFER_H__ +#define __GST_RDTBUFFER_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +/** + * GstRDTType: + * @GST_RDT_TYPE_INVALID: + * @GST_RDT_TYPE_ASMACTION: + * @GST_RDT_TYPE_ACK: + * @GST_RDT_TYPE_RTTREQ: + * @GST_RDT_TYPE_RTTRESP: + * @GST_RDT_TYPE_CONGESTION: + * @GST_RDT_TYPE_STREAMEND: + * @GST_RDT_TYPE_LATENCY: + * @GST_RDT_TYPE_INFOREQ: + * @GST_RDT_TYPE_INFORESP: + * @GST_RDT_TYPE_AUTOBW: + * + * Different RDT packet types. + */ +typedef enum +{ + GST_RDT_TYPE_INVALID = 0xffff, + GST_RDT_TYPE_ASMACTION = 0xff00, + GST_RDT_TYPE_BWREPORT = 0xff01, + GST_RDT_TYPE_ACK = 0xff02, + GST_RDT_TYPE_RTTREQ = 0xff03, + GST_RDT_TYPE_RTTRESP = 0xff04, + GST_RDT_TYPE_CONGESTION = 0xff05, + GST_RDT_TYPE_STREAMEND = 0xff06, + GST_RDT_TYPE_REPORT = 0xff07, + GST_RDT_TYPE_LATENCY = 0xff08, + GST_RDT_TYPE_INFOREQ = 0xff09, + GST_RDT_TYPE_INFORESP = 0xff0a, + GST_RDT_TYPE_AUTOBW = 0xff0b +} GstRDTType; + +/** + * GST_RDT_IS_DATA_TYPE: + * @t: the #GstRDTType to check + * + * Check if @t is a data packet type. + */ +#define GST_RDT_IS_DATA_TYPE(t) ((t) < 0xff00) + +typedef struct _GstRDTPacket GstRDTPacket; + +/** + * GstRDTPacket: + * @buffer: pointer to RDT buffer + * @offset: offset of packet in buffer data + * + * Data structure that points to a packet at @offset in @buffer. + * The size of the structure is made public to allow stack allocations. + */ +struct _GstRDTPacket +{ + GstBuffer *buffer; + guint offset; + + /*< private >*/ + GstRDTType type; /* type of current packet */ + guint16 length; /* length of current packet in bytes */ + GstMapInfo map; /* last mapped data */ +}; + +/* validate buffers */ +gboolean gst_rdt_buffer_validate_data (guint8 *data, guint len); +gboolean gst_rdt_buffer_validate (GstBuffer *buffer); + +/* retrieving packets */ +guint gst_rdt_buffer_get_packet_count (GstBuffer *buffer); +gboolean gst_rdt_buffer_get_first_packet (GstBuffer *buffer, GstRDTPacket *packet); +gboolean gst_rdt_packet_move_to_next (GstRDTPacket *packet); + +/* working with packets */ +GstRDTType gst_rdt_packet_get_type (GstRDTPacket *packet); +guint16 gst_rdt_packet_get_length (GstRDTPacket *packet); +GstBuffer* gst_rdt_packet_to_buffer (GstRDTPacket *packet); + + +/* data packets */ +guint16 gst_rdt_packet_data_get_seq (GstRDTPacket *packet); +guint8 * gst_rdt_packet_data_map (GstRDTPacket *packet, guint *size); +gboolean gst_rdt_packet_data_unmap (GstRDTPacket *packet); +guint16 gst_rdt_packet_data_get_stream_id (GstRDTPacket *packet); +guint32 gst_rdt_packet_data_get_timestamp (GstRDTPacket *packet); + +guint8 gst_rdt_packet_data_get_flags (GstRDTPacket * packet); + +/* utils */ +gint gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2); + +G_END_DECLS + +#endif /* __GST_RDTBUFFER_H__ */ + diff --git a/gst/realmedia/pnmsrc.c b/gst/realmedia/pnmsrc.c new file mode 100644 index 0000000..241036c --- /dev/null +++ b/gst/realmedia/pnmsrc.c @@ -0,0 +1,240 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include "pnmsrc.h" + +GST_DEBUG_CATEGORY_STATIC (pnmsrc_debug); +#define GST_CAT_DEFAULT pnmsrc_debug + +/* PNMSrc signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_LOCATION NULL + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_LAST +}; + +static GstStaticPadTemplate gst_pnm_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/vnd.rn-realmedia") + ); + +static GstFlowReturn gst_pnm_src_create (GstPushSrc * psrc, GstBuffer ** buf); + +static void gst_pnm_src_uri_handler_init (gpointer g_iface, + gpointer iface_data); + +#define gst_pnm_src_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstPNMSrc, gst_pnm_src, GST_TYPE_PUSH_SRC, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_pnm_src_uri_handler_init)); + +static void gst_pnm_src_finalize (GObject * object); + +static void gst_pnm_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_pnm_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_pnm_src_class_init (GstPNMSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstPushSrcClass *gstpushsrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_pnm_src_set_property; + gobject_class->get_property = gst_pnm_src_get_property; + + gobject_class->finalize = gst_pnm_src_finalize; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "PNM Location", + "Location of the PNM url to read", + DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_pnm_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "PNM packet receiver", "Source/Network", + "Receive data over the network via PNM", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstpushsrc_class->create = gst_pnm_src_create; + + GST_DEBUG_CATEGORY_INIT (pnmsrc_debug, "pnmsrc", + 0, "Source for the pnm:// uri"); +} + +static void +gst_pnm_src_init (GstPNMSrc * pnmsrc) +{ + pnmsrc->location = g_strdup (DEFAULT_LOCATION); +} + +gboolean +gst_pnm_src_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "pnmsrc", + GST_RANK_MARGINAL, GST_TYPE_PNM_SRC); +} + +static void +gst_pnm_src_finalize (GObject * object) +{ + GstPNMSrc *pnmsrc; + + pnmsrc = GST_PNM_SRC (object); + + g_free (pnmsrc->location); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_pnm_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstPNMSrc *src; + + src = GST_PNM_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + g_free (src->location); + src->location = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_pnm_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstPNMSrc *src; + + src = GST_PNM_SRC (object); + + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, src->location); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn +gst_pnm_src_create (GstPushSrc * psrc, GstBuffer ** buf) +{ + GstPNMSrc *src; + GstMessage *m; + gchar *url; + + src = GST_PNM_SRC (psrc); + + if (src->location == NULL) + return GST_FLOW_ERROR; + url = g_strdup_printf ("rtsp%s", &src->location[3]); + + /* the only thing we do is redirect to an RTSP url */ + m = gst_message_new_element (GST_OBJECT_CAST (src), + gst_structure_new ("redirect", "new-location", G_TYPE_STRING, url, NULL)); + g_free (url); + + gst_element_post_message (GST_ELEMENT_CAST (src), m); + + + return GST_FLOW_EOS; +} + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static GstURIType +gst_pnm_src_uri_get_type (GType type) +{ + return GST_URI_SRC; +} + +static const gchar *const * +gst_pnm_src_uri_get_protocols (GType type) +{ + static const gchar *protocols[] = { "pnm", NULL }; + + return protocols; +} + +static gchar * +gst_pnm_src_uri_get_uri (GstURIHandler * handler) +{ + GstPNMSrc *src = GST_PNM_SRC (handler); + + /* FIXME: make thread-safe */ + return g_strdup (src->location); +} + +static gboolean +gst_pnm_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) +{ + GstPNMSrc *src = GST_PNM_SRC (handler); + + g_free (src->location); + src->location = g_strdup (uri); + + return TRUE; +} + +static void +gst_pnm_src_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_pnm_src_uri_get_type; + iface->get_protocols = gst_pnm_src_uri_get_protocols; + iface->get_uri = gst_pnm_src_uri_get_uri; + iface->set_uri = gst_pnm_src_uri_set_uri; +} diff --git a/gst/realmedia/pnmsrc.h b/gst/realmedia/pnmsrc.h new file mode 100644 index 0000000..fa343e7 --- /dev/null +++ b/gst/realmedia/pnmsrc.h @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_PNM_SRC_H__ +#define __GST_PNM_SRC_H__ + +#include <gst/gst.h> +#include <gst/base/gstpushsrc.h> + +G_BEGIN_DECLS + +#define GST_TYPE_PNM_SRC \ + (gst_pnm_src_get_type()) +#define GST_PNM_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PNM_SRC,GstPNMSrc)) +#define GST_PNM_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PNM_SRC,GstPNMSrcClass)) +#define GST_IS_PNM_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PNM_SRC)) +#define GST_IS_PNM_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PNM_SRC)) + +typedef struct _GstPNMSrc GstPNMSrc; +typedef struct _GstPNMSrcClass GstPNMSrcClass; + +struct _GstPNMSrc +{ + GstPushSrc parent; + + gchar *location; +}; + +struct _GstPNMSrcClass +{ + GstPushSrcClass parent_class; +}; + +GType gst_pnm_src_get_type (void); +gboolean gst_pnm_src_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_PNM_SRC_H__ */ diff --git a/gst/realmedia/rademux.c b/gst/realmedia/rademux.c new file mode 100644 index 0000000..130f7a8 --- /dev/null +++ b/gst/realmedia/rademux.c @@ -0,0 +1,1007 @@ +/* GStreamer RealAudio demuxer + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rademux + * + * Demuxes/parses a RealAudio (.ra) file or stream into compressed audio. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch filesrc location=interview.ra ! rademux ! ffdec_real_288 ! audioconvert ! audioresample ! alsasink + * ]| Read a RealAudio file and decode it and output it to the soundcard using + * the ALSA element. The .ra file is assumed to contain RealAudio version 2. + * |[ + * gst-launch gnomevfssrc location=http://www.example.org/interview.ra ! rademux ! a52dec ! audioconvert ! audioresample ! alsasink + * ]| Stream RealAudio data containing AC3 (dnet) compressed audio and decode it + * and output it to the soundcard using the ALSA element. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rademux.h" +#include "rmdemux.h" +#include "rmutils.h" + +#include <string.h> + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-pn-realaudio") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (real_audio_demux_debug); +#define GST_CAT_DEFAULT real_audio_demux_debug + +#define gst_real_audio_demux_parent_class parent_class +G_DEFINE_TYPE (GstRealAudioDemux, gst_real_audio_demux, GST_TYPE_ELEMENT); + +static GstStateChangeReturn gst_real_audio_demux_change_state (GstElement * e, + GstStateChange transition); +static GstFlowReturn gst_real_audio_demux_chain (GstPad * pad, + GstObject * parent, GstBuffer * buf); +static gboolean gst_real_audio_demux_sink_event (GstPad * pad, + GstObject * parent, GstEvent * ev); +static gboolean gst_real_audio_demux_src_event (GstPad * pad, + GstObject * parent, GstEvent * ev); +static gboolean gst_real_audio_demux_src_query (GstPad * pad, + GstObject * parent, GstQuery * query); +static void gst_real_audio_demux_loop (GstRealAudioDemux * demux); +static gboolean gst_real_audio_demux_sink_activate (GstPad * sinkpad, + GstObject * parent); +static gboolean gst_real_audio_demux_sink_activate_mode (GstPad * sinkpad, + GstObject * parent, GstPadMode mode, gboolean active); + +static void +gst_real_audio_demux_finalize (GObject * obj) +{ + GstRealAudioDemux *demux = GST_REAL_AUDIO_DEMUX (obj); + + g_object_unref (demux->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_real_audio_demux_class_init (GstRealAudioDemuxClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_real_audio_demux_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RealAudio Demuxer", + "Codec/Demuxer", + "Demultiplex a RealAudio file", + "Tim-Philipp Müller <tim centricular net>"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_real_audio_demux_change_state); + + GST_DEBUG_CATEGORY_INIT (real_audio_demux_debug, "rademux", + 0, "Demuxer for RealAudio streams"); +} + +static void +gst_real_audio_demux_reset (GstRealAudioDemux * demux) +{ + gst_adapter_clear (demux->adapter); + + if (demux->srcpad) { + GST_DEBUG_OBJECT (demux, "Removing source pad"); + gst_element_remove_pad (GST_ELEMENT (demux), demux->srcpad); + demux->srcpad = NULL; + } + + if (demux->pending_tags) { + gst_tag_list_unref (demux->pending_tags); + demux->pending_tags = NULL; + } + + demux->state = REAL_AUDIO_DEMUX_STATE_MARKER; + demux->ra_version = 0; + demux->data_offset = 0; + demux->packet_size = 0; + + demux->sample_rate = 0; + demux->sample_width = 0; + demux->channels = 0; + demux->fourcc = 0; + + demux->need_newsegment = TRUE; + + demux->segment_running = FALSE; + + demux->byterate_num = 0; + demux->byterate_denom = 0; + + demux->duration = 0; + demux->upstream_size = 0; + + demux->offset = 0; + + demux->have_group_id = FALSE; + demux->group_id = G_MAXUINT; + + gst_adapter_clear (demux->adapter); +} + +static void +gst_real_audio_demux_init (GstRealAudioDemux * demux) +{ + demux->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + + gst_pad_set_chain_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_chain)); + gst_pad_set_event_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_sink_event)); + gst_pad_set_activate_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_sink_activate)); + gst_pad_set_activatemode_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_sink_activate_mode)); + + gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); + + demux->adapter = gst_adapter_new (); + gst_real_audio_demux_reset (demux); +} + +static gboolean +gst_real_audio_demux_sink_activate (GstPad * sinkpad, GstObject * parent) +{ + GstQuery *query; + gboolean pull_mode; + + query = gst_query_new_scheduling (); + + if (!gst_pad_peer_query (sinkpad, query)) { + gst_query_unref (query); + goto activate_push; + } + + pull_mode = gst_query_has_scheduling_mode_with_flags (query, + GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); + gst_query_unref (query); + + if (!pull_mode) + goto activate_push; + + GST_DEBUG_OBJECT (sinkpad, "activating pull"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); + +activate_push: + { + GST_DEBUG_OBJECT (sinkpad, "activating push"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } +} + +static gboolean +gst_real_audio_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean res; + GstRealAudioDemux *demux; + + demux = GST_REAL_AUDIO_DEMUX (parent); + + switch (mode) { + case GST_PAD_MODE_PUSH: + demux->seekable = FALSE; + res = TRUE; + break; + case GST_PAD_MODE_PULL: + if (active) { + demux->seekable = TRUE; + + res = gst_pad_start_task (sinkpad, + (GstTaskFunction) gst_real_audio_demux_loop, demux, NULL); + } else { + demux->seekable = FALSE; + res = gst_pad_stop_task (sinkpad); + } + break; + default: + res = FALSE; + break; + } + return res; +} + +static GstFlowReturn +gst_real_audio_demux_parse_marker (GstRealAudioDemux * demux) +{ + guint8 data[6]; + + if (gst_adapter_available (demux->adapter) < 6) { + GST_LOG_OBJECT (demux, "need at least 6 bytes, waiting for more data"); + return GST_FLOW_OK; + } + + gst_adapter_copy (demux->adapter, data, 0, 6); + if (memcmp (data, ".ra\375", 4) != 0) + goto wrong_format; + + demux->ra_version = GST_READ_UINT16_BE (data + 4); + GST_DEBUG_OBJECT (demux, "ra_version = %u", demux->ra_version); + if (demux->ra_version != 4 && demux->ra_version != 3) + goto unsupported_ra_version; + + gst_adapter_flush (demux->adapter, 6); + demux->state = REAL_AUDIO_DEMUX_STATE_HEADER; + return GST_FLOW_OK; + +/* ERRORS */ +wrong_format: + { + GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, WRONG_TYPE, (NULL), (NULL)); + return GST_FLOW_ERROR; + } + +unsupported_ra_version: + { + GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, DECODE, + ("Cannot decode this RealAudio file, please file a bug"), + ("ra_version = %u", demux->ra_version)); + return GST_FLOW_ERROR; + } +} + +static GstClockTime +gst_real_demux_get_timestamp_from_offset (GstRealAudioDemux * demux, + guint64 offset) +{ + if (offset >= demux->data_offset && demux->byterate_num > 0 && + demux->byterate_denom > 0) { + return gst_util_uint64_scale (offset - demux->data_offset, + demux->byterate_denom * GST_SECOND, demux->byterate_num); + } else if (offset == demux->data_offset) { + return (GstClockTime) 0; + } else { + return GST_CLOCK_TIME_NONE; + } +} + +static gboolean +gst_real_audio_demux_get_data_offset_from_header (GstRealAudioDemux * demux) +{ + guint8 data[16]; + + gst_adapter_copy (demux->adapter, data, 0, 16); + + switch (demux->ra_version) { + case 3: + demux->data_offset = GST_READ_UINT16_BE (data) + 8; + break; + case 4: + demux->data_offset = GST_READ_UINT32_BE (data + 12) + 16; + break; + default: + demux->data_offset = 0; + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +static GstFlowReturn +gst_real_audio_demux_parse_header (GstRealAudioDemux * demux) +{ + const guint8 *data; + gchar *codec_name = NULL; + GstCaps *caps = NULL; + GstEvent *event; + gchar *stream_id; + guint avail; + + g_assert (demux->ra_version == 4 || demux->ra_version == 3); + + avail = gst_adapter_available (demux->adapter); + if (avail < 16) + return GST_FLOW_OK; + + if (!gst_real_audio_demux_get_data_offset_from_header (demux)) + return GST_FLOW_ERROR; /* shouldn't happen */ + + GST_DEBUG_OBJECT (demux, "data_offset = %u", demux->data_offset); + + if (avail + 6 < demux->data_offset) { + GST_DEBUG_OBJECT (demux, "Need %u bytes, but only %u available now", + demux->data_offset - 6, avail); + return GST_FLOW_OK; + } + + data = gst_adapter_map (demux->adapter, demux->data_offset - 6); + g_assert (data); + + switch (demux->ra_version) { + case 3: + demux->fourcc = GST_RM_AUD_14_4; + demux->packet_size = 20; + demux->sample_rate = 8000; + demux->channels = 1; + demux->sample_width = 16; + demux->flavour = 1; + demux->leaf_size = 0; + demux->height = 0; + break; + case 4: + demux->flavour = GST_READ_UINT16_BE (data + 16); + /* demux->frame_size = GST_READ_UINT32_BE (data + 36); */ + demux->leaf_size = GST_READ_UINT16_BE (data + 38); + demux->height = GST_READ_UINT16_BE (data + 34); + demux->packet_size = GST_READ_UINT32_BE (data + 18); + demux->sample_rate = GST_READ_UINT16_BE (data + 42); + demux->sample_width = GST_READ_UINT16_BE (data + 46); + demux->channels = GST_READ_UINT16_BE (data + 48); + demux->fourcc = GST_READ_UINT32_LE (data + 56); + demux->pending_tags = gst_rm_utils_read_tags (data + 63, + demux->data_offset - 63, gst_rm_utils_read_string8); + if (demux->pending_tags) + gst_tag_list_set_scope (demux->pending_tags, GST_TAG_SCOPE_GLOBAL); + break; + default: + g_assert_not_reached (); +#if 0 + case 5: + demux->flavour = GST_READ_UINT16_BE (data + 16); + /* demux->frame_size = GST_READ_UINT32_BE (data + 36); */ + demux->leaf_size = GST_READ_UINT16_BE (data + 38); + demux->height = GST_READ_UINT16_BE (data + 34); + + demux->sample_rate = GST_READ_UINT16_BE (data + 48); + demux->sample_width = GST_READ_UINT16_BE (data + 52); + demux->n_channels = GST_READ_UINT16_BE (data + 54); + demux->fourcc = RMDEMUX_FOURCC_GET (data + 60); + break; +#endif + } + + GST_INFO_OBJECT (demux, "packet_size = %u", demux->packet_size); + GST_INFO_OBJECT (demux, "sample_rate = %u", demux->sample_rate); + GST_INFO_OBJECT (demux, "sample_width = %u", demux->sample_width); + GST_INFO_OBJECT (demux, "channels = %u", demux->channels); + GST_INFO_OBJECT (demux, "fourcc = '%" GST_FOURCC_FORMAT "' (%08X)", + GST_FOURCC_ARGS (demux->fourcc), demux->fourcc); + + switch (demux->fourcc) { + case GST_RM_AUD_14_4: + caps = gst_caps_new_simple ("audio/x-pn-realaudio", "raversion", + G_TYPE_INT, 1, NULL); + demux->byterate_num = 1000; + demux->byterate_denom = 1; + break; + + case GST_RM_AUD_28_8: + /* FIXME: needs descrambling */ + caps = gst_caps_new_simple ("audio/x-pn-realaudio", "raversion", + G_TYPE_INT, 2, NULL); + break; + + case GST_RM_AUD_DNET: + caps = gst_caps_new_simple ("audio/x-ac3", "rate", G_TYPE_INT, + demux->sample_rate, NULL); + if (demux->packet_size == 0 || demux->sample_rate == 0) + goto broken_file; + demux->byterate_num = demux->packet_size * demux->sample_rate; + demux->byterate_denom = 1536; + break; + + /* Sipro/ACELP.NET Voice Codec (MIME unknown) */ + case GST_RM_AUD_SIPR: + caps = gst_caps_new_empty_simple ("audio/x-sipro"); + break; + + default: + GST_WARNING_OBJECT (demux, "unknown fourcc %08X", demux->fourcc); + break; + } + + if (caps == NULL) + goto unknown_fourcc; + + gst_caps_set_simple (caps, + "flavor", G_TYPE_INT, demux->flavour, + "rate", G_TYPE_INT, demux->sample_rate, + "channels", G_TYPE_INT, demux->channels, + "width", G_TYPE_INT, demux->sample_width, + "leaf_size", G_TYPE_INT, demux->leaf_size, + "packet_size", G_TYPE_INT, demux->packet_size, + "height", G_TYPE_INT, demux->height, NULL); + + GST_INFO_OBJECT (demux, "Adding source pad, caps %" GST_PTR_FORMAT, caps); + demux->srcpad = gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_set_event_function (demux->srcpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_src_event)); + gst_pad_set_query_function (demux->srcpad, + GST_DEBUG_FUNCPTR (gst_real_audio_demux_src_query)); + gst_pad_set_active (demux->srcpad, TRUE); + gst_pad_use_fixed_caps (demux->srcpad); + + stream_id = + gst_pad_create_stream_id (demux->srcpad, GST_ELEMENT_CAST (demux), NULL); + + event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0); + if (event) { + if (gst_event_parse_group_id (event, &demux->group_id)) + demux->have_group_id = TRUE; + else + demux->have_group_id = FALSE; + gst_event_unref (event); + } else if (!demux->have_group_id) { + demux->have_group_id = TRUE; + demux->group_id = gst_util_group_id_next (); + } + + event = gst_event_new_stream_start (stream_id); + if (demux->have_group_id) + gst_event_set_group_id (event, demux->group_id); + + gst_pad_push_event (demux->srcpad, event); + g_free (stream_id); + + gst_pad_set_caps (demux->srcpad, caps); + codec_name = gst_pb_utils_get_codec_description (caps); + gst_caps_unref (caps); + + gst_element_add_pad (GST_ELEMENT (demux), demux->srcpad); + + if (demux->byterate_num > 0 && demux->byterate_denom > 0) { + GstFormat bformat = GST_FORMAT_BYTES; + gint64 size_bytes = 0; + + GST_INFO_OBJECT (demux, "byte rate = %u/%u = %u bytes/sec", + demux->byterate_num, demux->byterate_denom, + demux->byterate_num / demux->byterate_denom); + + if (gst_pad_peer_query_duration (demux->sinkpad, bformat, &size_bytes)) { + demux->duration = + gst_real_demux_get_timestamp_from_offset (demux, size_bytes); + demux->upstream_size = size_bytes; + GST_INFO_OBJECT (demux, "upstream_size = %" G_GUINT64_FORMAT, + demux->upstream_size); + GST_INFO_OBJECT (demux, "duration = %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->duration)); + } + } + + demux->need_newsegment = TRUE; + + if (codec_name) { + if (demux->pending_tags == NULL) { + demux->pending_tags = gst_tag_list_new_empty (); + gst_tag_list_set_scope (demux->pending_tags, GST_TAG_SCOPE_GLOBAL); + } + + gst_tag_list_add (demux->pending_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, codec_name, NULL); + g_free (codec_name); + } + + gst_adapter_unmap (demux->adapter); + gst_adapter_flush (demux->adapter, demux->data_offset - 6); + + demux->state = REAL_AUDIO_DEMUX_STATE_DATA; + demux->need_newsegment = TRUE; + + return GST_FLOW_OK; + +/* ERRORS */ +unknown_fourcc: + { + GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, DECODE, (NULL), + ("Unknown fourcc '0x%" G_GINT32_MODIFIER "x'", demux->fourcc)); + return GST_FLOW_ERROR; + } +broken_file: + { + GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, DECODE, (NULL), + ("Broken file - invalid sample_rate or other header value")); + return GST_FLOW_ERROR; + } + +} + +static GstFlowReturn +gst_real_audio_demux_parse_data (GstRealAudioDemux * demux) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint avail, unit_size; + + avail = gst_adapter_available (demux->adapter); + + if (demux->packet_size > 0) + unit_size = demux->packet_size; + else + unit_size = avail & 0xfffffff0; /* round down to next multiple of 16 */ + + GST_LOG_OBJECT (demux, "available = %u, unit_size = %u", avail, unit_size); + + while (ret == GST_FLOW_OK && unit_size > 0 && avail >= unit_size) { + GstClockTime ts; + GstBuffer *buf; + + buf = gst_adapter_take_buffer (demux->adapter, unit_size); + avail -= unit_size; + + if (demux->need_newsegment) { + gst_pad_push_event (demux->srcpad, + gst_event_new_segment (&demux->segment)); + demux->need_newsegment = FALSE; + } + + if (demux->pending_tags) { + gst_pad_push_event (demux->srcpad, + gst_event_new_tag (demux->pending_tags)); + demux->pending_tags = NULL; + } + + if (demux->fourcc == GST_RM_AUD_DNET) { + buf = gst_rm_utils_descramble_dnet_buffer (buf); + } + + ts = gst_real_demux_get_timestamp_from_offset (demux, demux->offset); + GST_BUFFER_TIMESTAMP (buf) = ts; + + demux->segment.position = ts; + + ret = gst_pad_push (demux->srcpad, buf); + } + + return ret; +} + +static GstFlowReturn +gst_real_audio_demux_handle_buffer (GstRealAudioDemux * demux, GstBuffer * buf) +{ + GstFlowReturn ret; + + gst_adapter_push (demux->adapter, buf); + buf = NULL; + + switch (demux->state) { + case REAL_AUDIO_DEMUX_STATE_MARKER:{ + ret = gst_real_audio_demux_parse_marker (demux); + if (ret != GST_FLOW_OK || demux->state != REAL_AUDIO_DEMUX_STATE_HEADER) + break; + /* otherwise fall through */ + } + case REAL_AUDIO_DEMUX_STATE_HEADER:{ + ret = gst_real_audio_demux_parse_header (demux); + if (ret != GST_FLOW_OK || demux->state != REAL_AUDIO_DEMUX_STATE_DATA) + break; + /* otherwise fall through */ + } + case REAL_AUDIO_DEMUX_STATE_DATA:{ + ret = gst_real_audio_demux_parse_data (demux); + break; + } + default: + g_return_val_if_reached (GST_FLOW_ERROR); + } + + return ret; +} + +static GstFlowReturn +gst_real_audio_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstRealAudioDemux *demux; + + demux = GST_REAL_AUDIO_DEMUX (parent); + + return gst_real_audio_demux_handle_buffer (demux, buf); +} + +static void +gst_real_audio_demux_loop (GstRealAudioDemux * demux) +{ + GstFlowReturn ret; + GstBuffer *buf; + guint bytes_needed; + + /* check how much data we need */ + switch (demux->state) { + case REAL_AUDIO_DEMUX_STATE_MARKER: + bytes_needed = 6 + 16; /* 16 are beginning of header */ + break; + case REAL_AUDIO_DEMUX_STATE_HEADER: + if (!gst_real_audio_demux_get_data_offset_from_header (demux)) + goto parse_header_error; + bytes_needed = demux->data_offset - (6 + 16); + break; + case REAL_AUDIO_DEMUX_STATE_DATA: + if (demux->packet_size > 0) { + /* TODO: should probably take into account width/height as well? */ + bytes_needed = demux->packet_size; + } else { + bytes_needed = 1024; + } + break; + default: + g_return_if_reached (); + } + + /* now get the data */ + GST_LOG_OBJECT (demux, "getting data: %5u bytes @ %8" G_GINT64_MODIFIER "u", + bytes_needed, demux->offset); + + if (demux->upstream_size > 0 && demux->offset >= demux->upstream_size) + goto eos; + + buf = NULL; + ret = gst_pad_pull_range (demux->sinkpad, demux->offset, bytes_needed, &buf); + + if (ret != GST_FLOW_OK) + goto pull_range_error; + + if (gst_buffer_get_size (buf) != bytes_needed) + goto pull_range_short_read; + + ret = gst_real_audio_demux_handle_buffer (demux, buf); + if (ret != GST_FLOW_OK) + goto handle_flow_error; + + /* TODO: increase this in chain function too (for timestamps)? */ + demux->offset += bytes_needed; + + /* check for the end of the segment */ + if (demux->segment.stop != -1 && demux->segment.position != -1 && + demux->segment.position > demux->segment.stop) { + GST_DEBUG_OBJECT (demux, "reached end of segment"); + goto eos; + } + + return; + +/* ERRORS */ +parse_header_error: + { + GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL), (NULL)); + goto pause_task; + } +handle_flow_error: + { + GST_WARNING_OBJECT (demux, "handle_buf flow: %s", gst_flow_get_name (ret)); + goto pause_task; + } +pull_range_error: + { + GST_WARNING_OBJECT (demux, "pull range flow: %s", gst_flow_get_name (ret)); + goto pause_task; + } +pull_range_short_read: + { + GST_WARNING_OBJECT (demux, "pull range short read: wanted %u bytes, but " + "got only %" G_GSIZE_FORMAT " bytes", bytes_needed, + gst_buffer_get_size (buf)); + gst_buffer_unref (buf); + goto eos; + } +eos: + { + if (demux->state != REAL_AUDIO_DEMUX_STATE_DATA) { + GST_WARNING_OBJECT (demux, "reached EOS before finished parsing header"); + goto parse_header_error; + } + GST_INFO_OBJECT (demux, "EOS"); + if ((demux->segment.flags & GST_SEEK_FLAG_SEGMENT) != 0) { + gint64 stop; + + /* for segment playback we need to post when (in stream time) + * we stopped, this is either stop (when set) or the duration. */ + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + + GST_DEBUG_OBJECT (demux, "sending segment done, at end of segment"); + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, + stop)); + gst_pad_push_event (demux->srcpad, + gst_event_new_segment_done (GST_FORMAT_TIME, stop)); + } else { + /* normal playback, send EOS event downstream */ + GST_DEBUG_OBJECT (demux, "sending EOS event, at end of stream"); + gst_pad_push_event (demux->srcpad, gst_event_new_eos ()); + } + goto pause_task; + } +pause_task: + { + demux->segment_running = FALSE; + gst_pad_pause_task (demux->sinkpad); + GST_DEBUG_OBJECT (demux, "pausing task"); + return; + } +} + +static gboolean +gst_real_audio_demux_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstRealAudioDemux *demux; + gboolean ret; + + demux = GST_REAL_AUDIO_DEMUX (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT:{ + /* FIXME */ + gst_event_unref (event); + demux->need_newsegment = TRUE; + ret = TRUE; + break; + } + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + return ret; +} + +static gboolean +gst_real_audio_demux_handle_seek (GstRealAudioDemux * demux, GstEvent * event) +{ + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gboolean flush, update; + gdouble rate; + guint64 seek_pos; + gint64 cur, stop; + + if (!demux->seekable) + goto not_seekable; + + if (demux->byterate_num == 0 || demux->byterate_denom == 0) + goto no_bitrate; + + gst_event_parse_seek (event, &rate, &format, &flags, + &cur_type, &cur, &stop_type, &stop); + + if (format != GST_FORMAT_TIME) + goto only_time_format_supported; + + if (rate <= 0.0) + goto cannot_do_backwards_playback; + + flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0); + + GST_DEBUG_OBJECT (demux, "flush=%d, rate=%g", flush, rate); + + /* unlock streaming thread and make streaming stop */ + if (flush) { + gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); + gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_pause_task (demux->sinkpad); + } + + GST_PAD_STREAM_LOCK (demux->sinkpad); + + gst_segment_do_seek (&demux->segment, rate, format, flags, + cur_type, cur, stop_type, stop, &update); + + GST_DEBUG_OBJECT (demux, "segment: %" GST_SEGMENT_FORMAT, &demux->segment); + + seek_pos = gst_util_uint64_scale (demux->segment.start, + demux->byterate_num, demux->byterate_denom * GST_SECOND); + if (demux->packet_size > 0) { + seek_pos -= seek_pos % demux->packet_size; + } + seek_pos += demux->data_offset; + + GST_DEBUG_OBJECT (demux, "seek_pos = %" G_GUINT64_FORMAT, seek_pos); + + /* stop flushing */ + gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop (TRUE)); + gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop (TRUE)); + + demux->offset = seek_pos; + demux->need_newsegment = TRUE; + + /* notify start of new segment */ + if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_segment_start (GST_OBJECT (demux), + GST_FORMAT_TIME, demux->segment.position)); + } + + demux->segment_running = TRUE; + /* restart our task since it might have been stopped when we did the flush */ + gst_pad_start_task (demux->sinkpad, + (GstTaskFunction) gst_real_audio_demux_loop, demux, NULL); + + /* streaming can continue now */ + GST_PAD_STREAM_UNLOCK (demux->sinkpad); + + return TRUE; + +/* ERRORS */ +not_seekable: + { + GST_DEBUG_OBJECT (demux, "seek failed: cannot seek in streaming mode"); + return FALSE; + } +no_bitrate: + { + GST_DEBUG_OBJECT (demux, "seek failed: bitrate unknown"); + return FALSE; + } +only_time_format_supported: + { + GST_DEBUG_OBJECT (demux, "can only seek in TIME format"); + return FALSE; + } +cannot_do_backwards_playback: + { + GST_DEBUG_OBJECT (demux, "can only seek with positive rate, not %lf", rate); + return FALSE; + } +} + +static gboolean +gst_real_audio_demux_src_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstRealAudioDemux *demux; + gboolean ret = FALSE; + + demux = GST_REAL_AUDIO_DEMUX (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_QOS: + gst_event_unref (event); + break; + case GST_EVENT_SEEK: + ret = gst_real_audio_demux_handle_seek (demux, event); + gst_event_unref (event); + break; + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static gboolean +gst_real_audio_demux_src_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstRealAudioDemux *demux; + gboolean ret = FALSE; + + demux = GST_REAL_AUDIO_DEMUX (parent); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION:{ + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (format == GST_FORMAT_TIME && demux->duration > 0) { + gst_query_set_duration (query, GST_FORMAT_TIME, demux->duration); + ret = TRUE; + } else if (format == GST_FORMAT_BYTES && demux->upstream_size > 0) { + gst_query_set_duration (query, GST_FORMAT_BYTES, + demux->upstream_size - demux->data_offset); + ret = TRUE; + } + break; + } + case GST_QUERY_SEEKING:{ + GstFormat format; + gboolean seekable; + + gst_query_parse_seeking (query, &format, NULL, NULL, NULL); + seekable = (format == GST_FORMAT_TIME && demux->seekable); + gst_query_set_seeking (query, format, seekable, 0, + (format == GST_FORMAT_TIME) ? demux->duration : -1); + ret = TRUE; + break; + } + case GST_QUERY_SEGMENT: + { + GstFormat format; + gint64 start, stop; + + format = demux->segment.format; + + start = + gst_segment_to_stream_time (&demux->segment, format, + demux->segment.start); + if ((stop = demux->segment.stop) == -1) + stop = demux->segment.duration; + else + stop = gst_segment_to_stream_time (&demux->segment, format, stop); + + gst_query_set_segment (query, demux->segment.rate, format, start, stop); + ret = TRUE; + break; + } + default: + ret = gst_pad_query_default (pad, parent, query); + break; + } + + return ret; +} + +static GstStateChangeReturn +gst_real_audio_demux_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstRealAudioDemux *demux = GST_REAL_AUDIO_DEMUX (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + demux->state = REAL_AUDIO_DEMUX_STATE_MARKER; + demux->segment_running = FALSE; + gst_segment_init (&demux->segment, GST_FORMAT_TIME); + gst_adapter_clear (demux->adapter); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY:{ + gst_real_audio_demux_reset (demux); + gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED); + break; + } + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +gboolean +gst_rademux_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rademux", + GST_RANK_SECONDARY, GST_TYPE_REAL_AUDIO_DEMUX); +} diff --git a/gst/realmedia/rademux.h b/gst/realmedia/rademux.h new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/gst/realmedia/rademux.h @@ -0,0 +1,104 @@ +/* GStreamer RealAudio demuxer + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_REAL_AUDIO_DEMUX_H__ +#define __GST_REAL_AUDIO_DEMUX_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_REAL_AUDIO_DEMUX \ + (gst_real_audio_demux_get_type()) +#define GST_REAL_AUDIO_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_REAL_AUDIO_DEMUX,GstRealAudioDemux)) +#define GST_REAL_AUDIO_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_REAL_AUDIO_DEMUX,GstRealAudioDemuxClass)) +#define GST_IS_REAL_AUDIO_DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_REAL_AUDIO_DEMUX)) +#define GST_IS_REAL_AUDIO_DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_REAL_AUDIO_DEMUX)) + +typedef enum +{ + REAL_AUDIO_DEMUX_STATE_MARKER, + REAL_AUDIO_DEMUX_STATE_HEADER, + REAL_AUDIO_DEMUX_STATE_DATA +} GstRealAudioDemuxState; + +typedef struct _GstRealAudioDemux GstRealAudioDemux; +typedef struct _GstRealAudioDemuxClass GstRealAudioDemuxClass; + +struct _GstRealAudioDemux { + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + gboolean have_group_id; + guint group_id; + + GstAdapter *adapter; + GstRealAudioDemuxState state; + + guint ra_version; + guint data_offset; + + guint packet_size; + guint leaf_size; + guint height; + guint flavour; + + guint sample_rate; + guint sample_width; + guint channels; + guint32 fourcc; + + gboolean segment_running; + + gboolean need_newsegment; + GstTagList *pending_tags; + + guint byterate_num; /* bytes per second */ + guint byterate_denom; + + gint64 duration; + gint64 upstream_size; + + guint64 offset; /* current read byte offset for + * pull_range-based mode */ + + /* playback start/stop positions */ + GstSegment segment; + + gboolean seekable; +}; + +struct _GstRealAudioDemuxClass { + GstElementClass element_class; +}; + +GType gst_real_audio_demux_get_type (void); + +gboolean gst_rademux_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_REAL_AUDIO_DEMUX_H__ */ diff --git a/gst/realmedia/rdtdepay.c b/gst/realmedia/rdtdepay.c new file mode 100644 index 0000000..308faec --- /dev/null +++ b/gst/realmedia/rdtdepay.c @@ -0,0 +1,501 @@ +/* GStreamer + * Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de> + * <2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include "gstrdtbuffer.h" +#include "rdtdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rdtdepay_debug); +#define GST_CAT_DEFAULT rdtdepay_debug + +/* RDTDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, +}; + +static GstStaticPadTemplate gst_rdt_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/vnd.rn-realmedia") + ); + +static GstStaticPadTemplate gst_rdt_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rdt, " + "media = (string) \"application\", " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) \"X-REAL-RDT\"" + /* All optional parameters + * + * "config=" + */ + ) + ); + +#define gst_rdt_depay_parent_class parent_class +G_DEFINE_TYPE (GstRDTDepay, gst_rdt_depay, GST_TYPE_ELEMENT); + +static void gst_rdt_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rdt_depay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstObject * parent, + GstBuffer * buf); + +static void +gst_rdt_depay_class_init (GstRDTDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gst_rdt_depay_finalize; + + gstelement_class->change_state = gst_rdt_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RDT packet parser", + "Codec/Depayloader/Network", + "Extracts RealMedia from RDT packets", + "Lutz Mueller <lutz at topfrose dot de>, " + "Wim Taymans <wim@fluendo.com>"); + + GST_DEBUG_CATEGORY_INIT (rdtdepay_debug, "rdtdepay", + 0, "Depayloader for RDT RealMedia packets"); +} + +static void +gst_rdt_depay_init (GstRDTDepay * rdtdepay) +{ + rdtdepay->sinkpad = + gst_pad_new_from_static_template (&gst_rdt_depay_sink_template, "sink"); + gst_pad_set_chain_function (rdtdepay->sinkpad, gst_rdt_depay_chain); + gst_pad_set_event_function (rdtdepay->sinkpad, gst_rdt_depay_sink_event); + gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->sinkpad); + + rdtdepay->srcpad = + gst_pad_new_from_static_template (&gst_rdt_depay_src_template, "src"); + gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->srcpad); +} + +static void +gst_rdt_depay_finalize (GObject * object) +{ + GstRDTDepay *rdtdepay; + + rdtdepay = GST_RDT_DEPAY (object); + + if (rdtdepay->header) + gst_buffer_unref (rdtdepay->header); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstRDTDepay *rdtdepay; + GstCaps *srccaps; + gint clock_rate = 1000; /* default */ + const GValue *value; + GstBuffer *header; + + rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad)); + + structure = gst_caps_get_structure (caps, 0); + + if (gst_structure_has_field (structure, "clock-rate")) + gst_structure_get_int (structure, "clock-rate", &clock_rate); + + /* config contains the RealMedia header as a buffer. */ + value = gst_structure_get_value (structure, "config"); + if (!value) + goto no_header; + + header = gst_value_get_buffer (value); + if (!header) + goto no_header; + + /* get other values for newsegment */ + value = gst_structure_get_value (structure, "npt-start"); + if (value && G_VALUE_HOLDS_UINT64 (value)) + rdtdepay->npt_start = g_value_get_uint64 (value); + else + rdtdepay->npt_start = 0; + GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT, + rdtdepay->npt_start); + + value = gst_structure_get_value (structure, "npt-stop"); + if (value && G_VALUE_HOLDS_UINT64 (value)) + rdtdepay->npt_stop = g_value_get_uint64 (value); + else + rdtdepay->npt_stop = -1; + + GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT, + rdtdepay->npt_stop); + + value = gst_structure_get_value (structure, "play-speed"); + if (value && G_VALUE_HOLDS_DOUBLE (value)) + rdtdepay->play_speed = g_value_get_double (value); + else + rdtdepay->play_speed = 1.0; + + value = gst_structure_get_value (structure, "play-scale"); + if (value && G_VALUE_HOLDS_DOUBLE (value)) + rdtdepay->play_scale = g_value_get_double (value); + else + rdtdepay->play_scale = 1.0; + + /* caps seem good, configure element */ + rdtdepay->clock_rate = clock_rate; + + /* set caps on pad and on header */ + srccaps = gst_caps_new_empty_simple ("application/vnd.rn-realmedia"); + gst_pad_set_caps (rdtdepay->srcpad, srccaps); + gst_caps_unref (srccaps); + + if (rdtdepay->header) + gst_buffer_unref (rdtdepay->header); + rdtdepay->header = gst_buffer_ref (header); + + return TRUE; + + /* ERRORS */ +no_header: + { + GST_ERROR_OBJECT (rdtdepay, "no header found in caps, no 'config' field"); + return FALSE; + } +} + +static gboolean +gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstRDTDepay *depay; + gboolean res = TRUE; + + depay = GST_RDT_DEPAY (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_rdt_depay_setcaps (pad, caps); + gst_event_unref (event); + break; + } + case GST_EVENT_FLUSH_STOP: + res = gst_pad_push_event (depay->srcpad, event); + + gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED); + depay->need_newsegment = TRUE; + depay->next_seqnum = -1; + break; + case GST_EVENT_SEGMENT: + { + gst_event_copy_segment (event, &depay->segment); + /* don't pass the event downstream, we generate our own segment + * including the NTP time and other things we receive in caps */ + gst_event_unref (event); + break; + } + default: + /* pass other events forward */ + res = gst_pad_push_event (depay->srcpad, event); + break; + } + return res; +} + +static GstEvent * +create_segment_event (GstRDTDepay * depay, gboolean update, + GstClockTime position) +{ + GstSegment segment; + + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.rate = depay->play_speed; + segment.applied_rate = depay->play_scale; + segment.start = position; + + if (depay->npt_stop != -1) + segment.stop = depay->npt_stop - depay->npt_start; + else + segment.stop = -1; + + segment.time = position + depay->npt_start; + + return gst_event_new_segment (&segment); +} + +static GstFlowReturn +gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer) +{ + GstFlowReturn ret; + + if (rdtdepay->need_newsegment) { + GstEvent *event; + + event = create_segment_event (rdtdepay, FALSE, 0); + gst_pad_push_event (rdtdepay->srcpad, event); + + rdtdepay->need_newsegment = FALSE; + } + + if (rdtdepay->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + rdtdepay->discont = FALSE; + } + ret = gst_pad_push (rdtdepay->srcpad, buffer); + + return ret; +} + +static GstFlowReturn +gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime, + GstRDTPacket * packet) +{ + GstFlowReturn ret; + GstBuffer *outbuf; + GstMapInfo outmap; + guint8 *data, *outdata; + guint size; + guint16 stream_id; + guint32 timestamp; + gint gap; + guint16 seqnum; + guint8 flags; + guint16 outflags; + + /* get pointers to the packet data */ + data = gst_rdt_packet_data_map (packet, &size); + + outbuf = gst_buffer_new_and_alloc (12 + size); + GST_BUFFER_TIMESTAMP (outbuf) = outtime; + + GST_DEBUG_OBJECT (rdtdepay, "have size %u", size); + + /* copy over some things */ + stream_id = gst_rdt_packet_data_get_stream_id (packet); + timestamp = gst_rdt_packet_data_get_timestamp (packet); + flags = gst_rdt_packet_data_get_flags (packet); + + seqnum = gst_rdt_packet_data_get_seq (packet); + + GST_DEBUG_OBJECT (rdtdepay, "stream_id %u, timestamp %u, seqnum %d, flags %d", + stream_id, timestamp, seqnum, flags); + + if (rdtdepay->next_seqnum != -1) { + gap = gst_rdt_buffer_compare_seqnum (seqnum, rdtdepay->next_seqnum); + + /* if we have no gap, all is fine */ + if (G_UNLIKELY (gap != 0)) { + GST_LOG_OBJECT (rdtdepay, "got packet %u, expected %u, gap %d", seqnum, + rdtdepay->next_seqnum, gap); + if (gap < 0) { + /* seqnum > next_seqnum, we are missing some packets, this is always a + * DISCONT. */ + GST_LOG_OBJECT (rdtdepay, "%d missing packets", gap); + rdtdepay->discont = TRUE; + } else { + /* seqnum < next_seqnum, we have seen this packet before or the sender + * could be restarted. If the packet is not too old, we throw it away as + * a duplicate, otherwise we mark discont and continue. 100 misordered + * packets is a good threshold. See also RFC 4737. */ + if (gap < 100) + goto dropping; + + GST_LOG_OBJECT (rdtdepay, + "%d > 100, packet too old, sender likely restarted", gap); + rdtdepay->discont = TRUE; + } + } + } + rdtdepay->next_seqnum = (seqnum + 1); + if (rdtdepay->next_seqnum == 0xff00) + rdtdepay->next_seqnum = 0; + + if ((flags & 1) == 0) + outflags = 2; + else + outflags = 0; + + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + outdata = outmap.data; + GST_WRITE_UINT16_BE (outdata + 0, 0); /* version */ + GST_WRITE_UINT16_BE (outdata + 2, size + 12); /* length */ + GST_WRITE_UINT16_BE (outdata + 4, stream_id); /* stream */ + GST_WRITE_UINT32_BE (outdata + 6, timestamp); /* timestamp */ + GST_WRITE_UINT16_BE (outdata + 10, outflags); /* flags */ + memcpy (outdata + 12, data, size); + gst_buffer_unmap (outbuf, &outmap); + gst_buffer_resize (outbuf, 0, 12 + size); + + gst_rdt_packet_data_unmap (packet); + + GST_DEBUG_OBJECT (rdtdepay, "Pushing packet, outtime %" GST_TIME_FORMAT, + GST_TIME_ARGS (outtime)); + + ret = gst_rdt_depay_push (rdtdepay, outbuf); + + return ret; + + /* ERRORS */ +dropping: + { + GST_WARNING_OBJECT (rdtdepay, "%d <= 100, dropping old packet", gap); + return GST_FLOW_OK; + } +} + +static GstFlowReturn +gst_rdt_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstRDTDepay *rdtdepay; + GstFlowReturn ret; + GstClockTime timestamp; + gboolean more; + GstRDTPacket packet; + + rdtdepay = GST_RDT_DEPAY (parent); + + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_LOG_OBJECT (rdtdepay, "received discont"); + rdtdepay->discont = TRUE; + } + + if (rdtdepay->header) { + GstBuffer *out; + + out = rdtdepay->header; + rdtdepay->header = NULL; + + /* push header data first */ + gst_rdt_depay_push (rdtdepay, out); + } + + /* save timestamp */ + timestamp = GST_BUFFER_TIMESTAMP (buf); + + ret = GST_FLOW_OK; + + GST_LOG_OBJECT (rdtdepay, "received buffer timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + + /* data is in RDT format. */ + more = gst_rdt_buffer_get_first_packet (buf, &packet); + while (more) { + GstRDTType type; + + type = gst_rdt_packet_get_type (&packet); + GST_DEBUG_OBJECT (rdtdepay, "Have packet of type %04x", type); + + if (GST_RDT_IS_DATA_TYPE (type)) { + GST_DEBUG_OBJECT (rdtdepay, "We have a data packet"); + ret = gst_rdt_depay_handle_data (rdtdepay, timestamp, &packet); + } else { + switch (type) { + default: + GST_DEBUG_OBJECT (rdtdepay, "Ignoring packet"); + break; + } + } + if (ret != GST_FLOW_OK) + break; + + more = gst_rdt_packet_move_to_next (&packet); + } + + gst_buffer_unref (buf); + + return ret; +} + +static GstStateChangeReturn +gst_rdt_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRDTDepay *rdtdepay; + GstStateChangeReturn ret; + + rdtdepay = GST_RDT_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_segment_init (&rdtdepay->segment, GST_FORMAT_UNDEFINED); + rdtdepay->next_seqnum = -1; + rdtdepay->need_newsegment = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (rdtdepay->header) + gst_buffer_unref (rdtdepay->header); + rdtdepay->header = NULL; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rdt_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rdtdepay", + GST_RANK_MARGINAL, GST_TYPE_RDT_DEPAY); +} diff --git a/gst/realmedia/rdtdepay.h b/gst/realmedia/rdtdepay.h new file mode 100644 index 0000000..a4ed291 --- /dev/null +++ b/gst/realmedia/rdtdepay.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de> + * <2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RDT_DEPAY_H__ +#define __GST_RDT_DEPAY_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RDT_DEPAY \ + (gst_rdt_depay_get_type()) +#define GST_RDT_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RDT_DEPAY,GstRDTDepay)) +#define GST_RDT_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RDT_DEPAY,GstRDTDepayClass)) +#define GST_IS_RDT_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RDT_DEPAY)) +#define GST_IS_RDT_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RDT_DEPAY)) + +typedef struct _GstRDTDepay GstRDTDepay; +typedef struct _GstRDTDepayClass GstRDTDepayClass; + +struct _GstRDTDepay +{ + GstElement parent; + + GstPad *sinkpad; + GstPad *srcpad; + + guint clock_rate; + GstClockTime npt_start; + GstClockTime npt_stop; + gdouble play_speed; + gdouble play_scale; + + guint32 next_seqnum; + + gboolean discont; + gboolean need_newsegment; + GstSegment segment; + GstBuffer *header; +}; + +struct _GstRDTDepayClass +{ + GstElementClass parent_class; +}; + +GType gst_rdt_depay_get_type (void); + +gboolean gst_rdt_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RDT_DEPAY_H__ */ diff --git a/gst/realmedia/rdtjitterbuffer.c b/gst/realmedia/rdtjitterbuffer.c new file mode 100644 index 0000000..4c6b0ae --- /dev/null +++ b/gst/realmedia/rdtjitterbuffer.c @@ -0,0 +1,531 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include <string.h> +#include <stdlib.h> + +#include "rdtjitterbuffer.h" +#include "gstrdtbuffer.h" + +GST_DEBUG_CATEGORY_STATIC (rdt_jitter_buffer_debug); +#define GST_CAT_DEFAULT rdt_jitter_buffer_debug + +#define MAX_WINDOW RDT_JITTER_BUFFER_MAX_WINDOW +#define MAX_TIME (2 * GST_SECOND) + +/* signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +/* GObject vmethods */ +static void rdt_jitter_buffer_finalize (GObject * object); + +/* static guint rdt_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */ + +G_DEFINE_TYPE (RDTJitterBuffer, rdt_jitter_buffer, G_TYPE_OBJECT); + +static void +rdt_jitter_buffer_class_init (RDTJitterBufferClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = rdt_jitter_buffer_finalize; + + GST_DEBUG_CATEGORY_INIT (rdt_jitter_buffer_debug, "rdtjitterbuffer", 0, + "RDT Jitter Buffer"); +} + +static void +rdt_jitter_buffer_init (RDTJitterBuffer * jbuf) +{ + jbuf->packets = g_queue_new (); + + rdt_jitter_buffer_reset_skew (jbuf); +} + +static void +rdt_jitter_buffer_finalize (GObject * object) +{ + RDTJitterBuffer *jbuf; + + jbuf = RDT_JITTER_BUFFER_CAST (object); + + rdt_jitter_buffer_flush (jbuf); + g_queue_free (jbuf->packets); + + G_OBJECT_CLASS (rdt_jitter_buffer_parent_class)->finalize (object); +} + +/** + * rdt_jitter_buffer_new: + * + * Create an #RDTJitterBuffer. + * + * Returns: a new #RDTJitterBuffer. Use g_object_unref() after usage. + */ +RDTJitterBuffer * +rdt_jitter_buffer_new (void) +{ + RDTJitterBuffer *jbuf; + + jbuf = g_object_new (RDT_TYPE_JITTER_BUFFER, NULL); + + return jbuf; +} + +void +rdt_jitter_buffer_reset_skew (RDTJitterBuffer * jbuf) +{ + jbuf->base_time = -1; + jbuf->base_rtptime = -1; + jbuf->ext_rtptime = -1; + jbuf->window_pos = 0; + jbuf->window_filling = TRUE; + jbuf->window_min = 0; + jbuf->skew = 0; + jbuf->prev_send_diff = -1; +} + +/* For the clock skew we use a windowed low point averaging algorithm as can be + * found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is + * composed of: + * + * J = N + n + * + * N : a constant network delay. + * n : random added noise. The noise is concentrated around 0 + * + * In the receiver we can track the elapsed time at the sender with: + * + * send_diff(i) = (Tsi - Ts0); + * + * Tsi : The time at the sender at packet i + * Ts0 : The time at the sender at the first packet + * + * This is the difference between the RDT timestamp in the first received packet + * and the current packet. + * + * At the receiver we have to deal with the jitter introduced by the network. + * + * recv_diff(i) = (Tri - Tr0) + * + * Tri : The time at the receiver at packet i + * Tr0 : The time at the receiver at the first packet + * + * Both of these values contain a jitter Ji, a jitter for packet i, so we can + * write: + * + * recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0)) + * + * Cri : The time of the clock at the receiver for packet i + * D + ni : The jitter when receiving packet i + * + * We see that the network delay is irrelevant here as we can elliminate D: + * + * recv_diff(i) = (Cri + ni) - (Cr0 + n0)) + * + * The drift is now expressed as: + * + * Drift(i) = recv_diff(i) - send_diff(i); + * + * We now keep the W latest values of Drift and find the minimum (this is the + * one with the lowest network jitter and thus the one which is least affected + * by it). We average this lowest value to smooth out the resulting network skew. + * + * Both the window and the weighting used for averaging influence the accuracy + * of the drift estimation. Finding the correct parameters turns out to be a + * compromise between accuracy and inertia. + * + * We use a 2 second window or up to 512 data points, which is statistically big + * enough to catch spikes (FIXME, detect spikes). + * We also use a rather large weighting factor (125) to smoothly adapt. During + * startup, when filling the window, we use a parabolic weighting factor, the + * more the window is filled, the faster we move to the detected possible skew. + * + * Returns: @time adjusted with the clock skew. + */ +static GstClockTime +calculate_skew (RDTJitterBuffer * jbuf, guint32 rtptime, GstClockTime time, + guint32 clock_rate) +{ + guint64 ext_rtptime; + guint64 send_diff, recv_diff; + gint64 delta; + gint64 old; + gint pos, i; + GstClockTime gstrtptime, out_time; + + //ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime); + ext_rtptime = rtptime; + + gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate); + +again: + /* first time, lock on to time and gstrtptime */ + if (jbuf->base_time == -1) + jbuf->base_time = time; + if (jbuf->base_rtptime == -1) + jbuf->base_rtptime = gstrtptime; + + if (gstrtptime >= jbuf->base_rtptime) + send_diff = gstrtptime - jbuf->base_rtptime; + else { + /* elapsed time at sender, timestamps can go backwards and thus be smaller + * than our base time, take a new base time in that case. */ + GST_DEBUG ("backward timestamps at server, taking new base time"); + jbuf->base_rtptime = gstrtptime; + jbuf->base_time = time; + send_diff = 0; + } + + GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %" + GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime, + GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime), + GST_TIME_ARGS (send_diff)); + + if (jbuf->prev_send_diff != -1 && time != -1) { + gint64 delta_diff; + + if (send_diff > jbuf->prev_send_diff) + delta_diff = send_diff - jbuf->prev_send_diff; + else + delta_diff = jbuf->prev_send_diff - send_diff; + + /* server changed rtp timestamps too quickly, reset skew detection and start + * again. This value is sortof arbitrary and can be a bad measurement up if + * there are many packets missing because then we get a big gap that is + * unrelated to a timestamp switch. */ + if (delta_diff > GST_SECOND) { + GST_DEBUG ("delta changed too quickly %" GST_TIME_FORMAT " reset skew", + GST_TIME_ARGS (delta_diff)); + rdt_jitter_buffer_reset_skew (jbuf); + goto again; + } + } + jbuf->prev_send_diff = send_diff; + + /* we don't have an arrival timestamp so we can't do skew detection. we + * should still apply a timestamp based on RDT timestamp and base_time */ + if (time == -1) + goto no_skew; + + /* elapsed time at receiver, includes the jitter */ + recv_diff = time - jbuf->base_time; + + GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %" + GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time), + GST_TIME_ARGS (recv_diff)); + + /* measure the diff */ + delta = ((gint64) recv_diff) - ((gint64) send_diff); + + pos = jbuf->window_pos; + + if (jbuf->window_filling) { + /* we are filling the window */ + GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta); + jbuf->window[pos++] = delta; + /* calc the min delta we observed */ + if (pos == 1 || delta < jbuf->window_min) + jbuf->window_min = delta; + + if (send_diff >= MAX_TIME || pos >= MAX_WINDOW) { + jbuf->window_size = pos; + + /* window filled */ + GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min); + + /* the skew is now the min */ + jbuf->skew = jbuf->window_min; + jbuf->window_filling = FALSE; + } else { + gint perc_time, perc_window, perc; + + /* figure out how much we filled the window, this depends on the amount of + * time we have or the max number of points we keep. */ + perc_time = send_diff * 100 / MAX_TIME; + perc_window = pos * 100 / MAX_WINDOW; + perc = MAX (perc_time, perc_window); + + /* make a parabolic function, the closer we get to the MAX, the more value + * we give to the scaling factor of the new value */ + perc = perc * perc; + + /* quickly go to the min value when we are filling up, slowly when we are + * just starting because we're not sure it's a good value yet. */ + jbuf->skew = + (perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000; + jbuf->window_size = pos + 1; + } + } else { + /* pick old value and store new value. We keep the previous value in order + * to quickly check if the min of the window changed */ + old = jbuf->window[pos]; + jbuf->window[pos++] = delta; + + if (delta <= jbuf->window_min) { + /* if the new value we inserted is smaller or equal to the current min, + * it becomes the new min */ + jbuf->window_min = delta; + } else if (old == jbuf->window_min) { + gint64 min = G_MAXINT64; + + /* if we removed the old min, we have to find a new min */ + for (i = 0; i < jbuf->window_size; i++) { + /* we found another value equal to the old min, we can stop searching now */ + if (jbuf->window[i] == old) { + min = old; + break; + } + if (jbuf->window[i] < min) + min = jbuf->window[i]; + } + jbuf->window_min = min; + } + /* average the min values */ + jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125; + GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT, + delta, jbuf->window_min); + } + /* wrap around in the window */ + if (pos >= jbuf->window_size) + pos = 0; + jbuf->window_pos = pos; + +no_skew: + /* the output time is defined as the base timestamp plus the RDT time + * adjusted for the clock skew .*/ + out_time = jbuf->base_time + send_diff + jbuf->skew; + + GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT, + jbuf->skew, GST_TIME_ARGS (out_time)); + + return out_time; +} + +/** + * rdt_jitter_buffer_insert: + * @jbuf: an #RDTJitterBuffer + * @buf: a buffer + * @time: a running_time when this buffer was received in nanoseconds + * @clock_rate: the clock-rate of the payload of @buf + * @tail: TRUE when the tail element changed. + * + * Inserts @buf into the packet queue of @jbuf. The sequence number of the + * packet will be used to sort the packets. This function takes ownerhip of + * @buf when the function returns %TRUE. + * @buf should have writable metadata when calling this function. + * + * Returns: %FALSE if a packet with the same number already existed. + */ +gboolean +rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf, + GstClockTime time, guint32 clock_rate, gboolean * tail) +{ + GList *list; + guint32 rtptime; + guint16 seqnum; + GstRDTPacket packet; + gboolean more; + + g_return_val_if_fail (jbuf != NULL, FALSE); + g_return_val_if_fail (buf != NULL, FALSE); + + more = gst_rdt_buffer_get_first_packet (buf, &packet); + /* programmer error */ + g_return_val_if_fail (more == TRUE, FALSE); + + seqnum = gst_rdt_packet_data_get_seq (&packet); + /* do skew calculation by measuring the difference between rtptime and the + * receive time, this function will retimestamp @buf with the skew corrected + * running time. */ + rtptime = gst_rdt_packet_data_get_timestamp (&packet); + + /* loop the list to skip strictly smaller seqnum buffers */ + for (list = jbuf->packets->head; list; list = g_list_next (list)) { + guint16 qseq; + gint gap; + + more = + gst_rdt_buffer_get_first_packet (GST_BUFFER_CAST (list->data), &packet); + /* programmer error */ + g_return_val_if_fail (more == TRUE, FALSE); + + qseq = gst_rdt_packet_data_get_seq (&packet); + + /* compare the new seqnum to the one in the buffer */ + gap = gst_rdt_buffer_compare_seqnum (seqnum, qseq); + + /* we hit a packet with the same seqnum, notify a duplicate */ + if (G_UNLIKELY (gap == 0)) + goto duplicate; + + /* seqnum > qseq, we can stop looking */ + if (G_LIKELY (gap < 0)) + break; + } + + + if (clock_rate) { + time = calculate_skew (jbuf, rtptime, time, clock_rate); + GST_BUFFER_TIMESTAMP (buf) = time; + } + + if (list) + g_queue_insert_before (jbuf->packets, list, buf); + else + g_queue_push_tail (jbuf->packets, buf); + + /* tail was changed when we did not find a previous packet, we set the return + * flag when requested. */ + if (tail) + *tail = (list == NULL); + + return TRUE; + + /* ERRORS */ +duplicate: + { + GST_WARNING ("duplicate packet %d found", (gint) seqnum); + return FALSE; + } +} + +/** + * rdt_jitter_buffer_pop: + * @jbuf: an #RDTJitterBuffer + * + * Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will + * have its timestamp adjusted with the incomming running_time and the detected + * clock skew. + * + * Returns: a #GstBuffer or %NULL when there was no packet in the queue. + */ +GstBuffer * +rdt_jitter_buffer_pop (RDTJitterBuffer * jbuf) +{ + GstBuffer *buf; + + g_return_val_if_fail (jbuf != NULL, FALSE); + + buf = g_queue_pop_tail (jbuf->packets); + + return buf; +} + +/** + * rdt_jitter_buffer_peek: + * @jbuf: an #RDTJitterBuffer + * + * Peek the oldest buffer from the packet queue of @jbuf. Register a callback + * with rdt_jitter_buffer_set_tail_changed() to be notified when an older packet + * was inserted in the queue. + * + * Returns: a #GstBuffer or %NULL when there was no packet in the queue. + */ +GstBuffer * +rdt_jitter_buffer_peek (RDTJitterBuffer * jbuf) +{ + GstBuffer *buf; + + g_return_val_if_fail (jbuf != NULL, FALSE); + + buf = g_queue_peek_tail (jbuf->packets); + + return buf; +} + +/** + * rdt_jitter_buffer_flush: + * @jbuf: an #RDTJitterBuffer + * + * Flush all packets from the jitterbuffer. + */ +void +rdt_jitter_buffer_flush (RDTJitterBuffer * jbuf) +{ + GstBuffer *buffer; + + g_return_if_fail (jbuf != NULL); + + while ((buffer = g_queue_pop_head (jbuf->packets))) + gst_buffer_unref (buffer); +} + +/** + * rdt_jitter_buffer_num_packets: + * @jbuf: an #RDTJitterBuffer + * + * Get the number of packets currently in "jbuf. + * + * Returns: The number of packets in @jbuf. + */ +guint +rdt_jitter_buffer_num_packets (RDTJitterBuffer * jbuf) +{ + g_return_val_if_fail (jbuf != NULL, 0); + + return jbuf->packets->length; +} + +/** + * rdt_jitter_buffer_get_ts_diff: + * @jbuf: an #RDTJitterBuffer + * + * Get the difference between the timestamps of first and last packet in the + * jitterbuffer. + * + * Returns: The difference expressed in the timestamp units of the packets. + */ +guint32 +rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer * jbuf) +{ + guint64 high_ts, low_ts; + GstBuffer *high_buf, *low_buf; + guint32 result; + + g_return_val_if_fail (jbuf != NULL, 0); + + high_buf = g_queue_peek_head (jbuf->packets); + low_buf = g_queue_peek_tail (jbuf->packets); + + if (!high_buf || !low_buf || high_buf == low_buf) + return 0; + + //high_ts = gst_rtp_buffer_get_timestamp (high_buf); + //low_ts = gst_rtp_buffer_get_timestamp (low_buf); + high_ts = 0; + low_ts = 0; + + /* it needs to work if ts wraps */ + if (high_ts >= low_ts) { + result = (guint32) (high_ts - low_ts); + } else { + result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts); + } + return result; +} diff --git a/gst/realmedia/rdtjitterbuffer.h b/gst/realmedia/rdtjitterbuffer.h new file mode 100644 index 0000000..7eea5e6 --- /dev/null +++ b/gst/realmedia/rdtjitterbuffer.h @@ -0,0 +1,91 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __RDT_JITTER_BUFFER_H__ +#define __RDT_JITTER_BUFFER_H__ + +#include <gst/gst.h> + +typedef struct _RDTJitterBuffer RDTJitterBuffer; +typedef struct _RDTJitterBufferClass RDTJitterBufferClass; + +#define RDT_TYPE_JITTER_BUFFER (rdt_jitter_buffer_get_type()) +#define RDT_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RDT_TYPE_JITTER_BUFFER,RDTJitterBuffer)) +#define RDT_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RDT_TYPE_JITTER_BUFFER,RDTJitterBufferClass)) +#define RDT_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RDT_TYPE_JITTER_BUFFER)) +#define RDT_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RDT_TYPE_JITTER_BUFFER)) +#define RDT_JITTER_BUFFER_CAST(src) ((RDTJitterBuffer *)(src)) + +/** + * RTPTailChanged: + * @jbuf: an #RDTJitterBuffer + * @user_data: user data specified when registering + * + * This callback will be called when the tail buffer of @jbuf changed. + */ +typedef void (*RTPTailChanged) (RDTJitterBuffer *jbuf, gpointer user_data); + +#define RDT_JITTER_BUFFER_MAX_WINDOW 512 +/** + * RDTJitterBuffer: + * + * A JitterBuffer in the #RTPSession + */ +struct _RDTJitterBuffer { + GObject object; + + GQueue *packets; + + /* for calculating skew */ + GstClockTime base_time; + GstClockTime base_rtptime; + guint64 ext_rtptime; + gint64 window[RDT_JITTER_BUFFER_MAX_WINDOW]; + guint window_pos; + guint window_size; + gboolean window_filling; + gint64 window_min; + gint64 skew; + gint64 prev_send_diff; +}; + +struct _RDTJitterBufferClass { + GObjectClass parent_class; +}; + +GType rdt_jitter_buffer_get_type (void); + +/* managing lifetime */ +RDTJitterBuffer* rdt_jitter_buffer_new (void); + +void rdt_jitter_buffer_reset_skew (RDTJitterBuffer *jbuf); + +gboolean rdt_jitter_buffer_insert (RDTJitterBuffer *jbuf, GstBuffer *buf, + GstClockTime time, + guint32 clock_rate, + gboolean *tail); +GstBuffer * rdt_jitter_buffer_peek (RDTJitterBuffer *jbuf); +GstBuffer * rdt_jitter_buffer_pop (RDTJitterBuffer *jbuf); + +void rdt_jitter_buffer_flush (RDTJitterBuffer *jbuf); + +guint rdt_jitter_buffer_num_packets (RDTJitterBuffer *jbuf); +guint32 rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer *jbuf); + +#endif /* __RDT_JITTER_BUFFER_H__ */ diff --git a/gst/realmedia/rdtmanager.c b/gst/realmedia/rdtmanager.c new file mode 100644 index 0000000..39c74b2 --- /dev/null +++ b/gst/realmedia/rdtmanager.c @@ -0,0 +1,1374 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * <2013> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* Element-Checklist-Version: 5 */ + +/** + * SECTION:element-rdtmanager + * @see_also: GstRtspSrc + * + * A simple RTP session manager used internally by rtspsrc. + */ + +/* #define HAVE_RTCP */ + +#include "gstrdtbuffer.h" +#include "rdtmanager.h" +#include "rdtjitterbuffer.h" + +#include <gst/glib-compat-private.h> + +#include <stdio.h> + +GST_DEBUG_CATEGORY_STATIC (rdtmanager_debug); +#define GST_CAT_DEFAULT (rdtmanager_debug) + +/* GstRDTManager signals and args */ +enum +{ + SIGNAL_REQUEST_PT_MAP, + SIGNAL_CLEAR_PT_MAP, + + SIGNAL_ON_NEW_SSRC, + SIGNAL_ON_SSRC_COLLISION, + SIGNAL_ON_SSRC_VALIDATED, + SIGNAL_ON_SSRC_ACTIVE, + SIGNAL_ON_SSRC_SDES, + SIGNAL_ON_BYE_SSRC, + SIGNAL_ON_BYE_TIMEOUT, + SIGNAL_ON_TIMEOUT, + SIGNAL_ON_NPT_STOP, + LAST_SIGNAL +}; + +#define DEFAULT_LATENCY_MS 200 + +enum +{ + PROP_0, + PROP_LATENCY +}; + +static GstStaticPadTemplate gst_rdt_manager_recv_rtp_sink_template = +GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rdt") + ); + +static GstStaticPadTemplate gst_rdt_manager_recv_rtcp_sink_template = +GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rtcp") + ); + +static GstStaticPadTemplate gst_rdt_manager_recv_rtp_src_template = +GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%u_%u_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("application/x-rdt") + ); + +static GstStaticPadTemplate gst_rdt_manager_rtcp_src_template = +GST_STATIC_PAD_TEMPLATE ("rtcp_src_%u", + GST_PAD_SRC, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rtcp") + ); + +static void gst_rdt_manager_finalize (GObject * object); +static void gst_rdt_manager_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_rdt_manager_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static gboolean gst_rdt_manager_query_src (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_rdt_manager_src_activate_mode (GstPad * pad, + GstObject * parent, GstPadMode mode, gboolean active); + +static GstClock *gst_rdt_manager_provide_clock (GstElement * element); +static GstStateChangeReturn gst_rdt_manager_change_state (GstElement * element, + GstStateChange transition); +static GstPad *gst_rdt_manager_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps); +static void gst_rdt_manager_release_pad (GstElement * element, GstPad * pad); + +static gboolean gst_rdt_manager_parse_caps (GstRDTManager * rdtmanager, + GstRDTManagerSession * session, GstCaps * caps); +static gboolean gst_rdt_manager_event_rdt (GstPad * pad, GstObject * parent, + GstEvent * event); + +static GstFlowReturn gst_rdt_manager_chain_rdt (GstPad * pad, + GstObject * parent, GstBuffer * buffer); +static GstFlowReturn gst_rdt_manager_chain_rtcp (GstPad * pad, + GstObject * parent, GstBuffer * buffer); +static void gst_rdt_manager_loop (GstPad * pad); + +static guint gst_rdt_manager_signals[LAST_SIGNAL] = { 0 }; + +#define JBUF_LOCK(sess) (g_mutex_lock (&(sess)->jbuf_lock)) + +#define JBUF_LOCK_CHECK(sess,label) G_STMT_START { \ + JBUF_LOCK (sess); \ + if (sess->srcresult != GST_FLOW_OK) \ + goto label; \ +} G_STMT_END + +#define JBUF_UNLOCK(sess) (g_mutex_unlock (&(sess)->jbuf_lock)) +#define JBUF_WAIT(sess) (g_cond_wait (&(sess)->jbuf_cond, &(sess)->jbuf_lock)) + +#define JBUF_WAIT_CHECK(sess,label) G_STMT_START { \ + JBUF_WAIT(sess); \ + if (sess->srcresult != GST_FLOW_OK) \ + goto label; \ +} G_STMT_END + +#define JBUF_SIGNAL(sess) (g_cond_signal (&(sess)->jbuf_cond)) + +/* Manages the receiving end of the packets. + * + * There is one such structure for each RTP session (audio/video/...). + * We get the RTP/RTCP packets and stuff them into the session manager. + */ +struct _GstRDTManagerSession +{ + /* session id */ + gint id; + /* the parent bin */ + GstRDTManager *dec; + + gboolean active; + /* we only support one ssrc and one pt */ + guint32 ssrc; + guint8 pt; + gint clock_rate; + GstCaps *caps; + gint64 clock_base; + + GstSegment segment; + + /* the last seqnum we pushed out */ + guint32 last_popped_seqnum; + /* the next expected seqnum */ + guint32 next_seqnum; + /* last output time */ + GstClockTime last_out_time; + + /* the pads of the session */ + GstPad *recv_rtp_sink; + GstPad *recv_rtp_src; + GstPad *recv_rtcp_sink; + GstPad *rtcp_src; + + GstFlowReturn srcresult; + gboolean blocked; + gboolean eos; + gboolean waiting; + gboolean discont; + GstClockID clock_id; + + /* jitterbuffer, lock and cond */ + RDTJitterBuffer *jbuf; + GMutex jbuf_lock; + GCond jbuf_cond; + + /* some accounting */ + guint64 num_late; + guint64 num_duplicates; +}; + +/* find a session with the given id */ +static GstRDTManagerSession * +find_session_by_id (GstRDTManager * rdtmanager, gint id) +{ + GSList *walk; + + for (walk = rdtmanager->sessions; walk; walk = g_slist_next (walk)) { + GstRDTManagerSession *sess = (GstRDTManagerSession *) walk->data; + + if (sess->id == id) + return sess; + } + return NULL; +} + +/* create a session with the given id */ +static GstRDTManagerSession * +create_session (GstRDTManager * rdtmanager, gint id) +{ + GstRDTManagerSession *sess; + + sess = g_new0 (GstRDTManagerSession, 1); + sess->id = id; + sess->dec = rdtmanager; + sess->jbuf = rdt_jitter_buffer_new (); + g_mutex_init (&sess->jbuf_lock); + g_cond_init (&sess->jbuf_cond); + rdtmanager->sessions = g_slist_prepend (rdtmanager->sessions, sess); + + return sess; +} + +static gboolean +forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data) +{ + GstPad *srcpad = GST_PAD_CAST (user_data); + + gst_pad_push_event (srcpad, gst_event_ref (*event)); + + return TRUE; +} + +static gboolean +activate_session (GstRDTManager * rdtmanager, GstRDTManagerSession * session, + guint32 ssrc, guint8 pt) +{ + GstPadTemplate *templ; + GstElementClass *klass; + gchar *name; + GstCaps *caps; + GValue ret = { 0 }; + GValue args[3] = { {0} + , {0} + , {0} + }; + + GST_DEBUG_OBJECT (rdtmanager, "creating stream"); + + session->ssrc = ssrc; + session->pt = pt; + + /* get pt map */ + g_value_init (&args[0], GST_TYPE_ELEMENT); + g_value_set_object (&args[0], rdtmanager); + g_value_init (&args[1], G_TYPE_UINT); + g_value_set_uint (&args[1], session->id); + g_value_init (&args[2], G_TYPE_UINT); + g_value_set_uint (&args[2], pt); + + g_value_init (&ret, GST_TYPE_CAPS); + g_value_set_boxed (&ret, NULL); + + g_signal_emitv (args, gst_rdt_manager_signals[SIGNAL_REQUEST_PT_MAP], 0, + &ret); + + g_value_unset (&args[0]); + g_value_unset (&args[1]); + g_value_unset (&args[2]); + caps = (GstCaps *) g_value_dup_boxed (&ret); + g_value_unset (&ret); + + if (caps) + gst_rdt_manager_parse_caps (rdtmanager, session, caps); + + name = g_strdup_printf ("recv_rtp_src_%u_%u_%u", session->id, ssrc, pt); + klass = GST_ELEMENT_GET_CLASS (rdtmanager); + templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%u_%u_%u"); + session->recv_rtp_src = gst_pad_new_from_template (templ, name); + g_free (name); + + gst_pad_set_element_private (session->recv_rtp_src, session); + gst_pad_set_query_function (session->recv_rtp_src, gst_rdt_manager_query_src); + gst_pad_set_activatemode_function (session->recv_rtp_src, + gst_rdt_manager_src_activate_mode); + + gst_pad_set_active (session->recv_rtp_src, TRUE); + + gst_pad_sticky_events_foreach (session->recv_rtp_sink, forward_sticky_events, + session->recv_rtp_src); + gst_pad_set_caps (session->recv_rtp_src, caps); + gst_caps_unref (caps); + + gst_element_add_pad (GST_ELEMENT_CAST (rdtmanager), session->recv_rtp_src); + + return TRUE; +} + +static void +free_session (GstRDTManagerSession * session) +{ + g_object_unref (session->jbuf); + g_cond_clear (&session->jbuf_cond); + g_mutex_clear (&session->jbuf_lock); + g_free (session); +} + +#define gst_rdt_manager_parent_class parent_class +G_DEFINE_TYPE (GstRDTManager, gst_rdt_manager, GST_TYPE_ELEMENT); + +/* BOXED:UINT,UINT */ +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) + +static void +gst_rdt_manager_marshal_BOXED__UINT_UINT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, gpointer marshal_data) +{ + typedef gpointer (*GMarshalFunc_BOXED__UINT_UINT) (gpointer data1, + guint arg_1, guint arg_2, gpointer data2); + register GMarshalFunc_BOXED__UINT_UINT callback; + register GCClosure *cc = (GCClosure *) closure; + register gpointer data1, data2; + gpointer v_return; + + g_return_if_fail (return_value != NULL); + 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_BOXED__UINT_UINT) (marshal_data ? marshal_data : + cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), data2); + + g_value_take_boxed (return_value, v_return); +} + +static void +gst_rdt_manager_marshal_VOID__UINT_UINT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1, + guint arg_1, guint arg_2, gpointer data2); + register GMarshalFunc_VOID__UINT_UINT 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__UINT_UINT) (marshal_data ? marshal_data : + cc->callback); + + callback (data1, + g_marshal_value_peek_uint (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), data2); +} + +static void +gst_rdt_manager_class_init (GstRDTManagerClass * g_class) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRDTManagerClass *klass; + + klass = (GstRDTManagerClass *) g_class; + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_rdt_manager_finalize; + gobject_class->set_property = gst_rdt_manager_set_property; + gobject_class->get_property = gst_rdt_manager_get_property; + + g_object_class_install_property (gobject_class, PROP_LATENCY, + g_param_spec_uint ("latency", "Buffer latency in ms", + "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstRDTManager::request-pt-map: + * @rdtmanager: the object which received the signal + * @session: the session + * @pt: the pt + * + * Request the payload type as #GstCaps for @pt in @session. + */ + gst_rdt_manager_signals[SIGNAL_REQUEST_PT_MAP] = + g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, request_pt_map), + NULL, NULL, gst_rdt_manager_marshal_BOXED__UINT_UINT, GST_TYPE_CAPS, 2, + G_TYPE_UINT, G_TYPE_UINT); + + /** + * GstRDTManager::clear-pt-map: + * @rtpbin: the object which received the signal + * + * Clear all previously cached pt-mapping obtained with + * GstRDTManager::request-pt-map. + */ + gst_rdt_manager_signals[SIGNAL_CLEAR_PT_MAP] = + g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, clear_pt_map), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); + + /** + * GstRDTManager::on-bye-ssrc: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the SSRC + * + * Notify of an SSRC that became inactive because of a BYE packet. + */ + gst_rdt_manager_signals[SIGNAL_ON_BYE_SSRC] = + g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, on_bye_ssrc), + NULL, NULL, gst_rdt_manager_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + /** + * GstRDTManager::on-bye-timeout: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the SSRC + * + * Notify of an SSRC that has timed out because of BYE + */ + gst_rdt_manager_signals[SIGNAL_ON_BYE_TIMEOUT] = + g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, on_bye_timeout), + NULL, NULL, gst_rdt_manager_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + /** + * GstRDTManager::on-timeout: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the SSRC + * + * Notify of an SSRC that has timed out + */ + gst_rdt_manager_signals[SIGNAL_ON_TIMEOUT] = + g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, on_timeout), + NULL, NULL, gst_rdt_manager_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + /** + * GstRDTManager::on-npt-stop: + * @rtpbin: the object which received the signal + * @session: the session + * @ssrc: the SSRC + * + * Notify that SSRC sender has sent data up to the configured NPT stop time. + */ + gst_rdt_manager_signals[SIGNAL_ON_NPT_STOP] = + g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRDTManagerClass, on_npt_stop), + NULL, NULL, gst_rdt_manager_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + + gstelement_class->provide_clock = + GST_DEBUG_FUNCPTR (gst_rdt_manager_provide_clock); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rdt_manager_change_state); + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_rdt_manager_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_rdt_manager_release_pad); + + /* sink pads */ + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_manager_recv_rtp_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_manager_recv_rtcp_sink_template)); + /* src pads */ + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_manager_recv_rtp_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rdt_manager_rtcp_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP Decoder", + "Codec/Parser/Network", + "Accepts raw RTP and RTCP packets and sends them forward", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rdtmanager_debug, "rdtmanager", 0, "RTP decoder"); +} + +static void +gst_rdt_manager_init (GstRDTManager * rdtmanager) +{ + rdtmanager->provided_clock = gst_system_clock_obtain (); + rdtmanager->latency = DEFAULT_LATENCY_MS; + GST_OBJECT_FLAG_SET (rdtmanager, GST_ELEMENT_FLAG_PROVIDE_CLOCK); +} + +static void +gst_rdt_manager_finalize (GObject * object) +{ + GstRDTManager *rdtmanager; + + rdtmanager = GST_RDT_MANAGER (object); + + g_slist_foreach (rdtmanager->sessions, (GFunc) free_session, NULL); + g_slist_free (rdtmanager->sessions); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rdt_manager_query_src (GstPad * pad, GstObject * parent, GstQuery * query) +{ + GstRDTManager *rdtmanager; + gboolean res; + + rdtmanager = GST_RDT_MANAGER (parent); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + { + GstClockTime latency; + + latency = rdtmanager->latency * GST_MSECOND; + + /* we pretend to be live with a 3 second latency */ + gst_query_set_latency (query, TRUE, latency, -1); + + GST_DEBUG_OBJECT (rdtmanager, "reporting %" GST_TIME_FORMAT " of latency", + GST_TIME_ARGS (latency)); + res = TRUE; + break; + } + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + return res; +} + +static gboolean +gst_rdt_manager_src_activate_mode (GstPad * pad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean result; + GstRDTManager *rdtmanager; + GstRDTManagerSession *session; + + session = gst_pad_get_element_private (pad); + rdtmanager = session->dec; + + switch (mode) { + case GST_PAD_MODE_PUSH: + if (active) { + /* allow data processing */ + JBUF_LOCK (session); + GST_DEBUG_OBJECT (rdtmanager, "Enabling pop on queue"); + /* Mark as non flushing */ + session->srcresult = GST_FLOW_OK; + gst_segment_init (&session->segment, GST_FORMAT_TIME); + session->last_popped_seqnum = -1; + session->last_out_time = -1; + session->next_seqnum = -1; + session->eos = FALSE; + JBUF_UNLOCK (session); + + /* start pushing out buffers */ + GST_DEBUG_OBJECT (rdtmanager, "Starting task on srcpad"); + result = + gst_pad_start_task (pad, (GstTaskFunction) gst_rdt_manager_loop, + pad, NULL); + } else { + /* make sure all data processing stops ASAP */ + JBUF_LOCK (session); + /* mark ourselves as flushing */ + session->srcresult = GST_FLOW_FLUSHING; + GST_DEBUG_OBJECT (rdtmanager, "Disabling pop on queue"); + /* this unblocks any waiting pops on the src pad task */ + JBUF_SIGNAL (session); + /* unlock clock, we just unschedule, the entry will be released by + * the locking streaming thread. */ + if (session->clock_id) + gst_clock_id_unschedule (session->clock_id); + JBUF_UNLOCK (session); + + /* NOTE this will hardlock if the state change is called from the src pad + * task thread because we will _join() the thread. */ + GST_DEBUG_OBJECT (rdtmanager, "Stopping task on srcpad"); + result = gst_pad_stop_task (pad); + } + break; + default: + result = FALSE; + break; + } + return result; +} + +static GstFlowReturn +gst_rdt_manager_handle_data_packet (GstRDTManagerSession * session, + GstClockTime timestamp, GstRDTPacket * packet) +{ + GstRDTManager *rdtmanager; + guint16 seqnum; + gboolean tail; + GstFlowReturn res; + GstBuffer *buffer; + + rdtmanager = session->dec; + + res = GST_FLOW_OK; + + seqnum = 0; + GST_DEBUG_OBJECT (rdtmanager, + "Received packet #%d at time %" GST_TIME_FORMAT, seqnum, + GST_TIME_ARGS (timestamp)); + + buffer = gst_rdt_packet_to_buffer (packet); + + JBUF_LOCK_CHECK (session, out_flushing); + + /* insert the packet into the queue now, FIXME, use seqnum */ + if (!rdt_jitter_buffer_insert (session->jbuf, buffer, timestamp, + session->clock_rate, &tail)) + goto duplicate; + + /* signal addition of new buffer when the _loop is waiting. */ + if (session->waiting) + JBUF_SIGNAL (session); + +finished: + JBUF_UNLOCK (session); + + return res; + + /* ERRORS */ +out_flushing: + { + res = session->srcresult; + GST_DEBUG_OBJECT (rdtmanager, "flushing %s", gst_flow_get_name (res)); + gst_buffer_unref (buffer); + goto finished; + } +duplicate: + { + GST_WARNING_OBJECT (rdtmanager, "Duplicate packet #%d detected, dropping", + seqnum); + session->num_duplicates++; + gst_buffer_unref (buffer); + goto finished; + } +} + +static gboolean +gst_rdt_manager_parse_caps (GstRDTManager * rdtmanager, + GstRDTManagerSession * session, GstCaps * caps) +{ + GstStructure *caps_struct; + guint val; + + /* first parse the caps */ + caps_struct = gst_caps_get_structure (caps, 0); + + GST_DEBUG_OBJECT (rdtmanager, "got caps"); + + /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to + * measure the amount of data in the buffer */ + if (!gst_structure_get_int (caps_struct, "clock-rate", &session->clock_rate)) + session->clock_rate = 1000; + + if (session->clock_rate <= 0) + goto wrong_rate; + + GST_DEBUG_OBJECT (rdtmanager, "got clock-rate %d", session->clock_rate); + + /* gah, clock-base is uint. If we don't have a base, we will use the first + * buffer timestamp as the base time. This will screw up sync but it's better + * than nothing. */ + if (gst_structure_get_uint (caps_struct, "clock-base", &val)) + session->clock_base = val; + else + session->clock_base = -1; + + GST_DEBUG_OBJECT (rdtmanager, "got clock-base %" G_GINT64_FORMAT, + session->clock_base); + + /* first expected seqnum */ + if (gst_structure_get_uint (caps_struct, "seqnum-base", &val)) + session->next_seqnum = val; + else + session->next_seqnum = -1; + + GST_DEBUG_OBJECT (rdtmanager, "got seqnum-base %d", session->next_seqnum); + + return TRUE; + + /* ERRORS */ +wrong_rate: + { + GST_DEBUG_OBJECT (rdtmanager, "Invalid clock-rate %d", session->clock_rate); + return FALSE; + } +} + +static gboolean +gst_rdt_manager_event_rdt (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstRDTManager *rdtmanager; + GstRDTManagerSession *session; + gboolean res; + + rdtmanager = GST_RDT_MANAGER (parent); + /* find session */ + session = gst_pad_get_element_private (pad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_rdt_manager_parse_caps (rdtmanager, session, caps); + gst_event_unref (event); + break; + } + default: + res = gst_pad_event_default (pad, parent, event); + break; + } + return res; +} + +static GstFlowReturn +gst_rdt_manager_chain_rdt (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstFlowReturn res; + GstRDTManager *rdtmanager; + GstRDTManagerSession *session; + GstClockTime timestamp; + GstRDTPacket packet; + guint32 ssrc; + guint8 pt; + gboolean more; + + rdtmanager = GST_RDT_MANAGER (parent); + + GST_DEBUG_OBJECT (rdtmanager, "got RDT packet"); + + ssrc = 0; + pt = 0; + + GST_DEBUG_OBJECT (rdtmanager, "SSRC %08x, PT %d", ssrc, pt); + + /* find session */ + session = gst_pad_get_element_private (pad); + + /* see if we have the pad */ + if (!session->active) { + activate_session (rdtmanager, session, ssrc, pt); + session->active = TRUE; + } + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rdtmanager, "received discont"); + session->discont = TRUE; + } + + res = GST_FLOW_OK; + + /* take the timestamp of the buffer. This is the time when the packet was + * received and is used to calculate jitter and clock skew. We will adjust + * this timestamp with the smoothed value after processing it in the + * jitterbuffer. */ + timestamp = GST_BUFFER_TIMESTAMP (buffer); + /* bring to running time */ + timestamp = gst_segment_to_running_time (&session->segment, GST_FORMAT_TIME, + timestamp); + + more = gst_rdt_buffer_get_first_packet (buffer, &packet); + while (more) { + GstRDTType type; + + type = gst_rdt_packet_get_type (&packet); + GST_DEBUG_OBJECT (rdtmanager, "Have packet of type %04x", type); + + if (GST_RDT_IS_DATA_TYPE (type)) { + GST_DEBUG_OBJECT (rdtmanager, "We have a data packet"); + res = gst_rdt_manager_handle_data_packet (session, timestamp, &packet); + } else { + switch (type) { + default: + GST_DEBUG_OBJECT (rdtmanager, "Ignoring packet"); + break; + } + } + if (res != GST_FLOW_OK) + break; + + more = gst_rdt_packet_move_to_next (&packet); + } + + gst_buffer_unref (buffer); + + return res; +} + +/* push packets from the queue to the downstream demuxer */ +static void +gst_rdt_manager_loop (GstPad * pad) +{ + GstRDTManager *rdtmanager; + GstRDTManagerSession *session; + GstBuffer *buffer; + GstFlowReturn result; + + rdtmanager = GST_RDT_MANAGER (GST_PAD_PARENT (pad)); + + session = gst_pad_get_element_private (pad); + + JBUF_LOCK_CHECK (session, flushing); + GST_DEBUG_OBJECT (rdtmanager, "Peeking item"); + while (TRUE) { + /* always wait if we are blocked */ + if (!session->blocked) { + /* if we have a packet, we can exit the loop and grab it */ + if (rdt_jitter_buffer_num_packets (session->jbuf) > 0) + break; + /* no packets but we are EOS, do eos logic */ + if (session->eos) + goto do_eos; + } + /* underrun, wait for packets or flushing now */ + session->waiting = TRUE; + JBUF_WAIT_CHECK (session, flushing); + session->waiting = FALSE; + } + + buffer = rdt_jitter_buffer_pop (session->jbuf); + + GST_DEBUG_OBJECT (rdtmanager, "Got item %p", buffer); + + if (session->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + session->discont = FALSE; + } + + JBUF_UNLOCK (session); + + result = gst_pad_push (session->recv_rtp_src, buffer); + if (result != GST_FLOW_OK) + goto pause; + + return; + + /* ERRORS */ +flushing: + { + GST_DEBUG_OBJECT (rdtmanager, "we are flushing"); + gst_pad_pause_task (session->recv_rtp_src); + JBUF_UNLOCK (session); + return; + } +do_eos: + { + /* store result, we are flushing now */ + GST_DEBUG_OBJECT (rdtmanager, "We are EOS, pushing EOS downstream"); + session->srcresult = GST_FLOW_EOS; + gst_pad_pause_task (session->recv_rtp_src); + gst_pad_push_event (session->recv_rtp_src, gst_event_new_eos ()); + JBUF_UNLOCK (session); + return; + } +pause: + { + GST_DEBUG_OBJECT (rdtmanager, "pausing task, reason %s", + gst_flow_get_name (result)); + + JBUF_LOCK (session); + /* store result */ + session->srcresult = result; + /* we don't post errors or anything because upstream will do that for us + * when we pass the return value upstream. */ + gst_pad_pause_task (session->recv_rtp_src); + JBUF_UNLOCK (session); + return; + } +} + +static GstFlowReturn +gst_rdt_manager_chain_rtcp (GstPad * pad, GstObject * parent, + GstBuffer * buffer) +{ + GstRDTManager *src; + +#ifdef HAVE_RTCP + gboolean valid; + GstRTCPPacket packet; + gboolean more; +#endif + + src = GST_RDT_MANAGER (parent); + + GST_DEBUG_OBJECT (src, "got rtcp packet"); + +#ifdef HAVE_RTCP + valid = gst_rtcp_buffer_validate (buffer); + if (!valid) + goto bad_packet; + + /* position on first packet */ + more = gst_rtcp_buffer_get_first_packet (buffer, &packet); + while (more) { + switch (gst_rtcp_packet_get_type (&packet)) { + case GST_RTCP_TYPE_SR: + { + guint32 ssrc, rtptime, packet_count, octet_count; + guint64 ntptime; + guint count, i; + + gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, &rtptime, + &packet_count, &octet_count); + + GST_DEBUG_OBJECT (src, + "got SR packet: SSRC %08x, NTP %" G_GUINT64_FORMAT + ", RTP %u, PC %u, OC %u", ssrc, ntptime, rtptime, packet_count, + octet_count); + + count = gst_rtcp_packet_get_rb_count (&packet); + for (i = 0; i < count; i++) { + guint32 ssrc, exthighestseq, jitter, lsr, dlsr; + guint8 fractionlost; + gint32 packetslost; + + gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost, + &packetslost, &exthighestseq, &jitter, &lsr, &dlsr); + + GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u" + ", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost, + packetslost, exthighestseq, jitter, lsr, dlsr); + } + break; + } + case GST_RTCP_TYPE_RR: + { + guint32 ssrc; + guint count, i; + + ssrc = gst_rtcp_packet_rr_get_ssrc (&packet); + + GST_DEBUG_OBJECT (src, "got RR packet: SSRC %08x", ssrc); + + count = gst_rtcp_packet_get_rb_count (&packet); + for (i = 0; i < count; i++) { + guint32 ssrc, exthighestseq, jitter, lsr, dlsr; + guint8 fractionlost; + gint32 packetslost; + + gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost, + &packetslost, &exthighestseq, &jitter, &lsr, &dlsr); + + GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u" + ", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost, + packetslost, exthighestseq, jitter, lsr, dlsr); + } + break; + } + case GST_RTCP_TYPE_SDES: + { + guint chunks, i, j; + gboolean more_chunks, more_items; + + chunks = gst_rtcp_packet_sdes_get_chunk_count (&packet); + GST_DEBUG_OBJECT (src, "got SDES packet with %d chunks", chunks); + + more_chunks = gst_rtcp_packet_sdes_first_chunk (&packet); + i = 0; + while (more_chunks) { + guint32 ssrc; + + ssrc = gst_rtcp_packet_sdes_get_ssrc (&packet); + + GST_DEBUG_OBJECT (src, "chunk %d, SSRC %08x", i, ssrc); + + more_items = gst_rtcp_packet_sdes_first_item (&packet); + j = 0; + while (more_items) { + GstRTCPSDESType type; + guint8 len; + gchar *data; + + gst_rtcp_packet_sdes_get_item (&packet, &type, &len, &data); + + GST_DEBUG_OBJECT (src, "item %d, type %d, len %d, data %s", j, + type, len, data); + + more_items = gst_rtcp_packet_sdes_next_item (&packet); + j++; + } + more_chunks = gst_rtcp_packet_sdes_next_chunk (&packet); + i++; + } + break; + } + case GST_RTCP_TYPE_BYE: + { + guint count, i; + gchar *reason; + + reason = gst_rtcp_packet_bye_get_reason (&packet); + GST_DEBUG_OBJECT (src, "got BYE packet (reason: %s)", + GST_STR_NULL (reason)); + g_free (reason); + + count = gst_rtcp_packet_bye_get_ssrc_count (&packet); + for (i = 0; i < count; i++) { + guint32 ssrc; + + + ssrc = gst_rtcp_packet_bye_get_nth_ssrc (&packet, i); + + GST_DEBUG_OBJECT (src, "SSRC: %08x", ssrc); + } + break; + } + case GST_RTCP_TYPE_APP: + GST_DEBUG_OBJECT (src, "got APP packet"); + break; + default: + GST_WARNING_OBJECT (src, "got unknown RTCP packet"); + break; + } + more = gst_rtcp_packet_move_to_next (&packet); + } + gst_buffer_unref (buffer); + return GST_FLOW_OK; + +bad_packet: + { + GST_WARNING_OBJECT (src, "got invalid RTCP packet"); + return GST_FLOW_OK; + } +#else + return GST_FLOW_OK; +#endif +} + +static void +gst_rdt_manager_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRDTManager *src; + + src = GST_RDT_MANAGER (object); + + switch (prop_id) { + case PROP_LATENCY: + src->latency = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rdt_manager_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstRDTManager *src; + + src = GST_RDT_MANAGER (object); + + switch (prop_id) { + case PROP_LATENCY: + g_value_set_uint (value, src->latency); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstClock * +gst_rdt_manager_provide_clock (GstElement * element) +{ + GstRDTManager *rdtmanager; + + rdtmanager = GST_RDT_MANAGER (element); + + return GST_CLOCK_CAST (gst_object_ref (rdtmanager->provided_clock)); +} + +static GstStateChangeReturn +gst_rdt_manager_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + /* we're NO_PREROLL when going to PAUSED */ + ret = GST_STATE_CHANGE_NO_PREROLL; + break; + default: + break; + } + + return ret; +} + +/* Create a pad for receiving RTP for the session in @name + */ +static GstPad * +create_recv_rtp (GstRDTManager * rdtmanager, GstPadTemplate * templ, + const gchar * name) +{ + guint sessid; + GstRDTManagerSession *session; + + /* first get the session number */ + if (name == NULL || sscanf (name, "recv_rtp_sink_%u", &sessid) != 1) + goto no_name; + + GST_DEBUG_OBJECT (rdtmanager, "finding session %d", sessid); + + /* get or create session */ + session = find_session_by_id (rdtmanager, sessid); + if (!session) { + GST_DEBUG_OBJECT (rdtmanager, "creating session %d", sessid); + /* create session now */ + session = create_session (rdtmanager, sessid); + if (session == NULL) + goto create_error; + } + /* check if pad was requested */ + if (session->recv_rtp_sink != NULL) + goto existed; + + GST_DEBUG_OBJECT (rdtmanager, "getting RTP sink pad"); + + session->recv_rtp_sink = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (session->recv_rtp_sink, session); + gst_pad_set_event_function (session->recv_rtp_sink, + gst_rdt_manager_event_rdt); + gst_pad_set_chain_function (session->recv_rtp_sink, + gst_rdt_manager_chain_rdt); + gst_pad_set_active (session->recv_rtp_sink, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rdtmanager), session->recv_rtp_sink); + + return session->recv_rtp_sink; + + /* ERRORS */ +no_name: + { + g_warning ("rdtmanager: invalid name given"); + return NULL; + } +create_error: + { + /* create_session already warned */ + return NULL; + } +existed: + { + g_warning ("rdtmanager: recv_rtp pad already requested for session %d", + sessid); + return NULL; + } +} + +/* Create a pad for receiving RTCP for the session in @name + */ +static GstPad * +create_recv_rtcp (GstRDTManager * rdtmanager, GstPadTemplate * templ, + const gchar * name) +{ + guint sessid; + GstRDTManagerSession *session; + + /* first get the session number */ + if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1) + goto no_name; + + GST_DEBUG_OBJECT (rdtmanager, "finding session %d", sessid); + + /* get the session, it must exist or we error */ + session = find_session_by_id (rdtmanager, sessid); + if (!session) + goto no_session; + + /* check if pad was requested */ + if (session->recv_rtcp_sink != NULL) + goto existed; + + GST_DEBUG_OBJECT (rdtmanager, "getting RTCP sink pad"); + + session->recv_rtcp_sink = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (session->recv_rtp_sink, session); + gst_pad_set_chain_function (session->recv_rtcp_sink, + gst_rdt_manager_chain_rtcp); + gst_pad_set_active (session->recv_rtcp_sink, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rdtmanager), session->recv_rtcp_sink); + + return session->recv_rtcp_sink; + + /* ERRORS */ +no_name: + { + g_warning ("rdtmanager: invalid name given"); + return NULL; + } +no_session: + { + g_warning ("rdtmanager: no session with id %d", sessid); + return NULL; + } +existed: + { + g_warning ("rdtmanager: recv_rtcp pad already requested for session %d", + sessid); + return NULL; + } +} + +/* Create a pad for sending RTCP for the session in @name + */ +static GstPad * +create_rtcp (GstRDTManager * rdtmanager, GstPadTemplate * templ, + const gchar * name) +{ + guint sessid; + GstRDTManagerSession *session; + + /* first get the session number */ + if (name == NULL || sscanf (name, "rtcp_src_%u", &sessid) != 1) + goto no_name; + + /* get or create session */ + session = find_session_by_id (rdtmanager, sessid); + if (!session) + goto no_session; + + /* check if pad was requested */ + if (session->rtcp_src != NULL) + goto existed; + + session->rtcp_src = gst_pad_new_from_template (templ, name); + gst_pad_set_active (session->rtcp_src, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (rdtmanager), session->rtcp_src); + + return session->rtcp_src; + + /* ERRORS */ +no_name: + { + g_warning ("rdtmanager: invalid name given"); + return NULL; + } +no_session: + { + g_warning ("rdtmanager: session with id %d does not exist", sessid); + return NULL; + } +existed: + { + g_warning ("rdtmanager: rtcp_src pad already requested for session %d", + sessid); + return NULL; + } +} + +/* + */ +static GstPad * +gst_rdt_manager_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps) +{ + GstRDTManager *rdtmanager; + GstElementClass *klass; + GstPad *result; + + g_return_val_if_fail (templ != NULL, NULL); + g_return_val_if_fail (GST_IS_RDT_MANAGER (element), NULL); + + rdtmanager = GST_RDT_MANAGER (element); + klass = GST_ELEMENT_GET_CLASS (element); + + /* figure out the template */ + if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%u")) { + result = create_recv_rtp (rdtmanager, templ, name); + } else if (templ == gst_element_class_get_pad_template (klass, + "recv_rtcp_sink_%u")) { + result = create_recv_rtcp (rdtmanager, templ, name); + } else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%u")) { + result = create_rtcp (rdtmanager, templ, name); + } else + goto wrong_template; + + return result; + + /* ERRORS */ +wrong_template: + { + g_warning ("rdtmanager: this is not our template"); + return NULL; + } +} + +static void +gst_rdt_manager_release_pad (GstElement * element, GstPad * pad) +{ +} + +gboolean +gst_rdt_manager_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rdtmanager", + GST_RANK_NONE, GST_TYPE_RDT_MANAGER); +} diff --git a/gst/realmedia/rdtmanager.h b/gst/realmedia/rdtmanager.h new file mode 100644 index 0000000..053953c --- /dev/null +++ b/gst/realmedia/rdtmanager.h @@ -0,0 +1,93 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GST_RDT_MANAGER_H__ +#define __GST_RDT_MANAGER_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RDT_MANAGER (gst_rdt_manager_get_type()) +#define GST_IS_RDT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RDT_MANAGER)) +#define GST_IS_RDT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RDT_MANAGER)) +#define GST_RDT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RDT_MANAGER, GstRDTManager)) +#define GST_RDT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RDT_MANAGER, GstRDTManagerClass)) + +typedef struct _GstRDTManager GstRDTManager; +typedef struct _GstRDTManagerClass GstRDTManagerClass; +typedef struct _GstRDTManagerSession GstRDTManagerSession; + +struct _GstRDTManager { + GstElement element; + + guint latency; + GSList *sessions; + GstClock *provided_clock; +}; + +struct _GstRDTManagerClass { + GstElementClass parent_class; + + /* get the caps for pt */ + GstCaps* (*request_pt_map) (GstRDTManager *rtpdec, guint session, guint pt); + + void (*clear_pt_map) (GstRDTManager *rtpdec); + + void (*on_new_ssrc) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_ssrc_collision) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_ssrc_validated) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_ssrc_active) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_ssrc_sdes) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_bye_ssrc) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_bye_timeout) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_timeout) (GstRDTManager *rtpdec, guint session, guint32 ssrc); + void (*on_npt_stop) (GstRDTManager *rtpdec, guint session, guint32 ssrc); +}; + +GType gst_rdt_manager_get_type(void); + +gboolean gst_rdt_manager_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RDT_MANAGER_H__ */ diff --git a/gst/realmedia/realhash.c b/gst/realmedia/realhash.c new file mode 100644 index 0000000..294f204 --- /dev/null +++ b/gst/realmedia/realhash.c @@ -0,0 +1,321 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* Element-Checklist-Version: 5 */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "_stdint.h" +#include <string.h> + +#include <gst/gst.h> + +#include "realhash.h" + +void rtsp_ext_real_calc_response_and_checksum (char *response, + char *chksum, char *challenge); + +/* + * The following code has been copied from + * xine-lib-1.1.1/src/input/libreal/real.c. + */ + +static const unsigned char xor_table[] = { + 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53, + 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70, + 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09, + 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02, + 0x10, 0x57, 0x05, 0x18, 0x54, 0x00, 0x00, 0x00 +}; + +#define LE_32(x) GST_READ_UINT32_LE(x) +#define BE_32C(x,y) GST_WRITE_UINT32_BE(x,y) +#define LE_32C(x,y) GST_WRITE_UINT32_LE(x,y) + +static void +hash (char *field, char *param) +{ + uint32_t a, b, c, d; + + /* fill variables */ + a = LE_32 (field); + b = LE_32 (field + 4); + c = LE_32 (field + 8); + d = LE_32 (field + 12); + + a = ((b & c) | (~b & d)) + LE_32 ((param + 0x00)) + a - 0x28955B88; + a = ((a << 0x07) | (a >> 0x19)) + b; + d = ((a & b) | (~a & c)) + LE_32 ((param + 0x04)) + d - 0x173848AA; + d = ((d << 0x0c) | (d >> 0x14)) + a; + c = ((d & a) | (~d & b)) + LE_32 ((param + 0x08)) + c + 0x242070DB; + c = ((c << 0x11) | (c >> 0x0f)) + d; + b = ((c & d) | (~c & a)) + LE_32 ((param + 0x0c)) + b - 0x3E423112; + b = ((b << 0x16) | (b >> 0x0a)) + c; + a = ((b & c) | (~b & d)) + LE_32 ((param + 0x10)) + a - 0x0A83F051; + a = ((a << 0x07) | (a >> 0x19)) + b; + d = ((a & b) | (~a & c)) + LE_32 ((param + 0x14)) + d + 0x4787C62A; + d = ((d << 0x0c) | (d >> 0x14)) + a; + c = ((d & a) | (~d & b)) + LE_32 ((param + 0x18)) + c - 0x57CFB9ED; + c = ((c << 0x11) | (c >> 0x0f)) + d; + b = ((c & d) | (~c & a)) + LE_32 ((param + 0x1c)) + b - 0x02B96AFF; + b = ((b << 0x16) | (b >> 0x0a)) + c; + a = ((b & c) | (~b & d)) + LE_32 ((param + 0x20)) + a + 0x698098D8; + a = ((a << 0x07) | (a >> 0x19)) + b; + d = ((a & b) | (~a & c)) + LE_32 ((param + 0x24)) + d - 0x74BB0851; + d = ((d << 0x0c) | (d >> 0x14)) + a; + c = ((d & a) | (~d & b)) + LE_32 ((param + 0x28)) + c - 0x0000A44F; + c = ((c << 0x11) | (c >> 0x0f)) + d; + b = ((c & d) | (~c & a)) + LE_32 ((param + 0x2C)) + b - 0x76A32842; + b = ((b << 0x16) | (b >> 0x0a)) + c; + a = ((b & c) | (~b & d)) + LE_32 ((param + 0x30)) + a + 0x6B901122; + a = ((a << 0x07) | (a >> 0x19)) + b; + d = ((a & b) | (~a & c)) + LE_32 ((param + 0x34)) + d - 0x02678E6D; + d = ((d << 0x0c) | (d >> 0x14)) + a; + c = ((d & a) | (~d & b)) + LE_32 ((param + 0x38)) + c - 0x5986BC72; + c = ((c << 0x11) | (c >> 0x0f)) + d; + b = ((c & d) | (~c & a)) + LE_32 ((param + 0x3c)) + b + 0x49B40821; + b = ((b << 0x16) | (b >> 0x0a)) + c; + + a = ((b & d) | (~d & c)) + LE_32 ((param + 0x04)) + a - 0x09E1DA9E; + a = ((a << 0x05) | (a >> 0x1b)) + b; + d = ((a & c) | (~c & b)) + LE_32 ((param + 0x18)) + d - 0x3FBF4CC0; + d = ((d << 0x09) | (d >> 0x17)) + a; + c = ((d & b) | (~b & a)) + LE_32 ((param + 0x2c)) + c + 0x265E5A51; + c = ((c << 0x0e) | (c >> 0x12)) + d; + b = ((c & a) | (~a & d)) + LE_32 ((param + 0x00)) + b - 0x16493856; + b = ((b << 0x14) | (b >> 0x0c)) + c; + a = ((b & d) | (~d & c)) + LE_32 ((param + 0x14)) + a - 0x29D0EFA3; + a = ((a << 0x05) | (a >> 0x1b)) + b; + d = ((a & c) | (~c & b)) + LE_32 ((param + 0x28)) + d + 0x02441453; + d = ((d << 0x09) | (d >> 0x17)) + a; + c = ((d & b) | (~b & a)) + LE_32 ((param + 0x3c)) + c - 0x275E197F; + c = ((c << 0x0e) | (c >> 0x12)) + d; + b = ((c & a) | (~a & d)) + LE_32 ((param + 0x10)) + b - 0x182C0438; + b = ((b << 0x14) | (b >> 0x0c)) + c; + a = ((b & d) | (~d & c)) + LE_32 ((param + 0x24)) + a + 0x21E1CDE6; + a = ((a << 0x05) | (a >> 0x1b)) + b; + d = ((a & c) | (~c & b)) + LE_32 ((param + 0x38)) + d - 0x3CC8F82A; + d = ((d << 0x09) | (d >> 0x17)) + a; + c = ((d & b) | (~b & a)) + LE_32 ((param + 0x0c)) + c - 0x0B2AF279; + c = ((c << 0x0e) | (c >> 0x12)) + d; + b = ((c & a) | (~a & d)) + LE_32 ((param + 0x20)) + b + 0x455A14ED; + b = ((b << 0x14) | (b >> 0x0c)) + c; + a = ((b & d) | (~d & c)) + LE_32 ((param + 0x34)) + a - 0x561C16FB; + a = ((a << 0x05) | (a >> 0x1b)) + b; + d = ((a & c) | (~c & b)) + LE_32 ((param + 0x08)) + d - 0x03105C08; + d = ((d << 0x09) | (d >> 0x17)) + a; + c = ((d & b) | (~b & a)) + LE_32 ((param + 0x1c)) + c + 0x676F02D9; + c = ((c << 0x0e) | (c >> 0x12)) + d; + b = ((c & a) | (~a & d)) + LE_32 ((param + 0x30)) + b - 0x72D5B376; + b = ((b << 0x14) | (b >> 0x0c)) + c; + + a = (b ^ c ^ d) + LE_32 ((param + 0x14)) + a - 0x0005C6BE; + a = ((a << 0x04) | (a >> 0x1c)) + b; + d = (a ^ b ^ c) + LE_32 ((param + 0x20)) + d - 0x788E097F; + d = ((d << 0x0b) | (d >> 0x15)) + a; + c = (d ^ a ^ b) + LE_32 ((param + 0x2c)) + c + 0x6D9D6122; + c = ((c << 0x10) | (c >> 0x10)) + d; + b = (c ^ d ^ a) + LE_32 ((param + 0x38)) + b - 0x021AC7F4; + b = ((b << 0x17) | (b >> 0x09)) + c; + a = (b ^ c ^ d) + LE_32 ((param + 0x04)) + a - 0x5B4115BC; + a = ((a << 0x04) | (a >> 0x1c)) + b; + d = (a ^ b ^ c) + LE_32 ((param + 0x10)) + d + 0x4BDECFA9; + d = ((d << 0x0b) | (d >> 0x15)) + a; + c = (d ^ a ^ b) + LE_32 ((param + 0x1c)) + c - 0x0944B4A0; + c = ((c << 0x10) | (c >> 0x10)) + d; + b = (c ^ d ^ a) + LE_32 ((param + 0x28)) + b - 0x41404390; + b = ((b << 0x17) | (b >> 0x09)) + c; + a = (b ^ c ^ d) + LE_32 ((param + 0x34)) + a + 0x289B7EC6; + a = ((a << 0x04) | (a >> 0x1c)) + b; + d = (a ^ b ^ c) + LE_32 ((param + 0x00)) + d - 0x155ED806; + d = ((d << 0x0b) | (d >> 0x15)) + a; + c = (d ^ a ^ b) + LE_32 ((param + 0x0c)) + c - 0x2B10CF7B; + c = ((c << 0x10) | (c >> 0x10)) + d; + b = (c ^ d ^ a) + LE_32 ((param + 0x18)) + b + 0x04881D05; + b = ((b << 0x17) | (b >> 0x09)) + c; + a = (b ^ c ^ d) + LE_32 ((param + 0x24)) + a - 0x262B2FC7; + a = ((a << 0x04) | (a >> 0x1c)) + b; + d = (a ^ b ^ c) + LE_32 ((param + 0x30)) + d - 0x1924661B; + d = ((d << 0x0b) | (d >> 0x15)) + a; + c = (d ^ a ^ b) + LE_32 ((param + 0x3c)) + c + 0x1fa27cf8; + c = ((c << 0x10) | (c >> 0x10)) + d; + b = (c ^ d ^ a) + LE_32 ((param + 0x08)) + b - 0x3B53A99B; + b = ((b << 0x17) | (b >> 0x09)) + c; + + a = ((~d | b) ^ c) + LE_32 ((param + 0x00)) + a - 0x0BD6DDBC; + a = ((a << 0x06) | (a >> 0x1a)) + b; + d = ((~c | a) ^ b) + LE_32 ((param + 0x1c)) + d + 0x432AFF97; + d = ((d << 0x0a) | (d >> 0x16)) + a; + c = ((~b | d) ^ a) + LE_32 ((param + 0x38)) + c - 0x546BDC59; + c = ((c << 0x0f) | (c >> 0x11)) + d; + b = ((~a | c) ^ d) + LE_32 ((param + 0x14)) + b - 0x036C5FC7; + b = ((b << 0x15) | (b >> 0x0b)) + c; + a = ((~d | b) ^ c) + LE_32 ((param + 0x30)) + a + 0x655B59C3; + a = ((a << 0x06) | (a >> 0x1a)) + b; + d = ((~c | a) ^ b) + LE_32 ((param + 0x0C)) + d - 0x70F3336E; + d = ((d << 0x0a) | (d >> 0x16)) + a; + c = ((~b | d) ^ a) + LE_32 ((param + 0x28)) + c - 0x00100B83; + c = ((c << 0x0f) | (c >> 0x11)) + d; + b = ((~a | c) ^ d) + LE_32 ((param + 0x04)) + b - 0x7A7BA22F; + b = ((b << 0x15) | (b >> 0x0b)) + c; + a = ((~d | b) ^ c) + LE_32 ((param + 0x20)) + a + 0x6FA87E4F; + a = ((a << 0x06) | (a >> 0x1a)) + b; + d = ((~c | a) ^ b) + LE_32 ((param + 0x3c)) + d - 0x01D31920; + d = ((d << 0x0a) | (d >> 0x16)) + a; + c = ((~b | d) ^ a) + LE_32 ((param + 0x18)) + c - 0x5CFEBCEC; + c = ((c << 0x0f) | (c >> 0x11)) + d; + b = ((~a | c) ^ d) + LE_32 ((param + 0x34)) + b + 0x4E0811A1; + b = ((b << 0x15) | (b >> 0x0b)) + c; + a = ((~d | b) ^ c) + LE_32 ((param + 0x10)) + a - 0x08AC817E; + a = ((a << 0x06) | (a >> 0x1a)) + b; + d = ((~c | a) ^ b) + LE_32 ((param + 0x2c)) + d - 0x42C50DCB; + d = ((d << 0x0a) | (d >> 0x16)) + a; + c = ((~b | d) ^ a) + LE_32 ((param + 0x08)) + c + 0x2AD7D2BB; + c = ((c << 0x0f) | (c >> 0x11)) + d; + b = ((~a | c) ^ d) + LE_32 ((param + 0x24)) + b - 0x14792C6F; + b = ((b << 0x15) | (b >> 0x0b)) + c; + + a += LE_32 (field); + b += LE_32 (field + 4); + c += LE_32 (field + 8); + d += LE_32 (field + 12); + + LE_32C (field, a); + LE_32C (field + 4, b); + LE_32C (field + 8, c); + LE_32C (field + 12, d); +} + +static void +call_hash (char *key, char *challenge, int len) +{ + uint8_t *ptr1, *ptr2; + uint32_t a, b, c, d, tmp; + + ptr1 = (uint8_t *) (key + 16); + ptr2 = (uint8_t *) (key + 20); + + a = LE_32 (ptr1); + b = (a >> 3) & 0x3f; + a += len * 8; + LE_32C (ptr1, a); + + if (a < (len << 3)) + ptr2 += 4; + + tmp = LE_32 (ptr2) + (len >> 0x1d); + LE_32C (ptr2, tmp); + a = 64 - b; + c = 0; + if (a <= len) { + + memcpy (key + b + 24, challenge, a); + hash (key, key + 24); + c = a; + d = c + 0x3f; + + while (d < len) { + hash (key, challenge + d - 0x3f); + d += 64; + c += 64; + } + b = 0; + } + + memcpy (key + b + 24, challenge + c, len - c); +} + +void +gst_rtsp_ext_real_calc_response_and_checksum (char *response, char *chksum, + char *challenge) +{ + int ch_len, table_len, resp_len; + int i; + char *ptr; + char buf[128]; + char field[128]; + char zres[20]; + char buf1[128]; + char buf2[128]; + + /* initialize return values */ + memset (response, 0, 64); + memset (chksum, 0, 34); + + /* initialize buffer */ + memset (buf, 0, 128); + ptr = buf; + BE_32C (ptr, 0xa1e9149d); + ptr += 4; + BE_32C (ptr, 0x0e6b3b59); + ptr += 4; + + if ((ch_len = MIN (strlen (challenge), 56)) == 40) { + challenge[32] = 0; + ch_len = 32; + } + memcpy (ptr, challenge, ch_len); + + /* xor challenge bytewise with xor_table */ + table_len = MIN (strlen ((char *) xor_table), 56); + for (i = 0; i < table_len; i++) + ptr[i] = ptr[i] ^ xor_table[i]; + + /* initialize our field */ + BE_32C (field, 0x01234567); + BE_32C (field + 4, 0x89ABCDEF); + BE_32C (field + 8, 0xFEDCBA98); + BE_32C (field + 12, 0x76543210); + BE_32C (field + 16, 0x00000000); + BE_32C (field + 20, 0x00000000); + + /* calculate response */ + call_hash (field, buf, 64); + memset (buf1, 0, 64); + *buf1 = 128; + memcpy (buf2, field + 16, 8); + i = (LE_32 ((buf2)) >> 3) & 0x3f; + if (i < 56) + i = 56 - i; + else + i = 120 - i; + call_hash (field, buf1, i); + call_hash (field, buf2, 8); + memcpy (zres, field, 16); + + /* convert zres to ascii string */ + for (i = 0; i < 16; i++) { + char a, b; + + a = (zres[i] >> 4) & 15; + b = zres[i] & 15; + + response[i * 2] = ((a < 10) ? (a + 48) : (a + 87)) & 255; + response[i * 2 + 1] = ((b < 10) ? (b + 48) : (b + 87)) & 255; + } + + /* add tail */ + resp_len = strlen (response); + strcpy (&response[resp_len], "01d0a8e3"); + + /* calculate checksum */ + for (i = 0; i < resp_len / 4; i++) + chksum[i] = response[i * 4]; +} diff --git a/gst/realmedia/realhash.h b/gst/realmedia/realhash.h new file mode 100644 index 0000000..40b6500 --- /dev/null +++ b/gst/realmedia/realhash.h @@ -0,0 +1,31 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTSP_HASH_H__ +#define __GST_RTSP_HASH_H__ + +G_BEGIN_DECLS + +void +gst_rtsp_ext_real_calc_response_and_checksum (char *response, char *chksum, + char *challenge); + +G_END_DECLS + +#endif /* __GST_RTSP_HASH_H__ */ diff --git a/gst/realmedia/realmedia.c b/gst/realmedia/realmedia.c new file mode 100644 index 0000000..2b05f54 --- /dev/null +++ b/gst/realmedia/realmedia.c @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "rmdemux.h" +#include "rademux.h" +#include "rdtdepay.h" +#include "rdtmanager.h" +#include "rtspreal.h" +#include "pnmsrc.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_rmdemux_plugin_init (plugin)) + return FALSE; + + if (!gst_rademux_plugin_init (plugin)) + return FALSE; + + if (!gst_rdt_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rdt_manager_plugin_init (plugin)) + return FALSE; + + if (!gst_rtsp_real_plugin_init (plugin)) + return FALSE; + + if (!gst_pnm_src_plugin_init (plugin)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + realmedia, + "RealMedia support plugins", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/realmedia/rmdemux.c b/gst/realmedia/rmdemux.c new file mode 100644 index 0000000..d4afae4 --- /dev/null +++ b/gst/realmedia/rmdemux.c @@ -0,0 +1,2586 @@ +/* GStreamer RealMedia demuxer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2003> David A. Schleef <ds@schleef.org> + * Copyright (C) <2004> Stephane Loeuillet <gstreamer@leroutier.net> + * Copyright (C) <2005> Owen Fraser-Green <owen@discobabe.net> + * Copyright (C) <2005> Michael Smith <fluendo.com> + * Copyright (C) <2006> Wim Taymans <wim@fluendo.com> + * Copyright (C) <2006> Tim-Philipp Müller <tim centricular net> + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "rmdemux.h" +#include "rmutils.h" + +#include <string.h> +#include <ctype.h> + +#define RMDEMUX_GUINT32_GET(a) GST_READ_UINT32_BE(a) +#define RMDEMUX_GUINT16_GET(a) GST_READ_UINT16_BE(a) +#define RMDEMUX_FOURCC_GET(a) GST_READ_UINT32_LE(a) +#define HEADER_SIZE 10 +#define DATA_SIZE 8 + +#define MAX_FRAGS 256 + +static const guint8 sipr_subpk_size[4] = { 29, 19, 37, 20 }; + +typedef struct _GstRMDemuxIndex GstRMDemuxIndex; + +struct _GstRMDemuxStream +{ + guint32 subtype; + guint32 fourcc; + guint32 subformat; + guint32 format; + + int id; + GstPad *pad; + gboolean discont; + int timescale; + + int sample_index; + GstRMDemuxIndex *index; + int index_length; + gint framerate_numerator; + gint framerate_denominator; + guint32 seek_offset; + + guint16 width; + guint16 height; + guint16 flavor; + guint16 rate; /* samplerate */ + guint16 n_channels; /* channels */ + guint16 sample_width; /* bits_per_sample */ + guint16 leaf_size; /* subpacket_size */ + guint32 packet_size; /* coded_frame_size */ + guint16 version; + guint32 extra_data_size; /* codec_data_length */ + guint8 *extra_data; /* extras */ + guint32 bitrate; + + gboolean needs_descrambling; + guint subpackets_needed; /* subpackets needed for descrambling */ + GPtrArray *subpackets; /* array containing subpacket GstBuffers */ + + /* Variables needed for fixing timestamps. */ + GstClockTime next_ts, last_ts; + guint16 next_seq, last_seq; + + gint frag_seqnum; + gint frag_subseq; + guint frag_length; + guint frag_current; + guint frag_count; + guint frag_offset[MAX_FRAGS]; + GstAdapter *adapter; + + GstTagList *pending_tags; +}; + +struct _GstRMDemuxIndex +{ + guint32 offset; + GstClockTime timestamp; +}; + +static GstStaticPadTemplate gst_rmdemux_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/vnd.rn-realmedia") + ); + +static GstStaticPadTemplate gst_rmdemux_videosrc_template = +GST_STATIC_PAD_TEMPLATE ("video_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_rmdemux_audiosrc_template = +GST_STATIC_PAD_TEMPLATE ("audio_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (rmdemux_debug); +#define GST_CAT_DEFAULT rmdemux_debug + +static GstElementClass *parent_class = NULL; + +static void gst_rmdemux_class_init (GstRMDemuxClass * klass); +static void gst_rmdemux_base_init (GstRMDemuxClass * klass); +static void gst_rmdemux_init (GstRMDemux * rmdemux); +static void gst_rmdemux_finalize (GObject * object); +static GstStateChangeReturn gst_rmdemux_change_state (GstElement * element, + GstStateChange transition); +static GstFlowReturn gst_rmdemux_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static void gst_rmdemux_loop (GstPad * pad); +static gboolean gst_rmdemux_sink_activate (GstPad * sinkpad, + GstObject * parent); +static gboolean gst_rmdemux_sink_activate_mode (GstPad * sinkpad, + GstObject * parent, GstPadMode mode, gboolean active); +static gboolean gst_rmdemux_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_rmdemux_src_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static void gst_rmdemux_send_event (GstRMDemux * rmdemux, GstEvent * event); +static gboolean gst_rmdemux_src_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_rmdemux_perform_seek (GstRMDemux * rmdemux, + GstEvent * event); + +static void gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, + int length); +static void gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, + int length); +static void gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, + const guint8 * data, int length); +static guint gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, + int length); +static void gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, + int length); +static void gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, + int length); +static GstFlowReturn gst_rmdemux_parse_packet (GstRMDemux * rmdemux, + GstBuffer * in, guint16 version); +static void gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, + const guint8 * data, int length); +static void gst_rmdemux_stream_clear_cached_subpackets (GstRMDemux * rmdemux, + GstRMDemuxStream * stream); +static GstRMDemuxStream *gst_rmdemux_get_stream_by_id (GstRMDemux * rmdemux, + int id); + +static GType +gst_rmdemux_get_type (void) +{ + static GType rmdemux_type = 0; + + if (!rmdemux_type) { + static const GTypeInfo rmdemux_info = { + sizeof (GstRMDemuxClass), + (GBaseInitFunc) gst_rmdemux_base_init, NULL, + (GClassInitFunc) gst_rmdemux_class_init, + NULL, NULL, sizeof (GstRMDemux), 0, + (GInstanceInitFunc) gst_rmdemux_init, + }; + + rmdemux_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstRMDemux", &rmdemux_info, + 0); + } + return rmdemux_type; +} + +static void +gst_rmdemux_base_init (GstRMDemuxClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rmdemux_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rmdemux_videosrc_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rmdemux_audiosrc_template)); + gst_element_class_set_static_metadata (element_class, "RealMedia Demuxer", + "Codec/Demuxer", + "Demultiplex a RealMedia file into audio and video streams", + "David Schleef <ds@schleef.org>"); +} + +static void +gst_rmdemux_class_init (GstRMDemuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rmdemux_change_state); + + GST_DEBUG_CATEGORY_INIT (rmdemux_debug, "rmdemux", + 0, "Demuxer for Realmedia streams"); + + gobject_class->finalize = gst_rmdemux_finalize; +} + +static void +gst_rmdemux_finalize (GObject * object) +{ + GstRMDemux *rmdemux = GST_RMDEMUX (object); + + if (rmdemux->adapter) { + g_object_unref (rmdemux->adapter); + rmdemux->adapter = NULL; + } + if (rmdemux->flowcombiner) { + gst_flow_combiner_free (rmdemux->flowcombiner); + rmdemux->flowcombiner = NULL; + } + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +gst_rmdemux_init (GstRMDemux * rmdemux) +{ + rmdemux->sinkpad = + gst_pad_new_from_static_template (&gst_rmdemux_sink_template, "sink"); + gst_pad_set_event_function (rmdemux->sinkpad, + GST_DEBUG_FUNCPTR (gst_rmdemux_sink_event)); + gst_pad_set_chain_function (rmdemux->sinkpad, + GST_DEBUG_FUNCPTR (gst_rmdemux_chain)); + gst_pad_set_activate_function (rmdemux->sinkpad, + GST_DEBUG_FUNCPTR (gst_rmdemux_sink_activate)); + gst_pad_set_activatemode_function (rmdemux->sinkpad, + GST_DEBUG_FUNCPTR (gst_rmdemux_sink_activate_mode)); + + gst_element_add_pad (GST_ELEMENT (rmdemux), rmdemux->sinkpad); + + rmdemux->adapter = gst_adapter_new (); + rmdemux->first_ts = GST_CLOCK_TIME_NONE; + rmdemux->base_ts = GST_CLOCK_TIME_NONE; + rmdemux->need_newsegment = TRUE; + rmdemux->have_group_id = FALSE; + rmdemux->group_id = G_MAXUINT; + rmdemux->flowcombiner = gst_flow_combiner_new (); + + gst_rm_utils_run_tests (); +} + +static gboolean +gst_rmdemux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + gboolean ret; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + gst_event_unref (event); + ret = TRUE; + break; + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + return ret; +} + +static gboolean +gst_rmdemux_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + gboolean ret = TRUE; + + GstRMDemux *rmdemux = GST_RMDEMUX (parent); + + GST_LOG_OBJECT (rmdemux, "handling src event"); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gboolean running; + + GST_LOG_OBJECT (rmdemux, "Event on src: SEEK"); + /* can't seek if we are not seekable, FIXME could pass the + * seek query upstream after converting it to bytes using + * the average bitrate of the stream. */ + if (!rmdemux->seekable) { + ret = FALSE; + GST_DEBUG ("seek on non seekable stream"); + goto done_unref; + } + + GST_OBJECT_LOCK (rmdemux); + /* check if we can do the seek now */ + running = rmdemux->running; + GST_OBJECT_UNLOCK (rmdemux); + + /* now do the seek */ + if (running) { + ret = gst_rmdemux_perform_seek (rmdemux, event); + } else + ret = TRUE; + + gst_event_unref (event); + break; + } + default: + GST_LOG_OBJECT (rmdemux, "Event on src: type=%d", GST_EVENT_TYPE (event)); + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; + +done_unref: + GST_DEBUG ("error handling event"); + gst_event_unref (event); + return ret; +} + +/* Validate that this looks like a reasonable point to seek to */ +static gboolean +gst_rmdemux_validate_offset (GstRMDemux * rmdemux) +{ + GstBuffer *buffer; + GstFlowReturn flowret; + guint16 version, length; + gboolean ret = TRUE; + GstMapInfo map; + + buffer = NULL; + flowret = gst_pad_pull_range (rmdemux->sinkpad, rmdemux->offset, 4, &buffer); + + if (flowret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (rmdemux, "Failed to pull data at offset %d", + rmdemux->offset); + return FALSE; + } + /* TODO: Can we also be seeking to a 'DATA' chunk header? Check this. + * Also, for the case we currently handle, can we check any more? It's pretty + * sucky to not be validating a little more heavily than this... */ + /* This should now be the start of a data packet header. That begins with + * a 2-byte 'version' field, which has to be 0 or 1, then a length. I'm not + * certain what values are valid for length, but it must always be at least + * 4 bytes, and we can check that it won't take us past our known total size + */ + + gst_buffer_map (buffer, &map, GST_MAP_READ); + version = RMDEMUX_GUINT16_GET (map.data); + if (version != 0 && version != 1) { + GST_DEBUG_OBJECT (rmdemux, "Expected version 0 or 1, got %d", + (int) version); + ret = FALSE; + } + + length = RMDEMUX_GUINT16_GET (map.data + 2); + /* TODO: Also check against total stream length */ + if (length < 4) { + GST_DEBUG_OBJECT (rmdemux, "Expected length >= 4, got %d", (int) length); + ret = FALSE; + } + gst_buffer_unmap (buffer, &map); + + if (ret) { + rmdemux->offset += 4; + gst_adapter_clear (rmdemux->adapter); + gst_adapter_push (rmdemux->adapter, buffer); + } else { + GST_WARNING_OBJECT (rmdemux, "Failed to validate seek offset at %d", + rmdemux->offset); + gst_buffer_unref (buffer); + } + + return ret; +} + +static gboolean +find_seek_offset_bytes (GstRMDemux * rmdemux, guint target) +{ + int i; + GSList *cur; + gboolean ret = FALSE; + + for (cur = rmdemux->streams; cur; cur = cur->next) { + GstRMDemuxStream *stream = cur->data; + + /* Search backwards through this stream's index until we find the first + * timestamp before our target time */ + for (i = stream->index_length - 1; i >= 0; i--) { + if (stream->index[i].offset <= target) { + /* Set the seek_offset for the stream so we don't bother parsing it + * until we've passed that point */ + stream->seek_offset = stream->index[i].offset; + rmdemux->offset = stream->index[i].offset; + ret = TRUE; + break; + } + } + } + return ret; +} + +static gboolean +find_seek_offset_time (GstRMDemux * rmdemux, GstClockTime time) +{ + int i, n_stream; + gboolean ret = FALSE; + GSList *cur; + GstClockTime earliest = GST_CLOCK_TIME_NONE; + + n_stream = 0; + for (cur = rmdemux->streams; cur; cur = cur->next, n_stream++) { + GstRMDemuxStream *stream = cur->data; + + /* Search backwards through this stream's index until we find the first + * timestamp before our target time */ + for (i = stream->index_length - 1; i >= 0; i--) { + if (stream->index[i].timestamp <= time) { + /* Set the seek_offset for the stream so we don't bother parsing it + * until we've passed that point */ + stream->seek_offset = stream->index[i].offset; + + /* If it's also the earliest timestamp we've seen of all streams, then + * that's our target! + */ + if (earliest == GST_CLOCK_TIME_NONE || + stream->index[i].timestamp < earliest) { + earliest = stream->index[i].timestamp; + rmdemux->offset = stream->index[i].offset; + GST_DEBUG_OBJECT (rmdemux, + "We're looking for %" GST_TIME_FORMAT + " and we found that stream %d has the latest index at %" + GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment.start), n_stream, + GST_TIME_ARGS (earliest)); + } + + ret = TRUE; + + break; + } + } + stream->discont = TRUE; + } + return ret; +} + +static gboolean +gst_rmdemux_perform_seek (GstRMDemux * rmdemux, GstEvent * event) +{ + gboolean validated; + gboolean ret = TRUE; + gboolean flush; + GstFormat format; + gdouble rate; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gboolean update; + + if (event) { + GST_DEBUG_OBJECT (rmdemux, "seek with event"); + + gst_event_parse_seek (event, &rate, &format, &flags, + &cur_type, &cur, &stop_type, &stop); + + /* we can only seek on time */ + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (rmdemux, "can only seek on TIME"); + goto error; + } + /* cannot yet do backwards playback */ + if (rate <= 0.0) { + GST_DEBUG_OBJECT (rmdemux, "can only seek with positive rate, not %lf", + rate); + goto error; + } + } else { + GST_DEBUG_OBJECT (rmdemux, "seek without event"); + + flags = 0; + rate = 1.0; + } + + GST_DEBUG_OBJECT (rmdemux, "seek, rate %g", rate); + + flush = flags & GST_SEEK_FLAG_FLUSH; + + /* first step is to unlock the streaming thread if it is + * blocked in a chain call, we do this by starting the flush. */ + if (flush) { + gst_pad_push_event (rmdemux->sinkpad, gst_event_new_flush_start ()); + gst_rmdemux_send_event (rmdemux, gst_event_new_flush_start ()); + } else { + gst_pad_pause_task (rmdemux->sinkpad); + } + + GST_LOG_OBJECT (rmdemux, "Done starting flushes"); + + /* now grab the stream lock so that streaming cannot continue, for + * non flushing seeks when the element is in PAUSED this could block + * forever. */ + GST_PAD_STREAM_LOCK (rmdemux->sinkpad); + + GST_LOG_OBJECT (rmdemux, "Took streamlock"); + + if (event) { + gst_segment_do_seek (&rmdemux->segment, rate, format, flags, + cur_type, cur, stop_type, stop, &update); + } + + GST_DEBUG_OBJECT (rmdemux, "segment positions set to %" GST_TIME_FORMAT "-%" + GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment.start), + GST_TIME_ARGS (rmdemux->segment.stop)); + + /* we need to stop flushing on the sinkpad as we're going to use it + * next. We can do this as we have the STREAM lock now. */ + gst_pad_push_event (rmdemux->sinkpad, gst_event_new_flush_stop (TRUE)); + + GST_LOG_OBJECT (rmdemux, "Pushed FLUSH_STOP event"); + + /* For each stream, find the first index offset equal to or before our seek + * target. Of these, find the smallest offset. That's where we seek to. + * + * Then we pull 4 bytes from that offset, and validate that we've seeked to a + * what looks like a plausible packet. + * If that fails, restart, with the seek target set to one less than the + * offset we just tried. If we run out of places to try, treat that as a fatal + * error. + */ + if (!find_seek_offset_time (rmdemux, rmdemux->segment.position)) { + GST_LOG_OBJECT (rmdemux, "Failed to find seek offset by time"); + ret = FALSE; + goto done; + } + + GST_LOG_OBJECT (rmdemux, "Validating offset %u", rmdemux->offset); + validated = gst_rmdemux_validate_offset (rmdemux); + while (!validated) { + GST_INFO_OBJECT (rmdemux, "Failed to validate offset at %u", + rmdemux->offset); + if (!find_seek_offset_bytes (rmdemux, rmdemux->offset - 1)) { + ret = FALSE; + goto done; + } + validated = gst_rmdemux_validate_offset (rmdemux); + } + + GST_LOG_OBJECT (rmdemux, "Found final offset. Excellent!"); + + /* now we have a new position, prepare for streaming again */ + { + /* Reset the demuxer state */ + rmdemux->state = RMDEMUX_STATE_DATA_PACKET; + + if (flush) + gst_rmdemux_send_event (rmdemux, gst_event_new_flush_stop (TRUE)); + + /* must send newsegment event from streaming thread, so just set flag */ + rmdemux->need_newsegment = TRUE; + + /* notify start of new segment */ + if (rmdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT_CAST (rmdemux), + gst_message_new_segment_start (GST_OBJECT_CAST (rmdemux), + GST_FORMAT_TIME, rmdemux->segment.position)); + } + + /* restart our task since it might have been stopped when we did the + * flush. */ + gst_pad_start_task (rmdemux->sinkpad, (GstTaskFunction) gst_rmdemux_loop, + rmdemux->sinkpad, NULL); + } + +done: + /* streaming can continue now */ + GST_PAD_STREAM_UNLOCK (rmdemux->sinkpad); + + return ret; + +error: + { + GST_DEBUG_OBJECT (rmdemux, "seek failed"); + return FALSE; + } +} + + +static gboolean +gst_rmdemux_src_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + gboolean res = FALSE; + GstRMDemux *rmdemux; + + rmdemux = GST_RMDEMUX (parent); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + GST_DEBUG_OBJECT (rmdemux, "Position query: no idea from demuxer!"); + break; + case GST_QUERY_DURATION:{ + GstFormat fmt; + + gst_query_parse_duration (query, &fmt, NULL); + if (fmt == GST_FORMAT_TIME) { + GST_OBJECT_LOCK (rmdemux); + if (G_LIKELY (rmdemux->running)) { + gst_query_set_duration (query, GST_FORMAT_TIME, rmdemux->duration); + GST_DEBUG_OBJECT (rmdemux, "duration set to %" GST_TIME_FORMAT, + GST_TIME_ARGS (rmdemux->duration)); + res = TRUE; + } + GST_OBJECT_UNLOCK (rmdemux); + } + break; + } + case GST_QUERY_SEEKING:{ + GstFormat fmt; + + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt == GST_FORMAT_TIME) { + GST_OBJECT_LOCK (rmdemux); + if (G_LIKELY (rmdemux->running)) { + gst_query_set_seeking (query, GST_FORMAT_TIME, rmdemux->seekable, + 0, rmdemux->duration); + res = TRUE; + } + GST_OBJECT_UNLOCK (rmdemux); + } + break; + } + case GST_QUERY_SEGMENT: + { + GstFormat format; + gint64 start, stop; + + format = rmdemux->segment.format; + + start = + gst_segment_to_stream_time (&rmdemux->segment, format, + rmdemux->segment.start); + if ((stop = rmdemux->segment.stop) == -1) + stop = rmdemux->segment.duration; + else + stop = gst_segment_to_stream_time (&rmdemux->segment, format, stop); + + gst_query_set_segment (query, rmdemux->segment.rate, format, start, stop); + res = TRUE; + break; + } + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + + return res; +} + +static void +gst_rmdemux_reset (GstRMDemux * rmdemux) +{ + GSList *cur; + + GST_OBJECT_LOCK (rmdemux); + rmdemux->running = FALSE; + GST_OBJECT_UNLOCK (rmdemux); + + for (cur = rmdemux->streams; cur; cur = cur->next) { + GstRMDemuxStream *stream = cur->data; + + g_object_unref (stream->adapter); + gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); + gst_flow_combiner_remove_pad (rmdemux->flowcombiner, stream->pad); + gst_element_remove_pad (GST_ELEMENT (rmdemux), stream->pad); + if (stream->pending_tags) + gst_tag_list_unref (stream->pending_tags); + if (stream->subpackets) + g_ptr_array_free (stream->subpackets, TRUE); + g_free (stream->index); + g_free (stream); + } + g_slist_free (rmdemux->streams); + rmdemux->streams = NULL; + rmdemux->n_audio_streams = 0; + rmdemux->n_video_streams = 0; + + if (rmdemux->pending_tags != NULL) { + gst_tag_list_unref (rmdemux->pending_tags); + rmdemux->pending_tags = NULL; + } + + gst_adapter_clear (rmdemux->adapter); + rmdemux->state = RMDEMUX_STATE_HEADER; + rmdemux->have_pads = FALSE; + + gst_segment_init (&rmdemux->segment, GST_FORMAT_UNDEFINED); + rmdemux->first_ts = GST_CLOCK_TIME_NONE; + rmdemux->base_ts = GST_CLOCK_TIME_NONE; + rmdemux->need_newsegment = TRUE; + + rmdemux->have_group_id = FALSE; + rmdemux->group_id = G_MAXUINT; +} + +static GstStateChangeReturn +gst_rmdemux_change_state (GstElement * element, GstStateChange transition) +{ + GstRMDemux *rmdemux = GST_RMDEMUX (element); + GstStateChangeReturn res; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + rmdemux->state = RMDEMUX_STATE_HEADER; + rmdemux->have_pads = FALSE; + gst_segment_init (&rmdemux->segment, GST_FORMAT_TIME); + rmdemux->running = FALSE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY:{ + gst_rmdemux_reset (rmdemux); + break; + } + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return res; +} + +/* this function is called when the pad is activated and should start + * processing data. + * + * We check if we can do random access to decide if we work push or + * pull based. + */ +static gboolean +gst_rmdemux_sink_activate (GstPad * sinkpad, GstObject * parent) +{ + GstQuery *query; + gboolean pull_mode; + + query = gst_query_new_scheduling (); + + if (!gst_pad_peer_query (sinkpad, query)) { + gst_query_unref (query); + goto activate_push; + } + + pull_mode = gst_query_has_scheduling_mode_with_flags (query, + GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); + gst_query_unref (query); + + if (!pull_mode) + goto activate_push; + + GST_DEBUG_OBJECT (sinkpad, "activating pull"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); + +activate_push: + { + GST_DEBUG_OBJECT (sinkpad, "activating push"); + return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } +} + +static gboolean +gst_rmdemux_sink_activate_mode (GstPad * sinkpad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + gboolean res; + GstRMDemux *demux; + + demux = GST_RMDEMUX (parent); + + switch (mode) { + case GST_PAD_MODE_PUSH: + demux->seekable = FALSE; + demux->running = active; + res = TRUE; + break; + case GST_PAD_MODE_PULL: + if (active) { + demux->seekable = TRUE; + demux->offset = 0; + demux->loop_state = RMDEMUX_LOOP_STATE_HEADER; + demux->data_offset = G_MAXUINT; + res = + gst_pad_start_task (sinkpad, (GstTaskFunction) gst_rmdemux_loop, + sinkpad, NULL); + } else { + res = gst_pad_stop_task (sinkpad); + } + break; + default: + res = FALSE; + break; + } + return res; +} + + +/* random access mode - just pass over to our chain function */ +static void +gst_rmdemux_loop (GstPad * pad) +{ + GstRMDemux *rmdemux; + GstBuffer *buffer; + GstFlowReturn ret = GST_FLOW_OK; + guint size; + + rmdemux = GST_RMDEMUX (GST_PAD_PARENT (pad)); + + GST_LOG_OBJECT (rmdemux, "loop with state=%d and offset=0x%x", + rmdemux->loop_state, rmdemux->offset); + + switch (rmdemux->state) { + case RMDEMUX_STATE_HEADER: + size = HEADER_SIZE; + break; + case RMDEMUX_STATE_HEADER_DATA: + size = DATA_SIZE; + break; + case RMDEMUX_STATE_DATA_PACKET: + size = rmdemux->avg_packet_size; + break; + case RMDEMUX_STATE_EOS: + GST_LOG_OBJECT (rmdemux, "At EOS, pausing task"); + ret = GST_FLOW_EOS; + goto need_pause; + default: + GST_LOG_OBJECT (rmdemux, "Default: requires %d bytes (state is %d)", + (int) rmdemux->size, rmdemux->state); + size = rmdemux->size; + } + + buffer = NULL; + ret = gst_pad_pull_range (pad, rmdemux->offset, size, &buffer); + if (ret != GST_FLOW_OK) { + if (rmdemux->offset == rmdemux->index_offset) { + /* The index isn't available so forget about it */ + rmdemux->loop_state = RMDEMUX_LOOP_STATE_DATA; + rmdemux->offset = rmdemux->data_offset; + GST_OBJECT_LOCK (rmdemux); + rmdemux->running = TRUE; + rmdemux->seekable = FALSE; + GST_OBJECT_UNLOCK (rmdemux); + return; + } else { + GST_DEBUG_OBJECT (rmdemux, "Unable to pull %d bytes at offset 0x%08x " + "(pull_range returned flow %s, state is %d)", (gint) size, + rmdemux->offset, gst_flow_get_name (ret), GST_STATE (rmdemux)); + goto need_pause; + } + } + + size = gst_buffer_get_size (buffer); + + /* Defer to the chain function */ + ret = gst_rmdemux_chain (pad, GST_OBJECT_CAST (rmdemux), buffer); + if (ret != GST_FLOW_OK) { + GST_DEBUG_OBJECT (rmdemux, "Chain flow failed at offset 0x%08x", + rmdemux->offset); + goto need_pause; + } + + rmdemux->offset += size; + + switch (rmdemux->loop_state) { + case RMDEMUX_LOOP_STATE_HEADER: + if (rmdemux->offset >= rmdemux->data_offset) { + /* It's the end of the header */ + rmdemux->loop_state = RMDEMUX_LOOP_STATE_INDEX; + rmdemux->offset = rmdemux->index_offset; + } + break; + case RMDEMUX_LOOP_STATE_INDEX: + if (rmdemux->state == RMDEMUX_STATE_HEADER) { + if (rmdemux->index_offset == 0) { + /* We've read the last index */ + rmdemux->loop_state = RMDEMUX_LOOP_STATE_DATA; + rmdemux->offset = rmdemux->data_offset; + GST_OBJECT_LOCK (rmdemux); + rmdemux->running = TRUE; + GST_OBJECT_UNLOCK (rmdemux); + } else { + /* Get the next index */ + rmdemux->offset = rmdemux->index_offset; + } + } + break; + case RMDEMUX_LOOP_STATE_DATA: + break; + } + + return; + + /* ERRORS */ +need_pause: + { + const gchar *reason = gst_flow_get_name (ret); + + GST_LOG_OBJECT (rmdemux, "pausing task, reason %s", reason); + rmdemux->segment_running = FALSE; + gst_pad_pause_task (rmdemux->sinkpad); + + if (ret == GST_FLOW_EOS) { + /* perform EOS logic */ + if (rmdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + gint64 stop; + + /* for segment playback we need to post when (in stream time) + * we stopped, this is either stop (when set) or the duration. */ + if ((stop = rmdemux->segment.stop) == -1) + stop = rmdemux->segment.duration; + + GST_LOG_OBJECT (rmdemux, "Sending segment done, at end of segment"); + gst_element_post_message (GST_ELEMENT (rmdemux), + gst_message_new_segment_done (GST_OBJECT (rmdemux), + GST_FORMAT_TIME, stop)); + gst_rmdemux_send_event (rmdemux, + gst_event_new_segment_done (GST_FORMAT_TIME, stop)); + } else { + /* normal playback, send EOS to all linked pads */ + GST_LOG_OBJECT (rmdemux, "Sending EOS, at end of stream"); + gst_rmdemux_send_event (rmdemux, gst_event_new_eos ()); + } + } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) { + GST_ELEMENT_ERROR (rmdemux, STREAM, FAILED, + (NULL), ("stream stopped, reason %s", reason)); + gst_rmdemux_send_event (rmdemux, gst_event_new_eos ()); + } + return; + } +} + +static gboolean +gst_rmdemux_fourcc_isplausible (guint32 fourcc) +{ + int i; + + for (i = 0; i < 4; i++) { + if (!isprint ((int) ((unsigned char *) (&fourcc))[i])) { + return FALSE; + } + } + return TRUE; +} + +static GstFlowReturn +gst_rmdemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstFlowReturn ret = GST_FLOW_OK; + const guint8 *data; + guint16 version; + guint avail; + + GstRMDemux *rmdemux = GST_RMDEMUX (parent); + + if (rmdemux->base_ts == -1) { + if (GST_BUFFER_DTS_IS_VALID (buffer)) + rmdemux->base_ts = GST_BUFFER_DTS (buffer); + else + rmdemux->base_ts = GST_BUFFER_PTS (buffer); + + GST_LOG_OBJECT (rmdemux, "base_ts %" GST_TIME_FORMAT, + GST_TIME_ARGS (rmdemux->base_ts)); + } + + gst_adapter_push (rmdemux->adapter, buffer); + + GST_LOG_OBJECT (rmdemux, "Chaining buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (buffer)); + + while (TRUE) { + avail = gst_adapter_available (rmdemux->adapter); + + GST_LOG_OBJECT (rmdemux, "looping in chain, avail %u", avail); + switch (rmdemux->state) { + case RMDEMUX_STATE_HEADER: + { + if (gst_adapter_available (rmdemux->adapter) < HEADER_SIZE) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, HEADER_SIZE); + + rmdemux->object_id = RMDEMUX_FOURCC_GET (data + 0); + rmdemux->size = RMDEMUX_GUINT32_GET (data + 4) - HEADER_SIZE; + rmdemux->object_version = RMDEMUX_GUINT16_GET (data + 8); + + /* Sanity-check. We assume that the FOURCC is printable ASCII */ + if (!gst_rmdemux_fourcc_isplausible (rmdemux->object_id)) { + /* Failed. Remain in HEADER state, try again... We flush only + * the actual FOURCC, not the entire header, because we could + * need to resync anywhere at all... really, this should never + * happen. */ + GST_WARNING_OBJECT (rmdemux, "Bogus looking header, unprintable " + "FOURCC"); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, 4); + + break; + } + + GST_LOG_OBJECT (rmdemux, "header found with object_id=%" + GST_FOURCC_FORMAT + " size=%08x object_version=%d", + GST_FOURCC_ARGS (rmdemux->object_id), rmdemux->size, + rmdemux->object_version); + + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, HEADER_SIZE); + + switch (rmdemux->object_id) { + case GST_MAKE_FOURCC ('.', 'R', 'M', 'F'): + rmdemux->state = RMDEMUX_STATE_HEADER_RMF; + break; + case GST_MAKE_FOURCC ('P', 'R', 'O', 'P'): + rmdemux->state = RMDEMUX_STATE_HEADER_PROP; + break; + case GST_MAKE_FOURCC ('M', 'D', 'P', 'R'): + rmdemux->state = RMDEMUX_STATE_HEADER_MDPR; + break; + case GST_MAKE_FOURCC ('I', 'N', 'D', 'X'): + rmdemux->state = RMDEMUX_STATE_HEADER_INDX; + break; + case GST_MAKE_FOURCC ('D', 'A', 'T', 'A'): + rmdemux->state = RMDEMUX_STATE_HEADER_DATA; + break; + case GST_MAKE_FOURCC ('C', 'O', 'N', 'T'): + rmdemux->state = RMDEMUX_STATE_HEADER_CONT; + break; + default: + rmdemux->state = RMDEMUX_STATE_HEADER_UNKNOWN; + break; + } + break; + } + case RMDEMUX_STATE_HEADER_UNKNOWN: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + GST_WARNING_OBJECT (rmdemux, "Unknown object_id %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (rmdemux->object_id)); + + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_HEADER_RMF: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + if ((rmdemux->object_version == 0) || (rmdemux->object_version == 1)) { + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse__rmf (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + } else { + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + } + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_HEADER_PROP: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse_prop (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_HEADER_MDPR: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse_mdpr (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_HEADER_CONT: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse_cont (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_HEADER_DATA: + { + /* If we haven't already done so then signal there are no more pads */ + if (!rmdemux->have_pads) { + GST_LOG_OBJECT (rmdemux, "no more pads"); + gst_element_no_more_pads (GST_ELEMENT (rmdemux)); + rmdemux->have_pads = TRUE; + } + + /* The actual header is only 8 bytes */ + rmdemux->size = DATA_SIZE; + GST_LOG_OBJECT (rmdemux, "data available %" G_GSIZE_FORMAT, + gst_adapter_available (rmdemux->adapter)); + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse_data (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + + rmdemux->state = RMDEMUX_STATE_DATA_PACKET; + break; + } + case RMDEMUX_STATE_HEADER_INDX: + { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + rmdemux->size = gst_rmdemux_parse_indx (rmdemux, data, rmdemux->size); + /* Only flush the header */ + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, HEADER_SIZE); + + rmdemux->state = RMDEMUX_STATE_INDX_DATA; + break; + } + case RMDEMUX_STATE_INDX_DATA: + { + /* There's not always an data to get... */ + if (rmdemux->size > 0) { + if (gst_adapter_available (rmdemux->adapter) < rmdemux->size) + goto unlock; + + data = gst_adapter_map (rmdemux->adapter, rmdemux->size); + gst_rmdemux_parse_indx_data (rmdemux, data, rmdemux->size); + gst_adapter_unmap (rmdemux->adapter); + gst_adapter_flush (rmdemux->adapter, rmdemux->size); + } + + rmdemux->state = RMDEMUX_STATE_HEADER; + break; + } + case RMDEMUX_STATE_DATA_PACKET: + { + guint8 header[4]; + + if (gst_adapter_available (rmdemux->adapter) < 2) + goto unlock; + + gst_adapter_copy (rmdemux->adapter, header, 0, 2); + version = RMDEMUX_GUINT16_GET (header); + GST_LOG_OBJECT (rmdemux, "Data packet with version=%d", version); + + if (version == 0 || version == 1) { + guint16 length; + + if (gst_adapter_available (rmdemux->adapter) < 4) + goto unlock; + + gst_adapter_copy (rmdemux->adapter, header, 0, 4); + + length = RMDEMUX_GUINT16_GET (header + 2); + GST_LOG_OBJECT (rmdemux, "Got length %d", length); + + if (length < 4) { + GST_LOG_OBJECT (rmdemux, "length too small, dropping"); + /* Invalid, just drop it */ + gst_adapter_flush (rmdemux->adapter, 4); + } else { + GstBuffer *buffer; + + avail = gst_adapter_available (rmdemux->adapter); + if (avail < length) + goto unlock; + + GST_LOG_OBJECT (rmdemux, "we have %u available and we needed %d", + avail, length); + + /* flush version and length */ + gst_adapter_flush (rmdemux->adapter, 4); + length -= 4; + + buffer = gst_adapter_take_buffer (rmdemux->adapter, length); + + ret = gst_rmdemux_parse_packet (rmdemux, buffer, version); + rmdemux->chunk_index++; + } + + if (rmdemux->chunk_index == rmdemux->n_chunks || length == 0) + rmdemux->state = RMDEMUX_STATE_HEADER; + } else { + /* Stream done */ + gst_adapter_flush (rmdemux->adapter, 2); + + if (rmdemux->data_offset == 0) { + GST_LOG_OBJECT (rmdemux, + "No further data, internal demux state EOS"); + rmdemux->state = RMDEMUX_STATE_EOS; + } else + rmdemux->state = RMDEMUX_STATE_HEADER; + } + break; + } + case RMDEMUX_STATE_EOS: + gst_rmdemux_send_event (rmdemux, gst_event_new_eos ()); + goto unlock; + default: + GST_WARNING_OBJECT (rmdemux, "Unhandled state %d", rmdemux->state); + goto unlock; + } + } + +unlock: + return ret; +} + +static GstRMDemuxStream * +gst_rmdemux_get_stream_by_id (GstRMDemux * rmdemux, int id) +{ + GSList *cur; + + for (cur = rmdemux->streams; cur; cur = cur->next) { + GstRMDemuxStream *stream = cur->data; + + if (stream->id == id) { + return stream; + } + } + + return NULL; +} + +static void +gst_rmdemux_send_event (GstRMDemux * rmdemux, GstEvent * event) +{ + GSList *cur; + + for (cur = rmdemux->streams; cur; cur = cur->next) { + GstRMDemuxStream *stream = cur->data; + + GST_DEBUG_OBJECT (rmdemux, "Pushing %s event on pad %s", + GST_EVENT_TYPE_NAME (event), GST_PAD_NAME (stream->pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + stream->last_ts = -1; + stream->next_ts = -1; + stream->last_seq = -1; + stream->next_seq = -1; + break; + default: + break; + } + gst_event_ref (event); + gst_pad_push_event (stream->pad, event); + } + gst_event_unref (event); +} + +static void +gst_rmdemux_add_stream (GstRMDemux * rmdemux, GstRMDemuxStream * stream) +{ + GstCaps *stream_caps = NULL; + const gchar *codec_tag = NULL; + gchar *codec_name = NULL; + gchar *stream_id; + int version = 0; + + if (stream->subtype == GST_RMDEMUX_STREAM_VIDEO) { + char *name = g_strdup_printf ("video_%u", rmdemux->n_video_streams); + + stream->pad = + gst_pad_new_from_static_template (&gst_rmdemux_videosrc_template, name); + g_free (name); + + codec_tag = GST_TAG_VIDEO_CODEC; + + switch (stream->fourcc) { + case GST_RM_VDO_RV10: + version = 1; + break; + case GST_RM_VDO_RV20: + version = 2; + break; + case GST_RM_VDO_RV30: + version = 3; + break; + case GST_RM_VDO_RV40: + version = 4; + break; + default: + stream_caps = gst_caps_new_simple ("video/x-unknown-fourcc", + "fourcc", G_TYPE_UINT, stream->fourcc, NULL); + GST_WARNING_OBJECT (rmdemux, + "Unknown video FOURCC code \"%" GST_FOURCC_FORMAT "\" (%08x)", + GST_FOURCC_ARGS (stream->fourcc), stream->fourcc); + } + + if (version) { + stream_caps = + gst_caps_new_simple ("video/x-pn-realvideo", "rmversion", G_TYPE_INT, + (int) version, + "format", G_TYPE_INT, + (int) stream->format, + "subformat", G_TYPE_INT, (int) stream->subformat, NULL); + } + + if (stream_caps) { + gst_caps_set_simple (stream_caps, + "width", G_TYPE_INT, stream->width, + "height", G_TYPE_INT, stream->height, + "framerate", GST_TYPE_FRACTION, stream->framerate_numerator, + stream->framerate_denominator, NULL); + } + rmdemux->n_video_streams++; + + } else if (stream->subtype == GST_RMDEMUX_STREAM_AUDIO) { + char *name = g_strdup_printf ("audio_%u", rmdemux->n_audio_streams); + + stream->pad = + gst_pad_new_from_static_template (&gst_rmdemux_audiosrc_template, name); + GST_LOG_OBJECT (rmdemux, "Created audio pad \"%s\"", name); + g_free (name); + + codec_tag = GST_TAG_AUDIO_CODEC; + + switch (stream->fourcc) { + /* Older RealAudio Codecs */ + case GST_RM_AUD_14_4: + version = 1; + break; + + case GST_RM_AUD_28_8: + version = 2; + break; + + /* DolbyNet (Dolby AC3, low bitrate) */ + case GST_RM_AUD_DNET: + stream_caps = + gst_caps_new_simple ("audio/x-ac3", "rate", G_TYPE_INT, + (int) stream->rate, NULL); + stream->needs_descrambling = TRUE; + stream->subpackets_needed = 1; + stream->subpackets = NULL; + break; + + /* MPEG-4 based */ + case GST_RM_AUD_RAAC: + case GST_RM_AUD_RACP: + stream_caps = + gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, + (int) 4, "framed", G_TYPE_BOOLEAN, TRUE, NULL); + if (stream->extra_data_size > 0) { + /* strip off an unknown byte in the extra data */ + stream->extra_data_size--; + stream->extra_data++; + } + stream->needs_descrambling = TRUE; + stream->subpackets_needed = 1; + stream->subpackets = NULL; + break; + + /* Sony ATRAC3 */ + case GST_RM_AUD_ATRC: + stream_caps = gst_caps_new_empty_simple ("audio/x-vnd.sony.atrac3"); + stream->needs_descrambling = TRUE; + stream->subpackets_needed = stream->height; + stream->subpackets = NULL; + break; + + /* RealAudio G2 audio */ + case GST_RM_AUD_COOK: + version = 8; + stream->needs_descrambling = TRUE; + stream->subpackets_needed = stream->height; + stream->subpackets = NULL; + break; + + /* RALF is lossless */ + case GST_RM_AUD_RALF: + GST_DEBUG_OBJECT (rmdemux, "RALF"); + stream_caps = gst_caps_new_empty_simple ("audio/x-ralf-mpeg4-generic"); + break; + + case GST_RM_AUD_SIPR: + + if (stream->flavor > 3) { + GST_WARNING_OBJECT (rmdemux, "bad SIPR flavor %d, freeing it", + stream->flavor); + g_free (stream); + goto beach; + } + + GST_DEBUG_OBJECT (rmdemux, "SIPR"); + stream_caps = gst_caps_new_empty_simple ("audio/x-sipro"); + stream->needs_descrambling = TRUE; + stream->subpackets_needed = stream->height; + stream->subpackets = NULL; + stream->leaf_size = sipr_subpk_size[stream->flavor]; + + break; + + default: + stream_caps = gst_caps_new_simple ("video/x-unknown-fourcc", + "fourcc", G_TYPE_UINT, stream->fourcc, NULL); + GST_WARNING_OBJECT (rmdemux, + "Unknown audio FOURCC code \"%" GST_FOURCC_FORMAT "\" (%08x)", + GST_FOURCC_ARGS (stream->fourcc), stream->fourcc); + break; + } + + if (version) { + stream_caps = + gst_caps_new_simple ("audio/x-pn-realaudio", "raversion", G_TYPE_INT, + (int) version, NULL); + } + + if (stream_caps) { + gst_caps_set_simple (stream_caps, + "flavor", G_TYPE_INT, (int) stream->flavor, + "rate", G_TYPE_INT, (int) stream->rate, + "channels", G_TYPE_INT, (int) stream->n_channels, + "width", G_TYPE_INT, (int) stream->sample_width, + "leaf_size", G_TYPE_INT, (int) stream->leaf_size, + "packet_size", G_TYPE_INT, (int) stream->packet_size, + "bitrate", G_TYPE_INT, (int) stream->bitrate, + "height", G_TYPE_INT, (int) stream->height, NULL); + } + rmdemux->n_audio_streams++; + } else { + GST_WARNING_OBJECT (rmdemux, "not adding stream of type %d, freeing it", + stream->subtype); + g_free (stream); + goto beach; + } + + GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream; + rmdemux->streams = g_slist_append (rmdemux->streams, stream); + GST_LOG_OBJECT (rmdemux, "n_streams is now %d", + g_slist_length (rmdemux->streams)); + + GST_LOG ("stream->pad = %p, stream_caps = %" GST_PTR_FORMAT, stream->pad, + stream_caps); + + if (stream->pad && stream_caps) { + GstEvent *event; + + GST_LOG_OBJECT (rmdemux, "%d bytes of extra data for stream %s", + stream->extra_data_size, GST_PAD_NAME (stream->pad)); + + /* add codec_data if there is any */ + if (stream->extra_data_size > 0) { + GstBuffer *buffer; + + buffer = gst_buffer_new_and_alloc (stream->extra_data_size); + gst_buffer_fill (buffer, 0, stream->extra_data, stream->extra_data_size); + + gst_caps_set_simple (stream_caps, "codec_data", GST_TYPE_BUFFER, + buffer, NULL); + + gst_buffer_unref (buffer); + } + + gst_pad_use_fixed_caps (stream->pad); + + gst_pad_set_event_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_rmdemux_src_event)); + gst_pad_set_query_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_rmdemux_src_query)); + + GST_DEBUG_OBJECT (rmdemux, "adding pad %s with caps %" GST_PTR_FORMAT + ", stream_id=%d", GST_PAD_NAME (stream->pad), stream_caps, stream->id); + gst_pad_set_active (stream->pad, TRUE); + + stream_id = + gst_pad_create_stream_id_printf (stream->pad, + GST_ELEMENT_CAST (rmdemux), "%03u", stream->id); + + event = + gst_pad_get_sticky_event (rmdemux->sinkpad, GST_EVENT_STREAM_START, 0); + if (event) { + if (gst_event_parse_group_id (event, &rmdemux->group_id)) + rmdemux->have_group_id = TRUE; + else + rmdemux->have_group_id = FALSE; + gst_event_unref (event); + } else if (!rmdemux->have_group_id) { + rmdemux->have_group_id = TRUE; + rmdemux->group_id = gst_util_group_id_next (); + } + + event = gst_event_new_stream_start (stream_id); + if (rmdemux->have_group_id) + gst_event_set_group_id (event, rmdemux->group_id); + + gst_pad_push_event (stream->pad, event); + g_free (stream_id); + + gst_pad_set_caps (stream->pad, stream_caps); + + codec_name = gst_pb_utils_get_codec_description (stream_caps); + + /* save for later, we must send the tags after the newsegment event */ + if (codec_tag != NULL && codec_name != NULL) { + if (stream->pending_tags == NULL) + stream->pending_tags = gst_tag_list_new_empty (); + gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_KEEP, + codec_tag, codec_name, NULL); + g_free (codec_name); + } + gst_element_add_pad (GST_ELEMENT_CAST (rmdemux), stream->pad); + gst_flow_combiner_add_pad (rmdemux->flowcombiner, stream->pad); + } + +beach: + + if (stream_caps) + gst_caps_unref (stream_caps); +} + +static int +re_skip_pascal_string (const guint8 * ptr) +{ + int length; + + length = ptr[0]; + + return length + 1; +} + +static void +gst_rmdemux_parse__rmf (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + GST_LOG_OBJECT (rmdemux, "file_version: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "num_headers: %d", RMDEMUX_GUINT32_GET (data + 4)); +} + +static void +gst_rmdemux_parse_prop (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + GST_LOG_OBJECT (rmdemux, "max bitrate: %d", RMDEMUX_GUINT32_GET (data)); + GST_LOG_OBJECT (rmdemux, "avg bitrate: %d", RMDEMUX_GUINT32_GET (data + 4)); + GST_LOG_OBJECT (rmdemux, "max packet size: %d", + RMDEMUX_GUINT32_GET (data + 8)); + rmdemux->avg_packet_size = RMDEMUX_GUINT32_GET (data + 12); + GST_LOG_OBJECT (rmdemux, "avg packet size: %d", rmdemux->avg_packet_size); + rmdemux->num_packets = RMDEMUX_GUINT32_GET (data + 16); + GST_LOG_OBJECT (rmdemux, "number of packets: %d", rmdemux->num_packets); + + GST_LOG_OBJECT (rmdemux, "duration: %d", RMDEMUX_GUINT32_GET (data + 20)); + rmdemux->duration = RMDEMUX_GUINT32_GET (data + 20) * GST_MSECOND; + + GST_LOG_OBJECT (rmdemux, "preroll: %d", RMDEMUX_GUINT32_GET (data + 24)); + rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 28); + GST_LOG_OBJECT (rmdemux, "offset of INDX section: 0x%08x", + rmdemux->index_offset); + rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 32); + GST_LOG_OBJECT (rmdemux, "offset of DATA section: 0x%08x", + rmdemux->data_offset); + GST_LOG_OBJECT (rmdemux, "n streams: %d", RMDEMUX_GUINT16_GET (data + 36)); + GST_LOG_OBJECT (rmdemux, "flags: 0x%04x", RMDEMUX_GUINT16_GET (data + 38)); +} + +static void +gst_rmdemux_parse_mdpr (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + GstRMDemuxStream *stream; + char *stream1_type_string; + char *stream2_type_string; + guint str_len = 0; + int stream_type; + int offset; + guint32 max_bitrate; + guint32 avg_bitrate; + + stream = g_new0 (GstRMDemuxStream, 1); + + stream->id = RMDEMUX_GUINT16_GET (data); + stream->index = NULL; + stream->seek_offset = 0; + stream->last_ts = -1; + stream->next_ts = -1; + stream->discont = TRUE; + stream->adapter = gst_adapter_new (); + GST_LOG_OBJECT (rmdemux, "stream_number=%d", stream->id); + + /* parse the bitrates */ + max_bitrate = RMDEMUX_GUINT32_GET (data + 2); + avg_bitrate = RMDEMUX_GUINT32_GET (data + 6); + stream->bitrate = avg_bitrate; + GST_LOG_OBJECT (rmdemux, "Stream max bitrate=%u", max_bitrate); + GST_LOG_OBJECT (rmdemux, "Stream avg bitrate=%u", avg_bitrate); + if (max_bitrate != 0) { + if (stream->pending_tags == NULL) + stream->pending_tags = gst_tag_list_new_empty (); + gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_MAXIMUM_BITRATE, max_bitrate, NULL); + } + if (avg_bitrate != 0) { + if (stream->pending_tags == NULL) + stream->pending_tags = gst_tag_list_new_empty (); + gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, avg_bitrate, NULL); + } + + offset = 30; + stream1_type_string = gst_rm_utils_read_string8 (data + offset, + length - offset, &str_len); + offset += str_len; + stream2_type_string = gst_rm_utils_read_string8 (data + offset, + length - offset, &str_len); + offset += str_len; + + /* stream1_type_string for audio and video stream is a "put_whatever_you_want" field : + * observed values : + * - "[The ]Video/Audio Stream" (File produced by an official Real encoder) + * - "RealVideoPremierePlugIn-VIDEO/AUDIO" (File produced by Abobe Premiere) + * + * so, we should not rely on it to know which stream type it is + */ + + GST_LOG_OBJECT (rmdemux, "stream type: %s", stream1_type_string); + GST_LOG_OBJECT (rmdemux, "MIME type=%s", stream2_type_string); + + if (strcmp (stream2_type_string, "video/x-pn-realvideo") == 0) { + stream_type = GST_RMDEMUX_STREAM_VIDEO; + } else if (strcmp (stream2_type_string, + "video/x-pn-multirate-realvideo") == 0) { + stream_type = GST_RMDEMUX_STREAM_VIDEO; + } else if (strcmp (stream2_type_string, "audio/x-pn-realaudio") == 0) { + stream_type = GST_RMDEMUX_STREAM_AUDIO; + } else if (strcmp (stream2_type_string, + "audio/x-pn-multirate-realaudio") == 0) { + stream_type = GST_RMDEMUX_STREAM_AUDIO; + } else if (strcmp (stream2_type_string, + "audio/x-pn-multirate-realaudio-live") == 0) { + stream_type = GST_RMDEMUX_STREAM_AUDIO; + } else if (strcmp (stream2_type_string, "audio/x-ralf-mpeg4-generic") == 0) { + /* Another audio type found in the real testsuite */ + stream_type = GST_RMDEMUX_STREAM_AUDIO; + } else if (strcmp (stream1_type_string, "") == 0 && + strcmp (stream2_type_string, "logical-fileinfo") == 0) { + stream_type = GST_RMDEMUX_STREAM_FILEINFO; + } else { + stream_type = GST_RMDEMUX_STREAM_UNKNOWN; + GST_WARNING_OBJECT (rmdemux, "unknown stream type \"%s\",\"%s\"", + stream1_type_string, stream2_type_string); + } + g_free (stream1_type_string); + g_free (stream2_type_string); + + offset += 4; + + stream->subtype = stream_type; + switch (stream_type) { + + case GST_RMDEMUX_STREAM_VIDEO: + /* RV10/RV20/RV30/RV40 => video/x-pn-realvideo, version=1,2,3,4 */ + stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 8); + stream->width = RMDEMUX_GUINT16_GET (data + offset + 12); + stream->height = RMDEMUX_GUINT16_GET (data + offset + 14); + stream->rate = RMDEMUX_GUINT16_GET (data + offset + 16); + stream->subformat = RMDEMUX_GUINT32_GET (data + offset + 26); + stream->format = RMDEMUX_GUINT32_GET (data + offset + 30); + stream->extra_data_size = length - (offset + 26); + stream->extra_data = (guint8 *) data + offset + 26; + /* Natural way to represent framerates here requires unsigned 32 bit + * numerator, which we don't have. For the nasty case, approximate... + */ + { + guint32 numerator = RMDEMUX_GUINT16_GET (data + offset + 22) * 65536 + + RMDEMUX_GUINT16_GET (data + offset + 24); + if (numerator > G_MAXINT) { + stream->framerate_numerator = (gint) (numerator >> 1); + stream->framerate_denominator = 32768; + } else { + stream->framerate_numerator = (gint) numerator; + stream->framerate_denominator = 65536; + } + } + + GST_DEBUG_OBJECT (rmdemux, + "Video stream with fourcc=%" GST_FOURCC_FORMAT + " width=%d height=%d rate=%d framerate=%d/%d subformat=%x format=%x extra_data_size=%d", + GST_FOURCC_ARGS (stream->fourcc), stream->width, stream->height, + stream->rate, stream->framerate_numerator, + stream->framerate_denominator, stream->subformat, stream->format, + stream->extra_data_size); + break; + case GST_RMDEMUX_STREAM_AUDIO:{ + stream->version = RMDEMUX_GUINT16_GET (data + offset + 4); + GST_INFO ("stream version = %u", stream->version); + switch (stream->version) { + case 3: + stream->fourcc = GST_RM_AUD_14_4; + stream->packet_size = 20; + stream->rate = 8000; + stream->n_channels = 1; + stream->sample_width = 16; + stream->flavor = 1; + stream->leaf_size = 0; + stream->height = 0; + break; + case 4: + stream->flavor = RMDEMUX_GUINT16_GET (data + offset + 22); + stream->packet_size = RMDEMUX_GUINT32_GET (data + offset + 24); + /* stream->frame_size = RMDEMUX_GUINT32_GET (data + offset + 42); */ + stream->leaf_size = RMDEMUX_GUINT16_GET (data + offset + 44); + stream->height = RMDEMUX_GUINT16_GET (data + offset + 40); + stream->rate = RMDEMUX_GUINT16_GET (data + offset + 48); + stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 52); + stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 54); + stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 62); + stream->extra_data_size = RMDEMUX_GUINT32_GET (data + offset + 69); + GST_DEBUG_OBJECT (rmdemux, "%u bytes of extra codec data", + stream->extra_data_size); + if (length - (offset + 73) >= stream->extra_data_size) { + stream->extra_data = (guint8 *) data + offset + 73; + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; + } + break; + case 5: + stream->flavor = RMDEMUX_GUINT16_GET (data + offset + 22); + stream->packet_size = RMDEMUX_GUINT32_GET (data + offset + 24); + /* stream->frame_size = RMDEMUX_GUINT32_GET (data + offset + 42); */ + stream->leaf_size = RMDEMUX_GUINT16_GET (data + offset + 44); + stream->height = RMDEMUX_GUINT16_GET (data + offset + 40); + stream->rate = RMDEMUX_GUINT16_GET (data + offset + 54); + stream->sample_width = RMDEMUX_GUINT16_GET (data + offset + 58); + stream->n_channels = RMDEMUX_GUINT16_GET (data + offset + 60); + stream->fourcc = RMDEMUX_FOURCC_GET (data + offset + 66); + stream->extra_data_size = RMDEMUX_GUINT32_GET (data + offset + 74); + GST_DEBUG_OBJECT (rmdemux, "%u bytes of extra codec data", + stream->extra_data_size); + if (length - (offset + 78) >= stream->extra_data_size) { + stream->extra_data = (guint8 *) data + offset + 78; + } else { + GST_WARNING_OBJECT (rmdemux, "codec data runs beyond MDPR chunk"); + stream->extra_data_size = 0; + } + break; + default:{ + GST_WARNING_OBJECT (rmdemux, "Unhandled audio stream version %d", + stream->version); + break; + } + } + /* 14_4, 28_8, cook, dnet, sipr, raac, racp, ralf, atrc */ + GST_DEBUG_OBJECT (rmdemux, + "Audio stream with rate=%d sample_width=%d n_channels=%d", + stream->rate, stream->sample_width, stream->n_channels); + + break; + } + case GST_RMDEMUX_STREAM_FILEINFO: + { + int element_nb; + + /* Length of this section */ + GST_DEBUG_OBJECT (rmdemux, "length2: 0x%08x", + RMDEMUX_GUINT32_GET (data + offset)); + offset += 4; + + /* Unknown : 00 00 00 00 */ + offset += 4; + + /* Number of variables that would follow (loop iterations) */ + element_nb = RMDEMUX_GUINT32_GET (data + offset); + offset += 4; + + while (element_nb) { + /* Category Id : 00 00 00 XX 00 00 */ + offset += 6; + + /* Variable Name */ + offset += re_skip_pascal_string (data + offset); + + /* Variable Value Type */ + /* 00 00 00 00 00 => integer/boolean, preceded by length */ + /* 00 00 00 02 00 => pascal string, preceded by length, no trailing \0 */ + offset += 5; + + /* Variable Value */ + offset += re_skip_pascal_string (data + offset); + + element_nb--; + } + } + break; + case GST_RMDEMUX_STREAM_UNKNOWN: + default: + break; + } + + gst_rmdemux_add_stream (rmdemux, stream); +} + +static guint +gst_rmdemux_parse_indx (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + int n; + int id; + + n = RMDEMUX_GUINT32_GET (data); + id = RMDEMUX_GUINT16_GET (data + 4); + rmdemux->index_offset = RMDEMUX_GUINT32_GET (data + 6); + + GST_DEBUG_OBJECT (rmdemux, "Number of indices=%d Stream ID=%d length=%d", n, + id, length); + + /* Point to the next index_stream */ + rmdemux->index_stream = gst_rmdemux_get_stream_by_id (rmdemux, id); + + /* Return the length of the index */ + return 14 * n; +} + +static void +gst_rmdemux_parse_indx_data (GstRMDemux * rmdemux, const guint8 * data, + int length) +{ + int i; + int n; + GstRMDemuxIndex *index; + + /* The number of index records */ + n = length / 14; + + if (rmdemux->index_stream == NULL) + return; + + /* don't parse the index a second time when operating pull-based and + * reaching the end of the file */ + if (rmdemux->index_stream->index_length > 0) { + GST_DEBUG_OBJECT (rmdemux, "Already have an index for this stream"); + return; + } + + index = g_malloc (sizeof (GstRMDemuxIndex) * n); + rmdemux->index_stream->index = index; + rmdemux->index_stream->index_length = n; + + for (i = 0; i < n; i++) { + index[i].timestamp = RMDEMUX_GUINT32_GET (data + 2) * GST_MSECOND; + index[i].offset = RMDEMUX_GUINT32_GET (data + 6); + + GST_DEBUG_OBJECT (rmdemux, "Index found for timestamp=%f (at offset=%x)", + gst_guint64_to_gdouble (index[i].timestamp) / GST_SECOND, + index[i].offset); + data += 14; + } +} + +static void +gst_rmdemux_parse_data (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + rmdemux->n_chunks = RMDEMUX_GUINT32_GET (data); + rmdemux->data_offset = RMDEMUX_GUINT32_GET (data + 4); + rmdemux->chunk_index = 0; + GST_DEBUG_OBJECT (rmdemux, "Data chunk found with %d packets " + "(next data at 0x%08x)", rmdemux->n_chunks, rmdemux->data_offset); +} + +static void +gst_rmdemux_parse_cont (GstRMDemux * rmdemux, const guint8 * data, int length) +{ + GstTagList *tags; + + tags = gst_rm_utils_read_tags (data, length, gst_rm_utils_read_string16); + + GST_LOG_OBJECT (rmdemux, "tags: %" GST_PTR_FORMAT, tags); + + rmdemux->pending_tags = + gst_tag_list_merge (rmdemux->pending_tags, tags, GST_TAG_MERGE_APPEND); +} + +static void +gst_rmdemux_stream_clear_cached_subpackets (GstRMDemux * rmdemux, + GstRMDemuxStream * stream) +{ + if (stream->subpackets == NULL || stream->subpackets->len == 0) + return; + + GST_DEBUG_OBJECT (rmdemux, "discarding %u previously collected subpackets", + stream->subpackets->len); + g_ptr_array_foreach (stream->subpackets, (GFunc) gst_mini_object_unref, NULL); + g_ptr_array_set_size (stream->subpackets, 0); +} + +static GstFlowReturn +gst_rmdemux_descramble_audio (GstRMDemux * rmdemux, GstRMDemuxStream * stream) +{ + GstFlowReturn ret = GST_FLOW_ERROR; + GstBuffer *outbuf; + GstMapInfo outmap; + guint packet_size = stream->packet_size; + guint height = stream->subpackets->len; + guint leaf_size = stream->leaf_size; + guint p, x; + + g_assert (stream->height == height); + + GST_LOG ("packet_size = %u, leaf_size = %u, height= %u", packet_size, + leaf_size, height); + + outbuf = gst_buffer_new_and_alloc (height * packet_size); + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + + for (p = 0; p < height; ++p) { + GstBuffer *b = g_ptr_array_index (stream->subpackets, p); + GstMapInfo map; + + gst_buffer_map (b, &map, GST_MAP_READ); + + if (p == 0) { + GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (b); + GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (b); + } + + for (x = 0; x < packet_size / leaf_size; ++x) { + guint idx; + + idx = height * x + ((height + 1) / 2) * (p % 2) + (p / 2); + + /* GST_LOG ("%3u => %3u", (height * p) + x, idx); */ + memcpy (outmap.data + leaf_size * idx, map.data + leaf_size * x, + leaf_size); + } + gst_buffer_unmap (b, &map); + } + gst_buffer_unmap (outbuf, &outmap); + + /* some decoders, such as realaudiodec, need to be fed in packet units */ + for (p = 0; p < height; ++p) { + GstBuffer *subbuf; + + subbuf = + gst_buffer_copy_region (outbuf, GST_BUFFER_COPY_ALL, p * packet_size, + packet_size); + + GST_LOG_OBJECT (rmdemux, "pushing buffer dts %" GST_TIME_FORMAT ", pts %" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_DTS (subbuf)), + GST_TIME_ARGS (GST_BUFFER_PTS (subbuf))); + + if (stream->discont) { + GST_BUFFER_FLAG_SET (subbuf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + + ret = gst_pad_push (stream->pad, subbuf); + if (ret != GST_FLOW_OK) + break; + } + + gst_buffer_unref (outbuf); + + gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); + + return ret; +} + +static GstFlowReturn +gst_rmdemux_descramble_dnet_audio (GstRMDemux * rmdemux, + GstRMDemuxStream * stream) +{ + GstBuffer *buf; + + buf = g_ptr_array_index (stream->subpackets, 0); + g_ptr_array_index (stream->subpackets, 0) = NULL; + g_ptr_array_set_size (stream->subpackets, 0); + + buf = gst_rm_utils_descramble_dnet_buffer (buf); + + if (stream->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + return gst_pad_push (stream->pad, buf); +} + +static GstFlowReturn +gst_rmdemux_descramble_mp4a_audio (GstRMDemux * rmdemux, + GstRMDemuxStream * stream) +{ + GstFlowReturn res; + GstBuffer *buf, *outbuf; + guint frames, index, i; + GstMapInfo map; + GstClockTime timestamp; + + res = GST_FLOW_OK; + + buf = g_ptr_array_index (stream->subpackets, 0); + g_ptr_array_index (stream->subpackets, 0) = NULL; + g_ptr_array_set_size (stream->subpackets, 0); + + gst_buffer_map (buf, &map, GST_MAP_READ); + timestamp = GST_BUFFER_PTS (buf); + + frames = (map.data[1] & 0xf0) >> 4; + index = 2 * frames + 2; + + for (i = 0; i < frames; i++) { + guint len = (map.data[i * 2 + 2] << 8) | map.data[i * 2 + 3]; + + outbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, index, len); + if (i == 0) { + GST_BUFFER_PTS (outbuf) = timestamp; + GST_BUFFER_DTS (outbuf) = timestamp; + } + + index += len; + + if (stream->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + res = gst_pad_push (stream->pad, outbuf); + if (res != GST_FLOW_OK) + break; + } + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + return res; +} + +static GstFlowReturn +gst_rmdemux_descramble_sipr_audio (GstRMDemux * rmdemux, + GstRMDemuxStream * stream) +{ + GstFlowReturn ret; + GstBuffer *outbuf; + GstMapInfo outmap; + guint packet_size = stream->packet_size; + guint height = stream->subpackets->len; + guint p; + + g_assert (stream->height == height); + + GST_LOG ("packet_size = %u, leaf_size = %u, height= %u", packet_size, + stream->leaf_size, height); + + outbuf = gst_buffer_new_and_alloc (height * packet_size); + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + + for (p = 0; p < height; ++p) { + GstBuffer *b = g_ptr_array_index (stream->subpackets, p); + + if (p == 0) { + GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (b); + GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (b); + } + + gst_buffer_extract (b, 0, outmap.data + packet_size * p, packet_size); + } + gst_buffer_unmap (outbuf, &outmap); + + GST_LOG_OBJECT (rmdemux, "pushing buffer dts %" GST_TIME_FORMAT ", pts %" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)), + GST_TIME_ARGS (GST_BUFFER_PTS (outbuf))); + + if (stream->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + + outbuf = gst_rm_utils_descramble_sipr_buffer (outbuf); + + ret = gst_pad_push (stream->pad, outbuf); + + gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); + + return ret; +} + +static GstFlowReturn +gst_rmdemux_handle_scrambled_packet (GstRMDemux * rmdemux, + GstRMDemuxStream * stream, GstBuffer * buf, gboolean keyframe) +{ + GstFlowReturn ret; + + if (stream->subpackets == NULL) + stream->subpackets = g_ptr_array_sized_new (stream->subpackets_needed); + + GST_LOG ("Got subpacket %u/%u, len=%" G_GSIZE_FORMAT ", key=%d", + stream->subpackets->len + 1, stream->subpackets_needed, + gst_buffer_get_size (buf), keyframe); + + if (keyframe && stream->subpackets->len > 0) { + gst_rmdemux_stream_clear_cached_subpackets (rmdemux, stream); + } + + g_ptr_array_add (stream->subpackets, buf); + + if (stream->subpackets->len < stream->subpackets_needed) + return GST_FLOW_OK; + + g_assert (stream->subpackets->len >= 1); + + switch (stream->fourcc) { + case GST_RM_AUD_DNET: + ret = gst_rmdemux_descramble_dnet_audio (rmdemux, stream); + break; + case GST_RM_AUD_COOK: + case GST_RM_AUD_ATRC: + ret = gst_rmdemux_descramble_audio (rmdemux, stream); + break; + case GST_RM_AUD_RAAC: + case GST_RM_AUD_RACP: + ret = gst_rmdemux_descramble_mp4a_audio (rmdemux, stream); + break; + case GST_RM_AUD_SIPR: + ret = gst_rmdemux_descramble_sipr_audio (rmdemux, stream); + break; + default: + ret = GST_FLOW_ERROR; + g_assert_not_reached (); + } + + return ret; +} + +#define PARSE_NUMBER(data, size, number, label) \ +G_STMT_START { \ + if (size < 2) \ + goto label; \ + number = GST_READ_UINT16_BE (data); \ + if (!(number & 0xc000)) { \ + if (size < 4) \ + goto label; \ + number = GST_READ_UINT32_BE (data); \ + data += 4; \ + size -= 4; \ + } else { \ + number &= 0x3fff; \ + data += 2; \ + size -= 2; \ + } \ +} G_STMT_END + +static GstFlowReturn +gst_rmdemux_parse_video_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream, + GstBuffer * in, guint offset, guint16 version, + GstClockTime timestamp, gboolean key) +{ + GstFlowReturn ret; + GstMapInfo map; + const guint8 *data; + gsize size; + + gst_buffer_map (in, &map, GST_MAP_READ); + + data = map.data + offset; + size = map.size - offset; + + /* if size <= 2, we want this method to return the same GstFlowReturn as it + * was previously for that given stream. */ + ret = GST_PAD_LAST_FLOW_RETURN (stream->pad); + + while (size > 2) { + guint8 pkg_header; + guint pkg_offset; + guint pkg_length; + guint pkg_subseq = 0, pkg_seqnum = G_MAXUINT; + guint fragment_size; + GstBuffer *fragment; + + pkg_header = *data++; + size--; + + /* packet header + * bit 7: 1=last block in block chain + * bit 6: 1=short header (only one block?) + */ + if ((pkg_header & 0xc0) == 0x40) { + /* skip unknown byte */ + data++; + size--; + pkg_offset = 0; + pkg_length = size; + } else { + if ((pkg_header & 0x40) == 0) { + pkg_subseq = (*data++) & 0x7f; + size--; + } else { + pkg_subseq = 0; + } + + /* length */ + PARSE_NUMBER (data, size, pkg_length, not_enough_data); + + /* offset */ + PARSE_NUMBER (data, size, pkg_offset, not_enough_data); + + /* seqnum */ + if (size < 1) + goto not_enough_data; + + pkg_seqnum = *data++; + size--; + } + + GST_DEBUG_OBJECT (rmdemux, + "seq %d, subseq %d, offset %d, length %d, size %" G_GSIZE_FORMAT + ", header %02x", pkg_seqnum, pkg_subseq, pkg_offset, pkg_length, size, + pkg_header); + + /* calc size of fragment */ + if ((pkg_header & 0xc0) == 0x80) { + fragment_size = pkg_offset; + } else { + if ((pkg_header & 0xc0) == 0) + fragment_size = size; + else + fragment_size = pkg_length; + } + GST_DEBUG_OBJECT (rmdemux, "fragment size %d", fragment_size); + + /* get the fragment */ + fragment = + gst_buffer_copy_region (in, GST_BUFFER_COPY_ALL, data - map.data, + fragment_size); + + if (pkg_subseq == 1) { + GST_DEBUG_OBJECT (rmdemux, "start new fragment"); + gst_adapter_clear (stream->adapter); + stream->frag_current = 0; + stream->frag_count = 0; + stream->frag_length = pkg_length; + } else if (pkg_subseq == 0) { + GST_DEBUG_OBJECT (rmdemux, "non fragmented packet"); + stream->frag_current = 0; + stream->frag_count = 0; + stream->frag_length = fragment_size; + } + + /* put fragment in adapter */ + gst_adapter_push (stream->adapter, fragment); + stream->frag_offset[stream->frag_count] = stream->frag_current; + stream->frag_current += fragment_size; + stream->frag_count++; + + if (stream->frag_count > MAX_FRAGS) + goto too_many_fragments; + + GST_DEBUG_OBJECT (rmdemux, "stored fragment in adapter %d/%d", + stream->frag_current, stream->frag_length); + + /* flush fragment when complete */ + if (stream->frag_current >= stream->frag_length) { + GstBuffer *out; + GstMapInfo outmap; + guint8 *outdata; + guint header_size; + gint i, avail; + + /* calculate header size, which is: + * 1 byte for the number of fragments - 1 + * for each fragment: + * 4 bytes 0x00000001 little endian + * 4 bytes fragment offset + * + * This is also the matroska header for realvideo, the decoder needs the + * fragment offsets, both in ffmpeg and real .so, so we just give it that + * in front of the data. + */ + header_size = 1 + (8 * (stream->frag_count)); + + GST_DEBUG_OBJECT (rmdemux, + "fragmented completed. count %d, header_size %u", stream->frag_count, + header_size); + + avail = gst_adapter_available (stream->adapter); + + out = gst_buffer_new_and_alloc (header_size + avail); + gst_buffer_map (out, &outmap, GST_MAP_WRITE); + outdata = outmap.data; + + /* create header */ + *outdata++ = stream->frag_count - 1; + for (i = 0; i < stream->frag_count; i++) { + GST_WRITE_UINT32_LE (outdata, 0x00000001); + outdata += 4; + GST_WRITE_UINT32_LE (outdata, stream->frag_offset[i]); + outdata += 4; + } + + /* copy packet data after the header now */ + gst_adapter_copy (stream->adapter, outdata, 0, avail); + gst_adapter_flush (stream->adapter, avail); + + stream->frag_current = 0; + stream->frag_count = 0; + stream->frag_length = 0; + + if (timestamp != -1) { + if (rmdemux->first_ts != -1 && timestamp > rmdemux->first_ts) + timestamp -= rmdemux->first_ts; + else + timestamp = 0; + + if (rmdemux->base_ts != -1) + timestamp += rmdemux->base_ts; + } + gst_buffer_unmap (out, &outmap); + + /* video has DTS */ + GST_BUFFER_DTS (out) = timestamp; + GST_BUFFER_PTS (out) = GST_CLOCK_TIME_NONE; + + GST_LOG_OBJECT (rmdemux, "pushing timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + + if (stream->discont) { + GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + + if (!key) { + GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT); + } + + ret = gst_pad_push (stream->pad, out); + ret = gst_flow_combiner_update_flow (rmdemux->flowcombiner, ret); + if (ret != GST_FLOW_OK) + break; + + timestamp = GST_CLOCK_TIME_NONE; + } + data += fragment_size; + size -= fragment_size; + } + GST_DEBUG_OBJECT (rmdemux, "%" G_GSIZE_FORMAT " bytes left", size); + +done: + gst_buffer_unmap (in, &map); + gst_buffer_unref (in); + + return ret; + + /* ERRORS */ +not_enough_data: + { + GST_ELEMENT_WARNING (rmdemux, STREAM, DECODE, ("Skipping bad packet."), + (NULL)); + ret = GST_FLOW_OK; + goto done; + } +too_many_fragments: + { + GST_ELEMENT_ERROR (rmdemux, STREAM, DECODE, + ("Got more fragments (%u) than can be handled (%u)", + stream->frag_count, MAX_FRAGS), (NULL)); + ret = GST_FLOW_ERROR; + goto done; + } +} + +static GstFlowReturn +gst_rmdemux_parse_audio_packet (GstRMDemux * rmdemux, GstRMDemuxStream * stream, + GstBuffer * in, guint offset, guint16 version, + GstClockTime timestamp, gboolean key) +{ + GstFlowReturn ret; + GstBuffer *buffer; + + buffer = gst_buffer_copy_region (in, GST_BUFFER_COPY_MEMORY, offset, -1); + + if (rmdemux->first_ts != -1 && timestamp > rmdemux->first_ts) + timestamp -= rmdemux->first_ts; + else + timestamp = 0; + + if (rmdemux->base_ts != -1) + timestamp += rmdemux->base_ts; + + GST_BUFFER_PTS (buffer) = timestamp; + GST_BUFFER_DTS (buffer) = timestamp; + + if (stream->needs_descrambling) { + GST_LOG_OBJECT (rmdemux, "descramble timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + ret = gst_rmdemux_handle_scrambled_packet (rmdemux, stream, buffer, key); + } else { + GST_LOG_OBJECT (rmdemux, + "Pushing buffer of size %" G_GSIZE_FORMAT ", timestamp %" + GST_TIME_FORMAT "to pad %s", gst_buffer_get_size (buffer), + GST_TIME_ARGS (timestamp), GST_PAD_NAME (stream->pad)); + + if (stream->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + ret = gst_pad_push (stream->pad, buffer); + } + + gst_buffer_unref (in); + + return ret; +} + +static GstFlowReturn +gst_rmdemux_parse_packet (GstRMDemux * rmdemux, GstBuffer * in, guint16 version) +{ + guint16 id; + GstRMDemuxStream *stream; + gsize size, offset; + GstFlowReturn cret, ret; + GstClockTime timestamp; + gboolean key; + GstMapInfo map; + guint8 *data; + guint8 flags; + guint32 ts; + + gst_buffer_map (in, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + /* stream number */ + id = RMDEMUX_GUINT16_GET (data); + + stream = gst_rmdemux_get_stream_by_id (rmdemux, id); + if (!stream || !stream->pad) + goto unknown_stream; + + /* timestamp in Msec */ + ts = RMDEMUX_GUINT32_GET (data + 2); + timestamp = ts * GST_MSECOND; + + rmdemux->segment.position = timestamp; + + GST_LOG_OBJECT (rmdemux, "Parsing a packet for stream=%d, timestamp=%" + GST_TIME_FORMAT ", size %" G_GSIZE_FORMAT ", version=%d, ts=%u", id, + GST_TIME_ARGS (timestamp), size, version, ts); + + if (rmdemux->first_ts == GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (rmdemux, "First timestamp: %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + rmdemux->first_ts = timestamp; + } + + /* skip stream_id and timestamp */ + data += (2 + 4); + size -= (2 + 4); + + /* get flags */ + flags = GST_READ_UINT8 (data + 1); + + data += 2; + size -= 2; + + /* version 1 has an extra byte */ + if (version == 1) { + data += 1; + size -= 1; + } + offset = data - map.data; + gst_buffer_unmap (in, &map); + + key = (flags & 0x02) != 0; + GST_DEBUG_OBJECT (rmdemux, "flags %d, Keyframe %d", flags, key); + + if (rmdemux->need_newsegment) { + GstEvent *event; + + event = gst_event_new_segment (&rmdemux->segment); + + GST_DEBUG_OBJECT (rmdemux, "sending NEWSEGMENT event, segment.start= %" + GST_TIME_FORMAT, GST_TIME_ARGS (rmdemux->segment.start)); + + gst_rmdemux_send_event (rmdemux, event); + rmdemux->need_newsegment = FALSE; + + if (rmdemux->pending_tags != NULL) { + gst_rmdemux_send_event (rmdemux, + gst_event_new_tag (rmdemux->pending_tags)); + rmdemux->pending_tags = NULL; + } + } + + if (stream->pending_tags != NULL) { + GST_LOG_OBJECT (stream->pad, "tags %" GST_PTR_FORMAT, stream->pending_tags); + gst_pad_push_event (stream->pad, gst_event_new_tag (stream->pending_tags)); + stream->pending_tags = NULL; + } + + if ((rmdemux->offset + size) <= stream->seek_offset) { + GST_DEBUG_OBJECT (rmdemux, + "Stream %d is skipping: seek_offset=%d, offset=%d, size=%" + G_GSIZE_FORMAT, stream->id, stream->seek_offset, rmdemux->offset, size); + cret = GST_FLOW_OK; + gst_buffer_unref (in); + goto beach; + } + + /* do special headers */ + if (stream->subtype == GST_RMDEMUX_STREAM_VIDEO) { + ret = + gst_rmdemux_parse_video_packet (rmdemux, stream, in, offset, + version, timestamp, key); + } else if (stream->subtype == GST_RMDEMUX_STREAM_AUDIO) { + ret = + gst_rmdemux_parse_audio_packet (rmdemux, stream, in, offset, + version, timestamp, key); + } else { + gst_buffer_unref (in); + ret = GST_FLOW_OK; + } + + cret = gst_flow_combiner_update_flow (rmdemux->flowcombiner, ret); + +beach: + return cret; + + /* ERRORS */ +unknown_stream: + { + GST_WARNING_OBJECT (rmdemux, "No stream for stream id %d in parsing " + "data packet", id); + gst_buffer_unmap (in, &map); + gst_buffer_unref (in); + return GST_FLOW_OK; + } +} + +gboolean +gst_rmdemux_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rmdemux", + GST_RANK_PRIMARY, GST_TYPE_RMDEMUX); +} diff --git a/gst/realmedia/rmdemux.h b/gst/realmedia/rmdemux.h new file mode 100644 index 0000000..68a1700 --- /dev/null +++ b/gst/realmedia/rmdemux.h @@ -0,0 +1,165 @@ +/* GStreamer RealMedia demuxer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_RMDEMUX_H__ +#define __GST_RMDEMUX_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/base/gstflowcombiner.h> +#include <gst/pbutils/descriptions.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RMDEMUX \ + (gst_rmdemux_get_type()) +#define GST_RMDEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RMDEMUX,GstRMDemux)) +#define GST_RMDEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RMDEMUX,GstRMDemuxClass)) +#define GST_IS_RMDEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RMDEMUX)) +#define GST_IS_RMDEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RMDEMUX)) + +typedef enum +{ + RMDEMUX_STATE_NULL, + RMDEMUX_STATE_HEADER, + RMDEMUX_STATE_HEADER_UNKNOWN, + RMDEMUX_STATE_HEADER_RMF, + RMDEMUX_STATE_HEADER_PROP, + RMDEMUX_STATE_HEADER_MDPR, + RMDEMUX_STATE_HEADER_INDX, + RMDEMUX_STATE_HEADER_DATA, + RMDEMUX_STATE_HEADER_CONT, + RMDEMUX_STATE_HEADER_SEEKING, + RMDEMUX_STATE_SEEKING, + RMDEMUX_STATE_DATA_PACKET, + RMDEMUX_STATE_SEEKING_EOS, + RMDEMUX_STATE_EOS, + RMDEMUX_STATE_INDX_DATA +} GstRMDemuxState; + +typedef enum +{ + RMDEMUX_LOOP_STATE_HEADER, + RMDEMUX_LOOP_STATE_INDEX, + RMDEMUX_LOOP_STATE_DATA +} GstRMDemuxLoopState; + +typedef enum +{ + GST_RMDEMUX_STREAM_UNKNOWN, + GST_RMDEMUX_STREAM_VIDEO, + GST_RMDEMUX_STREAM_AUDIO, + GST_RMDEMUX_STREAM_FILEINFO +} GstRMDemuxStreamType; + +typedef struct _GstRMDemux GstRMDemux; +typedef struct _GstRMDemuxClass GstRMDemuxClass; +typedef struct _GstRMDemuxStream GstRMDemuxStream; + +struct _GstRMDemux { + GstElement element; + + /* pads */ + GstPad *sinkpad; + + gboolean have_group_id; + guint group_id; + + GSList *streams; + guint n_video_streams; + guint n_audio_streams; + GstAdapter *adapter; + gboolean have_pads; + + GstFlowCombiner *flowcombiner; + + guint32 timescale; + guint64 duration; + guint32 avg_packet_size; + guint32 index_offset; + guint32 data_offset; + guint32 num_packets; + + guint offset; + gboolean seekable; + + GstRMDemuxState state; + GstRMDemuxLoopState loop_state; + GstRMDemuxStream *index_stream; + + /* playback start/stop positions */ + GstSegment segment; + gboolean segment_running; + gboolean running; + + /* Whether we need to send a newsegment event */ + gboolean need_newsegment; + + /* Current timestamp */ + GstClockTime cur_timestamp; + + /* First timestamp */ + GstClockTime base_ts; + GstClockTime first_ts; + + int n_chunks; + int chunk_index; + + guint32 object_id; + guint32 size; + guint16 object_version; + + /* container tags for all streams */ + GstTagList *pending_tags; +}; + +struct _GstRMDemuxClass { + GstElementClass parent_class; +}; + +/* RealMedia VideoCodec FOURCC codes */ +#define GST_RM_VDO_RV10 GST_MAKE_FOURCC('R','V','1','0') // RealVideo 1 +#define GST_RM_VDO_RV20 GST_MAKE_FOURCC('R','V','2','0') // RealVideo G2 +#define GST_RM_VDO_RV30 GST_MAKE_FOURCC('R','V','3','0') // RealVideo 8 +#define GST_RM_VDO_RV40 GST_MAKE_FOURCC('R','V','4','0') // RealVideo 9+10 + +/* RealMedia AudioCodec FOURCC codes */ +#define GST_RM_AUD_14_4 GST_MAKE_FOURCC('1','4','_','4') // 14.4 Audio Codec +#define GST_RM_AUD_28_8 GST_MAKE_FOURCC('2','8','_','8') // 28.8 Audio Codec +#define GST_RM_AUD_COOK GST_MAKE_FOURCC('c','o','o','k') // Cooker G2 Audio Codec +#define GST_RM_AUD_DNET GST_MAKE_FOURCC('d','n','e','t') // DolbyNet Audio Codec (low bitrate Dolby AC3) +#define GST_RM_AUD_SIPR GST_MAKE_FOURCC('s','i','p','r') // Sipro/ACELP.NET Voice Codec +#define GST_RM_AUD_RAAC GST_MAKE_FOURCC('r','a','a','c') // LE-AAC Audio Codec +#define GST_RM_AUD_RACP GST_MAKE_FOURCC('r','a','c','p') // HE-AAC Audio Codec +#define GST_RM_AUD_RALF GST_MAKE_FOURCC('r','a','l','f') // RealAudio Lossless +#define GST_RM_AUD_ATRC GST_MAKE_FOURCC('a','t','r','c') // Sony ATRAC3 Audio Codec + +#define GST_RM_AUD_xRA4 GST_MAKE_FOURCC('.','r','a','4') // Not a real audio codec +#define GST_RM_AUD_xRA5 GST_MAKE_FOURCC('.','r','a','5') // Not a real audio codec + +gboolean gst_rmdemux_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RMDEMUX_H__ */ diff --git a/gst/realmedia/rmutils.c b/gst/realmedia/rmutils.c new file mode 100644 index 0000000..c9bc098 --- /dev/null +++ b/gst/realmedia/rmutils.c @@ -0,0 +1,294 @@ +/* GStreamer RealMedia utility functions + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "rmutils.h" + +gchar * +gst_rm_utils_read_string8 (const guint8 * data, guint datalen, + guint * p_total_len) +{ + gint length; + + if (p_total_len) + *p_total_len = 0; + + if (datalen < 1) + return NULL; + + length = GST_READ_UINT8 (data); + if (datalen < (1 + length)) + return NULL; + + if (p_total_len) + *p_total_len = 1 + length; + + return g_strndup ((gchar *) data + 1, length); +} + +gchar * +gst_rm_utils_read_string16 (const guint8 * data, guint datalen, + guint * p_total_len) +{ + gint length; + + if (p_total_len) + *p_total_len = 0; + + if (datalen < 2) + return NULL; + + length = GST_READ_UINT16_BE (data); + if (datalen < (2 + length)) + return NULL; + + if (p_total_len) + *p_total_len = 2 + length; + + return g_strndup ((gchar *) data + 2, length); +} + +GstTagList * +gst_rm_utils_read_tags (const guint8 * data, guint datalen, + GstRmUtilsStringReadFunc read_string_func) +{ + const gchar *gst_tags[] = { GST_TAG_TITLE, GST_TAG_ARTIST, + GST_TAG_COPYRIGHT, GST_TAG_COMMENT + }; + GstTagList *tags; + guint i; + + g_assert (read_string_func != NULL); + + GST_DEBUG ("File Content : (CONT) len = %d", datalen); + + tags = gst_tag_list_new_empty (); + + for (i = 0; i < G_N_ELEMENTS (gst_tags); ++i) { + gchar *str = NULL; + guint total_length = 0; + + str = read_string_func (data, datalen, &total_length); + data += total_length; + datalen -= total_length; + + if (str != NULL && !g_utf8_validate (str, -1, NULL)) { + const gchar *encoding; + gchar *tmp; + + encoding = g_getenv ("GST_TAG_ENCODING"); + if (encoding == NULL || *encoding == '\0') { + if (g_get_charset (&encoding)) + encoding = "ISO-8859-15"; + } + GST_DEBUG ("converting tag from %s to UTF-8", encoding); + tmp = g_convert_with_fallback (str, -1, "UTF-8", encoding, (gchar *) "*", + NULL, NULL, NULL); + g_free (str); + str = tmp; + } + + GST_DEBUG ("%s = %s", gst_tags[i], GST_STR_NULL (str)); + if (str != NULL && *str != '\0') { + gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, gst_tags[i], str, NULL); + } + g_free (str); + } + + if (gst_tag_list_n_tags (tags) > 0) + return tags; + + gst_tag_list_unref (tags); + return NULL; +} + +GstBuffer * +gst_rm_utils_descramble_dnet_buffer (GstBuffer * buf) +{ + GstMapInfo map; + guint8 *data, *end, tmp; + + buf = gst_buffer_make_writable (buf); + + /* dnet = byte-order swapped AC3 */ + gst_buffer_map (buf, &map, GST_MAP_READWRITE); + data = map.data; + end = data + map.size; + while ((data + 1) < end) { + /* byte-swap */ + tmp = data[0]; + data[0] = data[1]; + data[1] = tmp; + data += sizeof (guint16); + } + gst_buffer_unmap (buf, &map); + return buf; +} + +static void +gst_rm_utils_swap_nibbles (guint8 * data, gint idx1, gint idx2, gint len) +{ + guint8 *d1, *d2, tmp1 = 0, tmp2, tmp1n, tmp2n; + + if ((idx2 & 1) && !(idx1 & 1)) { + /* align destination to a byte by swapping the indexes */ + tmp1 = idx1; + idx1 = idx2; + idx2 = tmp1; + } + d1 = data + (idx1 >> 1); + d2 = data + (idx2 >> 1); + + /* check if we have aligned offsets and we can copy bytes */ + if ((idx1 & 1) == (idx2 & 1)) { + if (idx1 & 1) { + /* swap first nibble */ + tmp1 = *d1; + tmp2 = *d2; + *d1++ = (tmp2 & 0xf0) | (tmp1 & 0x0f); + *d2++ = (tmp1 & 0xf0) | (tmp2 & 0x0f); + len--; + } + for (; len > 1; len -= 2) { + /* swap 2 nibbles */ + tmp1 = *d1; + *d1++ = *d2; + *d2++ = tmp1; + } + if (len) { + /* swap leftover nibble */ + tmp1 = *d1; + tmp2 = *d2; + *d1 = (tmp2 & 0x0f) | (tmp1 & 0xf0); + *d2 = (tmp1 & 0x0f) | (tmp2 & 0xf0); + } + } else { + /* preload nibbles from source */ + tmp2n = *d1; + tmp2 = *d2; + + for (; len > 1; len -= 2) { + /* assemble nibbles */ + *d1++ = (tmp2n & 0x0f) | (tmp2 << 4); + tmp1n = *d1; + *d2++ = (tmp1n << 4) | (tmp1 >> 4); + + tmp1 = tmp1n; + tmp2n = (tmp2 >> 4); + tmp2 = *d2; + } + if (len) { + /* last leftover */ + *d1 = (tmp2 << 4) | (tmp2n & 0x0f); + *d2 = (tmp1 >> 4) | (tmp2 & 0xf0); + } else { + *d1 = (tmp1 & 0xf0) | (tmp2n); + } + } +} + +static const gint sipr_swap_index[38][2] = { + {0, 63}, {1, 22}, {2, 44}, {3, 90}, + {5, 81}, {7, 31}, {8, 86}, {9, 58}, + {10, 36}, {12, 68}, {13, 39}, {14, 73}, + {15, 53}, {16, 69}, {17, 57}, {19, 88}, + {20, 34}, {21, 71}, {24, 46}, {25, 94}, + {26, 54}, {28, 75}, {29, 50}, {32, 70}, + {33, 92}, {35, 74}, {38, 85}, {40, 56}, + {42, 87}, {43, 65}, {45, 59}, {48, 79}, + {49, 93}, {51, 89}, {55, 95}, {61, 76}, + {67, 83}, {77, 80} +}; + +GstBuffer * +gst_rm_utils_descramble_sipr_buffer (GstBuffer * buf) +{ + GstMapInfo map; + gint n, bs; + gsize size; + + size = gst_buffer_get_size (buf); + + /* split the packet in 96 blocks of nibbles */ + bs = size * 2 / 96; + if (bs == 0) + return buf; + + buf = gst_buffer_make_writable (buf); + + gst_buffer_map (buf, &map, GST_MAP_WRITE); + + /* we need to perform 38 swaps on the blocks */ + for (n = 0; n < 38; n++) { + gint idx1, idx2; + + /* get the indexes of the blocks of nibbles that need swapping */ + idx1 = bs * sipr_swap_index[n][0]; + idx2 = bs * sipr_swap_index[n][1]; + + /* swap the blocks */ + gst_rm_utils_swap_nibbles (map.data, idx1, idx2, bs); + } + gst_buffer_unmap (buf, &map); + + return buf; +} + +void +gst_rm_utils_run_tests (void) +{ +#if 0 + guint8 tab1[] = { 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }; + guint8 tab2[8]; + + memcpy (tab2, tab1, 8); + gst_util_dump_mem (tab2, 8); + + gst_rm_utils_swap_nibbles (tab2, 0, 8, 4); + gst_util_dump_mem (tab2, 8); + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 0, 8, 5); + gst_util_dump_mem (tab2, 8); + + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 1, 8, 4); + gst_util_dump_mem (tab2, 8); + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 1, 8, 5); + gst_util_dump_mem (tab2, 8); + + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 0, 9, 4); + gst_util_dump_mem (tab2, 8); + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 0, 9, 5); + gst_util_dump_mem (tab2, 8); + + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 1, 9, 4); + gst_util_dump_mem (tab2, 8); + memcpy (tab2, tab1, 8); + gst_rm_utils_swap_nibbles (tab2, 1, 9, 5); + gst_util_dump_mem (tab2, 8); +#endif +} diff --git a/gst/realmedia/rmutils.h b/gst/realmedia/rmutils.h new file mode 100644 index 0000000..cf8bbb5 --- /dev/null +++ b/gst/realmedia/rmutils.h @@ -0,0 +1,50 @@ +/* GStreamer RealMedia utility functions + * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RM_UTILS_H__ +#define __GST_RM_UTILS_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +typedef gchar * (*GstRmUtilsStringReadFunc) (const guint8 * data, guint datalen, guint * p_strlen); + +gchar *gst_rm_utils_read_string8 (const guint8 * data, + guint datalen, + guint * p_totallen); + +gchar *gst_rm_utils_read_string16 (const guint8 * data, + guint datalen, + guint * p_totallen); + +GstTagList *gst_rm_utils_read_tags (const guint8 * data, + guint datalen, + GstRmUtilsStringReadFunc func); + +GstBuffer *gst_rm_utils_descramble_dnet_buffer (GstBuffer * buf); +GstBuffer *gst_rm_utils_descramble_sipr_buffer (GstBuffer * buf); + +void gst_rm_utils_run_tests (void); + + +G_END_DECLS + +#endif /* __GST_RM_UTILS_H__ */ + diff --git a/gst/realmedia/rtspreal.c b/gst/realmedia/rtspreal.c new file mode 100644 index 0000000..b7b3383 --- /dev/null +++ b/gst/realmedia/rtspreal.c @@ -0,0 +1,735 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +/* Element-Checklist-Version: 5 */ + +/** + * SECTION:element-rtspreal + * + * A RealMedia RTSP extension + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include "_stdint.h" +#include <string.h> + +#include <gst/rtsp/gstrtspextension.h> + +#include "realhash.h" +#include "rtspreal.h" +#include "asmrules.h" + +GST_DEBUG_CATEGORY_STATIC (rtspreal_debug); +#define GST_CAT_DEFAULT (rtspreal_debug) + +#define SERVER_PREFIX "RealServer" +#define DEFAULT_BANDWIDTH "10485800" + +static GstRTSPResult +rtsp_ext_real_get_transports (GstRTSPExtension * ext, + GstRTSPLowerTrans protocols, gchar ** transport) +{ + GstRTSPReal *ctx = (GstRTSPReal *) ext; + GString *str; + + if (!ctx->isreal) + return GST_RTSP_OK; + + GST_DEBUG_OBJECT (ext, "generating transports for %d", protocols); + + str = g_string_new (""); + + /* + if (protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST) { + g_string_append (str, "x-real-rdt/mcast;client_port=%%u1;mode=play,"); + } + if (protocols & GST_RTSP_LOWER_TRANS_UDP) { + g_string_append (str, "x-real-rdt/udp;client_port=%%u1;mode=play,"); + g_string_append (str, "x-pn-tng/udp;client_port=%%u1;mode=play,"); + } + */ + if (protocols & GST_RTSP_LOWER_TRANS_TCP) { + g_string_append (str, "x-real-rdt/tcp;mode=play,"); + g_string_append (str, "x-pn-tng/tcp;mode=play,"); + } + + /* if we added something, remove trailing ',' */ + if (str->len > 0) + g_string_truncate (str, str->len - 1); + + *transport = g_string_free (str, FALSE); + + return GST_RTSP_OK; +} + +static GstRTSPResult +rtsp_ext_real_before_send (GstRTSPExtension * ext, GstRTSPMessage * request) +{ + GstRTSPReal *ctx = (GstRTSPReal *) ext; + + switch (request->type_data.request.method) { + case GST_RTSP_OPTIONS: + { + gst_rtsp_message_add_header (request, GST_RTSP_HDR_USER_AGENT, + //"RealMedia Player (" GST_PACKAGE_NAME ")"); + "RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_CHALLENGE, + "9e26d33f2984236010ef6253fb1887f7"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_COMPANY_ID, + "KnKV4M4I/B2FjJ1TToLycw=="); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_GUID, + "00000000-0000-0000-0000-000000000000"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_REGION_DATA, "0"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_PLAYER_START_TIME, + "[28/03/2003:22:50:23 00:00]"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_ID, + "Linux_2.4_6.0.9.1235_play32_RN01_EN_586"); + ctx->isreal = FALSE; + break; + } + case GST_RTSP_DESCRIBE: + { + if (ctx->isreal) { + gst_rtsp_message_add_header (request, GST_RTSP_HDR_BANDWIDTH, + DEFAULT_BANDWIDTH); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_GUID, + "00000000-0000-0000-0000-000000000000"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_REGION_DATA, "0"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_ID, + "Linux_2.4_6.0.9.1235_play32_RN01_EN_586"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_MAX_ASM_WIDTH, "1"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_LANGUAGE, "en-US"); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_REQUIRE, + "com.real.retain-entity-for-setup"); + } + break; + } + case GST_RTSP_SETUP: + { + if (ctx->isreal) { + gchar *value = + g_strdup_printf ("%s, sd=%s", ctx->challenge2, ctx->checksum); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_REAL_CHALLENGE2, + value); + gst_rtsp_message_add_header (request, GST_RTSP_HDR_IF_MATCH, ctx->etag); + g_free (value); + } + break; + } + default: + break; + } + return GST_RTSP_OK; +} + +static GstRTSPResult +rtsp_ext_real_after_send (GstRTSPExtension * ext, GstRTSPMessage * req, + GstRTSPMessage * resp) +{ + GstRTSPReal *ctx = (GstRTSPReal *) ext; + + switch (req->type_data.request.method) { + case GST_RTSP_OPTIONS: + { + gchar *challenge1 = NULL; + gchar *server = NULL; + + gst_rtsp_message_get_header (resp, GST_RTSP_HDR_SERVER, &server, 0); + + gst_rtsp_message_get_header (resp, GST_RTSP_HDR_REAL_CHALLENGE1, + &challenge1, 0); + if (!challenge1) + goto no_challenge1; + + gst_rtsp_ext_real_calc_response_and_checksum (ctx->challenge2, + ctx->checksum, challenge1); + + GST_DEBUG_OBJECT (ctx, "Found Real challenge tag"); + ctx->isreal = TRUE; + break; + } + case GST_RTSP_DESCRIBE: + { + gchar *etag = NULL; + guint len; + + gst_rtsp_message_get_header (resp, GST_RTSP_HDR_ETAG, &etag, 0); + if (etag) { + len = sizeof (ctx->etag); + strncpy (ctx->etag, etag, len); + ctx->etag[len - 1] = '\0'; + } + break; + } + default: + break; + } + return GST_RTSP_OK; + + /* ERRORS */ +no_challenge1: + { + GST_DEBUG_OBJECT (ctx, "Could not find challenge tag."); + ctx->isreal = FALSE; + return GST_RTSP_OK; + } +} + +#define ENSURE_SIZE(size) \ +G_STMT_START { \ + while (data_len < size) { \ + data_len += 1024; \ + data = g_realloc (data, data_len); \ + } \ +} G_STMT_END + +#define READ_BUFFER_GEN(src, func, name, dest, dest_len) \ +G_STMT_START { \ + dest = (gchar *)func (src, name); \ + dest_len = 0; \ + if (!dest) { \ + dest = (char *) ""; \ + } \ + else if (!strncmp (dest, "buffer;\"", 8)) { \ + dest += 8; \ + dest_len = strlen (dest) - 1; \ + dest[dest_len] = '\0'; \ + g_base64_decode_inplace (dest, &dest_len); \ + } \ +} G_STMT_END + +#define READ_BUFFER(sdp, name, dest, dest_len) \ + READ_BUFFER_GEN(sdp, gst_sdp_message_get_attribute_val, name, dest, dest_len) +#define READ_BUFFER_M(media, name, dest, dest_len) \ + READ_BUFFER_GEN(media, gst_sdp_media_get_attribute_val, name, dest, dest_len) + +#define READ_INT_GEN(src, func, name, dest) \ +G_STMT_START { \ + const gchar *val = func (src, name); \ + if (val && !strncmp (val, "integer;", 8)) \ + dest = atoi (val + 8); \ + else \ + dest = 0; \ +} G_STMT_END + +#define READ_INT(sdp, name, dest) \ + READ_INT_GEN(sdp, gst_sdp_message_get_attribute_val, name, dest) +#define READ_INT_M(media, name, dest) \ + READ_INT_GEN(media, gst_sdp_media_get_attribute_val, name, dest) + +#define READ_STRING(media, name, dest, dest_len) \ +G_STMT_START { \ + const gchar *val = gst_sdp_media_get_attribute_val (media, name); \ + if (val && !strncmp (val, "string;\"", 8)) { \ + dest = (gchar *) val + 8; \ + dest_len = strlen (dest) - 1; \ + dest[dest_len] = '\0'; \ + } else { \ + dest = (char *) ""; \ + dest_len = 0; \ + } \ +} G_STMT_END + +#define WRITE_STRING1(datap, str, str_len) \ +G_STMT_START { \ + *datap = str_len; \ + memcpy ((datap) + 1, str, str_len); \ + datap += str_len + 1; \ +} G_STMT_END + +#define WRITE_STRING2(datap, str, str_len) \ +G_STMT_START { \ + GST_WRITE_UINT16_BE (datap, str_len); \ + memcpy (datap + 2, str, str_len); \ + datap += str_len + 2; \ +} G_STMT_END + +static GstRTSPResult +rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, + GstStructure * props) +{ + GstRTSPReal *ctx = (GstRTSPReal *) ext; + guint size; + gint i; + gchar *title, *author, *copyright, *comment; + gsize title_len, author_len, copyright_len, comment_len; + guint8 *data = NULL, *datap; + guint data_len = 0, offset; + GstBuffer *buf; + gchar *opaque_data; + gsize opaque_data_len, asm_rule_book_len; + GHashTable *vars; + GString *rules; + + /* don't bother for non-real formats */ + READ_INT (sdp, "IsRealDataType", ctx->isreal); + if (!ctx->isreal) + return TRUE; + + /* Force PAUSE | PLAY */ + //src->methods |= GST_RTSP_PLAY | GST_RTSP_PAUSE; + + ctx->n_streams = gst_sdp_message_medias_len (sdp); + + ctx->max_bit_rate = 0; + ctx->avg_bit_rate = 0; + ctx->max_packet_size = 0; + ctx->avg_packet_size = 0; + ctx->duration = 0; + + for (i = 0; i < ctx->n_streams; i++) { + const GstSDPMedia *media; + gint intval; + + media = gst_sdp_message_get_media (sdp, i); + + READ_INT_M (media, "MaxBitRate", intval); + ctx->max_bit_rate += intval; + READ_INT_M (media, "AvgBitRate", intval); + ctx->avg_bit_rate += intval; + READ_INT_M (media, "MaxPacketSize", intval); + ctx->max_packet_size = MAX (ctx->max_packet_size, intval); + READ_INT_M (media, "AvgPacketSize", intval); + ctx->avg_packet_size = (ctx->avg_packet_size * i + intval) / (i + 1); + READ_INT_M (media, "Duration", intval); + ctx->duration = MAX (ctx->duration, intval); + } + + /* FIXME: use GstByteWriter to write the header */ + /* PROP */ + offset = 0; + size = 50; + ENSURE_SIZE (size); + datap = data + offset; + + memcpy (datap + 0, "PROP", 4); + GST_WRITE_UINT32_BE (datap + 4, size); + GST_WRITE_UINT16_BE (datap + 8, 0); + GST_WRITE_UINT32_BE (datap + 10, ctx->max_bit_rate); + GST_WRITE_UINT32_BE (datap + 14, ctx->avg_bit_rate); + GST_WRITE_UINT32_BE (datap + 18, ctx->max_packet_size); + GST_WRITE_UINT32_BE (datap + 22, ctx->avg_packet_size); + GST_WRITE_UINT32_BE (datap + 26, 0); + GST_WRITE_UINT32_BE (datap + 30, ctx->duration); + GST_WRITE_UINT32_BE (datap + 34, 0); + GST_WRITE_UINT32_BE (datap + 38, 0); + GST_WRITE_UINT32_BE (datap + 42, 0); + GST_WRITE_UINT16_BE (datap + 46, ctx->n_streams); + GST_WRITE_UINT16_BE (datap + 48, 0); + offset += size; + + /* CONT */ + READ_BUFFER (sdp, "Title", title, title_len); + READ_BUFFER (sdp, "Author", author, author_len); + READ_BUFFER (sdp, "Comment", comment, comment_len); + READ_BUFFER (sdp, "Copyright", copyright, copyright_len); + + size = 18 + title_len + author_len + comment_len + copyright_len; + ENSURE_SIZE (offset + size); + datap = data + offset; + + memcpy (datap, "CONT", 4); + GST_WRITE_UINT32_BE (datap + 4, size); + GST_WRITE_UINT16_BE (datap + 8, 0); /* Version */ + datap += 10; + WRITE_STRING2 (datap, title, title_len); + WRITE_STRING2 (datap, author, author_len); + WRITE_STRING2 (datap, copyright, copyright_len); + WRITE_STRING2 (datap, comment, comment_len); + offset += size; + + /* fix the hashtale for the rule parser */ + rules = g_string_new (""); + vars = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (vars, (gchar *) "Bandwidth", + (gchar *) DEFAULT_BANDWIDTH); + + /* MDPR */ + for (i = 0; i < ctx->n_streams; i++) { + const GstSDPMedia *media; + guint32 len; + GstRTSPRealStream *stream; + gchar *str; + gint rulematches[MAX_RULEMATCHES]; + gint sel, j, n; + + media = gst_sdp_message_get_media (sdp, i); + + if (media->media && !strcmp (media->media, "data")) + continue; + + stream = g_new0 (GstRTSPRealStream, 1); + ctx->streams = g_list_append (ctx->streams, stream); + + READ_INT_M (media, "MaxBitRate", stream->max_bit_rate); + READ_INT_M (media, "AvgBitRate", stream->avg_bit_rate); + READ_INT_M (media, "MaxPacketSize", stream->max_packet_size); + READ_INT_M (media, "AvgPacketSize", stream->avg_packet_size); + READ_INT_M (media, "StartTime", stream->start_time); + READ_INT_M (media, "Preroll", stream->preroll); + READ_INT_M (media, "Duration", stream->duration); + READ_STRING (media, "StreamName", str, stream->stream_name_len); + stream->stream_name = g_strndup (str, stream->stream_name_len); + READ_STRING (media, "mimetype", str, stream->mime_type_len); + stream->mime_type = g_strndup (str, stream->mime_type_len); + + /* FIXME: Depending on the current bandwidth, we need to select one + * bandwith out of a list offered by the server. Someone needs to write + * a parser for strings like + * + * #($Bandwidth < 67959),TimestampDelivery=T,DropByN=T,priority=9; + * #($Bandwidth >= 67959) && ($Bandwidth < 167959),AverageBandwidth=67959, + * Priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959), + * AverageBandwidth=0,Priority=5,OnDepend=\"1\"; + * #($Bandwidth >= 167959) && ($Bandwidth < 267959), + * AverageBandwidth=167959,Priority=9; + * #($Bandwidth >= 167959) && ($Bandwidth < 267959),AverageBandwidth=0, + * Priority=5,OnDepend=\"3\";#($Bandwidth >= 267959), + * AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959), + * AverageBandwidth=0,Priority=5,OnDepend=\"5\"; + * + * As I don't know how to do that, I just use the first entry (sel = 0). + * But to give you a starting point, I offer you above string + * in the variable 'asm_rule_book'. + */ + READ_STRING (media, "ASMRuleBook", str, asm_rule_book_len); + stream->rulebook = gst_asm_rule_book_new (str); + + n = gst_asm_rule_book_match (stream->rulebook, vars, rulematches); + for (j = 0; j < n; j++) { + g_string_append_printf (rules, "stream=%u;rule=%u,", i, rulematches[j]); + } + + /* get the MLTI for the first matched rules */ + sel = rulematches[0]; + + READ_BUFFER_M (media, "OpaqueData", opaque_data, opaque_data_len); + + if (opaque_data_len < 4) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4", + opaque_data_len); + goto strange_opaque_data; + } + if (strncmp (opaque_data, "MLTI", 4)) { + GST_DEBUG_OBJECT (ctx, "no MLTI found, appending all"); + stream->type_specific_data_len = opaque_data_len; + stream->type_specific_data = g_memdup (opaque_data, opaque_data_len); + goto no_type_specific; + } + opaque_data += 4; + opaque_data_len -= 4; + + if (opaque_data_len < 2) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 2", + opaque_data_len); + goto strange_opaque_data; + } + stream->num_rules = GST_READ_UINT16_BE (opaque_data); + opaque_data += 2; + opaque_data_len -= 2; + + if (sel >= stream->num_rules) { + GST_DEBUG_OBJECT (ctx, "sel %d >= num_rules %d", sel, stream->num_rules); + goto strange_opaque_data; + } + + if (opaque_data_len < 2 * sel) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT + " < 2 * sel (%d)", opaque_data_len, 2 * sel); + goto strange_opaque_data; + } + opaque_data += 2 * sel; + opaque_data_len -= 2 * sel; + + if (opaque_data_len < 2) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 2", + opaque_data_len); + goto strange_opaque_data; + } + stream->codec = GST_READ_UINT16_BE (opaque_data); + opaque_data += 2; + opaque_data_len -= 2; + + if (opaque_data_len < 2 * (stream->num_rules - sel - 1)) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT + " < %d", opaque_data_len, 2 * (stream->num_rules - sel - 1)); + goto strange_opaque_data; + } + opaque_data += 2 * (stream->num_rules - sel - 1); + opaque_data_len -= 2 * (stream->num_rules - sel - 1); + + if (opaque_data_len < 2) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 2", + opaque_data_len); + goto strange_opaque_data; + } + stream->num_rules = GST_READ_UINT16_BE (opaque_data); + opaque_data += 2; + opaque_data_len -= 2; + + if (stream->codec > stream->num_rules) { + GST_DEBUG_OBJECT (ctx, "codec %d > num_rules %d", stream->codec, + stream->num_rules); + goto strange_opaque_data; + } + + for (j = 0; j < stream->codec; j++) { + if (opaque_data_len < 4) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4", + opaque_data_len); + goto strange_opaque_data; + } + len = GST_READ_UINT32_BE (opaque_data); + opaque_data += 4; + opaque_data_len -= 4; + + if (opaque_data_len < len) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < len %d", + opaque_data_len, len); + goto strange_opaque_data; + } + opaque_data += len; + opaque_data_len -= len; + } + + if (opaque_data_len < 4) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4", + opaque_data_len); + goto strange_opaque_data; + } + stream->type_specific_data_len = GST_READ_UINT32_BE (opaque_data); + opaque_data += 4; + opaque_data_len -= 4; + + if (opaque_data_len < stream->type_specific_data_len) { + GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < %d", + opaque_data_len, stream->type_specific_data_len); + goto strange_opaque_data; + } + stream->type_specific_data = + g_memdup (opaque_data, stream->type_specific_data_len); + + no_type_specific: + size = + 46 + stream->stream_name_len + stream->mime_type_len + + stream->type_specific_data_len; + ENSURE_SIZE (offset + size); + datap = data + offset; + + memcpy (datap, "MDPR", 4); + GST_WRITE_UINT32_BE (datap + 4, size); + GST_WRITE_UINT16_BE (datap + 8, 0); + GST_WRITE_UINT16_BE (datap + 10, i); + GST_WRITE_UINT32_BE (datap + 12, stream->max_bit_rate); + GST_WRITE_UINT32_BE (datap + 16, stream->avg_bit_rate); + GST_WRITE_UINT32_BE (datap + 20, stream->max_packet_size); + GST_WRITE_UINT32_BE (datap + 24, stream->avg_packet_size); + GST_WRITE_UINT32_BE (datap + 28, stream->start_time); + GST_WRITE_UINT32_BE (datap + 32, stream->preroll); + GST_WRITE_UINT32_BE (datap + 36, stream->duration); + datap += 40; + WRITE_STRING1 (datap, stream->stream_name, stream->stream_name_len); + WRITE_STRING1 (datap, stream->mime_type, stream->mime_type_len); + GST_WRITE_UINT32_BE (datap, stream->type_specific_data_len); + if (stream->type_specific_data_len) + memcpy (datap + 4, stream->type_specific_data, + stream->type_specific_data_len); + offset += size; + } + + /* destroy the rulebook hashtable now */ + g_hash_table_destroy (vars); + + /* strip final , if we added some stream rules */ + if (rules->len > 0) { + rules = g_string_truncate (rules, rules->len - 1); + } + + /* and store rules in the context */ + ctx->rules = g_string_free (rules, FALSE); + + /* DATA */ + size = 18; + ENSURE_SIZE (offset + size); + datap = data + offset; + + memcpy (datap, "DATA", 4); + GST_WRITE_UINT32_BE (datap + 4, size); + GST_WRITE_UINT16_BE (datap + 8, 0); + GST_WRITE_UINT32_BE (datap + 10, 0); /* number of packets */ + GST_WRITE_UINT32_BE (datap + 14, 0); /* next data header */ + offset += size; + + buf = gst_buffer_new_wrapped (data, offset); + + /* Set on caps */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); + gst_structure_set (props, "config", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + + /* Overwrite encoding and media fields */ + gst_structure_set (props, "encoding-name", G_TYPE_STRING, "X-REAL-RDT", NULL); + gst_structure_set (props, "media", G_TYPE_STRING, "application", NULL); + + return TRUE; + + /* ERRORS */ +strange_opaque_data: + { + g_string_free (rules, TRUE); + g_hash_table_destroy (vars); + g_free (data); + + GST_ELEMENT_ERROR (ctx, RESOURCE, WRITE, ("Strange opaque data."), (NULL)); + return FALSE; + } +} + +static GstRTSPResult +rtsp_ext_real_stream_select (GstRTSPExtension * ext, GstRTSPUrl * url) +{ + GstRTSPReal *ctx = (GstRTSPReal *) ext; + GstRTSPResult res; + GstRTSPMessage request = { 0 }; + GstRTSPMessage response = { 0 }; + gchar *req_url; + + if (!ctx->isreal) + return GST_RTSP_OK; + + if (!ctx->rules) + return GST_RTSP_OK; + + req_url = gst_rtsp_url_get_request_uri (url); + + /* create SET_PARAMETER */ + if ((res = gst_rtsp_message_init_request (&request, GST_RTSP_SET_PARAMETER, + req_url)) < 0) + goto create_request_failed; + + g_free (req_url); + + gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SUBSCRIBE, ctx->rules); + + /* send SET_PARAMETER */ + if ((res = gst_rtsp_extension_send (ext, &request, &response)) < 0) + goto send_error; + + gst_rtsp_message_unset (&request); + gst_rtsp_message_unset (&response); + + return GST_RTSP_OK; + + /* ERRORS */ +create_request_failed: + { + GST_ELEMENT_ERROR (ctx, LIBRARY, INIT, + ("Could not create request."), (NULL)); + goto reset; + } +send_error: + { + GST_ELEMENT_ERROR (ctx, RESOURCE, WRITE, + ("Could not send message."), (NULL)); + goto reset; + } +reset: + { + gst_rtsp_message_unset (&request); + gst_rtsp_message_unset (&response); + return res; + } +} + +static void gst_rtsp_real_extension_init (gpointer g_iface, + gpointer iface_data); +static void gst_rtsp_real_finalize (GObject * obj); + +#define gst_rtsp_real_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstRTSPReal, gst_rtsp_real, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_RTSP_EXTENSION, + gst_rtsp_real_extension_init)); + +static void +gst_rtsp_real_class_init (GstRTSPRealClass * g_class) +{ + GObjectClass *gobject_class = (GObjectClass *) g_class; + GstElementClass *gstelement_class = (GstElementClass *) g_class; + + gobject_class->finalize = gst_rtsp_real_finalize; + + gst_element_class_set_static_metadata (gstelement_class, + "RealMedia RTSP Extension", "Network/Extension/Protocol", + "Extends RTSP so that it can handle RealMedia setup", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtspreal_debug, "rtspreal", 0, + "RealMedia RTSP extension"); +} + +static void +gst_rtsp_real_init (GstRTSPReal * rtspreal) +{ + rtspreal->isreal = FALSE; +} + +static void +gst_rtsp_stream_free (GstRTSPRealStream * stream) +{ + g_free (stream->stream_name); + g_free (stream->mime_type); + gst_asm_rule_book_free (stream->rulebook); + g_free (stream->type_specific_data); + + g_free (stream); +} + +static void +gst_rtsp_real_finalize (GObject * obj) +{ + GstRTSPReal *r = (GstRTSPReal *) obj; + + g_list_foreach (r->streams, (GFunc) gst_rtsp_stream_free, NULL); + g_list_free (r->streams); + g_free (r->rules); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_rtsp_real_extension_init (gpointer g_iface, gpointer iface_data) +{ + GstRTSPExtensionInterface *iface = (GstRTSPExtensionInterface *) g_iface; + + iface->before_send = rtsp_ext_real_before_send; + iface->after_send = rtsp_ext_real_after_send; + iface->parse_sdp = rtsp_ext_real_parse_sdp; + iface->stream_select = rtsp_ext_real_stream_select; + iface->get_transports = rtsp_ext_real_get_transports; +} + +gboolean +gst_rtsp_real_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtspreal", + GST_RANK_MARGINAL, GST_TYPE_RTSP_REAL); +} diff --git a/gst/realmedia/rtspreal.h b/gst/realmedia/rtspreal.h new file mode 100644 index 0000000..fad1427 --- /dev/null +++ b/gst/realmedia/rtspreal.h @@ -0,0 +1,92 @@ +/* GStreamer + * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTSP_REAL_H__ +#define __GST_RTSP_REAL_H__ + +#include <gst/gst.h> + +#include "asmrules.h" + +G_BEGIN_DECLS + +#define GST_TYPE_RTSP_REAL (gst_rtsp_real_get_type()) +#define GST_IS_RTSP_REAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSP_REAL)) +#define GST_IS_RTSP_REAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSP_REAL)) +#define GST_RTSP_REAL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSP_REAL, GstRTSPReal)) +#define GST_RTSP_REAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSP_REAL, GstRTSPRealClass)) + +typedef struct _GstRTSPReal GstRTSPReal; +typedef struct _GstRTSPRealClass GstRTSPRealClass; + +typedef struct _GstRTSPRealStream GstRTSPRealStream; + +struct _GstRTSPRealStream { + guint id; + guint max_bit_rate; + guint avg_bit_rate; + guint max_packet_size; + guint avg_packet_size; + guint start_time; + guint preroll; + guint duration; + gchar *stream_name; + guint stream_name_len; + gchar *mime_type; + guint mime_type_len; + + GstASMRuleBook *rulebook; + + gchar *type_specific_data; + guint type_specific_data_len; + + guint16 num_rules, j, sel, codec; +}; + +struct _GstRTSPReal { + GstElement element; + + gchar checksum[34]; + gchar challenge2[64]; + gchar etag[64]; + gboolean isreal; + + guint n_streams; + GList *streams; + + guint max_bit_rate; + guint avg_bit_rate; + guint max_packet_size; + guint avg_packet_size; + guint duration; + + gchar *rules; +}; + +struct _GstRTSPRealClass { + GstElementClass parent_class; +}; + +GType gst_rtsp_real_get_type(void); + +gboolean gst_rtsp_real_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTSP_REAL_H__ */ diff --git a/gst/xingmux/Makefile.am b/gst/xingmux/Makefile.am new file mode 100644 index 0000000..c5d7692 --- /dev/null +++ b/gst/xingmux/Makefile.am @@ -0,0 +1,25 @@ +# FIXME 0.11: element should move somewhere else really, such as +# gst-plugins-good/gst/tags/ or so +plugin_LTLIBRARIES = libgstxingmux.la + +libgstxingmux_la_SOURCES = plugin.c gstxingmux.c +libgstxingmux_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstxingmux_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) +libgstxingmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstxingmux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstxingmux.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstxingmux -:SHARED libgstxingmux \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstxingmux_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstxingmux_la_CFLAGS) \ + -:LDFLAGS $(libgstxingmux_la_LDFLAGS) \ + $(libgstxingmux_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ diff --git a/gst/xingmux/Makefile.in b/gst/xingmux/Makefile.in new file mode 100644 index 0000000..52d03f6 --- /dev/null +++ b/gst/xingmux/Makefile.in @@ -0,0 +1,851 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 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__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +target_triplet = @target@ +subdir = gst/xingmux +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-function.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/a52.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-sid.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstxingmux_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libgstxingmux_la_OBJECTS = libgstxingmux_la-plugin.lo \ + libgstxingmux_la-gstxingmux.lo +libgstxingmux_la_OBJECTS = $(am_libgstxingmux_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstxingmux_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstxingmux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstxingmux_la_CFLAGS) $(CFLAGS) \ + $(libgstxingmux_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +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_CCLD_1 = +SOURCES = $(libgstxingmux_la_SOURCES) +DIST_SOURCES = $(libgstxingmux_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52DEC_CFLAGS = @A52DEC_CFLAGS@ +A52DEC_LIBS = @A52DEC_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMRNB_CFLAGS = @AMRNB_CFLAGS@ +AMRNB_LIBS = @AMRNB_LIBS@ +AMRWB_CFLAGS = @AMRWB_CFLAGS@ +AMRWB_LIBS = @AMRWB_LIBS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CDIO_CFLAGS = @CDIO_CFLAGS@ +CDIO_LIBS = @CDIO_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DVDREAD_LIBS = @DVDREAD_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@ +GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DVDREAD = @HAVE_DVDREAD@ +HAVE_LAME = @HAVE_LAME@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAD_CFLAGS = @MAD_CFLAGS@ +MAD_LIBS = @MAD_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +MPEG2DEC_LIBS = @MPEG2DEC_LIBS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIDPLAY_CFLAGS = @SIDPLAY_CFLAGS@ +SIDPLAY_LIBS = @SIDPLAY_LIBS@ +STRIP = @STRIP@ +TWOLAME_CFLAGS = @TWOLAME_CFLAGS@ +TWOLAME_LIBS = @TWOLAME_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WIN32_LIBS = @WIN32_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# FIXME 0.11: element should move somewhere else really, such as +# gst-plugins-good/gst/tags/ or so +plugin_LTLIBRARIES = libgstxingmux.la +libgstxingmux_la_SOURCES = plugin.c gstxingmux.c +libgstxingmux_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstxingmux_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) +libgstxingmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstxingmux_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = gstxingmux.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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 gst/xingmux/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/xingmux/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstxingmux.la: $(libgstxingmux_la_OBJECTS) $(libgstxingmux_la_DEPENDENCIES) $(EXTRA_libgstxingmux_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstxingmux_la_LINK) -rpath $(plugindir) $(libgstxingmux_la_OBJECTS) $(libgstxingmux_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstxingmux_la-gstxingmux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstxingmux_la-plugin.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +libgstxingmux_la-plugin.lo: plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstxingmux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstxingmux_la_CFLAGS) $(CFLAGS) -MT libgstxingmux_la-plugin.lo -MD -MP -MF $(DEPDIR)/libgstxingmux_la-plugin.Tpo -c -o libgstxingmux_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstxingmux_la-plugin.Tpo $(DEPDIR)/libgstxingmux_la-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='libgstxingmux_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstxingmux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstxingmux_la_CFLAGS) $(CFLAGS) -c -o libgstxingmux_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c + +libgstxingmux_la-gstxingmux.lo: gstxingmux.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstxingmux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstxingmux_la_CFLAGS) $(CFLAGS) -MT libgstxingmux_la-gstxingmux.lo -MD -MP -MF $(DEPDIR)/libgstxingmux_la-gstxingmux.Tpo -c -o libgstxingmux_la-gstxingmux.lo `test -f 'gstxingmux.c' || echo '$(srcdir)/'`gstxingmux.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstxingmux_la-gstxingmux.Tpo $(DEPDIR)/libgstxingmux_la-gstxingmux.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstxingmux.c' object='libgstxingmux_la-gstxingmux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstxingmux_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstxingmux_la_CFLAGS) $(CFLAGS) -c -o libgstxingmux_la-gstxingmux.lo `test -f 'gstxingmux.c' || echo '$(srcdir)/'`gstxingmux.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +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: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstxingmux -:SHARED libgstxingmux \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstxingmux_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstxingmux_la_CFLAGS) \ + -:LDFLAGS $(libgstxingmux_la_LDFLAGS) \ + $(libgstxingmux_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/xingmux/gstxingmux.c b/gst/xingmux/gstxingmux.c new file mode 100644 index 0000000..fd09ac0 --- /dev/null +++ b/gst/xingmux/gstxingmux.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (c) 2008 Sebastian Dröge <slomo@circular-chaos.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* Xing SDK: http://www.mp3-tech.org/programmer/sources/vbrheadersdk.zip */ + + +/** + * SECTION:element-xingmux + * + * xingmux adds a Xing header to MP3 files. This contains information about the duration and size + * of the file and a seek table and is very useful for getting an almost correct duration and better + * seeking on VBR MP3 files. + * + * This element will remove any existing Xing, LAME or VBRI headers from the beginning of the file. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch audiotestsrc num-buffers=1000 ! audioconvert ! lamemp3enc ! xingmux ! filesink location=test.mp3 + * gst-launch filesrc location=test.mp3 ! xingmux ! filesink location=test2.mp3 + * gst-launch filesrc location=test.mp3 ! mp3parse ! xingmux ! filesink location=test2.mp3 + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include "gstxingmux.h" + +GST_DEBUG_CATEGORY_STATIC (xing_mux_debug); +#define GST_CAT_DEFAULT xing_mux_debug + +#define gst_xing_mux_parent_class parent_class +G_DEFINE_TYPE (GstXingMux, gst_xing_mux, GST_TYPE_ELEMENT); + +/* Xing Header stuff */ +#define GST_XING_FRAME_FIELD (1 << 0) +#define GST_XING_BYTES_FIELD (1 << 1) +#define GST_XING_TOC_FIELD (1 << 2) +#define GST_XING_QUALITY_FIELD (1 << 3) + +typedef struct _GstXingSeekEntry +{ + gint64 timestamp; + gint byte; +} GstXingSeekEntry; + +static inline GstXingSeekEntry * +gst_xing_seek_entry_new (void) +{ + return g_slice_new (GstXingSeekEntry); +} + +static inline void +gst_xing_seek_entry_free (GstXingSeekEntry * entry) +{ + g_slice_free (GstXingSeekEntry, entry); +} + +static void gst_xing_mux_finalize (GObject * obj); +static GstStateChangeReturn +gst_xing_mux_change_state (GstElement * element, GstStateChange transition); +static GstFlowReturn gst_xing_mux_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static gboolean gst_xing_mux_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); + +static GstStaticPadTemplate gst_xing_mux_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " + "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ]")); + + +static GstStaticPadTemplate gst_xing_mux_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " + "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ]")); +static const guint mp3types_bitrates[2][3][16] = { + { + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,} + }, + { + {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,} + }, +}; + +static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000}, +{22050, 24000, 16000}, +{11025, 12000, 8000} +}; + +static gboolean +parse_header (guint32 header, guint * ret_size, guint * ret_spf, + gulong * ret_rate) +{ + guint length, spf; + gulong samplerate, bitrate, layer, padding; + gint lsf, mpg25; + + if ((header & 0xffe00000) != 0xffe00000) { + g_warning ("invalid sync"); + return FALSE; + } + + if (((header >> 19) & 3) == 0x01) { + g_warning ("invalid MPEG version"); + return FALSE; + } + + if (((header >> 17) & 3) == 0x00) { + g_warning ("invalid MPEG layer"); + return FALSE; + } + + if (((header >> 12) & 0xf) == 0xf || ((header >> 12) & 0xf) == 0x0) { + g_warning ("invalid bitrate"); + return FALSE; + } + + if (((header >> 10) & 0x3) == 0x3) { + g_warning ("invalid sampling rate"); + return FALSE; + } + + if (header & 0x00000002) { + g_warning ("invalid emphasis"); + return FALSE; + } + + if (header & (1 << 20)) { + lsf = (header & (1 << 19)) ? 0 : 1; + mpg25 = 0; + } else { + lsf = 1; + mpg25 = 1; + } + + layer = 4 - ((header >> 17) & 0x3); + + bitrate = (header >> 12) & 0xF; + bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000; + if (bitrate == 0) + return FALSE; + + samplerate = (header >> 10) & 0x3; + samplerate = mp3types_freqs[lsf + mpg25][samplerate]; + + padding = (header >> 9) & 0x1; + + switch (layer) { + case 1: + length = 4 * ((bitrate * 12) / samplerate + padding); + break; + case 2: + length = (bitrate * 144) / samplerate + padding; + break; + default: + case 3: + length = (bitrate * 144) / (samplerate << lsf) + padding; + break; + } + + if (layer == 1) + spf = 384; + else if (layer == 2 || lsf == 0) + spf = 1152; + else + spf = 576; + + if (ret_size) + *ret_size = length; + if (ret_spf) + *ret_spf = spf; + if (ret_rate) + *ret_rate = samplerate; + + return TRUE; +} + +static guint +get_xing_offset (guint32 header) +{ + guint mpeg_version = (header >> 19) & 0x3; + guint channel_mode = (header >> 6) & 0x3; + + if (mpeg_version == 0x3) { + if (channel_mode == 0x3) { + return 0x11; + } else { + return 0x20; + } + } else { + if (channel_mode == 0x3) { + return 0x09; + } else { + return 0x11; + } + } +} + +static gboolean +has_xing_header (guint32 header, GstBuffer * buffer, gsize size) +{ + gboolean ret; + GstMapInfo map; + guint8 *data; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + data += 4; + data += get_xing_offset (header); + + if (memcmp (data, "Xing", 4) == 0 || + memcmp (data, "Info", 4) == 0 || memcmp (data, "VBRI", 4) == 0) + ret = TRUE; + else + ret = FALSE; + + gst_buffer_unmap (buffer, &map); + return ret; +} + +static GstBuffer * +generate_xing_header (GstXingMux * xing) +{ + guint8 *xing_flags; + guint32 xing_flags_tmp = 0; + GstBuffer *xing_header; + GstMapInfo map; + guchar *data; + + guint32 header; + guint32 header_be; + guint size, spf, xing_offset; + gulong rate; + guint bitrate = 0x00; + + gint64 duration; + gint64 byte_count; + + header = xing->first_header; + + /* Set bitrate and choose lowest possible size */ + do { + bitrate++; + + header &= 0xffff0fff; + header |= bitrate << 12; + + parse_header (header, &size, &spf, &rate); + xing_offset = get_xing_offset (header); + } while (size < (4 + xing_offset + 4 + 4 + 4 + 4 + 100) && bitrate < 0xe); + + if (bitrate == 0xe) { + GST_ERROR ("No usable bitrate found!"); + return NULL; + } + + xing_header = gst_buffer_new_and_alloc (size); + + gst_buffer_map (xing_header, &map, GST_MAP_WRITE); + data = map.data; + memset (data, 0, size); + header_be = GUINT32_TO_BE (header); + memcpy (data, &header_be, 4); + + data += 4; + data += xing_offset; + + memcpy (data, "Xing", 4); + data += 4; + + xing_flags = data; + data += 4; + + if (xing->duration != GST_CLOCK_TIME_NONE) { + duration = xing->duration; + } else { + GstFormat fmt = GST_FORMAT_TIME; + + if (!gst_pad_peer_query_duration (xing->sinkpad, fmt, &duration)) + duration = GST_CLOCK_TIME_NONE; + } + + if (duration != GST_CLOCK_TIME_NONE) { + guint32 number_of_frames; + + /* The Xing Header contains a NumberOfFrames field, which verifies to: + * Duration = NumberOfFrames *SamplesPerFrame/SamplingRate + * SamplesPerFrame and SamplingRate are values for the current frame. + */ + number_of_frames = gst_util_uint64_scale (duration, rate, GST_SECOND) / spf; + number_of_frames += 1; /* Xing Header Frame */ + GST_DEBUG ("Setting number of frames to %u", number_of_frames); + number_of_frames = GUINT32_TO_BE (number_of_frames); + memcpy (data, &number_of_frames, 4); + xing_flags_tmp |= GST_XING_FRAME_FIELD; + data += 4; + } + + if (xing->byte_count != 0) { + byte_count = xing->byte_count; + } else { + GstFormat fmt = GST_FORMAT_BYTES; + + if (!gst_pad_peer_query_duration (xing->sinkpad, fmt, &byte_count)) + byte_count = 0; + if (byte_count == -1) + byte_count = 0; + } + + if (byte_count != 0) { + guint32 nbytes; + + if (byte_count > G_MAXUINT32) { + GST_DEBUG ("Too large stream: %" G_GINT64_FORMAT " > %u bytes", + byte_count, G_MAXUINT32); + } else { + nbytes = byte_count; + GST_DEBUG ("Setting number of bytes to %u", nbytes); + nbytes = GUINT32_TO_BE (nbytes); + memcpy (data, &nbytes, 4); + xing_flags_tmp |= GST_XING_BYTES_FIELD; + data += 4; + } + } + + if (xing->seek_table != NULL && byte_count != 0 + && duration != GST_CLOCK_TIME_NONE) { + GList *it; + gint percent = 0; + + xing_flags_tmp |= GST_XING_TOC_FIELD; + + GST_DEBUG ("Writing seek table"); + for (it = xing->seek_table; it != NULL && percent < 100; it = it->next) { + GstXingSeekEntry *entry = (GstXingSeekEntry *) it->data; + gint64 pos; + guchar byte; + + while ((entry->timestamp * 100) / duration >= percent) { + pos = (entry->byte * 256) / byte_count; + GST_DEBUG (" %d %% -- %" G_GINT64_FORMAT " 1/256", percent, pos); + byte = (guchar) pos; + memcpy (data, &byte, 1); + data++; + percent++; + } + } + + if (percent < 100) { + guchar b; + gint i; + + memcpy (&b, data - 1, 1); + + for (i = percent; i < 100; i++) { + GST_DEBUG (" %d %% -- %d 1/256", i, b); + memcpy (data, &b, 1); + data++; + } + } + } + + GST_DEBUG ("Setting Xing flags to 0x%x\n", xing_flags_tmp); + xing_flags_tmp = GUINT32_TO_BE (xing_flags_tmp); + memcpy (xing_flags, &xing_flags_tmp, 4); + gst_buffer_unmap (xing_header, &map); + return xing_header; +} + +static void +gst_xing_mux_class_init (GstXingMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_xing_mux_finalize); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_xing_mux_change_state); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_xing_mux_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_xing_mux_sink_template)); + + GST_DEBUG_CATEGORY_INIT (xing_mux_debug, "xingmux", 0, "Xing Header Muxer"); + + gst_element_class_set_static_metadata (gstelement_class, "MP3 Xing muxer", + "Formatter/Muxer/Metadata", + "Adds a Xing header to the beginning of a VBR MP3 file", + "Christophe Fergeau <teuf@gnome.org>"); +} + +static void +gst_xing_mux_finalize (GObject * obj) +{ + GstXingMux *xing = GST_XING_MUX (obj); + + if (xing->adapter) { + g_object_unref (xing->adapter); + xing->adapter = NULL; + } + + if (xing->seek_table) { + g_list_foreach (xing->seek_table, (GFunc) gst_xing_seek_entry_free, NULL); + g_list_free (xing->seek_table); + xing->seek_table = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +xing_reset (GstXingMux * xing) +{ + xing->duration = GST_CLOCK_TIME_NONE; + xing->byte_count = 0; + + gst_adapter_clear (xing->adapter); + + if (xing->seek_table) { + g_list_foreach (xing->seek_table, (GFunc) gst_xing_seek_entry_free, NULL); + g_list_free (xing->seek_table); + xing->seek_table = NULL; + } + + xing->sent_xing = FALSE; +} + + +static void +gst_xing_mux_init (GstXingMux * xing) +{ + /* pad through which data comes in to the element */ + xing->sinkpad = + gst_pad_new_from_static_template (&gst_xing_mux_sink_template, "sink"); + gst_pad_set_chain_function (xing->sinkpad, + GST_DEBUG_FUNCPTR (gst_xing_mux_chain)); + gst_pad_set_event_function (xing->sinkpad, + GST_DEBUG_FUNCPTR (gst_xing_mux_sink_event)); + GST_PAD_SET_PROXY_CAPS (xing->sinkpad); + gst_element_add_pad (GST_ELEMENT (xing), xing->sinkpad); + + /* pad through which data goes out of the element */ + xing->srcpad = + gst_pad_new_from_static_template (&gst_xing_mux_src_template, "src"); + gst_element_add_pad (GST_ELEMENT (xing), xing->srcpad); + + xing->adapter = gst_adapter_new (); + + xing_reset (xing); +} + +static GstFlowReturn +gst_xing_mux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstXingMux *xing = GST_XING_MUX (parent); + GstFlowReturn ret = GST_FLOW_OK; + + gst_adapter_push (xing->adapter, buffer); + + while (gst_adapter_available (xing->adapter) >= 4) { + const guchar *data; + guint32 header; + GstBuffer *outbuf; + GstClockTime duration; + guint size, spf; + gulong rate; + GstXingSeekEntry *seek_entry; + + data = gst_adapter_map (xing->adapter, 4); + header = GST_READ_UINT32_BE (data); + gst_adapter_unmap (xing->adapter); + + if (!parse_header (header, &size, &spf, &rate)) { + GST_DEBUG ("Lost sync, resyncing"); + gst_adapter_flush (xing->adapter, 1); + continue; + } + + if (gst_adapter_available (xing->adapter) < size) + break; + + outbuf = gst_adapter_take_buffer (xing->adapter, size); + + if (!xing->sent_xing) { + if (has_xing_header (header, outbuf, size)) { + GST_LOG_OBJECT (xing, "Dropping old Xing header"); + gst_buffer_unref (outbuf); + continue; + } else { + GstBuffer *xing_header; + guint64 xing_header_size; + + xing->first_header = header; + + xing_header = generate_xing_header (xing); + + if (xing_header == NULL) { + GST_ERROR ("Can't generate Xing header"); + gst_buffer_unref (outbuf); + return GST_FLOW_ERROR; + } + + xing_header_size = gst_buffer_get_size (xing_header); + + if ((ret = gst_pad_push (xing->srcpad, xing_header)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (xing, "Failed to push Xing header: %s", + gst_flow_get_name (ret)); + gst_buffer_unref (xing_header); + gst_buffer_unref (outbuf); + return ret; + } + + xing->byte_count += xing_header_size; + xing->sent_xing = TRUE; + } + } + + seek_entry = gst_xing_seek_entry_new (); + seek_entry->timestamp = + (xing->duration == GST_CLOCK_TIME_NONE) ? 0 : xing->duration; + /* Workaround for parsers checking that the first seek table entry is 0 */ + seek_entry->byte = (seek_entry->timestamp == 0) ? 0 : xing->byte_count; + xing->seek_table = g_list_append (xing->seek_table, seek_entry); + + duration = gst_util_uint64_scale_ceil (spf, GST_SECOND, rate); + + GST_BUFFER_TIMESTAMP (outbuf) = + (xing->duration == GST_CLOCK_TIME_NONE) ? 0 : xing->duration; + GST_BUFFER_DURATION (outbuf) = duration; + GST_BUFFER_OFFSET (outbuf) = xing->byte_count; + xing->byte_count += gst_buffer_get_size (outbuf); + GST_BUFFER_OFFSET_END (outbuf) = xing->byte_count; + + if (xing->duration == GST_CLOCK_TIME_NONE) + xing->duration = duration; + else + xing->duration += duration; + + if ((ret = gst_pad_push (xing->srcpad, outbuf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (xing, "Failed to push MP3 frame: %s", + gst_flow_get_name (ret)); + return ret; + } + } + + return ret; +} + +static gboolean +gst_xing_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstXingMux *xing; + gboolean result; + + xing = GST_XING_MUX (parent); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + if (xing->sent_xing) { + GST_ERROR ("Already sent Xing header, dropping NEWSEGMENT event!"); + gst_event_unref (event); + result = FALSE; + } else { + GstSegment segment; + + gst_event_copy_segment (event, &segment); + + if (segment.format == GST_FORMAT_BYTES) { + result = gst_pad_push_event (xing->srcpad, event); + } else { + + gst_event_unref (event); + gst_segment_init (&segment, GST_FORMAT_BYTES); + event = gst_event_new_segment (&segment); + + result = gst_pad_push_event (xing->srcpad, event); + } + } + break; + + case GST_EVENT_EOS:{ + GstEvent *n_event; + + GST_DEBUG_OBJECT (xing, "handling EOS event"); + + if (xing->sent_xing) { + GstSegment segment; + + gst_segment_init (&segment, GST_FORMAT_BYTES); + n_event = gst_event_new_segment (&segment); + + if (G_UNLIKELY (!gst_pad_push_event (xing->srcpad, n_event))) { + GST_WARNING + ("Failed to seek to position 0 for pushing the Xing header"); + } else { + GstBuffer *header; + GstFlowReturn ret; + + header = generate_xing_header (xing); + + if (header == NULL) { + GST_ERROR ("Can't generate Xing header"); + } else { + + GST_INFO ("Writing real Xing header to beginning of stream"); + + if ((ret = gst_pad_push (xing->srcpad, header)) != GST_FLOW_OK) + GST_WARNING ("Failed to push updated Xing header: %s\n", + gst_flow_get_name (ret)); + } + } + } + result = gst_pad_push_event (xing->srcpad, event); + break; + } + default: + result = gst_pad_event_default (pad, parent, event); + break; + } + + return result; +} + + +static GstStateChangeReturn +gst_xing_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstXingMux *xing; + GstStateChangeReturn result; + + xing = GST_XING_MUX (element); + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + xing_reset (xing); + break; + default: + break; + } + + return result; +} diff --git a/gst/xingmux/gstxingmux.h b/gst/xingmux/gstxingmux.h new file mode 100644 index 0000000..9f0c47a --- /dev/null +++ b/gst/xingmux/gstxingmux.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Christophe Fergeau <teuf@gnome.org> + * Copyright (c) 2008 Sebastian Dröge <slomo@circular-chaos.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +#ifndef __GST_XINGMUX_H__ +#define __GST_XINGMUX_H__ + +G_BEGIN_DECLS + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_XING_MUX \ + (gst_xing_mux_get_type()) +#define GST_XING_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_XING_MUX,GstXingMux)) +#define GST_XING_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_XING_MUX,GstXingMuxClass)) +#define GST_IS_XING_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_XING_MUX)) +#define GST_IS_XING_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_XING_MUX)) + +typedef struct _GstXingMux GstXingMux; +typedef struct _GstXingMuxClass GstXingMuxClass; + +/* Definition of structure storing data for this element. */ + +/** + * GstXingMux: + * + * Opaque data structure. + */ +struct _GstXingMux { + GstElement element; + + GstPad *sinkpad, *srcpad; + + /* < private > */ + + GstAdapter *adapter; + GstClockTime duration; + guint64 byte_count; + guint64 frame_count; + GList *seek_table; + gboolean sent_xing; + + /* Copy of the first frame header */ + guint32 first_header; +}; + +/* Standard definition defining a class for this element. */ + +/** + * GstXingMuxClass: + * + * Opaque data structure. + */ +struct _GstXingMuxClass { + GstElementClass parent_class; +}; + +/* Standard function returning type information. */ +GType gst_xing_mux_get_type (void); + +G_END_DECLS + +#endif /* __GST_XINGMUX_H__ */ diff --git a/gst/xingmux/plugin.c b/gst/xingmux/plugin.c new file mode 100644 index 0000000..470a24e --- /dev/null +++ b/gst/xingmux/plugin.c @@ -0,0 +1,41 @@ +/* GStreamer + * Copyright (C) <2008> Jan Schmidt <jan.schmidt@sun.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include "gstxingmux.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "xingmux", GST_RANK_MARGINAL, + GST_TYPE_XING_MUX)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + xingmux, + "Add XING tags to mpeg audio files", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); |