diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/Makefile.in | 564 | ||||
-rw-r--r-- | src/corrupt_mpeg2.c | 346 | ||||
-rw-r--r-- | src/dump_state.c | 445 | ||||
-rw-r--r-- | src/extract_mpeg2.1 | 35 | ||||
-rw-r--r-- | src/extract_mpeg2.c | 462 | ||||
-rw-r--r-- | src/getopt.c | 1055 | ||||
-rw-r--r-- | src/getopt.h | 169 | ||||
-rw-r--r-- | src/gettimeofday.c | 40 | ||||
-rw-r--r-- | src/gettimeofday.h | 52 | ||||
-rw-r--r-- | src/mpeg2dec.1 | 43 | ||||
-rw-r--r-- | src/mpeg2dec.c | 800 |
12 files changed, 4026 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..778032b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,15 @@ +AM_CFLAGS = $(MPEG2DEC_CFLAGS) $(LIBVO_CFLAGS) + +libmpeg2 = $(top_builddir)/libmpeg2/libmpeg2.la +libmpeg2convert = $(top_builddir)/libmpeg2/convert/libmpeg2convert.la +libvo = $(top_builddir)/libvo/libvo.a $(LIBVO_LIBS) + +bin_PROGRAMS = mpeg2dec extract_mpeg2 corrupt_mpeg2 +mpeg2dec_SOURCES = mpeg2dec.c dump_state.c getopt.c gettimeofday.c +mpeg2dec_LDADD = $(libvo) $(libmpeg2) $(libmpeg2convert) +extract_mpeg2_SOURCES = extract_mpeg2.c getopt.c +corrupt_mpeg2_SOURCES = corrupt_mpeg2.c getopt.c + +man_MANS = mpeg2dec.1 extract_mpeg2.1 + +EXTRA_DIST = getopt.h gettimeofday.h $(man_MANS) diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..22932d9 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,564 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = mpeg2dec$(EXEEXT) extract_mpeg2$(EXEEXT) \ + corrupt_mpeg2$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/cflags.m4 \ + $(top_srcdir)/m4/inttypes.m4 $(top_srcdir)/m4/keywords.m4 \ + $(top_srcdir)/m4/nonpic.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_corrupt_mpeg2_OBJECTS = corrupt_mpeg2.$(OBJEXT) getopt.$(OBJEXT) +corrupt_mpeg2_OBJECTS = $(am_corrupt_mpeg2_OBJECTS) +corrupt_mpeg2_LDADD = $(LDADD) +am_extract_mpeg2_OBJECTS = extract_mpeg2.$(OBJEXT) getopt.$(OBJEXT) +extract_mpeg2_OBJECTS = $(am_extract_mpeg2_OBJECTS) +extract_mpeg2_LDADD = $(LDADD) +am_mpeg2dec_OBJECTS = mpeg2dec.$(OBJEXT) dump_state.$(OBJEXT) \ + getopt.$(OBJEXT) gettimeofday.$(OBJEXT) +mpeg2dec_OBJECTS = $(am_mpeg2dec_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(top_builddir)/libvo/libvo.a \ + $(am__DEPENDENCIES_1) +mpeg2dec_DEPENDENCIES = $(am__DEPENDENCIES_2) $(libmpeg2) \ + $(libmpeg2convert) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include +depcomp = $(SHELL) $(top_srcdir)/.auto/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(corrupt_mpeg2_SOURCES) $(extract_mpeg2_SOURCES) \ + $(mpeg2dec_SOURCES) +DIST_SOURCES = $(corrupt_mpeg2_SOURCES) $(extract_mpeg2_SOURCES) \ + $(mpeg2dec_SOURCES) +man1dir = $(mandir)/man1 +NROFF = nroff +MANS = $(man_MANS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_CPPFLAGS = @AM_CPPFLAGS@ +AR = @AR@ +ARCH_OPT_CFLAGS = @ARCH_OPT_CFLAGS@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBMPEG2_CFLAGS = @LIBMPEG2_CFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBVO_CFLAGS = @LIBVO_CFLAGS@ +LIBVO_LIBS = @LIBVO_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MPEG2DEC_CFLAGS = @MPEG2DEC_CFLAGS@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPT_CFLAGS = @OPT_CFLAGS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SDLCONFIG = @SDLCONFIG@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CFLAGS = $(MPEG2DEC_CFLAGS) $(LIBVO_CFLAGS) +libmpeg2 = $(top_builddir)/libmpeg2/libmpeg2.la +libmpeg2convert = $(top_builddir)/libmpeg2/convert/libmpeg2convert.la +libvo = $(top_builddir)/libvo/libvo.a $(LIBVO_LIBS) +mpeg2dec_SOURCES = mpeg2dec.c dump_state.c getopt.c gettimeofday.c +mpeg2dec_LDADD = $(libvo) $(libmpeg2) $(libmpeg2convert) +extract_mpeg2_SOURCES = extract_mpeg2.c getopt.c +corrupt_mpeg2_SOURCES = corrupt_mpeg2.c getopt.c +man_MANS = mpeg2dec.1 extract_mpeg2.1 +EXTRA_DIST = getopt.h gettimeofday.h $(man_MANS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +corrupt_mpeg2$(EXEEXT): $(corrupt_mpeg2_OBJECTS) $(corrupt_mpeg2_DEPENDENCIES) + @rm -f corrupt_mpeg2$(EXEEXT) + $(LINK) $(corrupt_mpeg2_OBJECTS) $(corrupt_mpeg2_LDADD) $(LIBS) +extract_mpeg2$(EXEEXT): $(extract_mpeg2_OBJECTS) $(extract_mpeg2_DEPENDENCIES) + @rm -f extract_mpeg2$(EXEEXT) + $(LINK) $(extract_mpeg2_OBJECTS) $(extract_mpeg2_LDADD) $(LIBS) +mpeg2dec$(EXEEXT): $(mpeg2dec_OBJECTS) $(mpeg2dec_DEPENDENCIES) + @rm -f mpeg2dec$(EXEEXT) + $(LINK) $(mpeg2dec_OBJECTS) $(mpeg2dec_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/corrupt_mpeg2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump_state.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extract_mpeg2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gettimeofday.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpeg2dec.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man1_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)" + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \ + done +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man1dir)/$$inst"; \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(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 +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-man \ + uninstall-man1 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/corrupt_mpeg2.c b/src/corrupt_mpeg2.c new file mode 100644 index 0000000..86a71c6 --- /dev/null +++ b/src/corrupt_mpeg2.c @@ -0,0 +1,346 @@ +/* + * corrupt_mpeg2.c + * Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <ctype.h> +#ifdef HAVE_IO_H +#include <fcntl.h> +#include <io.h> +#endif +#include <inttypes.h> + +static FILE * in_file; +static FILE * seed_file; +static int seed_loaded = 0; +static uint32_t rsl[55]; +static int rsl_i = -1; + +typedef struct { + uint32_t p, q[8], r; +} randbyte_t; + +#define CORRUPT_RANDOM 0 +#define CORRUPT_VALUE 1 + +typedef struct corrupt_s { + int type; + int chunk_start, chunk_stop; + int bit_start, bit_stop; + union { + randbyte_t prob; + } u; + struct corrupt_s * next; + uint8_t mask; +} corrupt_t; + +#define CORRUPT_LIST_SIZE 10 +static corrupt_t corrupt_list[10]; +static int corrupt_list_index = 0; +static corrupt_t * corrupt_head = NULL; +static int current_chunk = -1, current_bit = 0, target_bit = 0x7fffffff; + +static inline uint32_t fastrand (void) +{ + if (++rsl_i == 55) rsl_i = 0; + return rsl[rsl_i] += rsl[(rsl_i < 31) ? rsl_i + 24 : rsl_i - 31]; +} + +static uint32_t clip (double p) +{ + return (p < 0) ? 0 : ((p >= 1) ? 0xffffffff : (uint32_t)(p*4294967296.0)); +} + +static void randbyte_init (double p, randbyte_t * rnd) +{ + double q, r; + int i; + + rnd->p = clip (p); + r = 1; + for (i = 0; i < 8; i++) { + r *= 1 - p; + q = p / (1 - r); + rnd->q[i] = clip (q); + } + rnd->r = clip (1 - r); +} + +static inline uint8_t randbyte (const randbyte_t * const rnd) +{ + int i, j; + + if (fastrand () > rnd->r || rnd->r == 0) + return 0; + + i = 7; j = 0; + do + if (fastrand () <= (j ? rnd->p : rnd->q[i])) + j |= 1 << i; + while (i--); + return j; +} + +static void print_usage (char ** argv) +{ + fprintf (stderr, "usage: %s [-h] [-l <seed>] [-s <seedfile>] \\\n" + "\t\t[-r prob[,restrict] [-v prob[,restrict]] <file>\n" + "\t-h\tdisplay help\n" + "\t-l load seed\n" + "\t-s save seed file\n" + "\t-r random corruption\n" + "\t-v random value\n" + "restrict: chunk[-endchunk][,bit[-endbit]]\n", argv[0]); + + exit (1); +} + +static void corrupt_arg (corrupt_t * corrupt, int type, char * s, char ** argv) +{ + corrupt->type = type; + if (! *s) + s = (char *)",0-0xff,0-"; + else if (*s != ',' || !isdigit (s[1])) + print_usage (argv); + corrupt->chunk_start = strtol (s + 1, &s, 0); + if (*s != '-') + corrupt->chunk_stop = corrupt->chunk_start; + else if (isdigit (* ++s)) + corrupt->chunk_stop = strtol (s, &s, 0); + else + print_usage (argv); + if (! *s) + s = (char *)",32-"; + else if (*s != ',' || !isdigit (s[1])) + print_usage (argv); + corrupt->bit_start = strtol (s + 1, &s, 0); + if (*s != '-') + corrupt->bit_stop = corrupt->bit_start; + else if (isdigit (* ++s)) + corrupt->bit_stop = strtol (s, &s, 0); + else + corrupt->bit_stop = 0x7ffffffe; + if (corrupt->chunk_start < 0 || + corrupt->chunk_start > corrupt->chunk_stop || + corrupt->chunk_stop >= 0x1000 || + corrupt->bit_start < 0 || corrupt->bit_start > corrupt->bit_stop || *s) + print_usage (argv); + if (corrupt->chunk_stop < 0x100) { + corrupt->chunk_start <<= 4; + corrupt->chunk_stop = (corrupt->chunk_stop << 4) | 0xf; + } +} + +static void handle_args (int argc, char ** argv) +{ + int c; + double prob; + char * s; + corrupt_t * corrupt; + + while ((c = getopt (argc, argv, "hl:s:r:v:")) != -1) + switch (c) { + case 'l': + if (seed_file || seed_loaded) + print_usage (argv); + if (sscanf (optarg, "%08x%08x%08x%08x", + rsl, rsl+1, rsl+2, rsl+3) != 4) + print_usage (argv); + seed_loaded = 1; + break; + + case 's': + if (seed_file || seed_loaded) + print_usage (argv); + seed_file = fopen (optarg, "wt"); + if (!seed_file) + print_usage (argv); + break; + + case 'r': + prob = strtod (optarg, &s); + if (prob < 0 || prob > 1 || + corrupt_list_index == CORRUPT_LIST_SIZE) + print_usage (argv); + corrupt = corrupt_list + corrupt_list_index++; + corrupt_arg (corrupt, CORRUPT_RANDOM, s, argv); + randbyte_init (prob, &corrupt->u.prob); + break; + + case 'v': + prob = strtod (optarg, &s); + if (prob < 0 || prob > 1 || + corrupt_list_index == CORRUPT_LIST_SIZE) + print_usage (argv); + corrupt = corrupt_list + corrupt_list_index++; + corrupt_arg (corrupt, CORRUPT_VALUE, s, argv); + randbyte_init (prob, &corrupt->u.prob); + break; + + default: + print_usage (argv); + } + + if (optind < argc) { + in_file = fopen (argv[optind], "rb"); + if (!in_file) { + fprintf (stderr, "%s - could not open file %s\n", strerror (errno), + argv[optind]); + exit (1); + } + } else + in_file = stdin; + + if (!seed_file && !seed_loaded) + seed_file = fopen ("seed", "wt"); +} + +static void update_corrupt_list (void) +{ + corrupt_t * corrupt; + corrupt_t ** corrupt_link; + + corrupt_link = &corrupt_head; + target_bit = 0x7fffffff; + for (corrupt = corrupt_list; + corrupt < corrupt_list + corrupt_list_index; corrupt++) + if (corrupt->chunk_start <= current_chunk && + corrupt->chunk_stop >= current_chunk && + corrupt->bit_stop >= current_bit) { + if (corrupt->bit_start >= current_bit + 8) { + if (corrupt->bit_start <= target_bit) + target_bit = corrupt->bit_start & ~7; + } else { + *corrupt_link = corrupt; + corrupt_link = &corrupt->next; + if (corrupt->bit_stop >= current_bit + 7) { + corrupt->mask = 0xff; + if (corrupt->bit_stop <= target_bit) + target_bit = (corrupt->bit_stop + 1) & ~7; + } else { + corrupt->mask = -1 << (7 - (corrupt->bit_stop & 7)); + target_bit = current_bit + 8; + } + if (corrupt->bit_start > current_bit) { + corrupt->mask &= 0xff >> (corrupt->bit_start & 7); + target_bit = current_bit + 8; + } + } + } + *corrupt_link = NULL; +} + +static void corrupt (uint8_t * ptr) +{ + corrupt_t * corrupt_ptr; + + if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 1) { + current_chunk = (ptr[3] << 4) | (ptr[4] >> 4); + current_bit = 0; + update_corrupt_list (); + } else if (current_bit == target_bit) + update_corrupt_list (); + + current_bit += 8; + + for (corrupt_ptr = corrupt_head; corrupt_ptr; corrupt_ptr = corrupt_ptr->next) + switch (corrupt_ptr->type) { + case CORRUPT_RANDOM: + *ptr ^= randbyte (&corrupt_ptr->u.prob) & corrupt_ptr->mask; + break; + case CORRUPT_VALUE: + *ptr = ((*ptr & ~corrupt_ptr->mask) | + (randbyte (&corrupt_ptr->u.prob) & corrupt_ptr->mask)); + break; + } +} + +static void corrupt_loop (void) +{ +#define BUFFER_SIZE 4096 + static uint8_t buffer1[BUFFER_SIZE + 4]; + static uint8_t buffer2[BUFFER_SIZE + 4]; + static uint8_t terminator[4] = {0xff, 0xff, 0xff, 0xff}; + + uint8_t * buf; + uint8_t * end; + uint8_t * current; + + buf = buffer1; + end = buf + fread (buf, 1, BUFFER_SIZE, in_file); + + while (end == buf + BUFFER_SIZE) { + uint8_t * lastbuf; + uint8_t * lastbuf_end; + + lastbuf = buf; lastbuf_end = buf + BUFFER_SIZE; + buf = (buf == buffer1) ? buffer2 : buffer1; + memcpy (buf, terminator, 4); + end = buf + fread (buf, 1, BUFFER_SIZE, in_file); + memcpy (lastbuf_end, buf, 4); + for (current = lastbuf; current < lastbuf_end; current++) + corrupt (current); + fwrite (lastbuf, BUFFER_SIZE, 1, stdout); + } + + memcpy (end, terminator, 4); + for (current = buf; current < end; current++) + corrupt (current); + fwrite (buf, end - buf, 1, stdout); +} + +int main (int argc, char ** argv) +{ + int i; + +#ifdef HAVE_IO_H + setmode (fileno (stdin), O_BINARY); + setmode (fileno (stdout), O_BINARY); +#endif + + handle_args (argc, argv); + + if (!seed_loaded) { + srand (time (NULL)); + for (i = 0; i < 4; i++) + rsl[i] = rand (); + fprintf (seed_file, "%08x%08x%08x%08x\n", + rsl[0], rsl[1], rsl[2], rsl[3]); + fclose (seed_file); + } + + for (i = 4; i < 55; i++) + rsl[i] = 0; + for (i = 0; i < 1000; i++) + fastrand (); + + corrupt_loop (); + + return 0; +} diff --git a/src/dump_state.c b/src/dump_state.c new file mode 100644 index 0000000..dbba70a --- /dev/null +++ b/src/dump_state.c @@ -0,0 +1,445 @@ +/* + * dump_state.c + * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "mpeg2.h" + +void dump_state (FILE * f, mpeg2_state_t state, const mpeg2_info_t * info, + int offset, int verbose); + +static struct { + const mpeg2_sequence_t * ptr; + mpeg2_sequence_t value; +} last_sequence; +static struct { + const mpeg2_gop_t * ptr; + mpeg2_gop_t value; +} last_gop; +static struct save_buf { + const mpeg2_fbuf_t * ptr; + mpeg2_fbuf_t value; +} last_curbuf, last_dispbuf, last_discbuf, buf_code_list[26]; +static int buf_code_index = 0; +static int buf_code_new = -1; +static struct save_pic { + const mpeg2_picture_t * ptr; + mpeg2_picture_t value; +} last_curpic, last_curpic2, last_disppic, last_disppic2, pic_code_list[26]; +static int pic_code_index = 0; +static int pic_code_new = -1; + +static int sequence_match (const mpeg2_sequence_t * seq) +{ + return (last_sequence.ptr == seq && + (seq == NULL || + !memcmp (seq, &last_sequence.value, sizeof (mpeg2_sequence_t)))); +} + +static void sequence_save (const mpeg2_sequence_t * seq) +{ + last_sequence.ptr = seq; + if (seq != NULL) + last_sequence.value = *seq; +} + +static char seq_code (const mpeg2_sequence_t * seq) +{ + if (seq == NULL) + return '-'; + else if (sequence_match (seq)) + return 'S'; + else + return '?'; +} + +static int gop_match (const mpeg2_gop_t * gop) +{ + return (last_gop.ptr == gop && + (gop == NULL || + !memcmp (gop, &last_gop.value, sizeof (mpeg2_gop_t)))); +} + +static void gop_save (const mpeg2_gop_t * gop) +{ + last_gop.ptr = gop; + if (gop != NULL) + last_gop.value = *gop; +} + +static char gop_code (const mpeg2_gop_t * gop) +{ + if (gop == NULL) + return '-'; + else if (gop_match (gop)) + return 'G'; + else + return '?'; +} + +static int fbuf_match (const mpeg2_fbuf_t * fbuf, struct save_buf * saved) +{ + return (saved->ptr == fbuf && + (fbuf == NULL || + !memcmp (fbuf, &saved->value, sizeof (mpeg2_fbuf_t)))); +} + +static void fbuf_save (const mpeg2_fbuf_t * fbuf, struct save_buf * saved) +{ + saved->ptr = fbuf; + if (fbuf != NULL) + saved->value = *fbuf; +} + +static char buf_code (const mpeg2_fbuf_t * fbuf) +{ + int i; + + if (fbuf == NULL) + return '-'; + for (i = 0; i < 26; i++) + if (fbuf_match (fbuf, buf_code_list + i)) + return ((i == buf_code_new) ? 'A' : 'a') + i; + return '?'; +} + +static void buf_code_add (const mpeg2_fbuf_t * fbuf, FILE * f) +{ + int i; + + if (fbuf == NULL) + fprintf (f, "buf_code_add error\n"); + for (i = 0; i < 26; i++) + if (buf_code_list[i].ptr == fbuf) + fprintf (f, "buf_code_add error\n"); + buf_code_new = buf_code_index; + fbuf_save (fbuf, buf_code_list + buf_code_index); + if (++buf_code_index == 26) + buf_code_index = 0; +} + +static void buf_code_del (const mpeg2_fbuf_t * fbuf) +{ + int i; + + if (fbuf == NULL) + return; + for (i = 0; i < 26; i++) + if (fbuf_match (fbuf, buf_code_list + i)) { + buf_code_list[i].ptr = NULL; + return; + } +} + +static int picture_match (const mpeg2_picture_t * pic, struct save_pic * saved) +{ + return (saved->ptr == pic && + (pic == NULL || + !memcmp (pic, &saved->value, sizeof (mpeg2_picture_t)))); +} + +static void picture_save (const mpeg2_picture_t * pic, struct save_pic * saved) +{ + saved->ptr = pic; + if (pic != NULL) + saved->value = *pic; +} + +static char pic_code (const mpeg2_picture_t * pic) +{ + int i; + + if (pic == NULL) + return '-'; + for (i = 0; i < 26; i++) + if (picture_match (pic, pic_code_list + i)) + return ((i == pic_code_new) ? 'A' : 'a') + i; + return '?'; +} + +static void pic_code_add (const mpeg2_picture_t * pic, FILE * f) +{ + int i; + + if (pic == NULL) + fprintf (f, "pic_code_add error\n"); + for (i = 0; i < 26; i++) + if (pic_code_list[i].ptr == pic) + fprintf (f, "pic_code_add error\n"); + pic_code_new = pic_code_index; + picture_save (pic, pic_code_list + pic_code_index); + if (++pic_code_index == 26) + pic_code_index = 0; +} + +static void pic_code_del (const mpeg2_picture_t * pic) +{ + int i; + + if (pic == NULL) + return; + for (i = 0; i < 26; i++) + if (picture_match (pic, pic_code_list + i)) { + pic_code_list[i].ptr = NULL; + return; + } +} + +void dump_state (FILE * f, mpeg2_state_t state, const mpeg2_info_t * info, + int offset, int verbose) +{ + static const char * state_name[] = { + "BUFFER", "SEQUENCE", "SEQUENCE_REPEATED","GOP", + "PICTURE", "SLICE_1ST", "PICTURE_2ND", "SLICE", "END", + "INVALID", "INVALID_END", "SEQUENCE_MODIFIED" + }; + static const char * profile[] = { "HP", "Spatial", "SNR", "MP", "SP" }; + static const char * level[] = { "HL", "H-14", "ML", "LL" }; + static const char * profile2[] = { "422@HL", NULL, NULL, "422@ML", + NULL, NULL, NULL, NULL, "MV@HL", + "MV@H-14", NULL, "MV@ML", "MV@LL" }; + static const char * video_fmt[] = { "COMPONENT", "PAL", "NTSC", "SECAM", "MAC"}; + static const char coding_type[] = { '0', 'I', 'P', 'B', 'D', '5', '6', '7'}; + static const char * colour[] = { NULL, "BT.709", "UNSPECIFIED", NULL, + "BT.470-2/M", "BT.470-2/B,G", + "SMPTE170M", "SMPTE240M", "LINEAR" }; + static const char * colour3[] = { NULL, "BT.709", "UNSPEC_COLORS", NULL, NULL, + "BT.470-2/B,G", "SMPTE170M", "SMPTE240M" }; + const mpeg2_sequence_t * seq = info->sequence; + const mpeg2_gop_t * gop = info->gop; + const mpeg2_picture_t * pic; + unsigned int i, nb_pos, pixel_width, pixel_height; + + if (state == STATE_BUFFER && + sequence_match (seq) && gop_match (gop) && + info->user_data == NULL && info->user_data_len == 0 && + fbuf_match (info->current_fbuf, &last_curbuf) && + fbuf_match (info->display_fbuf, &last_dispbuf) && + fbuf_match (info->discard_fbuf, &last_discbuf) && + picture_match (info->current_picture, &last_curpic) && + picture_match (info->current_picture_2nd, &last_curpic2) && + picture_match (info->display_picture, &last_disppic) && + picture_match (info->display_picture_2nd, &last_disppic2)) + return; + fprintf (f, "%8x", offset); + if (verbose > 1) { + switch (state) { + case STATE_PICTURE: + buf_code_add (info->current_fbuf, f); + pic_code_add (info->current_picture, f); + break; + case STATE_PICTURE_2ND: + pic_code_add (info->current_picture_2nd, f); + break; + case STATE_SEQUENCE_MODIFIED: + if (last_sequence.value.width != seq->width || + last_sequence.value.height != seq->height || + last_sequence.value.chroma_width != seq->chroma_width || + last_sequence.value.chroma_height != seq->chroma_height || + ((last_sequence.value.flags & SEQ_FLAG_LOW_DELAY) != + (seq->flags & SEQ_FLAG_LOW_DELAY))) + fprintf (f, " (INVALID)"); + case STATE_SEQUENCE: + sequence_save (seq); + break; + break; + case STATE_GOP: + gop_save (gop); + break; + default: + break; + } + fprintf (f, " %c%c %c%c%c %c%c%c %c", seq_code (seq), gop_code (gop), + buf_code (info->current_fbuf), + pic_code (info->current_picture), + pic_code (info->current_picture_2nd), + buf_code (info->display_fbuf), + pic_code (info->display_picture), + pic_code (info->display_picture_2nd), + buf_code (info->discard_fbuf)); + if (state == STATE_SLICE || state == STATE_END || + state == STATE_INVALID_END) { + if (state != STATE_SLICE) + buf_code_del (info->display_fbuf); + buf_code_del (info->discard_fbuf); + pic_code_del (info->display_picture); + pic_code_del (info->display_picture_2nd); + } + buf_code_new = pic_code_new = -1; + } + fprintf (f, " %s", state_name[state]); + switch (state) { + case STATE_SEQUENCE: + case STATE_SEQUENCE_REPEATED: + case STATE_SEQUENCE_MODIFIED: + if (seq->flags & SEQ_FLAG_MPEG2) + fprintf (f, " MPEG2"); + if (0x10 <= seq->profile_level_id && seq->profile_level_id < 0x60 && + !(seq->profile_level_id & 1) && + 4 <= (seq->profile_level_id & 15) && + (seq->profile_level_id & 15) <= 10) + fprintf (f, " %s@%s", + profile[(seq->profile_level_id >> 4) - 1], + level[((seq->profile_level_id & 15) - 4) >> 1]); + else if (0x82 <= seq->profile_level_id && + seq->profile_level_id<= 0x8e && + profile2[seq->profile_level_id - 0x82]) + fprintf (f, " %s", profile2[seq->profile_level_id - 0x82]); + else if (seq->flags & SEQ_FLAG_MPEG2) + fprintf (f, " profile %02x", seq->profile_level_id); + if (seq->flags & SEQ_FLAG_CONSTRAINED_PARAMETERS) + fprintf (f, " CONST"); + if (seq->flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE) + fprintf (f, " PROG"); + if (seq->flags & SEQ_FLAG_LOW_DELAY) + fprintf (f, " LOWDELAY"); + if ((seq->flags & SEQ_MASK_VIDEO_FORMAT) < + SEQ_VIDEO_FORMAT_UNSPECIFIED) + fprintf (f, " %s", video_fmt[(seq->flags & SEQ_MASK_VIDEO_FORMAT) / + SEQ_VIDEO_FORMAT_PAL]); + if (seq->flags & SEQ_FLAG_COLOUR_DESCRIPTION) { + if (seq->colour_primaries == seq->transfer_characteristics && + seq->colour_primaries == seq->matrix_coefficients && + seq->colour_primaries <= 7 && colour3[seq->colour_primaries]) + fprintf (f, " %s", colour3[seq->colour_primaries]); + else { + char prim[16], trans[16], matrix[16]; + sprintf (prim, "%d", seq->colour_primaries); + sprintf (trans, "%d", seq->transfer_characteristics); + sprintf (matrix, "%d", seq->matrix_coefficients); + if (seq->colour_primaries <= 7 && + colour[seq->colour_primaries]) + strncpy (prim, colour[seq->colour_primaries], 15); + if (seq->transfer_characteristics <= 8 && + colour[seq->transfer_characteristics]) + strncpy (trans, colour[seq->transfer_characteristics], 15); + if (seq->matrix_coefficients == 4) + strncpy (matrix, "FCC", 15); + else if (seq->matrix_coefficients <= 7 && + colour[seq->matrix_coefficients]) + strncpy (matrix, colour[seq->matrix_coefficients], 15); + fprintf (f, " COLORS (prim %s trans %s matrix %s)", + prim, trans, matrix); + } + } + fprintf (f, " %dx%d chroma %dx%d fps %.*f maxBps %d vbv %d " + "picture %dx%d display %dx%d pixel %dx%d", + seq->width, seq->height, + seq->chroma_width, seq->chroma_height, + 27000000%seq->frame_period?2:0, 27000000.0/seq->frame_period, + seq->byte_rate, seq->vbv_buffer_size, + seq->picture_width, seq->picture_height, + seq->display_width, seq->display_height, + seq->pixel_width, seq->pixel_height); + if (mpeg2_guess_aspect (seq, &pixel_width, &pixel_height)) + fprintf (f, " guessed %dx%d", pixel_width, pixel_height); + fprintf (f, "\n"); + break; + case STATE_GOP: + if (gop->flags & GOP_FLAG_DROP_FRAME) + fprintf (f, " DROP"); + if (gop->flags & GOP_FLAG_CLOSED_GOP) + fprintf (f, " CLOSED"); + if (gop->flags & GOP_FLAG_BROKEN_LINK) + fprintf (f, " BROKEN"); + fprintf (f, " %2d:%2d:%2d:%2d\n", + gop->hours, gop->minutes, gop->seconds, gop->pictures); + break; + case STATE_PICTURE: + case STATE_PICTURE_2ND: + pic = ((state == STATE_PICTURE) ? + info->current_picture : info->current_picture_2nd); + fprintf (f, " %c", + coding_type[pic->flags & PIC_MASK_CODING_TYPE]); + if (pic->flags & PIC_FLAG_PROGRESSIVE_FRAME) + fprintf (f, " PROG"); + if (pic->flags & PIC_FLAG_SKIP) + fprintf (f, " SKIP"); + fprintf (f, " fields %d", pic->nb_fields); + if (pic->flags & PIC_FLAG_TOP_FIELD_FIRST) + fprintf (f, " TFF"); + if (pic->flags & PIC_FLAG_TAGS) + fprintf (f, " pts %08x dts %08x", pic->tag, pic->tag2); + fprintf (f, " time_ref %d", pic->temporal_reference); + if (pic->flags & PIC_FLAG_COMPOSITE_DISPLAY) + fprintf (f, " composite %05x", pic->flags >> 12); + fprintf (f, " offset"); + nb_pos = pic->nb_fields; + if (seq->flags & SEQ_FLAG_PROGRESSIVE_SEQUENCE) + nb_pos >>= 1; + for (i = 0; i < nb_pos; i++) + fprintf (f, " %d/%d", + pic->display_offset[i].x, pic->display_offset[i].y); + fprintf (f, "\n"); + break; + default: + fprintf (f, "\n"); + } + if (verbose > 2 && info->user_data_len) { + fprintf (f, " USER_DATA %d bytes\n", info->user_data_len); + if (verbose > 3) + for (i = 0; i < info->user_data_len; i += 16) { + unsigned int j; + + fprintf (f, " "); + for (j = i; j < i + 16; j++) + if (j < info->user_data_len) + fprintf (f, "%02x ", info->user_data[j]); + else + fprintf (f, " "); + fprintf (f, " "); + for (j = i; j < i + 16; j++) + if (j < info->user_data_len && + 32 <= info->user_data[j] && info->user_data[j] <= 126) + fprintf (f, "%c", info->user_data[j]); + else + fprintf (f, " "); + fprintf (f, "\n"); + } + } + if (state == STATE_END || state == STATE_INVALID_END) { + sequence_save (NULL); + gop_save (NULL); + fbuf_save (NULL, &last_curbuf); + fbuf_save (NULL, &last_dispbuf); + fbuf_save (NULL, &last_discbuf); + picture_save (NULL, &last_curpic); + picture_save (NULL, &last_curpic2); + picture_save (NULL, &last_disppic); + picture_save (NULL, &last_disppic2); + } else { + sequence_save (seq); + gop_save (gop); + fbuf_save (info->current_fbuf, &last_curbuf); + fbuf_save (info->display_fbuf, &last_dispbuf); + fbuf_save (info->discard_fbuf, &last_discbuf); + picture_save (info->current_picture, &last_curpic); + picture_save (info->current_picture_2nd, &last_curpic2); + picture_save (info->display_picture, &last_disppic); + picture_save (info->display_picture_2nd, &last_disppic2); + } +} diff --git a/src/extract_mpeg2.1 b/src/extract_mpeg2.1 new file mode 100644 index 0000000..99c6c5a --- /dev/null +++ b/src/extract_mpeg2.1 @@ -0,0 +1,35 @@ +.TH mpeg2dec "1" "extract_mpeg2" +.SH NAME +extract_mpeg2 \- extract MPEG video streams from a multiplexed stream. +.SH SYNOPSIS +.B extract_mpeg2 +[\fI-h\fR] [\fI-s [track]\fR] [\fI-t pid\fR] [\fIfile\fR] +.SH DESCRIPTION +`extract_mpeg2' extracts MPEG video streams from a multiplexed stream. +Input is from stdin if no file is given. +.TP +\fB\-h\fR +display help +.TP +\fB\-s track\fR +set track number (0-0xf or 0xe0-0xef) +.TP +\fB\-t pid\fR +use transport stream demultiplexer, pid 0x10-0x1ffe +.SH AUTHORS +Michel Lespinasse <walken@zoy.org> +.br +Aaron Holtzman <aholtzma@ess.engr.uvic.ca> +.br +And many others on the net. +.SH "REPORTING BUGS" +Report bugs to <libmpeg2-devel@lists.sourceforge.net>. +.SH COPYRIGHT +Copyright \(co 2000-2003 Michel Lespinasse +.br +Copyright \(co 1999-2000 Aaron Holtzman +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.BR mpeg2dec "(1)" diff --git a/src/extract_mpeg2.c b/src/extract_mpeg2.c new file mode 100644 index 0000000..ba3c469 --- /dev/null +++ b/src/extract_mpeg2.c @@ -0,0 +1,462 @@ +/* + * extract_mpeg2.c + * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#ifdef HAVE_IO_H +#include <fcntl.h> +#include <io.h> +#endif +#include <inttypes.h> + +#define BUFFER_SIZE 4096 +static uint8_t buffer[BUFFER_SIZE]; +static FILE * in_file; +static int demux_track = 0xe0; +static int demux_pid = 0; +static int demux_pva = 0; + +static void print_usage (char ** argv) +{ + fprintf (stderr, "usage: %s [-h] [-s <track>] [-t <pid>] [-p] <file>\n" + "\t-h\tdisplay help\n" + "\t-s\tset track number (0-15 or 0xe0-0xef)\n" + "\t-t\tuse transport stream demultiplexer, pid 0x10-0x1ffe\n" + "\t-p\tuse pva demultiplexer\n", + argv[0]); + + exit (1); +} + +static void handle_args (int argc, char ** argv) +{ + int c; + char * s; + + while ((c = getopt (argc, argv, "hs:t:p")) != -1) + switch (c) { + case 's': + demux_track = strtol (optarg, &s, 0); + if (demux_track < 0xe0) + demux_track += 0xe0; + if (demux_track < 0xe0 || demux_track > 0xef || *s) { + fprintf (stderr, "Invalid track number: %s\n", optarg); + print_usage (argv); + } + break; + + case 't': + demux_pid = strtol (optarg, &s, 0); + if (demux_pid < 0x10 || demux_pid > 0x1ffe || *s) { + fprintf (stderr, "Invalid pid: %s\n", optarg); + print_usage (argv); + } + break; + + case 'p': + demux_pva = 1; + break; + + default: + print_usage (argv); + } + + if (optind < argc) { + in_file = fopen (argv[optind], "rb"); + if (!in_file) { + fprintf (stderr, "%s - could not open file %s\n", strerror (errno), + argv[optind]); + exit (1); + } + } else + in_file = stdin; +} + +#define DEMUX_PAYLOAD_START 1 +static int demux (uint8_t * buf, uint8_t * end, int flags) +{ + static int mpeg1_skip_table[16] = { + 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + /* + * the demuxer keeps some state between calls: + * if "state" = DEMUX_HEADER, then "head_buf" contains the first + * "bytes" bytes from some header. + * if "state" == DEMUX_DATA, then we need to copy "bytes" bytes + * of ES data before the next header. + * if "state" == DEMUX_SKIP, then we need to skip "bytes" bytes + * of data before the next header. + * + * NEEDBYTES makes sure we have the requested number of bytes for a + * header. If we dont, it copies what we have into head_buf and returns, + * so that when we come back with more data we finish decoding this header. + * + * DONEBYTES updates "buf" to point after the header we just parsed. + */ + +#define DEMUX_HEADER 0 +#define DEMUX_DATA 1 +#define DEMUX_SKIP 2 + static int state = DEMUX_SKIP; + static int state_bytes = 0; + static uint8_t head_buf[264]; + + uint8_t * header; + int bytes; + int len; + +#define NEEDBYTES(x) \ + do { \ + int missing; \ + \ + missing = (x) - bytes; \ + if (missing > 0) { \ + if (header == head_buf) { \ + if (missing <= end - buf) { \ + memcpy (header + bytes, buf, missing); \ + buf += missing; \ + bytes = (x); \ + } else { \ + memcpy (header + bytes, buf, end - buf); \ + state_bytes = bytes + end - buf; \ + return 0; \ + } \ + } else { \ + memcpy (head_buf, header, bytes); \ + state = DEMUX_HEADER; \ + state_bytes = bytes; \ + return 0; \ + } \ + } \ + } while (0) + +#define DONEBYTES(x) \ + do { \ + if (header != head_buf) \ + buf = header + (x); \ + } while (0) + + if (flags & DEMUX_PAYLOAD_START) + goto payload_start; + switch (state) { + case DEMUX_HEADER: + if (state_bytes > 0) { + header = head_buf; + bytes = state_bytes; + goto continue_header; + } + break; + case DEMUX_DATA: + if (demux_pid || (state_bytes > end - buf)) { + fwrite (buf, end - buf, 1, stdout); + state_bytes -= end - buf; + return 0; + } + fwrite (buf, state_bytes, 1, stdout); + buf += state_bytes; + break; + case DEMUX_SKIP: + if (demux_pid || (state_bytes > end - buf)) { + state_bytes -= end - buf; + return 0; + } + buf += state_bytes; + break; + } + + while (1) { + if (demux_pid) { + state = DEMUX_SKIP; + return 0; + } + payload_start: + header = buf; + bytes = end - buf; + continue_header: + NEEDBYTES (4); + if (header[0] || header[1] || (header[2] != 1)) { + if (demux_pid) { + state = DEMUX_SKIP; + return 0; + } else if (header != head_buf) { + buf++; + goto payload_start; + } else { + header[0] = header[1]; + header[1] = header[2]; + header[2] = header[3]; + bytes = 3; + goto continue_header; + } + } + if (demux_pid) { + if ((header[3] >= 0xe0) && (header[3] <= 0xef)) + goto pes; + fprintf (stderr, "bad stream id %x\n", header[3]); + exit (1); + } + switch (header[3]) { + case 0xb9: /* program end code */ + /* DONEBYTES (4); */ + /* break; */ + return 1; + case 0xba: /* pack header */ + NEEDBYTES (5); + if ((header[4] & 0xc0) == 0x40) { /* mpeg2 */ + NEEDBYTES (14); + len = 14 + (header[13] & 7); + NEEDBYTES (len); + DONEBYTES (len); + /* header points to the mpeg2 pack header */ + } else if ((header[4] & 0xf0) == 0x20) { /* mpeg1 */ + NEEDBYTES (12); + DONEBYTES (12); + /* header points to the mpeg1 pack header */ + } else { + fprintf (stderr, "weird pack header\n"); + DONEBYTES (5); + } + break; + default: + if (header[3] == demux_track) { + pes: + NEEDBYTES (7); + if ((header[6] & 0xc0) == 0x80) { /* mpeg2 */ + NEEDBYTES (9); + len = 9 + header[8]; + NEEDBYTES (len); + /* header points to the mpeg2 pes header */ + } else { /* mpeg1 */ + len = 7; + while ((header-1)[len] == 0xff) { + len++; + NEEDBYTES (len); + if (len > 23) { + fprintf (stderr, "too much stuffing\n"); + break; + } + } + if (((header-1)[len] & 0xc0) == 0x40) { + len += 2; + NEEDBYTES (len); + } + len += mpeg1_skip_table[(header - 1)[len] >> 4]; + NEEDBYTES (len); + /* header points to the mpeg1 pes header */ + } + DONEBYTES (len); + bytes = 6 + (header[4] << 8) + header[5] - len; + if (demux_pid || (bytes > end - buf)) { + fwrite (buf, end - buf, 1, stdout); + state = DEMUX_DATA; + state_bytes = bytes - (end - buf); + return 0; + } else if (bytes <= 0) + continue; + fwrite (buf, bytes, 1, stdout); + buf += bytes; + } else if (header[3] < 0xb9) { + fprintf (stderr, + "looks like a video stream, not system stream\n"); + DONEBYTES (4); + } else { + NEEDBYTES (6); + DONEBYTES (6); + bytes = (header[4] << 8) + header[5]; + if (bytes > end - buf) { + state = DEMUX_SKIP; + state_bytes = bytes - (end - buf); + return 0; + } + buf += bytes; + } + } + } +} + +static void ps_loop (void) +{ + uint8_t * end; + + do { + end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file); + if (demux (buffer, end, 0)) + break; /* hit program_end_code */ + } while (end == buffer + BUFFER_SIZE); +} + +static int pva_demux (uint8_t * buf, uint8_t * end) +{ + static int state = DEMUX_SKIP; + static int state_bytes = 0; + static uint8_t head_buf[12]; + + uint8_t * header; + int bytes; + int len; + + switch (state) { + case DEMUX_HEADER: + if (state_bytes > 0) { + header = head_buf; + bytes = state_bytes; + goto continue_header; + } + break; + case DEMUX_DATA: + if (state_bytes > end - buf) { + fwrite (buf, end - buf, 1, stdout); + state_bytes -= end - buf; + return 0; + } + fwrite (buf, state_bytes, 1, stdout); + buf += state_bytes; + break; + case DEMUX_SKIP: + if (state_bytes > end - buf) { + state_bytes -= end - buf; + return 0; + } + buf += state_bytes; + break; + } + + while (1) { + payload_start: + header = buf; + bytes = end - buf; + continue_header: + NEEDBYTES (2); + if (header[0] != 0x41 || header[1] != 0x56) { + if (header != head_buf) { + buf++; + goto payload_start; + } else { + header[0] = header[1]; + bytes = 1; + goto continue_header; + } + } + NEEDBYTES (8); + if (header[2] != 1) { + DONEBYTES (8); + bytes = (header[6] << 8) + header[7]; + if (bytes > end - buf) { + state = DEMUX_SKIP; + state_bytes = bytes - (end - buf); + return 0; + } + buf += bytes; + } else { + len = 8; + if (header[5] & 0x10) { + len = 12; + NEEDBYTES (len); + } + DONEBYTES (len); + bytes = (header[6] << 8) + header[7] + 8 - len; + if (bytes > end - buf) { + fwrite (buf, end - buf, 1, stdout); + state = DEMUX_DATA; + state_bytes = bytes - (end - buf); + return 0; + } else if (bytes > 0) { + fwrite (buf, bytes, 1, stdout); + buf += bytes; + } + } + } +} + +static void pva_loop (void) +{ + uint8_t * end; + + do { + end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file); + pva_demux (buffer, end); + } while (end == buffer + BUFFER_SIZE); +} + +static void ts_loop (void) +{ + uint8_t * buf; + uint8_t * nextbuf; + uint8_t * data; + uint8_t * end; + int pid; + + buf = buffer; + while (1) { + end = buf + fread (buf, 1, buffer + BUFFER_SIZE - buf, in_file); + buf = buffer; + for (; (nextbuf = buf + 188) <= end; buf = nextbuf) { + if (*buf != 0x47) { + fprintf (stderr, "bad sync byte\n"); + nextbuf = buf + 1; + continue; + } + pid = ((buf[1] << 8) + buf[2]) & 0x1fff; + if (pid != demux_pid) + continue; + data = buf + 4; + if (buf[3] & 0x20) { /* buf contains an adaptation field */ + data = buf + 5 + buf[4]; + if (data > nextbuf) + continue; + } + if (buf[3] & 0x10) + demux (data, nextbuf, + (buf[1] & 0x40) ? DEMUX_PAYLOAD_START : 0); + } + if (end != buffer + BUFFER_SIZE) + break; + memcpy (buffer, buf, end - buf); + buf = buffer + (end - buf); + } +} + +int main (int argc, char ** argv) +{ +#ifdef HAVE_IO_H + setmode (fileno (stdin), O_BINARY); + setmode (fileno (stdout), O_BINARY); +#endif + + handle_args (argc, argv); + + if (demux_pva) + pva_loop (); + if (demux_pid) + ts_loop (); + else + ps_loop (); + + return 0; +} diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 0000000..4744e43 --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,1055 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 + Free Software Foundation, Inc. + + The GNU C 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. + + The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include <gnu-versions.h> +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include <stdlib.h> +# include <unistd.h> +#endif /* GNU C library. */ + +#ifdef VMS +# include <unixlib.h> +# if HAVE_STRING_H - 0 +# include <string.h> +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include <libintl.h> +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include <string.h> +# define my_index strchr +#else + +# if HAVE_STRING_H +# include <string.h> +# else +# include <strings.h> +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 0000000..b0147e9 --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,169 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C 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. + + The GNU C 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 the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if defined __STDC__ && __STDC__ + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if defined __STDC__ && __STDC__ +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/src/gettimeofday.c b/src/gettimeofday.c new file mode 100644 index 0000000..b0eefc9 --- /dev/null +++ b/src/gettimeofday.c @@ -0,0 +1,40 @@ +/* + * gettimeofday.c + * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "gettimeofday.h" + +#ifdef CUSTOM_GETTIMEOFDAY + +#include <sys/timeb.h> + +void gettimeofday (struct timeval * tp, void * dummy) +{ + struct timeb tm; + ftime (&tm); + tp->tv_sec = tm.time; + tp->tv_usec = tm.millitm * 1000; +} + +#endif diff --git a/src/gettimeofday.h b/src/gettimeofday.h new file mode 100644 index 0000000..bad06b4 --- /dev/null +++ b/src/gettimeofday.h @@ -0,0 +1,52 @@ +/* + * gettimeofday.h + * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LIBMPEG2_GETTIMEOFDAY_H +#define LIBMPEG2_GETTIMEOFDAY_H + +#if defined(HAVE_STRUCT_TIMEVAL) && defined(HAVE_GETTIMEOFDAY) +#if defined(TIME_WITH_SYS_TIME) +#include <sys/time.h> +#include <time.h> +#elif defined(HAVE_SYS_TIME_H) +#include <sys/time.h> +#else +#include <time.h> +#endif +#elif defined(HAVE_SYS_TIMEB_H) && defined(HAVE_FTIME) + +#define HAVE_GETTIMEOFDAY 1 +#define CUSTOM_GETTIMEOFDAY 1 + +struct timeval { + long tv_sec; + long tv_usec; +}; + +void gettimeofday (struct timeval * tp, void * dummy); + +#else +#undef HAVE_GETTIMEOFDAY +#endif + +#endif /* LIBMPEG2_GETTIMEOFDAY_H */ diff --git a/src/mpeg2dec.1 b/src/mpeg2dec.1 new file mode 100644 index 0000000..547ee6c --- /dev/null +++ b/src/mpeg2dec.1 @@ -0,0 +1,43 @@ +.TH mpeg2dec "1" "mpeg2dec" +.SH NAME +mpeg2dec \- decode MPEG and MPEG2 video streams +.SH SYNOPSIS +.B mpeg2dec +[\fI-h\fR] [\fI-s [track]\fR] [\fI-t pid\fR] [\fI-c\fR] [\fI-o mode\fR] [\fIfile\fR] +.SH DESCRIPTION +`mpeg2dec' displays MPEG1 and MPEG2 video stream. +Input is from stdin if no file is given. +.TP +\fB\-h\fR +display help and available video modes +.TP +\fB\-s\fR +use program stream demultiplexer, track 0-0xf or 0xe0-0xef +.TP +\fB\-t pid\fR +use transport stream demultiplexer, pid 0x10-0x1ffe +.TP +\fB\-c\fR +use c implementation, disables all accelerations +.TP +\fB\-o\fR \fImode\fR +use video output driver `mode'. +.br +A list of modes is available using the \fB\-h\fR option. +.SH AUTHORS +Michel Lespinasse <walken@zoy.org> +.br +Aaron Holtzman <aholtzma@ess.engr.uvic.ca> +.br +And many others on the net. +.SH "REPORTING BUGS" +Report bugs to <libmpeg2-devel@lists.sourceforge.net>. +.SH COPYRIGHT +Copyright \(co 2000-2003 Michel Lespinasse +.br +Copyright \(co 1999-2000 Aaron Holtzman +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +.SH "SEE ALSO" +.BR extract_mpeg2 "(1)" diff --git a/src/mpeg2dec.c b/src/mpeg2dec.c new file mode 100644 index 0000000..59b36d7 --- /dev/null +++ b/src/mpeg2dec.c @@ -0,0 +1,800 @@ +/* + * mpeg2dec.c + * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * See http://libmpeg2.sourceforge.net/ for updates. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <getopt.h> +#ifdef HAVE_IO_H +#include <fcntl.h> +#include <io.h> +#endif +#ifdef LIBVO_SDL +#include <SDL/SDL.h> +#endif +#include <inttypes.h> + +#include "mpeg2.h" +#include "video_out.h" +#include "gettimeofday.h" + +static int buffer_size = 4096; +static FILE * in_file; +static int demux_track = 0; +static int demux_pid = 0; +static int demux_pva = 0; +static mpeg2dec_t * mpeg2dec; +static vo_open_t * output_open = NULL; +static vo_instance_t * output; +static int sigint = 0; +static int total_offset = 0; +static int verbose = 0; + +void dump_state (FILE * f, mpeg2_state_t state, const mpeg2_info_t * info, + int offset, int verbose); + +#ifdef HAVE_GETTIMEOFDAY + +static RETSIGTYPE signal_handler (int sig) +{ + sigint = 1; + signal (sig, SIG_DFL); + return (RETSIGTYPE)0; +} + +static void print_fps (int final) +{ + static uint32_t frame_counter = 0; + static struct timeval tv_beg, tv_start; + static int total_elapsed; + static int last_count = 0; + struct timeval tv_end; + double fps, tfps; + int frames, elapsed; + + if (verbose) + return; + + gettimeofday (&tv_end, NULL); + + if (!frame_counter) { + tv_start = tv_beg = tv_end; + signal (SIGINT, signal_handler); + } + + elapsed = (tv_end.tv_sec - tv_beg.tv_sec) * 100 + + (tv_end.tv_usec - tv_beg.tv_usec) / 10000; + total_elapsed = (tv_end.tv_sec - tv_start.tv_sec) * 100 + + (tv_end.tv_usec - tv_start.tv_usec) / 10000; + + if (final) { + if (total_elapsed) + tfps = frame_counter * 100.0 / total_elapsed; + else + tfps = 0; + + fprintf (stderr,"\n%d frames decoded in %.2f seconds (%.2f fps)\n", + frame_counter, total_elapsed / 100.0, tfps); + + return; + } + + frame_counter++; + + if (elapsed < 50) /* only display every 0.50 seconds */ + return; + + tv_beg = tv_end; + frames = frame_counter - last_count; + + fps = frames * 100.0 / elapsed; + tfps = frame_counter * 100.0 / total_elapsed; + + fprintf (stderr, "%d frames in %.2f sec (%.2f fps), " + "%d last %.2f sec (%.2f fps)\033[K\r", frame_counter, + total_elapsed / 100.0, tfps, frames, elapsed / 100.0, fps); + + last_count = frame_counter; +} + +#else /* !HAVE_GETTIMEOFDAY */ + +static void print_fps (int final) +{ +} + +#endif + +static void print_usage (char ** argv) +{ + int i; + vo_driver_t const * drivers; + + fprintf (stderr, "usage: " + "%s [-h] [-o <mode>] [-s [<track>]] [-t <pid>] [-p] [-c] \\\n" + "\t\t[-v] [-b <bufsize>] <file>\n" + "\t-h\tdisplay help and available video output modes\n" + "\t-s\tuse program stream demultiplexer, " + "track 0-15 or 0xe0-0xef\n" + "\t-t\tuse transport stream demultiplexer, pid 0x10-0x1ffe\n" + "\t-p\tuse pva demultiplexer\n" + "\t-c\tuse c implementation, disables all accelerations\n" + "\t-v\tverbose information about the MPEG stream\n" + "\t-b\tset input buffer size, default 4096 bytes\n" + "\t-o\tvideo output mode\n", argv[0]); + + drivers = vo_drivers (); + for (i = 0; drivers[i].name; i++) + fprintf (stderr, "\t\t\t%s\n", drivers[i].name); + + exit (1); +} + +static void handle_args (int argc, char ** argv) +{ + int c; + vo_driver_t const * drivers; + int i; + char * s; + + drivers = vo_drivers (); + while ((c = getopt (argc, argv, "hs::t:pco:vb::")) != -1) + switch (c) { + case 'o': + for (i = 0; drivers[i].name != NULL; i++) + if (strcmp (drivers[i].name, optarg) == 0) + output_open = drivers[i].open; + if (output_open == NULL) { + fprintf (stderr, "Invalid video driver: %s\n", optarg); + print_usage (argv); + } + break; + + case 's': + demux_track = 0xe0; + if (optarg != NULL) { + demux_track = strtol (optarg, &s, 0); + if (demux_track < 0xe0) + demux_track += 0xe0; + if (demux_track < 0xe0 || demux_track > 0xef || *s) { + fprintf (stderr, "Invalid track number: %s\n", optarg); + print_usage (argv); + } + } + break; + + case 't': + demux_pid = strtol (optarg, &s, 0); + if (demux_pid < 0x10 || demux_pid > 0x1ffe || *s) { + fprintf (stderr, "Invalid pid: %s\n", optarg); + print_usage (argv); + } + break; + + case 'p': + demux_pva = 1; + break; + + case 'c': + mpeg2_accel (0); + break; + + case 'v': + if (++verbose > 4) + print_usage (argv); + break; + + case 'b': + buffer_size = 1; + if (optarg != NULL) { + buffer_size = strtol (optarg, &s, 0); + if (buffer_size < 1 || *s) { + fprintf (stderr, "Invalid buffer size: %s\n", optarg); + print_usage (argv); + } + } + break; + + default: + print_usage (argv); + } + + /* -o not specified, use a default driver */ + if (output_open == NULL) + output_open = drivers[0].open; + + if (optind < argc) { + in_file = fopen (argv[optind], "rb"); + if (!in_file) { + fprintf (stderr, "%s - could not open file %s\n", strerror (errno), + argv[optind]); + exit (1); + } + } else + in_file = stdin; +} + +static void * malloc_hook (unsigned size, mpeg2_alloc_t reason) +{ + void * buf; + + /* + * Invalid streams can refer to fbufs that have not been + * initialized yet. For example the stream could start with a + * picture type onther than I. Or it could have a B picture before + * it gets two reference frames. Or, some slices could be missing. + * + * Consequently, the output depends on the content 2 output + * buffers have when the sequence begins. In release builds, this + * does not matter (garbage in, garbage out), but in test code, we + * always zero all our output buffers to: + * - make our test produce deterministic outputs + * - hint checkergcc that it is fine to read from all our output + * buffers at any time + */ + if ((int)reason < 0) { + return NULL; + } + buf = mpeg2_malloc (size, (mpeg2_alloc_t)-1); + if (buf && (reason == MPEG2_ALLOC_YUV || reason == MPEG2_ALLOC_CONVERTED)) + memset (buf, 0, size); + return buf; +} + +static void decode_mpeg2 (uint8_t * current, uint8_t * end) +{ + const mpeg2_info_t * info; + mpeg2_state_t state; + vo_setup_result_t setup_result; + + mpeg2_buffer (mpeg2dec, current, end); + total_offset += end - current; + + info = mpeg2_info (mpeg2dec); + while (1) { + state = mpeg2_parse (mpeg2dec); + if (verbose) + dump_state (stderr, state, info, + total_offset - mpeg2_getpos (mpeg2dec), verbose); + switch (state) { + case STATE_BUFFER: + return; + case STATE_SEQUENCE: + /* might set nb fbuf, convert format, stride */ + /* might set fbufs */ + if (output->setup (output, info->sequence->width, + info->sequence->height, + info->sequence->chroma_width, + info->sequence->chroma_height, &setup_result)) { + fprintf (stderr, "display setup failed\n"); + exit (1); + } + if (setup_result.convert && + mpeg2_convert (mpeg2dec, setup_result.convert, NULL)) { + fprintf (stderr, "color conversion setup failed\n"); + exit (1); + } + if (output->set_fbuf) { + uint8_t * buf[3]; + void * id; + + mpeg2_custom_fbuf (mpeg2dec, 1); + output->set_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + output->set_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + } else if (output->setup_fbuf) { + uint8_t * buf[3]; + void * id; + + output->setup_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + output->setup_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + output->setup_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + } + mpeg2_skip (mpeg2dec, (output->draw == NULL)); + break; + case STATE_PICTURE: + /* might skip */ + /* might set fbuf */ + if (output->set_fbuf) { + uint8_t * buf[3]; + void * id; + + output->set_fbuf (output, buf, &id); + mpeg2_set_buf (mpeg2dec, buf, id); + } + if (output->start_fbuf) + output->start_fbuf (output, info->current_fbuf->buf, + info->current_fbuf->id); + break; + case STATE_SLICE: + case STATE_END: + case STATE_INVALID_END: + /* draw current picture */ + /* might free frame buffer */ + if (info->display_fbuf) { + if (output->draw) + output->draw (output, info->display_fbuf->buf, + info->display_fbuf->id); + print_fps (0); + } + if (output->discard && info->discard_fbuf) + output->discard (output, info->discard_fbuf->buf, + info->discard_fbuf->id); + break; + default: + break; + } + } +} + +#define DEMUX_PAYLOAD_START 1 +static int demux (uint8_t * buf, uint8_t * end, int flags) +{ + static int mpeg1_skip_table[16] = { + 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + /* + * the demuxer keeps some state between calls: + * if "state" = DEMUX_HEADER, then "head_buf" contains the first + * "bytes" bytes from some header. + * if "state" == DEMUX_DATA, then we need to copy "bytes" bytes + * of ES data before the next header. + * if "state" == DEMUX_SKIP, then we need to skip "bytes" bytes + * of data before the next header. + * + * NEEDBYTES makes sure we have the requested number of bytes for a + * header. If we dont, it copies what we have into head_buf and returns, + * so that when we come back with more data we finish decoding this header. + * + * DONEBYTES updates "buf" to point after the header we just parsed. + */ + +#define DEMUX_HEADER 0 +#define DEMUX_DATA 1 +#define DEMUX_SKIP 2 + static int state = DEMUX_SKIP; + static int state_bytes = 0; + static uint8_t head_buf[264]; + + uint8_t * header; + int bytes; + int len; + +#define NEEDBYTES(x) \ + do { \ + int missing; \ + \ + missing = (x) - bytes; \ + if (missing > 0) { \ + if (header == head_buf) { \ + if (missing <= end - buf) { \ + memcpy (header + bytes, buf, missing); \ + buf += missing; \ + bytes = (x); \ + } else { \ + memcpy (header + bytes, buf, end - buf); \ + state_bytes = bytes + end - buf; \ + return 0; \ + } \ + } else { \ + memcpy (head_buf, header, bytes); \ + state = DEMUX_HEADER; \ + state_bytes = bytes; \ + return 0; \ + } \ + } \ + } while (0) + +#define DONEBYTES(x) \ + do { \ + if (header != head_buf) \ + buf = header + (x); \ + } while (0) + + if (flags & DEMUX_PAYLOAD_START) + goto payload_start; + switch (state) { + case DEMUX_HEADER: + if (state_bytes > 0) { + header = head_buf; + bytes = state_bytes; + goto continue_header; + } + break; + case DEMUX_DATA: + if (demux_pid || (state_bytes > end - buf)) { + decode_mpeg2 (buf, end); + state_bytes -= end - buf; + return 0; + } + decode_mpeg2 (buf, buf + state_bytes); + buf += state_bytes; + break; + case DEMUX_SKIP: + if (demux_pid || (state_bytes > end - buf)) { + state_bytes -= end - buf; + return 0; + } + buf += state_bytes; + break; + } + + while (1) { + if (demux_pid) { + state = DEMUX_SKIP; + return 0; + } + payload_start: + header = buf; + bytes = end - buf; + continue_header: + NEEDBYTES (4); + if (header[0] || header[1] || (header[2] != 1)) { + if (demux_pid) { + state = DEMUX_SKIP; + return 0; + } else if (header != head_buf) { + buf++; + goto payload_start; + } else { + header[0] = header[1]; + header[1] = header[2]; + header[2] = header[3]; + bytes = 3; + goto continue_header; + } + } + if (demux_pid) { + if ((header[3] >= 0xe0) && (header[3] <= 0xef)) + goto pes; + fprintf (stderr, "bad stream id %x\n", header[3]); + exit (1); + } + switch (header[3]) { + case 0xb9: /* program end code */ + /* DONEBYTES (4); */ + /* break; */ + return 1; + case 0xba: /* pack header */ + NEEDBYTES (5); + if ((header[4] & 0xc0) == 0x40) { /* mpeg2 */ + NEEDBYTES (14); + len = 14 + (header[13] & 7); + NEEDBYTES (len); + DONEBYTES (len); + /* header points to the mpeg2 pack header */ + } else if ((header[4] & 0xf0) == 0x20) { /* mpeg1 */ + NEEDBYTES (12); + DONEBYTES (12); + /* header points to the mpeg1 pack header */ + } else { + fprintf (stderr, "weird pack header\n"); + DONEBYTES (5); + } + break; + default: + if (header[3] == demux_track) { + pes: + NEEDBYTES (7); + if ((header[6] & 0xc0) == 0x80) { /* mpeg2 */ + NEEDBYTES (9); + len = 9 + header[8]; + NEEDBYTES (len); + /* header points to the mpeg2 pes header */ + if (header[7] & 0x80) { + uint32_t pts, dts; + + pts = (((header[9] >> 1) << 30) | + (header[10] << 22) | ((header[11] >> 1) << 15) | + (header[12] << 7) | (header[13] >> 1)); + dts = (!(header[7] & 0x40) ? pts : + (uint32_t)(((header[14] >> 1) << 30) | + (header[15] << 22) | + ((header[16] >> 1) << 15) | + (header[17] << 7) | (header[18] >> 1))); + mpeg2_tag_picture (mpeg2dec, pts, dts); + } + } else { /* mpeg1 */ + int len_skip; + uint8_t * ptsbuf; + + len = 7; + while (header[len - 1] == 0xff) { + len++; + NEEDBYTES (len); + if (len > 23) { + fprintf (stderr, "too much stuffing\n"); + break; + } + } + if ((header[len - 1] & 0xc0) == 0x40) { + len += 2; + NEEDBYTES (len); + } + len_skip = len; + len += mpeg1_skip_table[header[len - 1] >> 4]; + NEEDBYTES (len); + /* header points to the mpeg1 pes header */ + ptsbuf = header + len_skip; + if ((ptsbuf[-1] & 0xe0) == 0x20) { + uint32_t pts, dts; + + pts = (((ptsbuf[-1] >> 1) << 30) | + (ptsbuf[0] << 22) | ((ptsbuf[1] >> 1) << 15) | + (ptsbuf[2] << 7) | (ptsbuf[3] >> 1)); + dts = (((ptsbuf[-1] & 0xf0) != 0x30) ? pts : + (uint32_t)(((ptsbuf[4] >> 1) << 30) | + (ptsbuf[5] << 22) | ((ptsbuf[6] >> 1) << 15) | + (ptsbuf[7] << 7) | (ptsbuf[18] >> 1))); + mpeg2_tag_picture (mpeg2dec, pts, dts); + } + } + DONEBYTES (len); + bytes = 6 + (header[4] << 8) + header[5] - len; + if (demux_pid || (bytes > end - buf)) { + decode_mpeg2 (buf, end); + state = DEMUX_DATA; + state_bytes = bytes - (end - buf); + return 0; + } else if (bytes > 0) { + decode_mpeg2 (buf, buf + bytes); + buf += bytes; + } + } else if (header[3] < 0xb9) { + fprintf (stderr, + "looks like a video stream, not system stream\n"); + DONEBYTES (4); + } else { + NEEDBYTES (6); + DONEBYTES (6); + bytes = (header[4] << 8) + header[5]; + if (bytes > end - buf) { + state = DEMUX_SKIP; + state_bytes = bytes - (end - buf); + return 0; + } + buf += bytes; + } + } + } +} + +static void ps_loop (void) +{ + uint8_t * buffer = (uint8_t *) malloc (buffer_size); + uint8_t * end; + + if (buffer == NULL) + exit (1); + do { + end = buffer + fread (buffer, 1, buffer_size, in_file); + if (demux (buffer, end, 0)) + break; /* hit program_end_code */ + } while (end == buffer + buffer_size && !sigint); + free (buffer); +} + +static int pva_demux (uint8_t * buf, uint8_t * end) +{ + static int state = DEMUX_SKIP; + static int state_bytes = 0; + static uint8_t head_buf[15]; + + uint8_t * header; + int bytes; + int len; + + switch (state) { + case DEMUX_HEADER: + if (state_bytes > 0) { + header = head_buf; + bytes = state_bytes; + goto continue_header; + } + break; + case DEMUX_DATA: + if (state_bytes > end - buf) { + decode_mpeg2 (buf, end); + state_bytes -= end - buf; + return 0; + } + decode_mpeg2 (buf, buf + state_bytes); + buf += state_bytes; + break; + case DEMUX_SKIP: + if (state_bytes > end - buf) { + state_bytes -= end - buf; + return 0; + } + buf += state_bytes; + break; + } + + while (1) { + payload_start: + header = buf; + bytes = end - buf; + continue_header: + NEEDBYTES (2); + if (header[0] != 0x41 || header[1] != 0x56) { + if (header != head_buf) { + buf++; + goto payload_start; + } else { + header[0] = header[1]; + bytes = 1; + goto continue_header; + } + } + NEEDBYTES (8); + if (header[2] != 1) { + DONEBYTES (8); + bytes = (header[6] << 8) + header[7]; + if (bytes > end - buf) { + state = DEMUX_SKIP; + state_bytes = bytes - (end - buf); + return 0; + } + buf += bytes; + } else { + len = 8; + if (header[5] & 0x10) { + len = 12 + (header[5] & 3); + NEEDBYTES (len); + decode_mpeg2 (header + 12, header + len); + mpeg2_tag_picture (mpeg2dec, + ((header[8] << 24) | (header[9] << 16) | + (header[10] << 8) | header[11]), 0); + } + DONEBYTES (len); + bytes = (header[6] << 8) + header[7] + 8 - len; + if (bytes > end - buf) { + decode_mpeg2 (buf, end); + state = DEMUX_DATA; + state_bytes = bytes - (end - buf); + return 0; + } else if (bytes > 0) { + decode_mpeg2 (buf, buf + bytes); + buf += bytes; + } + } + } +} + +static void pva_loop (void) +{ + uint8_t * buffer = (uint8_t *) malloc (buffer_size); + uint8_t * end; + + if (buffer == NULL) + exit (1); + do { + end = buffer + fread (buffer, 1, buffer_size, in_file); + pva_demux (buffer, end); + } while (end == buffer + buffer_size && !sigint); + free (buffer); +} + +static void ts_loop (void) +{ + uint8_t * buffer = (uint8_t *) malloc (buffer_size); + uint8_t * buf; + uint8_t * nextbuf; + uint8_t * data; + uint8_t * end; + int pid; + + if (buffer == NULL || buffer_size < 188) + exit (1); + buf = buffer; + do { + end = buf + fread (buf, 1, buffer + buffer_size - buf, in_file); + buf = buffer; + for (; (nextbuf = buf + 188) <= end; buf = nextbuf) { + if (*buf != 0x47) { + fprintf (stderr, "bad sync byte\n"); + nextbuf = buf + 1; + continue; + } + pid = ((buf[1] << 8) + buf[2]) & 0x1fff; + if (pid != demux_pid) + continue; + data = buf + 4; + if (buf[3] & 0x20) { /* buf contains an adaptation field */ + data = buf + 5 + buf[4]; + if (data > nextbuf) + continue; + } + if (buf[3] & 0x10) + demux (data, nextbuf, + (buf[1] & 0x40) ? DEMUX_PAYLOAD_START : 0); + } + if (end != buffer + buffer_size) + break; + memcpy (buffer, buf, end - buf); + buf = buffer + (end - buf); + } while (!sigint); + free (buffer); +} + +static void es_loop (void) +{ + uint8_t * buffer = (uint8_t *) malloc (buffer_size); + uint8_t * end; + + if (buffer == NULL) + exit (1); + do { + end = buffer + fread (buffer, 1, buffer_size, in_file); + decode_mpeg2 (buffer, end); + } while (end == buffer + buffer_size && !sigint); + free (buffer); +} + +int main (int argc, char ** argv) +{ +#ifdef HAVE_IO_H + setmode (fileno (stdin), O_BINARY); + setmode (fileno (stdout), O_BINARY); +#endif + + fprintf (stderr, PACKAGE"-"VERSION + " - by Michel Lespinasse <walken@zoy.org> and Aaron Holtzman\n"); + + handle_args (argc, argv); + + output = output_open (); + if (output == NULL) { + fprintf (stderr, "Can not open output\n"); + return 1; + } + mpeg2dec = mpeg2_init (); + if (mpeg2dec == NULL) + exit (1); + mpeg2_malloc_hooks (malloc_hook, NULL); + + if (demux_pva) + pva_loop (); + else if (demux_pid) + ts_loop (); + else if (demux_track) + ps_loop (); + else + es_loop (); + + mpeg2_close (mpeg2dec); + if (output->close) + output->close (output); + print_fps (1); + fclose (in_file); + return 0; +} |