diff options
Diffstat (limited to 'gst/multifile')
-rw-r--r-- | gst/multifile/Makefile.am | 13 | ||||
-rw-r--r-- | gst/multifile/Makefile.in | 134 | ||||
-rw-r--r-- | gst/multifile/gstmultifile.c | 3 | ||||
-rw-r--r-- | gst/multifile/gstmultifilesink.c | 466 | ||||
-rw-r--r-- | gst/multifile/gstmultifilesink.h | 24 | ||||
-rw-r--r-- | gst/multifile/gstmultifilesrc.c | 71 | ||||
-rw-r--r-- | gst/multifile/gstmultifilesrc.h | 4 | ||||
-rw-r--r-- | gst/multifile/gstsplitfilesrc.c | 616 | ||||
-rw-r--r-- | gst/multifile/gstsplitfilesrc.h | 74 | ||||
-rw-r--r-- | gst/multifile/patternspec.c | 334 | ||||
-rw-r--r-- | gst/multifile/patternspec.h | 47 |
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, ×tamp, + &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__ */ |