summaryrefslogtreecommitdiff
path: root/gst/asfdemux
diff options
context:
space:
mode:
Diffstat (limited to 'gst/asfdemux')
-rw-r--r--gst/asfdemux/Makefile.am27
-rw-r--r--gst/asfdemux/Makefile.in888
-rw-r--r--gst/asfdemux/README88
-rw-r--r--gst/asfdemux/asfheaders.c212
-rw-r--r--gst/asfdemux/asfheaders.h188
-rwxr-xr-xgst/asfdemux/asfpacket.c658
-rw-r--r--gst/asfdemux/asfpacket.h74
-rw-r--r--gst/asfdemux/gstasf.c72
-rw-r--r--gst/asfdemux/gstasfdemux.c4367
-rw-r--r--gst/asfdemux/gstasfdemux.h223
-rw-r--r--gst/asfdemux/gstrtpasfdepay.c545
-rw-r--r--gst/asfdemux/gstrtpasfdepay.h64
-rw-r--r--gst/asfdemux/gstrtspwms.c236
-rw-r--r--gst/asfdemux/gstrtspwms.h50
14 files changed, 7692 insertions, 0 deletions
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__ */