summaryrefslogtreecommitdiff
path: root/gst/multifile
diff options
context:
space:
mode:
Diffstat (limited to 'gst/multifile')
-rw-r--r--gst/multifile/Makefile.am13
-rw-r--r--gst/multifile/Makefile.in134
-rw-r--r--gst/multifile/gstmultifile.c3
-rw-r--r--gst/multifile/gstmultifilesink.c466
-rw-r--r--gst/multifile/gstmultifilesink.h24
-rw-r--r--gst/multifile/gstmultifilesrc.c71
-rw-r--r--gst/multifile/gstmultifilesrc.h4
-rw-r--r--gst/multifile/gstsplitfilesrc.c616
-rw-r--r--gst/multifile/gstsplitfilesrc.h74
-rw-r--r--gst/multifile/patternspec.c334
-rw-r--r--gst/multifile/patternspec.h47
11 files changed, 1644 insertions, 142 deletions
diff --git a/gst/multifile/Makefile.am b/gst/multifile/Makefile.am
index 52e33bc..3c9e316 100644
--- a/gst/multifile/Makefile.am
+++ b/gst/multifile/Makefile.am
@@ -1,13 +1,18 @@
plugin_LTLIBRARIES = libgstmultifile.la
-libgstmultifile_la_SOURCES = gstmultifilesink.c gstmultifilesrc.c gstmultifile.c
-libgstmultifile_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS)
-libgstmultifile_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
+libgstmultifile_la_SOURCES = \
+ gstmultifilesink.c \
+ gstmultifilesrc.c \
+ gstmultifile.c \
+ gstsplitfilesrc.c \
+ patternspec.c
+libgstmultifile_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS)
+libgstmultifile_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS)
libgstmultifile_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstmultifile_la_LIBTOOLFLAGS = --tag=disable-static
-noinst_HEADERS = gstmultifilesrc.h gstmultifilesink.h
+noinst_HEADERS = gstmultifilesrc.h gstmultifilesink.h gstsplitfilesrc.h patternspec.h
Android.mk: Makefile.am $(BUILT_SOURCES)
diff --git a/gst/multifile/Makefile.in b/gst/multifile/Makefile.in
index 84e0303..60d4d58 100644
--- a/gst/multifile/Makefile.in
+++ b/gst/multifile/Makefile.in
@@ -1,9 +1,9 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -67,8 +67,7 @@ am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \
$(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \
$(top_srcdir)/m4/aalib.m4 $(top_srcdir)/m4/esd.m4 \
$(top_srcdir)/m4/gconf-2.m4 $(top_srcdir)/m4/gettext.m4 \
- $(top_srcdir)/m4/gst-fionread.m4 \
- $(top_srcdir)/m4/gst-shout2.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/gst-fionread.m4 $(top_srcdir)/m4/iconv.m4 \
$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -103,18 +102,27 @@ am__nobase_list = $(am__nobase_strip_setup); \
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 =
libgstmultifile_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
am_libgstmultifile_la_OBJECTS = \
libgstmultifile_la-gstmultifilesink.lo \
libgstmultifile_la-gstmultifilesrc.lo \
- libgstmultifile_la-gstmultifile.lo
+ libgstmultifile_la-gstmultifile.lo \
+ libgstmultifile_la-gstsplitfilesrc.lo \
+ libgstmultifile_la-patternspec.lo
libgstmultifile_la_OBJECTS = $(am_libgstmultifile_la_OBJECTS)
-AM_V_lt = $(am__v_lt_$(V))
-am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
libgstmultifile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
@@ -130,21 +138,21 @@ LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_$(V))
-am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_$(V))
-am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
SOURCES = $(libgstmultifile_la_SOURCES)
DIST_SOURCES = $(libgstmultifile_la_SOURCES)
@@ -227,7 +235,10 @@ GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@
GLIB_LIBS = @GLIB_LIBS@
GLIB_PREFIX = @GLIB_PREFIX@
GLIB_REQ = @GLIB_REQ@
@@ -302,7 +313,6 @@ JPEG_LIBS = @JPEG_LIBS@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBCACA_CFLAGS = @LIBCACA_CFLAGS@
-LIBCACA_CONFIG = @LIBCACA_CONFIG@
LIBCACA_LIBS = @LIBCACA_LIBS@
LIBDV_CFLAGS = @LIBDV_CFLAGS@
LIBDV_LIBS = @LIBDV_LIBS@
@@ -326,6 +336,7 @@ LTLIBINTL = @LTLIBINTL@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MSGFMT = @MSGFMT@
MSGFMT_015 = @MSGFMT_015@
@@ -360,18 +371,10 @@ PKG_CONFIG = @PKG_CONFIG@
PLUGINDIR = @PLUGINDIR@
POSUB = @POSUB@
PROFILE_CFLAGS = @PROFILE_CFLAGS@
-PULSE_0_9_11_CFLAGS = @PULSE_0_9_11_CFLAGS@
-PULSE_0_9_11_LIBS = @PULSE_0_9_11_LIBS@
-PULSE_0_9_12_CFLAGS = @PULSE_0_9_12_CFLAGS@
-PULSE_0_9_12_LIBS = @PULSE_0_9_12_LIBS@
-PULSE_0_9_13_CFLAGS = @PULSE_0_9_13_CFLAGS@
-PULSE_0_9_13_LIBS = @PULSE_0_9_13_LIBS@
-PULSE_0_9_15_CFLAGS = @PULSE_0_9_15_CFLAGS@
-PULSE_0_9_15_LIBS = @PULSE_0_9_15_LIBS@
-PULSE_0_9_16_CFLAGS = @PULSE_0_9_16_CFLAGS@
-PULSE_0_9_16_LIBS = @PULSE_0_9_16_LIBS@
PULSE_0_9_20_CFLAGS = @PULSE_0_9_20_CFLAGS@
PULSE_0_9_20_LIBS = @PULSE_0_9_20_LIBS@
+PULSE_1_0_CFLAGS = @PULSE_1_0_CFLAGS@
+PULSE_1_0_LIBS = @PULSE_1_0_LIBS@
PULSE_CFLAGS = @PULSE_CFLAGS@
PULSE_LIBS = @PULSE_LIBS@
PYTHON = @PYTHON@
@@ -424,6 +427,7 @@ 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@
@@ -458,7 +462,6 @@ libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
-lt_ECHO = @lt_ECHO@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
@@ -480,12 +483,18 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
plugin_LTLIBRARIES = libgstmultifile.la
-libgstmultifile_la_SOURCES = gstmultifilesink.c gstmultifilesrc.c gstmultifile.c
-libgstmultifile_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS)
-libgstmultifile_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
+libgstmultifile_la_SOURCES = \
+ gstmultifilesink.c \
+ gstmultifilesrc.c \
+ gstmultifile.c \
+ gstsplitfilesrc.c \
+ patternspec.c
+
+libgstmultifile_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS)
+libgstmultifile_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS)
libgstmultifile_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstmultifile_la_LIBTOOLFLAGS = --tag=disable-static
-noinst_HEADERS = gstmultifilesrc.h gstmultifilesink.h
+noinst_HEADERS = gstmultifilesrc.h gstmultifilesink.h gstsplitfilesrc.h patternspec.h
all: all-am
.SUFFIXES:
@@ -551,7 +560,7 @@ clean-pluginLTLIBRARIES:
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
-libgstmultifile.la: $(libgstmultifile_la_OBJECTS) $(libgstmultifile_la_DEPENDENCIES)
+libgstmultifile.la: $(libgstmultifile_la_OBJECTS) $(libgstmultifile_la_DEPENDENCIES) $(EXTRA_libgstmultifile_la_DEPENDENCIES)
$(AM_V_CCLD)$(libgstmultifile_la_LINK) -rpath $(plugindir) $(libgstmultifile_la_OBJECTS) $(libgstmultifile_la_LIBADD) $(LIBS)
mostlyclean-compile:
@@ -563,54 +572,64 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstmultifile_la-gstmultifile.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstmultifile_la-gstmultifilesink.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstmultifile_la-gstmultifilesrc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstmultifile_la-gstsplitfilesrc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstmultifile_la-patternspec.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@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@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@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@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libgstmultifile_la-gstmultifilesink.lo: gstmultifilesink.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -MT libgstmultifile_la-gstmultifilesink.lo -MD -MP -MF $(DEPDIR)/libgstmultifile_la-gstmultifilesink.Tpo -c -o libgstmultifile_la-gstmultifilesink.lo `test -f 'gstmultifilesink.c' || echo '$(srcdir)/'`gstmultifilesink.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstmultifile_la-gstmultifilesink.Tpo $(DEPDIR)/libgstmultifile_la-gstmultifilesink.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmultifilesink.c' object='libgstmultifile_la-gstmultifilesink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstmultifilesink.c' object='libgstmultifile_la-gstmultifilesink.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifilesink.lo `test -f 'gstmultifilesink.c' || echo '$(srcdir)/'`gstmultifilesink.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifilesink.lo `test -f 'gstmultifilesink.c' || echo '$(srcdir)/'`gstmultifilesink.c
libgstmultifile_la-gstmultifilesrc.lo: gstmultifilesrc.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -MT libgstmultifile_la-gstmultifilesrc.lo -MD -MP -MF $(DEPDIR)/libgstmultifile_la-gstmultifilesrc.Tpo -c -o libgstmultifile_la-gstmultifilesrc.lo `test -f 'gstmultifilesrc.c' || echo '$(srcdir)/'`gstmultifilesrc.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstmultifile_la-gstmultifilesrc.Tpo $(DEPDIR)/libgstmultifile_la-gstmultifilesrc.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmultifilesrc.c' object='libgstmultifile_la-gstmultifilesrc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstmultifilesrc.c' object='libgstmultifile_la-gstmultifilesrc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifilesrc.lo `test -f 'gstmultifilesrc.c' || echo '$(srcdir)/'`gstmultifilesrc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifilesrc.lo `test -f 'gstmultifilesrc.c' || echo '$(srcdir)/'`gstmultifilesrc.c
libgstmultifile_la-gstmultifile.lo: gstmultifile.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -MT libgstmultifile_la-gstmultifile.lo -MD -MP -MF $(DEPDIR)/libgstmultifile_la-gstmultifile.Tpo -c -o libgstmultifile_la-gstmultifile.lo `test -f 'gstmultifile.c' || echo '$(srcdir)/'`gstmultifile.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstmultifile_la-gstmultifile.Tpo $(DEPDIR)/libgstmultifile_la-gstmultifile.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gstmultifile.c' object='libgstmultifile_la-gstmultifile.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstmultifile.c' object='libgstmultifile_la-gstmultifile.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifile.lo `test -f 'gstmultifile.c' || echo '$(srcdir)/'`gstmultifile.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstmultifile.lo `test -f 'gstmultifile.c' || echo '$(srcdir)/'`gstmultifile.c
+
+libgstmultifile_la-gstsplitfilesrc.lo: gstsplitfilesrc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -MT libgstmultifile_la-gstsplitfilesrc.lo -MD -MP -MF $(DEPDIR)/libgstmultifile_la-gstsplitfilesrc.Tpo -c -o libgstmultifile_la-gstsplitfilesrc.lo `test -f 'gstsplitfilesrc.c' || echo '$(srcdir)/'`gstsplitfilesrc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstmultifile_la-gstsplitfilesrc.Tpo $(DEPDIR)/libgstmultifile_la-gstsplitfilesrc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstsplitfilesrc.c' object='libgstmultifile_la-gstsplitfilesrc.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 $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-gstsplitfilesrc.lo `test -f 'gstsplitfilesrc.c' || echo '$(srcdir)/'`gstsplitfilesrc.c
+
+libgstmultifile_la-patternspec.lo: patternspec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -MT libgstmultifile_la-patternspec.lo -MD -MP -MF $(DEPDIR)/libgstmultifile_la-patternspec.Tpo -c -o libgstmultifile_la-patternspec.lo `test -f 'patternspec.c' || echo '$(srcdir)/'`patternspec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstmultifile_la-patternspec.Tpo $(DEPDIR)/libgstmultifile_la-patternspec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='patternspec.c' object='libgstmultifile_la-patternspec.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 $(libgstmultifile_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstmultifile_la_CFLAGS) $(CFLAGS) -c -o libgstmultifile_la-patternspec.lo `test -f 'patternspec.c' || echo '$(srcdir)/'`patternspec.c
mostlyclean-libtool:
-rm -f *.lo
@@ -717,10 +736,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ 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:
diff --git a/gst/multifile/gstmultifile.c b/gst/multifile/gstmultifile.c
index d2ba57b..7441103 100644
--- a/gst/multifile/gstmultifile.c
+++ b/gst/multifile/gstmultifile.c
@@ -30,6 +30,7 @@
#include "gstmultifilesink.h"
#include "gstmultifilesrc.h"
+#include "gstsplitfilesrc.h"
static gboolean
plugin_init (GstPlugin * plugin)
@@ -38,6 +39,8 @@ plugin_init (GstPlugin * plugin)
gst_multi_file_src_get_type ());
gst_element_register (plugin, "multifilesink", GST_RANK_NONE,
gst_multi_file_sink_get_type ());
+ gst_element_register (plugin, "splitfilesrc", GST_RANK_NONE,
+ gst_split_file_src_get_type ());
return TRUE;
}
diff --git a/gst/multifile/gstmultifilesink.c b/gst/multifile/gstmultifilesink.c
index b333e80..2be3b56 100644
--- a/gst/multifile/gstmultifilesink.c
+++ b/gst/multifile/gstmultifilesink.c
@@ -3,6 +3,7 @@
* 2000 Wim Taymans <wtay@chello.be>
* 2006 Wim Taymans <wim@fluendo.com>
* 2006 David A. Schleef <ds@schleef.org>
+ * 2011 Collabora Ltd. <tim.muller@collabora.co.uk>
*
* gstmultifilesink.c:
*
@@ -110,6 +111,7 @@
# include "config.h"
#endif
#include <gst/base/gstbasetransform.h>
+#include <gst/video/video.h>
#include <glib/gstdio.h>
#include "gstmultifilesink.h"
@@ -125,6 +127,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_multi_file_sink_debug);
#define DEFAULT_INDEX 0
#define DEFAULT_POST_MESSAGES FALSE
#define DEFAULT_NEXT_FILE GST_MULTI_FILE_SINK_NEXT_BUFFER
+#define DEFAULT_MAX_FILES 0
+#define DEFAULT_MAX_FILE_SIZE G_GUINT64_CONSTANT(2*1024*1024*1024)
enum
{
@@ -133,6 +137,8 @@ enum
PROP_INDEX,
PROP_POST_MESSAGES,
PROP_NEXT_FILE,
+ PROP_MAX_FILES,
+ PROP_MAX_FILE_SIZE,
PROP_LAST
};
@@ -146,8 +152,18 @@ static void gst_multi_file_sink_get_property (GObject * object, guint prop_id,
static gboolean gst_multi_file_sink_stop (GstBaseSink * sink);
static GstFlowReturn gst_multi_file_sink_render (GstBaseSink * sink,
GstBuffer * buffer);
+static GstFlowReturn gst_multi_file_sink_render_list (GstBaseSink * sink,
+ GstBufferList * buffer_list);
static gboolean gst_multi_file_sink_set_caps (GstBaseSink * sink,
GstCaps * caps);
+static gboolean gst_multi_file_sink_open_next_file (GstMultiFileSink *
+ multifilesink);
+static void gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
+ GstBuffer * buffer);
+static void gst_multi_file_sink_ensure_max_files (GstMultiFileSink *
+ multifilesink);
+static gboolean gst_multi_file_sink_event (GstBaseSink * sink,
+ GstEvent * event);
#define GST_TYPE_MULTI_FILE_SINK_NEXT (gst_multi_file_sink_next_get_type ())
static GType
@@ -160,6 +176,11 @@ gst_multi_file_sink_next_get_type (void)
"discont"},
{GST_MULTI_FILE_SINK_NEXT_KEY_FRAME, "New file at each key frame "
"(Useful for MPEG-TS segmenting)", "key-frame"},
+ {GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
+ "New file after a force key unit event", "key-unit-event"},
+ {GST_MULTI_FILE_SINK_NEXT_MAX_SIZE, "New file when the configured maximum "
+ "file size would be exceeded with the next buffer or buffer list",
+ "max-size"},
{0, NULL, NULL}
};
@@ -182,8 +203,7 @@ gst_multi_file_sink_base_init (gpointer g_class)
GST_DEBUG_CATEGORY_INIT (gst_multi_file_sink_debug, "multifilesink", 0,
"multifilesink element");
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sinktemplate));
+ gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
gst_element_class_set_details_simple (gstelement_class, "Multi-File Sink",
"Sink/File",
"Write buffers to a sequentially named set of files",
@@ -234,13 +254,45 @@ gst_multi_file_sink_class_init (GstMultiFileSinkClass * klass)
GST_TYPE_MULTI_FILE_SINK_NEXT, DEFAULT_NEXT_FILE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstMultiFileSink:max-files
+ *
+ * Maximum number of files to keep on disk. Once the maximum is reached, old
+ * files start to be deleted to make room for new ones.
+ *
+ * Since: 0.10.31
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_FILES,
+ g_param_spec_uint ("max-files", "Max files",
+ "Maximum number of files to keep on disk. Once the maximum is reached,"
+ "old files start to be deleted to make room for new ones.",
+ 0, G_MAXUINT, DEFAULT_MAX_FILES,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstMultiFileSink:max-file-size
+ *
+ * Maximum file size before starting a new file in max-size mode.
+ *
+ * Since: 0.10.31
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_FILE_SIZE,
+ g_param_spec_uint64 ("max-file-size", "Maximum File Size",
+ "Maximum file size before starting a new file in max-size mode",
+ 0, G_MAXUINT64, DEFAULT_MAX_FILE_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
gobject_class->finalize = gst_multi_file_sink_finalize;
gstbasesink_class->get_times = NULL;
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_multi_file_sink_stop);
gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_file_sink_render);
+ gstbasesink_class->render_list =
+ GST_DEBUG_FUNCPTR (gst_multi_file_sink_render_list);
gstbasesink_class->set_caps =
GST_DEBUG_FUNCPTR (gst_multi_file_sink_set_caps);
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_multi_file_sink_event);
}
static void
@@ -250,10 +302,15 @@ gst_multi_file_sink_init (GstMultiFileSink * multifilesink,
multifilesink->filename = g_strdup (DEFAULT_LOCATION);
multifilesink->index = DEFAULT_INDEX;
multifilesink->post_messages = DEFAULT_POST_MESSAGES;
+ multifilesink->max_files = DEFAULT_MAX_FILES;
+ multifilesink->max_file_size = DEFAULT_MAX_FILE_SIZE;
+ multifilesink->files = NULL;
+ multifilesink->n_files = 0;
gst_base_sink_set_sync (GST_BASE_SINK (multifilesink), FALSE);
multifilesink->next_segment = GST_CLOCK_TIME_NONE;
+ multifilesink->force_key_unit_count = -1;
}
static void
@@ -262,6 +319,8 @@ gst_multi_file_sink_finalize (GObject * object)
GstMultiFileSink *sink = GST_MULTI_FILE_SINK (object);
g_free (sink->filename);
+ g_slist_foreach (sink->files, (GFunc) g_free, NULL);
+ g_slist_free (sink->files);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -296,6 +355,12 @@ gst_multi_file_sink_set_property (GObject * object, guint prop_id,
case PROP_NEXT_FILE:
sink->next_file = g_value_get_enum (value);
break;
+ case PROP_MAX_FILES:
+ sink->max_files = g_value_get_uint (value);
+ break;
+ case PROP_MAX_FILE_SIZE:
+ sink->max_file_size = g_value_get_uint64 (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -321,6 +386,12 @@ gst_multi_file_sink_get_property (GObject * object, guint prop_id,
case PROP_NEXT_FILE:
g_value_set_enum (value, sink->next_file);
break;
+ case PROP_MAX_FILES:
+ g_value_set_uint (value, sink->max_files);
+ break;
+ case PROP_MAX_FILE_SIZE:
+ g_value_set_uint64 (value, sink->max_file_size);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -345,48 +416,95 @@ gst_multi_file_sink_stop (GstBaseSink * sink)
gst_buffer_unref (multifilesink->streamheaders[i]);
}
g_free (multifilesink->streamheaders);
+ multifilesink->streamheaders = NULL;
}
+ multifilesink->force_key_unit_count = -1;
+
return TRUE;
}
static void
+gst_multi_file_sink_post_message_full (GstMultiFileSink * multifilesink,
+ GstClockTime timestamp, GstClockTime duration, GstClockTime offset,
+ GstClockTime offset_end, GstClockTime running_time,
+ GstClockTime stream_time, const char *filename)
+{
+ GstStructure *s;
+
+ if (!multifilesink->post_messages)
+ return;
+
+ s = gst_structure_new ("GstMultiFileSink",
+ "filename", G_TYPE_STRING, filename,
+ "index", G_TYPE_INT, multifilesink->index,
+ "timestamp", G_TYPE_UINT64, timestamp,
+ "stream-time", G_TYPE_UINT64, stream_time,
+ "running-time", G_TYPE_UINT64, running_time,
+ "duration", G_TYPE_UINT64, duration,
+ "offset", G_TYPE_UINT64, offset,
+ "offset-end", G_TYPE_UINT64, offset_end, NULL);
+
+ gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
+ gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
+}
+
+
+static void
gst_multi_file_sink_post_message (GstMultiFileSink * multifilesink,
GstBuffer * buffer, const char *filename)
{
- if (multifilesink->post_messages) {
- GstClockTime duration, timestamp;
- GstClockTime running_time, stream_time;
- guint64 offset, offset_end;
- GstStructure *s;
- GstSegment *segment;
- GstFormat format;
-
- segment = &GST_BASE_SINK (multifilesink)->segment;
- format = segment->format;
-
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- duration = GST_BUFFER_DURATION (buffer);
- offset = GST_BUFFER_OFFSET (buffer);
- offset_end = GST_BUFFER_OFFSET_END (buffer);
-
- running_time = gst_segment_to_running_time (segment, format, timestamp);
- stream_time = gst_segment_to_stream_time (segment, format, timestamp);
-
- s = gst_structure_new ("GstMultiFileSink",
- "filename", G_TYPE_STRING, filename,
- "index", G_TYPE_INT, multifilesink->index,
- "timestamp", G_TYPE_UINT64, timestamp,
- "stream-time", G_TYPE_UINT64, stream_time,
- "running-time", G_TYPE_UINT64, running_time,
- "duration", G_TYPE_UINT64, duration,
- "offset", G_TYPE_UINT64, offset,
- "offset-end", G_TYPE_UINT64, offset_end, NULL);
-
- gst_element_post_message (GST_ELEMENT_CAST (multifilesink),
- gst_message_new_element (GST_OBJECT_CAST (multifilesink), s));
+ GstClockTime duration, timestamp;
+ GstClockTime running_time, stream_time;
+ guint64 offset, offset_end;
+ GstSegment *segment;
+ GstFormat format;
+
+ if (!multifilesink->post_messages)
+ return;
+
+ segment = &GST_BASE_SINK (multifilesink)->segment;
+ format = segment->format;
+
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ duration = GST_BUFFER_DURATION (buffer);
+ offset = GST_BUFFER_OFFSET (buffer);
+ offset_end = GST_BUFFER_OFFSET_END (buffer);
+
+ running_time = gst_segment_to_running_time (segment, format, timestamp);
+ stream_time = gst_segment_to_stream_time (segment, format, timestamp);
+
+ gst_multi_file_sink_post_message_full (multifilesink, timestamp, duration,
+ offset, offset_end, running_time, stream_time, filename);
+}
+
+static gboolean
+gst_multi_file_sink_write_stream_headers (GstMultiFileSink * sink)
+{
+ int i;
+
+ if (sink->streamheaders == NULL)
+ return TRUE;
+
+ /* we want to write these at the beginning */
+ g_assert (sink->cur_file_size == 0);
+
+ for (i = 0; i < sink->n_streamheaders; i++) {
+ GstBuffer *hdr;
+ int ret;
+
+ hdr = sink->streamheaders[i];
+
+ ret = fwrite (GST_BUFFER_DATA (hdr), GST_BUFFER_SIZE (hdr), 1, sink->file);
+
+ if (ret != 1)
+ return FALSE;
+
+ sink->cur_file_size += GST_BUFFER_SIZE (hdr);
}
+
+ return TRUE;
}
static GstFlowReturn
@@ -406,39 +524,29 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
switch (multifilesink->next_file) {
case GST_MULTI_FILE_SINK_NEXT_BUFFER:
+ gst_multi_file_sink_ensure_max_files (multifilesink);
+
filename = g_strdup_printf (multifilesink->filename,
multifilesink->index);
-
ret = g_file_set_contents (filename, (char *) data, size, &error);
if (!ret)
goto write_error;
+ multifilesink->files = g_slist_append (multifilesink->files, filename);
+ multifilesink->n_files += 1;
+
gst_multi_file_sink_post_message (multifilesink, buffer, filename);
multifilesink->index++;
- g_free (filename);
break;
case GST_MULTI_FILE_SINK_NEXT_DISCONT:
if (GST_BUFFER_IS_DISCONT (buffer)) {
- if (multifilesink->file) {
- fclose (multifilesink->file);
- multifilesink->file = NULL;
-
- filename = g_strdup_printf (multifilesink->filename,
- multifilesink->index);
- gst_multi_file_sink_post_message (multifilesink, buffer, filename);
- g_free (filename);
- multifilesink->index++;
- }
+ if (multifilesink->file)
+ gst_multi_file_sink_close_file (multifilesink, buffer);
}
if (multifilesink->file == NULL) {
- filename = g_strdup_printf (multifilesink->filename,
- multifilesink->index);
- multifilesink->file = g_fopen (filename, "wb");
- g_free (filename);
-
- if (multifilesink->file == NULL)
+ if (!gst_multi_file_sink_open_next_file (multifilesink))
goto stdio_write_error;
}
@@ -459,48 +567,68 @@ gst_multi_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
GST_BUFFER_TIMESTAMP (buffer) >= multifilesink->next_segment &&
!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
- if (multifilesink->file) {
- fclose (multifilesink->file);
- multifilesink->file = NULL;
-
- filename = g_strdup_printf (multifilesink->filename,
- multifilesink->index);
- gst_multi_file_sink_post_message (multifilesink, buffer, filename);
- g_free (filename);
- multifilesink->index++;
- }
+ if (multifilesink->file)
+ gst_multi_file_sink_close_file (multifilesink, buffer);
multifilesink->next_segment += 10 * GST_SECOND;
}
if (multifilesink->file == NULL) {
- int i;
+ if (!gst_multi_file_sink_open_next_file (multifilesink))
+ goto stdio_write_error;
- filename = g_strdup_printf (multifilesink->filename,
- multifilesink->index);
- multifilesink->file = g_fopen (filename, "wb");
- g_free (filename);
+ gst_multi_file_sink_write_stream_headers (multifilesink);
+ }
- if (multifilesink->file == NULL)
+ ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
+ multifilesink->file);
+ if (ret != 1)
+ goto stdio_write_error;
+
+ break;
+ case GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT:
+ if (multifilesink->file == NULL) {
+ if (!gst_multi_file_sink_open_next_file (multifilesink))
goto stdio_write_error;
+ }
- if (multifilesink->streamheaders) {
- for (i = 0; i < multifilesink->n_streamheaders; i++) {
- ret = fwrite (GST_BUFFER_DATA (multifilesink->streamheaders[i]),
- GST_BUFFER_SIZE (multifilesink->streamheaders[i]), 1,
- multifilesink->file);
- if (ret != 1)
- goto stdio_write_error;
- }
- }
+ ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
+ multifilesink->file);
+ if (ret != 1)
+ goto stdio_write_error;
+
+ break;
+ case GST_MULTI_FILE_SINK_NEXT_MAX_SIZE:{
+ guint64 new_size;
+
+ new_size = multifilesink->cur_file_size + GST_BUFFER_SIZE (buffer);
+ if (new_size > multifilesink->max_file_size) {
+
+ GST_INFO_OBJECT (multifilesink, "current size: %" G_GUINT64_FORMAT
+ ", new_size: %" G_GUINT64_FORMAT ", max. size %" G_GUINT64_FORMAT,
+ multifilesink->cur_file_size, new_size,
+ multifilesink->max_file_size);
+
+ if (multifilesink->file != NULL)
+ gst_multi_file_sink_close_file (multifilesink, NULL);
+ }
+
+ if (multifilesink->file == NULL) {
+ if (!gst_multi_file_sink_open_next_file (multifilesink))
+ goto stdio_write_error;
+
+ gst_multi_file_sink_write_stream_headers (multifilesink);
}
ret = fwrite (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 1,
multifilesink->file);
+
if (ret != 1)
goto stdio_write_error;
+ multifilesink->cur_file_size += GST_BUFFER_SIZE (buffer);
break;
+ }
default:
g_assert_not_reached ();
}
@@ -528,11 +656,71 @@ write_error:
return GST_FLOW_ERROR;
}
stdio_write_error:
- GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
- ("Error while writing to file."), (NULL));
+ switch (errno) {
+ case ENOSPC:
+ GST_ELEMENT_ERROR (multifilesink, RESOURCE, NO_SPACE_LEFT,
+ ("Error while writing to file."), ("%s", g_strerror (errno)));
+ break;
+ default:
+ GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
+ ("Error while writing to file."), ("%s", g_strerror (errno)));
+ }
return GST_FLOW_ERROR;
}
+static GstBufferListItem
+buffer_list_calc_size (GstBuffer ** buf, guint group, guint idx, gpointer data)
+{
+ guint *p_size = data;
+ guint buf_size;
+
+ buf_size = GST_BUFFER_SIZE (*buf);
+ GST_TRACE ("buffer %u in group %u has size %u", idx, group, buf_size);
+ *p_size += buf_size;
+
+ return GST_BUFFER_LIST_CONTINUE;
+}
+
+static GstBufferListItem
+buffer_list_copy_data (GstBuffer ** buf, guint group, guint idx, gpointer data)
+{
+ GstBuffer *dest = data;
+
+ if (group == 0 && idx == 0)
+ gst_buffer_copy_metadata (dest, *buf, GST_BUFFER_COPY_ALL);
+
+ memcpy (GST_BUFFER_DATA (dest) + GST_BUFFER_SIZE (dest),
+ GST_BUFFER_DATA (*buf), GST_BUFFER_SIZE (*buf));
+ GST_BUFFER_SIZE (dest) += GST_BUFFER_SIZE (*buf);
+
+ return GST_BUFFER_LIST_CONTINUE;
+}
+
+/* Our assumption for now is that the buffers in a buffer list should always
+ * end up in the same file. If someone wants different behaviour, they'll just
+ * have to add a property for that. */
+static GstFlowReturn
+gst_multi_file_sink_render_list (GstBaseSink * sink, GstBufferList * list)
+{
+ GstBuffer *buf;
+ guint size;
+
+ gst_buffer_list_foreach (list, buffer_list_calc_size, &size);
+ GST_LOG_OBJECT (sink, "total size of buffer list %p: %u", list, size);
+
+ /* copy all buffers in the list into one single buffer, so we can use
+ * the normal render function (FIXME: optimise to avoid the memcpy) */
+ buf = gst_buffer_new_and_alloc (size);
+ GST_BUFFER_SIZE (buf) = 0;
+ gst_buffer_list_foreach (list, buffer_list_copy_data, buf);
+ g_assert (GST_BUFFER_SIZE (buf) == size);
+
+ gst_multi_file_sink_render (sink, buf);
+ gst_buffer_unref (buf);
+
+ return GST_FLOW_OK;
+}
+
static gboolean
gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
{
@@ -571,3 +759,125 @@ gst_multi_file_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
return TRUE;
}
+
+static void
+gst_multi_file_sink_ensure_max_files (GstMultiFileSink * multifilesink)
+{
+ char *filename;
+
+ while (multifilesink->max_files &&
+ multifilesink->n_files >= multifilesink->max_files) {
+ filename = multifilesink->files->data;
+ g_remove (filename);
+ g_free (filename);
+ multifilesink->files = g_slist_delete_link (multifilesink->files,
+ multifilesink->files);
+ multifilesink->n_files -= 1;
+ }
+}
+
+static gboolean
+gst_multi_file_sink_event (GstBaseSink * sink, GstEvent * event)
+{
+ GstMultiFileSink *multifilesink;
+ gchar *filename;
+ gboolean res = TRUE;
+
+ multifilesink = GST_MULTI_FILE_SINK (sink);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CUSTOM_DOWNSTREAM:
+ {
+ GstClockTime timestamp, duration;
+ GstClockTime running_time, stream_time;
+ guint64 offset, offset_end;
+ gboolean all_headers;
+ guint count;
+
+ if (multifilesink->next_file != GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT ||
+ !gst_video_event_is_force_key_unit (event))
+ goto out;
+
+ gst_video_event_parse_downstream_force_key_unit (event, &timestamp,
+ &stream_time, &running_time, &all_headers, &count);
+
+ if (multifilesink->force_key_unit_count != -1 &&
+ multifilesink->force_key_unit_count == count)
+ goto out;
+
+ multifilesink->force_key_unit_count = count;
+
+ if (multifilesink->file) {
+ duration = GST_CLOCK_TIME_NONE;
+ offset = offset_end = -1;
+ filename = g_strdup_printf (multifilesink->filename,
+ multifilesink->index);
+ gst_multi_file_sink_post_message_full (multifilesink, timestamp,
+ duration, offset, offset_end, running_time, stream_time, filename);
+
+ g_free (filename);
+
+ gst_multi_file_sink_close_file (multifilesink, NULL);
+
+ }
+
+ if (multifilesink->file == NULL) {
+ if (!gst_multi_file_sink_open_next_file (multifilesink))
+ goto stdio_write_error;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+out:
+ return res;
+
+stdio_write_error:
+ GST_ELEMENT_ERROR (multifilesink, RESOURCE, WRITE,
+ ("Error while writing to file."), (NULL));
+ return FALSE;
+}
+
+static gboolean
+gst_multi_file_sink_open_next_file (GstMultiFileSink * multifilesink)
+{
+ char *filename;
+
+ g_return_val_if_fail (multifilesink->file == NULL, FALSE);
+
+ gst_multi_file_sink_ensure_max_files (multifilesink);
+ filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
+ multifilesink->file = g_fopen (filename, "wb");
+ if (multifilesink->file == NULL) {
+ g_free (filename);
+ return FALSE;
+ }
+
+ GST_INFO_OBJECT (multifilesink, "opening file %s", filename);
+ multifilesink->files = g_slist_append (multifilesink->files, filename);
+ multifilesink->n_files += 1;
+
+ multifilesink->cur_file_size = 0;
+ return TRUE;
+}
+
+static void
+gst_multi_file_sink_close_file (GstMultiFileSink * multifilesink,
+ GstBuffer * buffer)
+{
+ char *filename;
+
+ fclose (multifilesink->file);
+ multifilesink->file = NULL;
+
+ if (buffer) {
+ filename = g_strdup_printf (multifilesink->filename, multifilesink->index);
+ gst_multi_file_sink_post_message (multifilesink, buffer, filename);
+ g_free (filename);
+ }
+
+ multifilesink->index++;
+}
diff --git a/gst/multifile/gstmultifilesink.h b/gst/multifile/gstmultifilesink.h
index c551570..dd234d6 100644
--- a/gst/multifile/gstmultifilesink.h
+++ b/gst/multifile/gstmultifilesink.h
@@ -52,10 +52,25 @@ G_BEGIN_DECLS
typedef struct _GstMultiFileSink GstMultiFileSink;
typedef struct _GstMultiFileSinkClass GstMultiFileSinkClass;
+/**
+ * GstMultiFileSinkNext:
+ * @GST_MULTI_FILE_SINK_NEXT_BUFFER: New file for each buffer
+ * @GST_MULTI_FILE_SINK_NEXT_DISCONT: New file after each discontinuity
+ * @GST_MULTI_FILE_SINK_NEXT_KEY_FRAME: New file at each key frame
+ * (Useful for MPEG-TS segmenting)
+ * @GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT: New file after a force key unit
+ * event (Since: 0.10.31)
+ * @GST_MULTI_FILE_SINK_NEXT_MAX_SIZE: New file when the configured maximum file
+ * size would be exceeded with the next buffer or buffer list (Since: 0.10.31)
+ *
+ * File splitting modes.
+ */
typedef enum {
GST_MULTI_FILE_SINK_NEXT_BUFFER,
GST_MULTI_FILE_SINK_NEXT_DISCONT,
- GST_MULTI_FILE_SINK_NEXT_KEY_FRAME
+ GST_MULTI_FILE_SINK_NEXT_KEY_FRAME,
+ GST_MULTI_FILE_SINK_NEXT_KEY_UNIT_EVENT,
+ GST_MULTI_FILE_SINK_NEXT_MAX_SIZE
} GstMultiFileSinkNext;
struct _GstMultiFileSink
@@ -67,11 +82,18 @@ struct _GstMultiFileSink
gboolean post_messages;
GstMultiFileSinkNext next_file;
FILE *file;
+ guint max_files;
+ GSList *files;
+ guint n_files;
gint64 next_segment;
int n_streamheaders;
GstBuffer **streamheaders;
+ guint force_key_unit_count;
+
+ guint64 cur_file_size;
+ guint64 max_file_size;
};
struct _GstMultiFileSinkClass
diff --git a/gst/multifile/gstmultifilesrc.c b/gst/multifile/gstmultifilesrc.c
index 7f945b8..48f5c1f 100644
--- a/gst/multifile/gstmultifilesrc.c
+++ b/gst/multifile/gstmultifilesrc.c
@@ -74,7 +74,10 @@ enum
ARG_0,
ARG_LOCATION,
ARG_INDEX,
- ARG_CAPS
+ ARG_START_INDEX,
+ ARG_STOP_INDEX,
+ ARG_CAPS,
+ ARG_LOOP
};
#define DEFAULT_LOCATION "%05d"
@@ -92,8 +95,8 @@ gst_multi_file_src_base_init (gpointer g_class)
GST_DEBUG_CATEGORY_INIT (gst_multi_file_src_debug, "multifilesrc", 0,
"multifilesrc element");
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_multi_file_src_pad_template));
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &gst_multi_file_src_pad_template);
gst_element_class_set_details_simple (gstelement_class, "Multi-File Source",
"Source/File",
"Read a sequentially named set of files into buffers",
@@ -122,10 +125,26 @@ gst_multi_file_src_class_init (GstMultiFileSrcClass * klass)
"index is incremented by one for each buffer read.",
0, INT_MAX, DEFAULT_INDEX,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, ARG_START_INDEX,
+ g_param_spec_int ("start-index", "Start Index",
+ "Start value of index. The initial value of index can be set "
+ "either by setting index or start-index. When the end of the loop "
+ "is reached, the index will be set to the value start-index.",
+ 0, INT_MAX, DEFAULT_INDEX,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, ARG_STOP_INDEX,
+ g_param_spec_int ("stop-index", "Start Index",
+ "Stop value of index. The special value -1 means no stop.",
+ -1, INT_MAX, DEFAULT_INDEX,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_CAPS,
g_param_spec_boxed ("caps", "Caps",
"Caps describing the format of the data.",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, ARG_LOOP,
+ g_param_spec_boolean ("loop", "Loop",
+ "Whether to repeat from the beginning when all files have been read.",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gobject_class->dispose = gst_multi_file_src_dispose;
@@ -144,7 +163,9 @@ static void
gst_multi_file_src_init (GstMultiFileSrc * multifilesrc,
GstMultiFileSrcClass * g_class)
{
+ multifilesrc->start_index = DEFAULT_INDEX;
multifilesrc->index = DEFAULT_INDEX;
+ multifilesrc->stop_index = -1;
multifilesrc->filename = g_strdup (DEFAULT_LOCATION);
multifilesrc->successful_read = FALSE;
}
@@ -235,6 +256,12 @@ gst_multi_file_src_set_property (GObject * object, guint prop_id,
case ARG_INDEX:
src->index = g_value_get_int (value);
break;
+ case ARG_START_INDEX:
+ src->start_index = g_value_get_int (value);
+ break;
+ case ARG_STOP_INDEX:
+ src->stop_index = g_value_get_int (value);
+ break;
case ARG_CAPS:
{
const GstCaps *caps = gst_value_get_caps (value);
@@ -249,6 +276,9 @@ gst_multi_file_src_set_property (GObject * object, guint prop_id,
gst_pad_set_caps (GST_BASE_SRC_PAD (src), new_caps);
}
break;
+ case ARG_LOOP:
+ src->loop = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -268,9 +298,18 @@ gst_multi_file_src_get_property (GObject * object, guint prop_id,
case ARG_INDEX:
g_value_set_int (value, src->index);
break;
+ case ARG_START_INDEX:
+ g_value_set_int (value, src->start_index);
+ break;
+ case ARG_STOP_INDEX:
+ g_value_set_int (value, src->stop_index);
+ break;
case ARG_CAPS:
gst_value_set_caps (value, src->caps);
break;
+ case ARG_LOOP:
+ g_value_set_boolean (value, src->loop);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -282,6 +321,7 @@ gst_multi_file_src_get_filename (GstMultiFileSrc * multifilesrc)
{
gchar *filename;
+ GST_DEBUG ("%d", multifilesrc->index);
filename = g_strdup_printf (multifilesrc->filename, multifilesrc->index);
return filename;
@@ -300,6 +340,9 @@ gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
multifilesrc = GST_MULTI_FILE_SRC (src);
+ if (multifilesrc->index < multifilesrc->start_index) {
+ multifilesrc->index = multifilesrc->start_index;
+ }
filename = gst_multi_file_src_get_filename (multifilesrc);
GST_DEBUG_OBJECT (multifilesrc, "reading from file \"%s\".", filename);
@@ -312,7 +355,23 @@ gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
g_free (filename);
if (error != NULL)
g_error_free (error);
- return GST_FLOW_UNEXPECTED;
+
+ if (multifilesrc->loop) {
+ error = NULL;
+ multifilesrc->index = multifilesrc->start_index;
+
+ filename = gst_multi_file_src_get_filename (multifilesrc);
+ ret = g_file_get_contents (filename, &data, &size, &error);
+ if (!ret) {
+ g_free (filename);
+ if (error != NULL)
+ g_error_free (error);
+
+ return GST_FLOW_UNEXPECTED;
+ }
+ } else {
+ return GST_FLOW_UNEXPECTED;
+ }
} else {
goto handle_error;
}
@@ -320,6 +379,10 @@ gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
multifilesrc->successful_read = TRUE;
multifilesrc->index++;
+ if (multifilesrc->stop_index != -1 &&
+ multifilesrc->index >= multifilesrc->stop_index) {
+ multifilesrc->index = multifilesrc->start_index;
+ }
buf = gst_buffer_new ();
GST_BUFFER_DATA (buf) = (unsigned char *) data;
diff --git a/gst/multifile/gstmultifilesrc.h b/gst/multifile/gstmultifilesrc.h
index 686ae4f..57ed038 100644
--- a/gst/multifile/gstmultifilesrc.h
+++ b/gst/multifile/gstmultifilesrc.h
@@ -46,10 +46,14 @@ struct _GstMultiFileSrc
GstPushSrc parent;
gchar *filename;
+ int start_index;
+ int stop_index;
int index;
int offset;
+ gboolean loop;
+
GstCaps *caps;
gboolean successful_read;
};
diff --git a/gst/multifile/gstsplitfilesrc.c b/gst/multifile/gstsplitfilesrc.c
new file mode 100644
index 0000000..3125997
--- /dev/null
+++ b/gst/multifile/gstsplitfilesrc.c
@@ -0,0 +1,616 @@
+/* GStreamer Split File Source
+ * Copyright (C) 2011 Collabora Ltd. <tim.muller@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION:element-splitfilesrc
+ * @see_also: #GstFileSrc, #GstMultiFileSrc
+ *
+ * Reads data from multiple files, presenting those files as one continuous
+ * file to downstream elements. This is useful for reading a large file that
+ * had to be split into multiple parts due to filesystem file size limitations,
+ * for example.
+ *
+ * The files to select are chosen via the location property, which supports
+ * (and expects) shell-style wildcards (but only for the filename, not for
+ * directories). The results will be sorted.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch splitfilesrc location="/path/to/part-*.mpg" ! decodebin ! ... \
+ * ]| Plays the different parts as if they were one single MPEG file.
+ * </refsect2>
+ *
+ * Since: 0.10.31
+ */
+
+/* TODO:
+ * - implement splitfile:// URI handler?
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstsplitfilesrc.h"
+#include "patternspec.h"
+
+#include <string.h>
+
+#ifdef G_OS_WIN32
+#define DEFAULT_PATTERN_MATCH_MODE MATCH_MODE_UTF8
+#else
+#define DEFAULT_PATTERN_MATCH_MODE MATCH_MODE_AUTO
+#endif
+
+enum
+{
+ PROP_LOCATION = 1
+};
+
+#define DEFAULT_LOCATION NULL
+
+static void gst_split_file_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_split_file_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_split_file_src_finalize (GObject * obj);
+
+static gboolean gst_split_file_src_start (GstBaseSrc * basesrc);
+static gboolean gst_split_file_src_stop (GstBaseSrc * basesrc);
+static gboolean gst_split_file_src_can_seek (GstBaseSrc * basesrc);
+static gboolean gst_split_file_src_check_get_range (GstBaseSrc * basesrc);
+static gboolean gst_split_file_src_get_size (GstBaseSrc * basesrc, guint64 * s);
+static gboolean gst_split_file_src_unlock (GstBaseSrc * basesrc);
+static GstFlowReturn gst_split_file_src_create (GstBaseSrc * basesrc,
+ guint64 offset, guint size, GstBuffer ** buffer);
+
+static GstStaticPadTemplate gst_split_file_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (splitfilesrc_debug);
+#define GST_CAT_DEFAULT splitfilesrc_debug
+
+GST_BOILERPLATE (GstSplitFileSrc, gst_split_file_src, GstBaseSrc,
+ GST_TYPE_BASE_SRC);
+
+static void
+gst_split_file_src_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ GST_DEBUG_CATEGORY_INIT (splitfilesrc_debug, "splitfilesrc", 0,
+ "splitfilesrc element");
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&gst_split_file_src_pad_template));
+
+ gst_element_class_set_details_simple (gstelement_class, "Split-File Source",
+ "Source/File",
+ "Read a sequentially named set of files as if it was one large file",
+ "Tim-Philipp Müller <tim.muller@collabora.co.uk>");
+}
+
+#ifdef G_OS_WIN32
+#define WIN32_BLURB " Location string must be in UTF-8 encoding (on Windows)."
+#else
+#define WIN32_BLURB /* nothing */
+#endif
+
+static void
+gst_split_file_src_class_init (GstSplitFileSrcClass * klass)
+{
+ GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gst_split_file_src_set_property;
+ gobject_class->get_property = gst_split_file_src_get_property;
+ gobject_class->finalize = gst_split_file_src_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_LOCATION,
+ g_param_spec_string ("location", "File Location",
+ "Wildcard pattern to match file names of the input files. If "
+ "the location is an absolute path or contains directory components, "
+ "only the base file name part will be considered for pattern "
+ "matching. The results will be sorted." WIN32_BLURB,
+ DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_split_file_src_start);
+ gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_split_file_src_stop);
+ gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_split_file_src_create);
+ gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_split_file_src_get_size);
+ gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_split_file_src_unlock);
+ gstbasesrc_class->is_seekable =
+ GST_DEBUG_FUNCPTR (gst_split_file_src_can_seek);
+ gstbasesrc_class->check_get_range =
+ GST_DEBUG_FUNCPTR (gst_split_file_src_check_get_range);
+}
+
+static void
+gst_split_file_src_init (GstSplitFileSrc * splitfilesrc,
+ GstSplitFileSrcClass * g_class)
+{
+}
+
+static void
+gst_split_file_src_finalize (GObject * obj)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (obj);
+
+ g_free (src->location);
+ src->location = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static gboolean
+gst_split_file_src_can_seek (GstBaseSrc * basesrc)
+{
+ return TRUE;
+}
+
+static gboolean
+gst_split_file_src_check_get_range (GstBaseSrc * basesrc)
+{
+ return TRUE;
+}
+
+static gboolean
+gst_split_file_src_unlock (GstBaseSrc * basesrc)
+{
+ /* This is not actually that useful, since all normal file
+ * operations are fully blocking anyway */
+#if 0
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (basesrc);
+
+ GST_DEBUG_OBJECT (src, "cancelling pending I/O operation if there is one");
+ /* g_cancellable_cancel (src->cancellable); */
+ GST_DEBUG_OBJECT (src, "done");
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+gst_split_file_src_get_size (GstBaseSrc * basesrc, guint64 * size)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (basesrc);
+
+ *size = src->parts[src->num_parts - 1].stop + 1;
+ return TRUE;
+}
+
+static void
+gst_split_file_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (object);
+
+ switch (prop_id) {
+ case PROP_LOCATION:
+ GST_OBJECT_LOCK (src);
+ g_free (src->location);
+ src->location = g_value_dup_string (value);
+#ifdef G_OS_WIN32
+ if (!g_utf8_validate (src->location, -1, NULL)) {
+ g_warning ("splitfilesrc 'location' property must be in UTF-8 "
+ "encoding on Windows");
+ }
+#endif
+ GST_OBJECT_UNLOCK (src);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_split_file_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (object);
+
+ switch (prop_id) {
+ case PROP_LOCATION:
+ GST_OBJECT_LOCK (src);
+ g_value_set_string (value, src->location);
+ GST_OBJECT_UNLOCK (src);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static int
+gst_split_file_src_array_sortfunc (gchar ** a, gchar ** b)
+{
+ return strcmp (*a, *b);
+}
+
+static gchar **
+gst_split_file_src_find_files (GstSplitFileSrc * src, const gchar * dirname,
+ const gchar * basename, GError ** err)
+{
+ PatternSpec *pspec;
+ GPtrArray *files;
+ const gchar *name;
+ GDir *dir;
+
+ if (dirname == NULL || basename == NULL)
+ goto invalid_location;
+
+ GST_INFO_OBJECT (src, "checking in directory '%s' for pattern '%s'",
+ dirname, basename);
+
+ dir = g_dir_open (dirname, 0, err);
+ if (dir == NULL)
+ return NULL;
+
+ if (DEFAULT_PATTERN_MATCH_MODE == MATCH_MODE_UTF8 &&
+ !g_utf8_validate (basename, -1, NULL)) {
+ goto not_utf8;
+ }
+
+ /* mode will be AUTO on linux/unix and UTF8 on win32 */
+ pspec = pattern_spec_new (basename, DEFAULT_PATTERN_MATCH_MODE);
+
+ files = g_ptr_array_new ();
+
+ while ((name = g_dir_read_name (dir))) {
+ GST_TRACE_OBJECT (src, "check: %s", name);
+ if (pattern_match_string (pspec, name)) {
+ GST_DEBUG_OBJECT (src, "match: %s", name);
+ g_ptr_array_add (files, g_build_filename (dirname, name, NULL));
+ }
+ }
+
+ if (files->len == 0)
+ goto no_matches;
+
+ g_ptr_array_sort (files, (GCompareFunc) gst_split_file_src_array_sortfunc);
+ g_ptr_array_add (files, NULL);
+
+ pattern_spec_free (pspec);
+ g_dir_close (dir);
+
+ return (gchar **) g_ptr_array_free (files, FALSE);
+
+/* ERRORS */
+invalid_location:
+ {
+ g_set_error_literal (err, G_FILE_ERROR, G_FILE_ERROR_INVAL,
+ "No filename specified.");
+ return NULL;
+ }
+not_utf8:
+ {
+ g_dir_close (dir);
+ g_set_error_literal (err, G_FILE_ERROR, G_FILE_ERROR_INVAL,
+ "Filename pattern must be UTF-8 on Windows.");
+ return NULL;
+ }
+no_matches:
+ {
+ pattern_spec_free (pspec);
+ g_dir_close (dir);
+ g_set_error_literal (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+ "Found no files matching the pattern.");
+ return NULL;
+ }
+}
+
+static gboolean
+gst_split_file_src_start (GstBaseSrc * basesrc)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (basesrc);
+ GCancellable *cancel;
+ gboolean ret = FALSE;
+ guint64 offset;
+ GError *err = NULL;
+ gchar *basename = NULL;
+ gchar *dirname = NULL;
+ gchar **files;
+ guint i;
+
+ GST_OBJECT_LOCK (src);
+ if (src->location != NULL && src->location[0] != '\0') {
+ basename = g_path_get_basename (src->location);
+ dirname = g_path_get_dirname (src->location);
+ }
+ GST_OBJECT_UNLOCK (src);
+
+ files = gst_split_file_src_find_files (src, dirname, basename, &err);
+
+ if (files == NULL || *files == NULL)
+ goto no_files;
+
+ src->num_parts = g_strv_length (files);
+ src->parts = g_new0 (GstFilePart, src->num_parts);
+
+ cancel = src->cancellable;
+
+ offset = 0;
+ for (i = 0; i < src->num_parts; ++i) {
+ GFileInputStream *stream;
+ GFileInfo *info;
+ goffset size;
+ GFile *file;
+
+ file = g_file_new_for_path (files[i]);
+ stream = g_file_read (file, cancel, &err);
+ g_object_unref (file);
+
+ if (err != NULL)
+ goto open_read_error;
+
+ info = g_file_input_stream_query_info (stream, "standard::*", NULL, &err);
+ if (err != NULL) {
+ g_object_unref (stream);
+ goto query_info_error;
+ }
+
+ size = g_file_info_get_size (info);
+ g_object_unref (info);
+
+ src->parts[i].stream = stream;
+ src->parts[i].path = g_strdup (files[i]);
+ src->parts[i].start = offset;
+ src->parts[i].stop = offset + size - 1;
+
+ GST_DEBUG ("[%010" G_GUINT64_FORMAT "-%010" G_GUINT64_FORMAT "] %s",
+ src->parts[i].start, src->parts[i].stop, src->parts[i].path);
+
+ offset += size;
+ }
+
+ GST_INFO ("Successfully opened %u file parts for reading", src->num_parts);
+
+ src->cur_part = 0;
+
+ src->cancellable = g_cancellable_new ();
+
+ ret = TRUE;
+
+done:
+ if (err != NULL)
+ g_error_free (err);
+ g_strfreev (files);
+ g_free (basename);
+ g_free (dirname);
+ return ret;
+
+/* ERRORS */
+no_files:
+ {
+ if (err->code == G_IO_ERROR_CANCELLED)
+ goto cancelled;
+
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("%s", err->message),
+ ("Failed to find files in '%s' for pattern '%s'",
+ GST_STR_NULL (dirname), GST_STR_NULL (basename)));
+ goto done;
+ }
+open_read_error:
+ {
+ if (err->code == G_IO_ERROR_CANCELLED)
+ goto cancelled;
+
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("%s", err->message),
+ ("Failed to open file '%s' for reading", files[i]));
+ goto done;
+ }
+query_info_error:
+ {
+ if (err->code == G_IO_ERROR_CANCELLED)
+ goto cancelled;
+
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("%s", err->message),
+ ("Failed to query info for file '%s'", files[i]));
+ goto done;
+ }
+cancelled:
+ {
+ GST_DEBUG_OBJECT (src, "I/O operation cancelled from another thread");
+ goto done;
+ }
+}
+
+static gboolean
+gst_split_file_src_stop (GstBaseSrc * basesrc)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (basesrc);
+ guint i;
+
+ for (i = 0; i < src->num_parts; ++i) {
+ if (src->parts[i].stream != NULL)
+ g_object_unref (src->parts[i].stream);
+ g_free (src->parts[i].path);
+ }
+ g_free (src->parts);
+ src->parts = NULL;
+ src->num_parts = 0;
+
+ g_object_unref (src->cancellable);
+ src->cancellable = NULL;
+
+ return TRUE;
+}
+
+static gboolean
+gst_split_file_src_find_part_for_offset (GstSplitFileSrc * src, guint64 offset,
+ guint * part_number)
+{
+ GstFilePart *part;
+ guint i;
+
+ /* TODO: could use gst_util_array_binary_search() here */
+ part = src->parts;
+ for (i = 0; i < src->num_parts; ++i) {
+ if (offset >= part->start && offset <= part->stop) {
+ *part_number = i;
+ return TRUE;
+ }
+ ++part;
+ }
+
+ return FALSE;
+}
+
+static GstFlowReturn
+gst_split_file_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
+ GstBuffer ** buffer)
+{
+ GstSplitFileSrc *src = GST_SPLIT_FILE_SRC (basesrc);
+ GstFilePart cur_part;
+ GInputStream *stream;
+ GCancellable *cancel;
+ GSeekable *seekable;
+ GstBuffer *buf;
+ GError *err = NULL;
+ guint64 read_offset;
+ guint8 *data;
+ guint to_read;
+
+ cur_part = src->parts[src->cur_part];
+ if (offset < cur_part.start || offset > cur_part.stop) {
+ if (!gst_split_file_src_find_part_for_offset (src, offset, &src->cur_part))
+ return GST_FLOW_UNEXPECTED;
+ cur_part = src->parts[src->cur_part];
+ }
+
+ GST_LOG_OBJECT (src, "current part: %u (%" G_GUINT64_FORMAT " - "
+ "%" G_GUINT64_FORMAT ", %s)", src->cur_part, cur_part.start,
+ cur_part.stop, cur_part.path);
+
+ buf = gst_buffer_new_and_alloc (size);
+
+ GST_BUFFER_OFFSET (buf) = offset;
+
+ data = GST_BUFFER_DATA (buf);
+
+ cancel = src->cancellable;
+
+ while (size > 0) {
+ guint64 bytes_to_end_of_part;
+ gsize read = 0;
+
+ /* we want the offset into the file part */
+ read_offset = offset - cur_part.start;
+
+ GST_LOG ("Reading part %03u from offset %" G_GUINT64_FORMAT " (%s)",
+ src->cur_part, read_offset, cur_part.path);
+
+ /* FIXME: only seek when needed (hopefully gio is smart) */
+ seekable = G_SEEKABLE (cur_part.stream);
+ if (!g_seekable_seek (seekable, read_offset, G_SEEK_SET, cancel, &err))
+ goto seek_failed;
+
+ GST_LOG_OBJECT (src, "now: %" G_GUINT64_FORMAT, g_seekable_tell (seekable));
+
+ bytes_to_end_of_part = (cur_part.stop - cur_part.start) + 1 - read_offset;
+ to_read = MIN (size, bytes_to_end_of_part);
+
+ GST_LOG_OBJECT (src, "reading %u bytes from part %u (bytes to end of "
+ "part: %u)", to_read, src->cur_part, (guint) bytes_to_end_of_part);
+
+ stream = G_INPUT_STREAM (cur_part.stream);
+
+ /* NB: we won't try to read beyond EOF */
+ if (!g_input_stream_read_all (stream, data, to_read, &read, cancel, &err))
+ goto read_failed;
+
+ GST_LOG_OBJECT (src, "read %u bytes", (guint) read);
+
+ data += read;
+ size -= read;
+ offset += read;
+
+ /* are we done? */
+ if (size == 0)
+ break;
+
+ GST_LOG_OBJECT (src, "%u bytes left to read for this chunk", size);
+
+ /* corner case, this should never really happen (assuming basesrc clips
+ * requests beyond the file size) */
+ if (read < to_read) {
+ if (src->cur_part == src->num_parts - 1) {
+ /* last file part, stop reading and truncate buffer */
+ GST_BUFFER_SIZE (buf) = offset - GST_BUFFER_OFFSET (buf);
+ break;
+ } else {
+ goto file_part_changed;
+ }
+ }
+
+ ++src->cur_part;
+ cur_part = src->parts[src->cur_part];
+ }
+
+ GST_BUFFER_OFFSET_END (buf) = offset;
+
+ *buffer = buf;
+ GST_LOG_OBJECT (src, "read %u bytes into buf %p", GST_BUFFER_SIZE (buf), buf);
+ return GST_FLOW_OK;
+
+/* ERRORS */
+seek_failed:
+ {
+ if (err->code == G_IO_ERROR_CANCELLED)
+ goto cancelled;
+
+ GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
+ ("Seek to %" G_GUINT64_FORMAT " in %s failed", read_offset,
+ cur_part.path));
+ g_error_free (err);
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
+read_failed:
+ {
+ if (err->code == G_IO_ERROR_CANCELLED)
+ goto cancelled;
+
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, ("%s", err->message),
+ ("Read from %" G_GUINT64_FORMAT " in %s failed", read_offset,
+ cur_part.path));
+ g_error_free (err);
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
+file_part_changed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ,
+ ("Read error while reading file part %s", cur_part.path),
+ ("Short read in file part, file may have been modified since start"));
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
+cancelled:
+ {
+ GST_DEBUG_OBJECT (src, "I/O operation cancelled from another thread");
+ g_error_free (err);
+ gst_buffer_unref (buf);
+ return GST_FLOW_WRONG_STATE;
+ }
+}
diff --git a/gst/multifile/gstsplitfilesrc.h b/gst/multifile/gstsplitfilesrc.h
new file mode 100644
index 0000000..f52b3bf
--- /dev/null
+++ b/gst/multifile/gstsplitfilesrc.h
@@ -0,0 +1,74 @@
+/* GStreamer Split File Source
+ * Copyright (C) 2011 Collabora Ltd. <tim.muller@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GST_SPLIT_FILE_SRC_H__
+#define __GST_SPLIT_FILE_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesrc.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SPLIT_FILE_SRC \
+ (gst_split_file_src_get_type())
+#define GST_SPLIT_FILE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPLIT_FILE_SRC,GstSplitFileSrc))
+#define GST_SPLIT_FILE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPLIT_FILE_SRC,GstSplitFileSrcClass))
+#define GST_IS_SPLIT_FILE_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPLIT_FILE_SRC))
+#define GST_IS_SPLIT_FILE_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPLIT_FILE_SRC))
+
+typedef struct _GstFilePart GstFilePart;
+typedef struct _GstSplitFileSrc GstSplitFileSrc;
+typedef struct _GstSplitFileSrcClass GstSplitFileSrcClass;
+
+struct _GstFilePart
+{
+ GFileInputStream *stream;
+ gchar *path;
+ guint64 start; /* inclusive */
+ guint64 stop; /* inclusive */
+};
+
+struct _GstSplitFileSrc
+{
+ GstBaseSrc parent;
+
+ gchar *location; /* OBJECT_LOCK */
+
+ GstFilePart *parts;
+ guint num_parts;
+
+ guint cur_part; /* part used last (likely also to be used next) */
+
+ GCancellable *cancellable; /* so we can interrupt blocking operations */
+};
+
+struct _GstSplitFileSrcClass
+{
+ GstBaseSrcClass parent_class;
+};
+
+GType gst_split_file_src_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_SPLIT_FILE_SRC_H__ */
diff --git a/gst/multifile/patternspec.c b/gst/multifile/patternspec.c
new file mode 100644
index 0000000..59de8d1
--- /dev/null
+++ b/gst/multifile/patternspec.c
@@ -0,0 +1,334 @@
+/* GPattern copy that supports raw (non-utf8) matching
+ * based on: GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997, 1999 Peter Mattis, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "patternspec.h"
+#include <string.h>
+
+typedef enum
+{
+ MATCH_ALL, /* "*A?A*" */
+ MATCH_ALL_TAIL, /* "*A?AA" */
+ MATCH_HEAD, /* "AAAA*" */
+ MATCH_TAIL, /* "*AAAA" */
+ MATCH_EXACT, /* "AAAAA" */
+ MATCH_LAST
+} MatchType;
+
+struct _PatternSpec
+{
+ MatchMode match_mode;
+ MatchType match_type;
+ guint pattern_length;
+ guint min_length;
+ guint max_length;
+ gchar *pattern;
+};
+
+static inline gchar *
+raw_strreverse (const gchar * str, gssize size)
+{
+ g_assert (size > 0);
+ return g_strreverse (g_strndup (str, size));
+}
+
+static inline gboolean
+pattern_ph_match (const gchar * match_pattern, MatchMode match_mode,
+ const gchar * match_string, gboolean * wildcard_reached_p)
+{
+ register const gchar *pattern, *string;
+ register gchar ch;
+
+ pattern = match_pattern;
+ string = match_string;
+
+ ch = *pattern;
+ pattern++;
+ while (ch) {
+ switch (ch) {
+ case '?':
+ if (!*string)
+ return FALSE;
+ if (match_mode == MATCH_MODE_UTF8)
+ string = g_utf8_next_char (string);
+ else
+ ++string;
+ break;
+
+ case '*':
+ *wildcard_reached_p = TRUE;
+ do {
+ ch = *pattern;
+ pattern++;
+ if (ch == '?') {
+ if (!*string)
+ return FALSE;
+ if (match_mode == MATCH_MODE_UTF8)
+ string = g_utf8_next_char (string);
+ else
+ ++string;
+ }
+ }
+ while (ch == '*' || ch == '?');
+ if (!ch)
+ return TRUE;
+ do {
+ gboolean next_wildcard_reached = FALSE;
+ while (ch != *string) {
+ if (!*string)
+ return FALSE;
+ if (match_mode == MATCH_MODE_UTF8)
+ string = g_utf8_next_char (string);
+ else
+ ++string;
+ }
+ string++;
+ if (pattern_ph_match (pattern, match_mode, string,
+ &next_wildcard_reached))
+ return TRUE;
+ if (next_wildcard_reached)
+ /* the forthcoming pattern substring up to the next wildcard has
+ * been matched, but a mismatch occoured for the rest of the
+ * pattern, following the next wildcard.
+ * there's no need to advance the current match position any
+ * further if the rest pattern will not match.
+ */
+ return FALSE;
+ }
+ while (*string);
+ break;
+
+ default:
+ if (ch == *string)
+ string++;
+ else
+ return FALSE;
+ break;
+ }
+
+ ch = *pattern;
+ pattern++;
+ }
+
+ return *string == 0;
+}
+
+static gboolean
+pattern_match (PatternSpec * pspec, guint string_length,
+ const gchar * string, const gchar * string_reversed)
+{
+ MatchMode match_mode;
+
+ g_assert (pspec != NULL);
+ g_assert (string != NULL);
+
+ if (string_length < pspec->min_length || string_length > pspec->max_length)
+ return FALSE;
+
+ match_mode = pspec->match_mode;
+ if (match_mode == MATCH_MODE_AUTO) {
+ if (!g_utf8_validate (string, string_length, NULL))
+ match_mode = MATCH_MODE_RAW;
+ else
+ match_mode = MATCH_MODE_UTF8;
+ }
+
+ switch (pspec->match_type) {
+ gboolean dummy;
+ case MATCH_ALL:
+ return pattern_ph_match (pspec->pattern, match_mode, string, &dummy);
+ case MATCH_ALL_TAIL:
+ if (string_reversed)
+ return pattern_ph_match (pspec->pattern, match_mode, string_reversed,
+ &dummy);
+ else {
+ gboolean result;
+ gchar *tmp;
+ if (match_mode == MATCH_MODE_UTF8) {
+ tmp = g_utf8_strreverse (string, string_length);
+ } else {
+ tmp = raw_strreverse (string, string_length);
+ }
+ result = pattern_ph_match (pspec->pattern, match_mode, tmp, &dummy);
+ g_free (tmp);
+ return result;
+ }
+ case MATCH_HEAD:
+ if (pspec->pattern_length == string_length)
+ return memcmp (pspec->pattern, string, string_length) == 0;
+ else if (pspec->pattern_length)
+ return memcmp (pspec->pattern, string, pspec->pattern_length) == 0;
+ else
+ return TRUE;
+ case MATCH_TAIL:
+ if (pspec->pattern_length)
+ /* compare incl. NUL terminator */
+ return memcmp (pspec->pattern,
+ string + (string_length - pspec->pattern_length),
+ pspec->pattern_length + 1) == 0;
+ else
+ return TRUE;
+ case MATCH_EXACT:
+ if (pspec->pattern_length != string_length)
+ return FALSE;
+ else
+ return memcmp (pspec->pattern, string, string_length) == 0;
+ default:
+ g_return_val_if_fail (pspec->match_type < MATCH_LAST, FALSE);
+ return FALSE;
+ }
+}
+
+PatternSpec *
+pattern_spec_new (const gchar * pattern, MatchMode match_mode)
+{
+ PatternSpec *pspec;
+ gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
+ gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
+ gboolean follows_wildcard = FALSE;
+ guint pending_jokers = 0;
+ const gchar *s;
+ gchar *d;
+ guint i;
+
+ g_assert (pattern != NULL);
+ g_assert (match_mode != MATCH_MODE_UTF8
+ || g_utf8_validate (pattern, -1, NULL));
+
+ /* canonicalize pattern and collect necessary stats */
+ pspec = g_new (PatternSpec, 1);
+ pspec->match_mode = match_mode;
+ pspec->pattern_length = strlen (pattern);
+ pspec->min_length = 0;
+ pspec->max_length = 0;
+ pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
+
+ if (pspec->match_mode == MATCH_MODE_AUTO) {
+ if (!g_utf8_validate (pattern, -1, NULL))
+ pspec->match_mode = MATCH_MODE_RAW;
+ }
+
+ d = pspec->pattern;
+ for (i = 0, s = pattern; *s != 0; s++) {
+ switch (*s) {
+ case '*':
+ if (follows_wildcard) { /* compress multiple wildcards */
+ pspec->pattern_length--;
+ continue;
+ }
+ follows_wildcard = TRUE;
+ if (hw_pos < 0)
+ hw_pos = i;
+ tw_pos = i;
+ break;
+ case '?':
+ pending_jokers++;
+ pspec->min_length++;
+ if (pspec->match_mode == MATCH_MODE_RAW) {
+ pspec->max_length += 1;
+ } else {
+ pspec->max_length += 4; /* maximum UTF-8 character length */
+ }
+ continue;
+ default:
+ for (; pending_jokers; pending_jokers--, i++) {
+ *d++ = '?';
+ if (hj_pos < 0)
+ hj_pos = i;
+ tj_pos = i;
+ }
+ follows_wildcard = FALSE;
+ pspec->min_length++;
+ pspec->max_length++;
+ break;
+ }
+ *d++ = *s;
+ i++;
+ }
+ for (; pending_jokers; pending_jokers--) {
+ *d++ = '?';
+ if (hj_pos < 0)
+ hj_pos = i;
+ tj_pos = i;
+ }
+ *d++ = 0;
+ seen_joker = hj_pos >= 0;
+ seen_wildcard = hw_pos >= 0;
+ more_wildcards = seen_wildcard && hw_pos != tw_pos;
+ if (seen_wildcard)
+ pspec->max_length = G_MAXUINT;
+
+ /* special case sole head/tail wildcard or exact matches */
+ if (!seen_joker && !more_wildcards) {
+ if (pspec->pattern[0] == '*') {
+ pspec->match_type = MATCH_TAIL;
+ memmove (pspec->pattern, pspec->pattern + 1, --pspec->pattern_length);
+ pspec->pattern[pspec->pattern_length] = 0;
+ return pspec;
+ }
+ if (pspec->pattern_length > 0 &&
+ pspec->pattern[pspec->pattern_length - 1] == '*') {
+ pspec->match_type = MATCH_HEAD;
+ pspec->pattern[--pspec->pattern_length] = 0;
+ return pspec;
+ }
+ if (!seen_wildcard) {
+ pspec->match_type = MATCH_EXACT;
+ return pspec;
+ }
+ }
+
+ /* now just need to distinguish between head or tail match start */
+ tw_pos = pspec->pattern_length - 1 - tw_pos; /* last pos to tail distance */
+ tj_pos = pspec->pattern_length - 1 - tj_pos; /* last pos to tail distance */
+ if (seen_wildcard)
+ pspec->match_type = tw_pos > hw_pos ? MATCH_ALL_TAIL : MATCH_ALL;
+ else /* seen_joker */
+ pspec->match_type = tj_pos > hj_pos ? MATCH_ALL_TAIL : MATCH_ALL;
+ if (pspec->match_type == MATCH_ALL_TAIL) {
+ gchar *tmp = pspec->pattern;
+
+ if (pspec->match_mode == MATCH_MODE_RAW) {
+ pspec->pattern = raw_strreverse (pspec->pattern, pspec->pattern_length);
+ } else {
+ pspec->pattern =
+ g_utf8_strreverse (pspec->pattern, pspec->pattern_length);
+ }
+ g_free (tmp);
+ }
+ return pspec;
+}
+
+void
+pattern_spec_free (PatternSpec * pspec)
+{
+ g_assert (pspec != NULL);
+
+ g_free (pspec->pattern);
+ g_free (pspec);
+}
+
+gboolean
+pattern_match_string (PatternSpec * pspec, const gchar * string)
+{
+ return pattern_match (pspec, strlen (string), string, NULL);
+}
diff --git a/gst/multifile/patternspec.h b/gst/multifile/patternspec.h
new file mode 100644
index 0000000..c3e9436
--- /dev/null
+++ b/gst/multifile/patternspec.h
@@ -0,0 +1,47 @@
+/* GPattern copy that supports raw (non-utf8) matching
+ * based on: GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997, 1999 Peter Mattis, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PATTERN_SPEC_H__
+#define __PATTERN_SPEC_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ MATCH_MODE_AUTO = 0,
+ MATCH_MODE_UTF8,
+ MATCH_MODE_RAW
+} MatchMode;
+
+typedef struct _PatternSpec PatternSpec;
+
+PatternSpec * pattern_spec_new (const gchar * pattern,
+ MatchMode match_mode);
+
+void pattern_spec_free (PatternSpec * pspec);
+
+gboolean pattern_match_string (PatternSpec * pspec,
+ const gchar * string);
+
+G_END_DECLS
+
+#endif /* __PATTERN_SPEC_H__ */