summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-04 07:15:40 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-11-04 07:15:40 -0800
commit20e158548a02a6ac87e5e662d790e88882e0e4d9 (patch)
tree9dc5064254d717cb8e242a1b68e4d8d77b04e84d /examples
downloadlibtheora-20e158548a02a6ac87e5e662d790e88882e0e4d9.tar.gz
libtheora-20e158548a02a6ac87e5e662d790e88882e0e4d9.tar.bz2
libtheora-20e158548a02a6ac87e5e662d790e88882e0e4d9.zip
Imported Upstream version 1.1.1upstream/1.1.1upstream
Diffstat (limited to 'examples')
-rw-r--r--examples/Makefile.am41
-rw-r--r--examples/Makefile.in580
-rw-r--r--examples/dump_psnr.c1201
-rw-r--r--examples/dump_video.c496
-rw-r--r--examples/encoder_example.c1826
-rw-r--r--examples/getopt.c1055
-rw-r--r--examples/getopt.h180
-rw-r--r--examples/getopt1.c188
-rw-r--r--examples/player_example.c857
-rw-r--r--examples/png2theora.c912
10 files changed, 7336 insertions, 0 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..fe66a21
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,41 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I$(top_srcdir)/include
+
+noinst_PROGRAMS = dump_video dump_psnr $(BUILDABLE_EXAMPLES)
+
+# possible contents of BUILDABLE_EXAMPLES:
+EXTRA_PROGRAMS = player_example encoder_example png2theora
+
+AM_CFLAGS = $(OGG_CFLAGS)
+LDADD = ../lib/libtheora.la $(OGG_LIBS)
+LDADDDEC = ../lib/libtheoradec.la $(OGG_LIBS)
+LDADDENC = ../lib/libtheoraenc.la ../lib/libtheoradec.la $(OGG_LIBS)
+
+dump_video_SOURCES = dump_video.c
+EXTRA_dump_video_SOURCES = getopt.c getopt1.c getopt.h
+dump_video_LDADD = $(GETOPT_OBJS) $(LDADDDEC)
+
+dump_psnr_SOURCES = dump_psnr.c
+EXTRA_dump_psnr_SOURCES = getopt.c getopt1.c getopt.h
+dump_psnr_LDADD = $(GETOPT_OBJS) $(LDADDDEC) -lm
+
+player_example_SOURCES = player_example.c
+player_example_CFLAGS = $(SDL_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)
+player_example_LDADD = $(LDADDDEC) $(SDL_LIBS) $(VORBIS_LIBS) $(OSS_LIBS)
+
+encoder_example_SOURCES = encoder_example.c
+EXTRA_encoder_example_SOURCES = getopt.c getopt1.c getopt.h
+encoder_example_CFLAGS = $(OGG_CFLAGS) $(VORBIS_CFLAGS)
+encoder_example_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(VORBIS_LIBS) $(VORBISENC_LIBS) -lm
+
+png2theora_SOURCES = png2theora.c
+png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
+png2theora_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(PNG_LIBS) -lm
+
+debug:
+ $(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+ $(MAKE) all CFLAGS="@PROFILE@"
+
diff --git a/examples/Makefile.in b/examples/Makefile.in
new file mode 100644
index 0000000..64e6741
--- /dev/null
+++ b/examples/Makefile.in
@@ -0,0 +1,580 @@
+# Makefile.in generated by automake 1.6.3 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# 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@
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+AMTAR = @AMTAR@
+AR = @AR@
+ARGZ_H = @ARGZ_H@
+AS = @AS@
+AWK = @AWK@
+BUILDABLE_EXAMPLES = @BUILDABLE_EXAMPLES@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CPP = @CPP@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+DEBUG = @DEBUG@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+F77 = @F77@
+GCJ = @GCJ@
+GCJFLAGS = @GCJFLAGS@
+GETOPT_OBJS = @GETOPT_OBJS@
+GREP = @GREP@
+HAVE_BIBTEX = @HAVE_BIBTEX@
+HAVE_DOXYGEN = @HAVE_DOXYGEN@
+HAVE_PDFLATEX = @HAVE_PDFLATEX@
+HAVE_PKG_CONFIG = @HAVE_PKG_CONFIG@
+HAVE_TRANSFIG = @HAVE_TRANSFIG@
+HAVE_VALGRIND = @HAVE_VALGRIND@
+INCLTDL = @INCLTDL@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBLTDL = @LIBLTDL@
+LIBM = @LIBM@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTDLDEPS = @LTDLDEPS@
+LTDLINCL = @LTDLINCL@
+LTDLOPEN = @LTDLOPEN@
+LT_CONFIG_H = @LT_CONFIG_H@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+MAINT = @MAINT@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+OSS_LIBS = @OSS_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PKG_CONFIG = @PKG_CONFIG@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+PROFILE = @PROFILE@
+RANLIB = @RANLIB@
+RC = @RC@
+SDL_CFLAGS = @SDL_CFLAGS@
+SDL_CONFIG = @SDL_CONFIG@
+SDL_LIBS = @SDL_LIBS@
+SED = @SED@
+STRIP = @STRIP@
+THDEC_LIB_AGE = @THDEC_LIB_AGE@
+THDEC_LIB_CURRENT = @THDEC_LIB_CURRENT@
+THDEC_LIB_REVISION = @THDEC_LIB_REVISION@
+THENC_LIB_AGE = @THENC_LIB_AGE@
+THENC_LIB_CURRENT = @THENC_LIB_CURRENT@
+THENC_LIB_REVISION = @THENC_LIB_REVISION@
+THEORADEC_LDFLAGS = @THEORADEC_LDFLAGS@
+THEORAENC_LDFLAGS = @THEORAENC_LDFLAGS@
+THEORA_LDFLAGS = @THEORA_LDFLAGS@
+TH_LIB_AGE = @TH_LIB_AGE@
+TH_LIB_CURRENT = @TH_LIB_CURRENT@
+TH_LIB_REVISION = @TH_LIB_REVISION@
+VALGRIND_ENVIRONMENT = @VALGRIND_ENVIRONMENT@
+VERSION = @VERSION@
+VORBISENC_LIBS = @VORBISENC_LIBS@
+VORBISFILE_LIBS = @VORBISFILE_LIBS@
+VORBIS_CFLAGS = @VORBIS_CFLAGS@
+VORBIS_LIBS = @VORBIS_LIBS@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+lt_ECHO = @lt_ECHO@
+ltdl_LIBOBJS = @ltdl_LIBOBJS@
+ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
+sys_symbol_underscore = @sys_symbol_underscore@
+
+INCLUDES = -I$(top_srcdir)/include
+
+noinst_PROGRAMS = dump_video dump_psnr $(BUILDABLE_EXAMPLES)
+
+# possible contents of BUILDABLE_EXAMPLES:
+EXTRA_PROGRAMS = player_example encoder_example png2theora
+
+AM_CFLAGS = $(OGG_CFLAGS)
+LDADD = ../lib/libtheora.la $(OGG_LIBS)
+LDADDDEC = ../lib/libtheoradec.la $(OGG_LIBS)
+LDADDENC = ../lib/libtheoraenc.la ../lib/libtheoradec.la $(OGG_LIBS)
+
+dump_video_SOURCES = dump_video.c
+EXTRA_dump_video_SOURCES = getopt.c getopt1.c getopt.h
+dump_video_LDADD = $(GETOPT_OBJS) $(LDADDDEC)
+
+dump_psnr_SOURCES = dump_psnr.c
+EXTRA_dump_psnr_SOURCES = getopt.c getopt1.c getopt.h
+dump_psnr_LDADD = $(GETOPT_OBJS) $(LDADDDEC) -lm
+
+player_example_SOURCES = player_example.c
+player_example_CFLAGS = $(SDL_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)
+player_example_LDADD = $(LDADDDEC) $(SDL_LIBS) $(VORBIS_LIBS) $(OSS_LIBS)
+
+encoder_example_SOURCES = encoder_example.c
+EXTRA_encoder_example_SOURCES = getopt.c getopt1.c getopt.h
+encoder_example_CFLAGS = $(OGG_CFLAGS) $(VORBIS_CFLAGS)
+encoder_example_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(VORBIS_LIBS) $(VORBISENC_LIBS) -lm
+
+png2theora_SOURCES = png2theora.c
+png2theora_CFLAGS = $(OGG_CFLAGS) $(PNG_CFLAGS)
+png2theora_LDADD = $(GETOPT_OBJS) $(LDADDENC) $(PNG_LIBS) -lm
+subdir = examples
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+EXTRA_PROGRAMS = player_example$(EXEEXT) encoder_example$(EXEEXT) \
+ png2theora$(EXEEXT)
+noinst_PROGRAMS = dump_video$(EXEEXT) dump_psnr$(EXEEXT) \
+ @BUILDABLE_EXAMPLES@
+PROGRAMS = $(noinst_PROGRAMS)
+
+am_dump_psnr_OBJECTS = dump_psnr.$(OBJEXT)
+dump_psnr_OBJECTS = $(am_dump_psnr_OBJECTS)
+dump_psnr_DEPENDENCIES = ../lib/libtheoradec.la
+dump_psnr_LDFLAGS =
+am_dump_video_OBJECTS = dump_video.$(OBJEXT)
+dump_video_OBJECTS = $(am_dump_video_OBJECTS)
+dump_video_DEPENDENCIES = ../lib/libtheoradec.la
+dump_video_LDFLAGS =
+am_encoder_example_OBJECTS = encoder_example-encoder_example.$(OBJEXT)
+encoder_example_OBJECTS = $(am_encoder_example_OBJECTS)
+encoder_example_DEPENDENCIES = ../lib/libtheoraenc.la \
+ ../lib/libtheoradec.la
+encoder_example_LDFLAGS =
+am_player_example_OBJECTS = player_example-player_example.$(OBJEXT)
+player_example_OBJECTS = $(am_player_example_OBJECTS)
+player_example_DEPENDENCIES = ../lib/libtheoradec.la
+player_example_LDFLAGS =
+am_png2theora_OBJECTS = png2theora-png2theora.$(OBJEXT)
+png2theora_OBJECTS = $(am_png2theora_OBJECTS)
+png2theora_DEPENDENCIES = ../lib/libtheoraenc.la ../lib/libtheoradec.la
+png2theora_LDFLAGS =
+
+DEFS = @DEFS@
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/dump_psnr.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/dump_video.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/encoder_example-encoder_example.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/encoder_example-getopt.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/encoder_example-getopt1.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/getopt.Po ./$(DEPDIR)/getopt1.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/player_example-player_example.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/png2theora-png2theora.Po
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
+ $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+CFLAGS = @CFLAGS@
+DIST_SOURCES = $(dump_psnr_SOURCES) $(EXTRA_dump_psnr_SOURCES) \
+ $(dump_video_SOURCES) $(EXTRA_dump_video_SOURCES) \
+ $(encoder_example_SOURCES) $(EXTRA_encoder_example_SOURCES) \
+ $(player_example_SOURCES) $(png2theora_SOURCES)
+DIST_COMMON = Makefile.am Makefile.in
+SOURCES = $(dump_psnr_SOURCES) $(EXTRA_dump_psnr_SOURCES) $(dump_video_SOURCES) $(EXTRA_dump_video_SOURCES) $(encoder_example_SOURCES) $(EXTRA_encoder_example_SOURCES) $(player_example_SOURCES) $(png2theora_SOURCES)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu examples/Makefile
+Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+dump_psnr$(EXEEXT): $(dump_psnr_OBJECTS) $(dump_psnr_DEPENDENCIES)
+ @rm -f dump_psnr$(EXEEXT)
+ $(LINK) $(dump_psnr_LDFLAGS) $(dump_psnr_OBJECTS) $(dump_psnr_LDADD) $(LIBS)
+dump_video$(EXEEXT): $(dump_video_OBJECTS) $(dump_video_DEPENDENCIES)
+ @rm -f dump_video$(EXEEXT)
+ $(LINK) $(dump_video_LDFLAGS) $(dump_video_OBJECTS) $(dump_video_LDADD) $(LIBS)
+encoder_example-encoder_example.$(OBJEXT): encoder_example.c
+encoder_example-getopt.$(OBJEXT): getopt.c
+encoder_example-getopt1.$(OBJEXT): getopt1.c
+encoder_example$(EXEEXT): $(encoder_example_OBJECTS) $(encoder_example_DEPENDENCIES)
+ @rm -f encoder_example$(EXEEXT)
+ $(LINK) $(encoder_example_LDFLAGS) $(encoder_example_OBJECTS) $(encoder_example_LDADD) $(LIBS)
+player_example-player_example.$(OBJEXT): player_example.c
+player_example$(EXEEXT): $(player_example_OBJECTS) $(player_example_DEPENDENCIES)
+ @rm -f player_example$(EXEEXT)
+ $(LINK) $(player_example_LDFLAGS) $(player_example_OBJECTS) $(player_example_LDADD) $(LIBS)
+png2theora-png2theora.$(OBJEXT): png2theora.c
+png2theora$(EXEEXT): $(png2theora_OBJECTS) $(png2theora_DEPENDENCIES)
+ @rm -f png2theora$(EXEEXT)
+ $(LINK) $(png2theora_LDFLAGS) $(png2theora_OBJECTS) $(png2theora_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump_psnr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump_video.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder_example-encoder_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder_example-getopt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder_example-getopt1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/player_example-player_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/png2theora-png2theora.Po@am__quote@
+
+distclean-depend:
+ -rm -rf ./$(DEPDIR)
+
+.c.o:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$<
+
+.c.obj:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `cygpath -w $<`
+
+.c.lo:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
+encoder_example-encoder_example.o: encoder_example.c
+@AMDEP_TRUE@ source='encoder_example.c' object='encoder_example-encoder_example.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-encoder_example.Po' tmpdepfile='$(DEPDIR)/encoder_example-encoder_example.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-encoder_example.o `test -f 'encoder_example.c' || echo '$(srcdir)/'`encoder_example.c
+
+encoder_example-encoder_example.obj: encoder_example.c
+@AMDEP_TRUE@ source='encoder_example.c' object='encoder_example-encoder_example.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-encoder_example.Po' tmpdepfile='$(DEPDIR)/encoder_example-encoder_example.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-encoder_example.obj `cygpath -w encoder_example.c`
+
+encoder_example-encoder_example.lo: encoder_example.c
+@AMDEP_TRUE@ source='encoder_example.c' object='encoder_example-encoder_example.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-encoder_example.Plo' tmpdepfile='$(DEPDIR)/encoder_example-encoder_example.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-encoder_example.lo `test -f 'encoder_example.c' || echo '$(srcdir)/'`encoder_example.c
+
+encoder_example-getopt.o: getopt.c
+@AMDEP_TRUE@ source='getopt.c' object='encoder_example-getopt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt.Po' tmpdepfile='$(DEPDIR)/encoder_example-getopt.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt.o `test -f 'getopt.c' || echo '$(srcdir)/'`getopt.c
+
+encoder_example-getopt.obj: getopt.c
+@AMDEP_TRUE@ source='getopt.c' object='encoder_example-getopt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt.Po' tmpdepfile='$(DEPDIR)/encoder_example-getopt.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt.obj `cygpath -w getopt.c`
+
+encoder_example-getopt.lo: getopt.c
+@AMDEP_TRUE@ source='getopt.c' object='encoder_example-getopt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt.Plo' tmpdepfile='$(DEPDIR)/encoder_example-getopt.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt.lo `test -f 'getopt.c' || echo '$(srcdir)/'`getopt.c
+
+encoder_example-getopt1.o: getopt1.c
+@AMDEP_TRUE@ source='getopt1.c' object='encoder_example-getopt1.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt1.Po' tmpdepfile='$(DEPDIR)/encoder_example-getopt1.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt1.o `test -f 'getopt1.c' || echo '$(srcdir)/'`getopt1.c
+
+encoder_example-getopt1.obj: getopt1.c
+@AMDEP_TRUE@ source='getopt1.c' object='encoder_example-getopt1.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt1.Po' tmpdepfile='$(DEPDIR)/encoder_example-getopt1.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt1.obj `cygpath -w getopt1.c`
+
+encoder_example-getopt1.lo: getopt1.c
+@AMDEP_TRUE@ source='getopt1.c' object='encoder_example-getopt1.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/encoder_example-getopt1.Plo' tmpdepfile='$(DEPDIR)/encoder_example-getopt1.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(encoder_example_CFLAGS) $(CFLAGS) -c -o encoder_example-getopt1.lo `test -f 'getopt1.c' || echo '$(srcdir)/'`getopt1.c
+
+player_example-player_example.o: player_example.c
+@AMDEP_TRUE@ source='player_example.c' object='player_example-player_example.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/player_example-player_example.Po' tmpdepfile='$(DEPDIR)/player_example-player_example.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(player_example_CFLAGS) $(CFLAGS) -c -o player_example-player_example.o `test -f 'player_example.c' || echo '$(srcdir)/'`player_example.c
+
+player_example-player_example.obj: player_example.c
+@AMDEP_TRUE@ source='player_example.c' object='player_example-player_example.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/player_example-player_example.Po' tmpdepfile='$(DEPDIR)/player_example-player_example.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(player_example_CFLAGS) $(CFLAGS) -c -o player_example-player_example.obj `cygpath -w player_example.c`
+
+player_example-player_example.lo: player_example.c
+@AMDEP_TRUE@ source='player_example.c' object='player_example-player_example.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/player_example-player_example.Plo' tmpdepfile='$(DEPDIR)/player_example-player_example.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(player_example_CFLAGS) $(CFLAGS) -c -o player_example-player_example.lo `test -f 'player_example.c' || echo '$(srcdir)/'`player_example.c
+
+png2theora-png2theora.o: png2theora.c
+@AMDEP_TRUE@ source='png2theora.c' object='png2theora-png2theora.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/png2theora-png2theora.Po' tmpdepfile='$(DEPDIR)/png2theora-png2theora.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(png2theora_CFLAGS) $(CFLAGS) -c -o png2theora-png2theora.o `test -f 'png2theora.c' || echo '$(srcdir)/'`png2theora.c
+
+png2theora-png2theora.obj: png2theora.c
+@AMDEP_TRUE@ source='png2theora.c' object='png2theora-png2theora.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/png2theora-png2theora.Po' tmpdepfile='$(DEPDIR)/png2theora-png2theora.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(png2theora_CFLAGS) $(CFLAGS) -c -o png2theora-png2theora.obj `cygpath -w png2theora.c`
+
+png2theora-png2theora.lo: png2theora.c
+@AMDEP_TRUE@ source='png2theora.c' object='png2theora-png2theora.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/png2theora-png2theora.Plo' tmpdepfile='$(DEPDIR)/png2theora-png2theora.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(png2theora_CFLAGS) $(CFLAGS) -c -o png2theora-png2theora.lo `test -f 'png2theora.c' || echo '$(srcdir)/'`png2theora.c
+CCDEPMODE = @CCDEPMODE@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ETAGS = etags
+ETAGSFLAGS =
+
+tags: TAGS
+
+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; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+
+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; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$tags$$unique" \
+ || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_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
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ @list='$(DISTFILES)'; for file in $$list; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ 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)
+
+installdirs:
+
+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_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(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-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-compile distclean-depend \
+ distclean-generic distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+uninstall-am: uninstall-info-am
+
+.PHONY: GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS distclean distclean-compile \
+ distclean-depend distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am info info-am install \
+ install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ tags uninstall uninstall-am uninstall-info-am
+
+
+debug:
+ $(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+ $(MAKE) all CFLAGS="@PROFILE@"
+# 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/examples/dump_psnr.c b/examples/dump_psnr.c
new file mode 100644
index 0000000..7340cad
--- /dev/null
+++ b/examples/dump_psnr.c
@@ -0,0 +1,1201 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example dumpvid application; dumps Theora streams
+ last mod: $Id: dump_video.c 15675 2009-02-06 09:43:27Z tterribe $
+
+ ********************************************************************/
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+#define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include <stdio.h>
+#if !defined(_WIN32)
+#include <getopt.h>
+#include <unistd.h>
+#else
+#include "getopt.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timeb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/*Yes, yes, we're going to hell.*/
+#if defined(_WIN32)
+#include <io.h>
+#endif
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include "theora/theoradec.h"
+
+const char *optstring = "fsy";
+struct option options [] = {
+ {"frame-type",no_argument,NULL,'f'},
+ {"summary",no_argument,NULL,'s'},
+ {"luma-only",no_argument,NULL,'y'},
+ {NULL,0,NULL,0}
+};
+
+static int show_frame_type;
+static int summary_only;
+static int luma_only;
+
+typedef struct y4m_input y4m_input;
+
+/*The function used to perform chroma conversion.*/
+typedef void (*y4m_convert_func)(y4m_input *_y4m,
+ unsigned char *_dst,unsigned char *_aux);
+
+struct y4m_input{
+ int frame_w;
+ int frame_h;
+ int pic_w;
+ int pic_h;
+ int pic_x;
+ int pic_y;
+ int fps_n;
+ int fps_d;
+ int par_n;
+ int par_d;
+ char interlace;
+ int src_c_dec_h;
+ int src_c_dec_v;
+ int dst_c_dec_h;
+ int dst_c_dec_v;
+ char chroma_type[16];
+ /*The size of each converted frame buffer.*/
+ size_t dst_buf_sz;
+ /*The amount to read directly into the converted frame buffer.*/
+ size_t dst_buf_read_sz;
+ /*The size of the auxilliary buffer.*/
+ size_t aux_buf_sz;
+ /*The amount to read into the auxilliary buffer.*/
+ size_t aux_buf_read_sz;
+ y4m_convert_func convert;
+ unsigned char *dst_buf;
+ unsigned char *aux_buf;
+};
+
+
+static int y4m_parse_tags(y4m_input *_y4m,char *_tags){
+ int got_w;
+ int got_h;
+ int got_fps;
+ int got_interlace;
+ int got_par;
+ int got_chroma;
+ char *p;
+ char *q;
+ got_w=got_h=got_fps=got_interlace=got_par=got_chroma=0;
+ for(p=_tags;;p=q){
+ /*Skip any leading spaces.*/
+ while(*p==' ')p++;
+ /*If that's all we have, stop.*/
+ if(p[0]=='\0')break;
+ /*Find the end of this tag.*/
+ for(q=p+1;*q!='\0'&&*q!=' ';q++);
+ /*Process the tag.*/
+ switch(p[0]){
+ case 'W':{
+ if(sscanf(p+1,"%d",&_y4m->pic_w)!=1)return -1;
+ got_w=1;
+ }break;
+ case 'H':{
+ if(sscanf(p+1,"%d",&_y4m->pic_h)!=1)return -1;
+ got_h=1;
+ }break;
+ case 'F':{
+ if(sscanf(p+1,"%d:%d",&_y4m->fps_n,&_y4m->fps_d)!=2){
+ return -1;
+ }
+ got_fps=1;
+ }break;
+ case 'I':{
+ _y4m->interlace=p[1];
+ got_interlace=1;
+ }break;
+ case 'A':{
+ if(sscanf(p+1,"%d:%d",&_y4m->par_n,&_y4m->par_d)!=2){
+ return -1;
+ }
+ got_par=1;
+ }break;
+ case 'C':{
+ if(q-p>16)return -1;
+ memcpy(_y4m->chroma_type,p+1,q-p-1);
+ _y4m->chroma_type[q-p-1]='\0';
+ got_chroma=1;
+ }break;
+ /*Ignore unknown tags.*/
+ }
+ }
+ if(!got_w||!got_h||!got_fps||!got_interlace||!got_par)return -1;
+ /*Chroma-type is not specified in older files, e.g., those generated by
+ mplayer.*/
+ if(!got_chroma)strcpy(_y4m->chroma_type,"420");
+ return 0;
+}
+
+/*All anti-aliasing filters in the following conversion functions are based on
+ one of two window functions:
+ The 6-tap Lanczos window (for down-sampling and shifts):
+ sinc(\pi*t)*sinc(\pi*t/3), |t|<3 (sinc(t)==sin(t)/t)
+ 0, |t|>=3
+ The 4-tap Mitchell window (for up-sampling):
+ 7|t|^3-12|t|^2+16/3, |t|<1
+ -(7/3)|x|^3+12|x|^2-20|x|+32/3, |t|<2
+ 0, |t|>=2
+ The number of taps is intentionally kept small to reduce computational
+ overhead and limit ringing.
+
+ The taps from these filters are scaled so that their sum is 1, and the result
+ is scaled by 128 and rounded to integers to create a filter whose
+ intermediate values fit inside 16 bits.
+ Coefficients are rounded in such a way as to ensure their sum is still 128,
+ which is usually equivalent to normal rounding.*/
+
+#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a))
+#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a))
+#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c)))
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420mpeg2 chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ The 4:2:2 modes look exactly the same, except there are twice as many chroma
+ lines, and they are vertically co-sited with the luma samples in both the
+ mpeg2 and jpeg cases (thus requiring no vertical resampling).*/
+static void y4m_convert_42xmpeg2_42xjpeg(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+ int c_w;
+ int c_h;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=_y4m->pic_w*_y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h;
+ c_h=(_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+ window.*/
+ for(x=0;x<OC_MINI(c_w,2);x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-3;x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[c_w-1]+64>>7,255);
+ }
+ _dst+=c_w;
+ _aux+=c_w;
+ }
+ }
+}
+
+/*This format is only used for interlaced content, but is included for
+ completeness.
+
+ 420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420paldv chroma samples are sited like:
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ Then we use another filter to move the C_r location down one quarter pixel,
+ and the C_b location up one quarter pixel.*/
+static void y4m_convert_42xpaldv_42xjpeg(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=_y4m->pic_w*_y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(_y4m->pic_w+1)/2;
+ c_h=(_y4m->pic_h+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h;
+ c_sz=c_w*c_h;
+ /*First do the horizontal re-sampling.
+ This is the same as the mpeg2 case, except that after the horizontal case,
+ we need to apply a second vertical filter.*/
+ tmp=_aux+2*c_sz;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+ window.*/
+ for(x=0;x<OC_MINI(c_w,2);x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-3;x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[c_w-1]+64>>7,255);
+ }
+ tmp+=c_w;
+ _aux+=c_w;
+ }
+ switch(pli){
+ case 1:{
+ tmp-=c_sz;
+ /*Slide C_b up a quarter-pel.
+ This is the same filter used above, but in the other order.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(c_h,3);y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[0]-
+ 9*tmp[OC_MAXI(y-2,0)*c_w]+35*tmp[OC_MAXI(y-1,0)*c_w]+
+ 114*tmp[y*c_w]-17*tmp[OC_MINI(y+1,c_h-1)*c_w]+
+ 4*tmp[OC_MINI(y+2,c_h-1)*c_w]+64>>7,255);
+ }
+ for(;y<c_h-2;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[(y-3)*c_w]-
+ 9*tmp[(y-2)*c_w]+35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-
+ 17*tmp[(y+1)*c_w]+4*tmp[(y+2)*c_w]+64>>7,255);
+ }
+ for(;y<c_h;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[(y-3)*c_w]-
+ 9*tmp[(y-2)*c_w]+35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-
+ 17*tmp[OC_MINI(y+1,c_h-1)*c_w]+4*tmp[(c_h-1)*c_w]+64>>7,255);
+ }
+ _dst++;
+ tmp++;
+ }
+ _dst+=c_sz-c_w;
+ tmp-=c_w;
+ }break;
+ case 2:{
+ tmp-=c_sz;
+ /*Slide C_r down a quarter-pel.
+ This is the same as the horizontal filter.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(c_h,2);y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[0]-
+ 17*tmp[OC_MAXI(y-1,0)*c_w]+114*tmp[y*c_w]+
+ 35*tmp[OC_MINI(y+1,c_h-1)*c_w]-9*tmp[OC_MINI(y+2,c_h-1)*c_w]+
+ tmp[OC_MINI(y+3,c_h-1)*c_w]+64>>7,255);
+ }
+ for(;y<c_h-3;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[(y-2)*c_w]-
+ 17*tmp[(y-1)*c_w]+114*tmp[y*c_w]+35*tmp[(y+1)*c_w]-
+ 9*tmp[(y+2)*c_w]+tmp[(y+3)*c_w]+64>>7,255);
+ }
+ for(;y<c_h;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[(y-2)*c_w]-
+ 17*tmp[(y-1)*c_w]+114*tmp[y*c_w]+35*tmp[OC_MINI(y+1,c_h-1)*c_w]-
+ 9*tmp[OC_MINI(y+2,c_h-1)*c_w]+tmp[(c_h-1)*c_w]+64>>7,255);
+ }
+ _dst++;
+ tmp++;
+ }
+ }break;
+ }
+ /*For actual interlaced material, this would have to be done separately on
+ each field, and the shift amounts would be different.
+ C_r moves down 1/8, C_b up 3/8 in the top field, and C_r moves down 3/8,
+ C_b up 1/8 in the bottom field.
+ The corresponding filters would be:
+ Down 1/8 (reverse order for up): [3 -11 125 15 -4 0]/128
+ Down 3/8 (reverse order for up): [4 -19 98 56 -13 2]/128*/
+ }
+}
+
+/*422jpeg chroma samples are sited like:
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 411 chroma samples are sited like:
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a filter to resample at site locations one eighth pixel (at the source
+ chroma plane's horizontal resolution) and five eighths of a pixel to the
+ right.*/
+static void y4m_convert_411_422jpeg(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+ int c_w;
+ int dst_c_w;
+ int c_h;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=_y4m->pic_w*_y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(_y4m->pic_w+_y4m->src_c_dec_h-1)/_y4m->src_c_dec_h;
+ dst_c_w=(_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h;
+ c_h=(_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filters: [1 110 18 -1]/128 and [-3 50 86 -5]/128, both derived from a
+ 4-tap Mitchell window.*/
+ for(x=0;x<OC_MINI(c_w,1);x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,111*_aux[0]+
+ 18*_aux[OC_MINI(1,c_w-1)]-_aux[OC_MINI(2,c_w-1)]+64>>7,255);
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,47*_aux[0]+
+ 86*_aux[OC_MINI(1,c_w-1)]-5*_aux[OC_MINI(2,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-2;x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+
+ 18*_aux[x+1]-_aux[x+2]+64>>7,255);
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+
+ 86*_aux[x+1]-5*_aux[x+2]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+
+ 18*_aux[OC_MINI(x+1,c_w-1)]-_aux[c_w-1]+64>>7,255);
+ if((x<<1|1)<dst_c_w){
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+
+ 86*_aux[OC_MINI(x+1,c_w-1)]-5*_aux[c_w-1]+64>>7,255);
+ }
+ }
+ _dst+=dst_c_w;
+ _aux+=c_w;
+ }
+ }
+}
+
+/*The image is padded with empty chroma components at 4:2:0.
+ This costs about 17 bits a frame to code.*/
+static void y4m_convert_mono_420jpeg(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+ int c_sz;
+ _dst+=_y4m->pic_w*_y4m->pic_h;
+ c_sz=((_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h)*
+ ((_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v);
+ memset(_dst,128,c_sz*2);
+}
+
+#if 0
+/*Right now just 444 to 420.
+ Not too hard to generalize.*/
+static void y4m_convert_4xxjpeg_42xjpeg(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int pic_sz;
+ int tmp_sz;
+ int c_sz;
+ int pli;
+ int y;
+ int x;
+ /*Compute the size of each chroma plane.*/
+ c_w=(_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h;
+ c_h=(_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v;
+ pic_sz=_y4m->pic_w*_y4m->pic_h;
+ tmp_sz=c_w*_y4m->pic_h;
+ c_sz=c_w*c_h;
+ _dst+=pic_sz;
+ for(pli=1;pli<3;pli++){
+ tmp=_aux+pic_sz;
+ /*In reality, the horizontal and vertical steps could be pipelined, for
+ less memory consumption and better cache performance, but we do them
+ separately for simplicity.*/
+ /*First do horizontal filtering (convert to 4:2:2)*/
+ /*Filter: [3 -17 78 78 -17 3]/128, derived from a 6-tap Lanczos window.*/
+ for(y=0;y<_y4m->pic_h;y++){
+ for(x=0;x<OC_MINI(_y4m->pic_w,2);x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,64*_aux[0]+78*_aux[OC_MINI(1,_y4m->pic_w-1)]
+ -17*_aux[OC_MINI(2,_y4m->pic_w-1)]
+ +3*_aux[OC_MINI(3,_y4m->pic_w-1)]+64>>7,255);
+ }
+ for(;x<_y4m->pic_w-3;x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[x+3])-17*(_aux[x-1]+_aux[x+2])+
+ 78*(_aux[x]+_aux[x+1])+64>>7,255);
+ }
+ for(;x<_y4m->pic_w;x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[_y4m->pic_w-1])-
+ 17*(_aux[x-1]+_aux[OC_MINI(x+2,_y4m->pic_w-1)])+
+ 78*(_aux[x]+_aux[OC_MINI(x+1,_y4m->pic_w-1)])+64>>7,255);
+ }
+ tmp+=c_w;
+ _aux+=_y4m->pic_w;
+ }
+ _aux-=pic_sz;
+ tmp-=tmp_sz;
+ /*Now do the vertical filtering.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(_y4m->pic_h,2);y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,64*tmp[0]
+ +78*tmp[OC_MINI(1,_y4m->pic_h-1)*c_w]
+ -17*tmp[OC_MINI(2,_y4m->pic_h-1)*c_w]
+ +3*tmp[OC_MINI(3,_y4m->pic_h-1)*c_w]+64>>7,255);
+ }
+ for(;y<_y4m->pic_h-3;y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]+tmp[(y+3)*c_w])-
+ 17*(tmp[(y-1)*c_w]+tmp[(y+2)*c_w])+78*(tmp[y*c_w]+tmp[(y+1)*c_w])+
+ 64>>7,255);
+ }
+ for(;y<_y4m->pic_h;y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]
+ +tmp[(_y4m->pic_h-1)*c_w])-17*(tmp[(y-1)*c_w]
+ +tmp[OC_MINI(y+2,_y4m->pic_h-1)*c_w])
+ +78*(tmp[y*c_w]+tmp[OC_MINI(y+1,_y4m->pic_h-1)*c_w])+64>>7,255);
+ }
+ tmp++;
+ _dst++;
+ }
+ _dst-=c_w;
+ }
+}
+#endif
+
+/*No conversion function needed.*/
+static void y4m_convert_null(y4m_input *_y4m,unsigned char *_dst,
+ unsigned char *_aux){
+}
+
+static int y4m_input_open(y4m_input *_y4m,FILE *_fin,char *_skip,int _nskip){
+ char buffer[80];
+ int ret;
+ int i;
+ /*Read until newline, or 80 cols, whichever happens first.*/
+ for(i=0;i<79;i++){
+ if(_nskip>0){
+ buffer[i]=*_skip++;
+ _nskip--;
+ }
+ else{
+ ret=fread(buffer+i,1,1,_fin);
+ if(ret<1)return -1;
+ }
+ if(buffer[i]=='\n')break;
+ }
+ /*We skipped too much header data.*/
+ if(_nskip>0)return -1;
+ if(i==79){
+ fprintf(stderr,"Error parsing header; not a YUV2MPEG2 file?\n");
+ return -1;
+ }
+ buffer[i]='\0';
+ if(memcmp(buffer,"YUV4MPEG",8)){
+ fprintf(stderr,"Incomplete magic for YUV4MPEG file.\n");
+ return -1;
+ }
+ if(buffer[8]!='2'){
+ fprintf(stderr,"Incorrect YUV input file version; YUV4MPEG2 required.\n");
+ }
+ ret=y4m_parse_tags(_y4m,buffer+5);
+ if(ret<0){
+ fprintf(stderr,"Error parsing YUV4MPEG2 header.\n");
+ return ret;
+ }
+ if(_y4m->interlace!='p'){
+ fprintf(stderr,"Input video is interlaced; "
+ "Theora only handles progressive scan.\n");
+ return -1;
+ }
+ if(strcmp(_y4m->chroma_type,"420")==0||
+ strcmp(_y4m->chroma_type,"420jpeg")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=_y4m->src_c_dec_v=_y4m->dst_c_dec_v=2;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h
+ +2*((_y4m->pic_w+1)/2)*((_y4m->pic_h+1)/2);
+ /*Natively supported: no conversion required.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=0;
+ _y4m->convert=y4m_convert_null;
+ }
+ else if(strcmp(_y4m->chroma_type,"420mpeg2")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=_y4m->src_c_dec_v=_y4m->dst_c_dec_v=2;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=
+ 2*((_y4m->pic_w+1)/2)*((_y4m->pic_h+1)/2);
+ _y4m->convert=y4m_convert_42xmpeg2_42xjpeg;
+ }
+ else if(strcmp(_y4m->chroma_type,"420paldv")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=_y4m->src_c_dec_v=_y4m->dst_c_dec_v=2;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ _y4m->aux_buf_sz=3*((_y4m->pic_w+1)/2)*((_y4m->pic_h+1)/2);
+ _y4m->aux_buf_read_sz=2*((_y4m->pic_w+1)/2)*((_y4m->pic_h+1)/2);
+ _y4m->convert=y4m_convert_42xpaldv_42xjpeg;
+ }
+ else if(strcmp(_y4m->chroma_type,"422")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=2;
+ _y4m->src_c_dec_v=_y4m->dst_c_dec_v=1;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=2*((_y4m->pic_w+1)/2)*_y4m->pic_h;
+ _y4m->convert=y4m_convert_42xmpeg2_42xjpeg;
+ }
+ else if(strcmp(_y4m->chroma_type,"411")==0){
+ _y4m->src_c_dec_h=4;
+ /*We don't want to introduce any additional sub-sampling, so we
+ promote 4:1:1 material to 4:2:2, as the closest format Theora can
+ handle.*/
+ _y4m->dst_c_dec_h=2;
+ _y4m->src_c_dec_v=_y4m->dst_c_dec_v=1;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=2*((_y4m->pic_w+3)/4)*_y4m->pic_h;
+ _y4m->convert=y4m_convert_411_422jpeg;
+ }
+ else if(strcmp(_y4m->chroma_type,"444")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=_y4m->src_c_dec_v=_y4m->dst_c_dec_v=1;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h*3;
+ /*Natively supported: no conversion required.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=0;
+ _y4m->convert=y4m_convert_null;
+ }
+ else if(strcmp(_y4m->chroma_type,"444alpha")==0){
+ _y4m->src_c_dec_h=_y4m->dst_c_dec_h=_y4m->src_c_dec_v=_y4m->dst_c_dec_v=1;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h*3;
+ /*Read the extra alpha plane into the aux buf.
+ It will be discarded.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ _y4m->convert=y4m_convert_null;
+ }
+ else if(strcmp(_y4m->chroma_type,"mono")==0){
+ _y4m->src_c_dec_h=_y4m->src_c_dec_v=0;
+ _y4m->dst_c_dec_h=_y4m->dst_c_dec_v=2;
+ _y4m->dst_buf_read_sz=_y4m->pic_w*_y4m->pic_h;
+ /*No extra space required, but we need to clear the chroma planes.*/
+ _y4m->aux_buf_sz=_y4m->aux_buf_read_sz=0;
+ _y4m->convert=y4m_convert_mono_420jpeg;
+ }
+ else{
+ fprintf(stderr,"Unknown chroma sampling type: %s\n",_y4m->chroma_type);
+ return -1;
+ }
+ /*The size of the final frame buffers is always computed from the
+ destination chroma decimation type.*/
+ _y4m->dst_buf_sz=_y4m->pic_w*_y4m->pic_h
+ +2*((_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h)*
+ ((_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v);
+ /*Scale the picture size up to a multiple of 16.*/
+ _y4m->frame_w=_y4m->pic_w+15&~0xF;
+ _y4m->frame_h=_y4m->pic_h+15&~0xF;
+ /*Force the offsets to be even so that chroma samples line up like we
+ expect.*/
+ _y4m->pic_x=_y4m->frame_w-_y4m->pic_w>>1&~1;
+ _y4m->pic_y=_y4m->frame_h-_y4m->pic_h>>1&~1;
+ _y4m->dst_buf=(unsigned char *)malloc(_y4m->dst_buf_sz);
+ _y4m->aux_buf=(unsigned char *)malloc(_y4m->aux_buf_sz);
+ return 0;
+}
+
+static void y4m_input_get_info(y4m_input *_y4m,th_info *_ti){
+ _ti->frame_width=_y4m->frame_w;
+ _ti->frame_height=_y4m->frame_h;
+ _ti->pic_width=_y4m->pic_w;
+ _ti->pic_height=_y4m->pic_h;
+ _ti->pic_x=_y4m->pic_x;
+ _ti->pic_y=_y4m->pic_y;
+ _ti->fps_numerator=_y4m->fps_n;
+ _ti->fps_denominator=_y4m->fps_d;
+ _ti->aspect_numerator=_y4m->par_n;
+ _ti->aspect_denominator=_y4m->par_d;
+ _ti->pixel_fmt=_y4m->dst_c_dec_h==2?
+ (_y4m->dst_c_dec_v==2?TH_PF_420:TH_PF_422):TH_PF_444;
+}
+
+static int y4m_input_fetch_frame(y4m_input *_y4m,FILE *_fin,
+ th_ycbcr_buffer _ycbcr){
+ char frame[6];
+ int pic_sz;
+ int frame_c_w;
+ int frame_c_h;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int ret;
+ pic_sz=_y4m->pic_w*_y4m->pic_h;
+ frame_c_w=_y4m->frame_w/_y4m->dst_c_dec_h;
+ frame_c_h=_y4m->frame_h/_y4m->dst_c_dec_v;
+ c_w=(_y4m->pic_w+_y4m->dst_c_dec_h-1)/_y4m->dst_c_dec_h;
+ c_h=(_y4m->pic_h+_y4m->dst_c_dec_v-1)/_y4m->dst_c_dec_v;
+ c_sz=c_w*c_h;
+ /*Read and skip the frame header.*/
+ ret=fread(frame,1,6,_fin);
+ if(ret<6)return 0;
+ if(memcmp(frame,"FRAME",5)){
+ fprintf(stderr,"Loss of framing in YUV input data\n");
+ exit(1);
+ }
+ if(frame[5]!='\n'){
+ char c;
+ int j;
+ for(j=0;j<79&&fread(&c,1,1,_fin)&&c!='\n';j++);
+ if(j==79){
+ fprintf(stderr,"Error parsing YUV frame header\n");
+ return -1;
+ }
+ }
+ /*Read the frame data that needs no conversion.*/
+ if(fread(_y4m->dst_buf,1,_y4m->dst_buf_read_sz,_fin)!=_y4m->dst_buf_read_sz){
+ fprintf(stderr,"Error reading YUV frame data.\n");
+ return -1;
+ }
+ /*Read the frame data that does need conversion.*/
+ if(fread(_y4m->aux_buf,1,_y4m->aux_buf_read_sz,_fin)!=_y4m->aux_buf_read_sz){
+ fprintf(stderr,"Error reading YUV frame data.\n");
+ return -1;
+ }
+ /*Now convert the just read frame.*/
+ (*_y4m->convert)(_y4m,_y4m->dst_buf,_y4m->aux_buf);
+ /*Fill in the frame buffer pointers.*/
+ _ycbcr[0].width=_y4m->frame_w;
+ _ycbcr[0].height=_y4m->frame_h;
+ _ycbcr[0].stride=_y4m->pic_w;
+ _ycbcr[0].data=_y4m->dst_buf-_y4m->pic_x-_y4m->pic_y*_y4m->pic_w;
+ _ycbcr[1].width=frame_c_w;
+ _ycbcr[1].height=frame_c_h;
+ _ycbcr[1].stride=c_w;
+ _ycbcr[1].data=_y4m->dst_buf+pic_sz-(_y4m->pic_x/_y4m->dst_c_dec_h)-
+ (_y4m->pic_y/_y4m->dst_c_dec_v)*c_w;
+ _ycbcr[2].width=frame_c_w;
+ _ycbcr[2].height=frame_c_h;
+ _ycbcr[2].stride=c_w;
+ _ycbcr[2].data=_ycbcr[1].data+c_sz;
+ return 1;
+}
+
+static void y4m_input_close(y4m_input *_y4m){
+ free(_y4m->dst_buf);
+ free(_y4m->aux_buf);
+}
+
+
+
+typedef struct th_input th_input;
+
+struct th_input{
+ ogg_sync_state oy;
+ int theora_p;
+ ogg_stream_state to;
+ th_info ti;
+ th_comment tc;
+ th_dec_ctx *td;
+};
+
+
+
+/*Grab some more compressed bitstream and sync it for page extraction.*/
+static int th_input_buffer_data(th_input *_th,FILE *_fin){
+ char *buffer;
+ int bytes;
+ buffer=ogg_sync_buffer(&_th->oy,4096);
+ bytes=fread(buffer,1,4096,_fin);
+ ogg_sync_wrote(&_th->oy,bytes);
+ return bytes;
+}
+
+/*Push a page into the appropriate steam.
+ This can be done blindly; a stream won't accept a page that doesn't belong to
+ it.*/
+static void th_input_queue_page(th_input *_th,ogg_page *_og){
+ if(_th->theora_p)ogg_stream_pagein(&_th->to,_og);
+}
+
+static int th_input_open_impl(th_input *_th,th_setup_info **_ts,FILE *_fin,
+ char *_sig,int _nsig){
+ ogg_packet op;
+ ogg_page og;
+ int nheaders_left;
+ int done_headers;
+ ogg_sync_init(&_th->oy);
+ th_info_init(&_th->ti);
+ th_comment_init(&_th->tc);
+ *_ts=NULL;
+ /*Buffer any initial data read for file ID.*/
+ if(_nsig>0){
+ char *buffer;
+ buffer=ogg_sync_buffer(&_th->oy,_nsig);
+ memcpy(buffer,_sig,_nsig);
+ ogg_sync_wrote(&_th->oy,_nsig);
+ }
+ _th->theora_p=0;
+ nheaders_left=0;
+ for(done_headers=0;!done_headers;){
+ if(th_input_buffer_data(_th,_fin)==0)break;
+ while(ogg_sync_pageout(&_th->oy,&og)>0){
+ ogg_stream_state test;
+ /*Is this a mandated initial header?
+ If not, stop parsing.*/
+ if(!ogg_page_bos(&og)){
+ /*Don't leak the page; get it into the appropriate stream.*/
+ th_input_queue_page(_th,&og);
+ done_headers=1;
+ break;
+ }
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ ogg_stream_packetpeek(&test,&op);
+ /*Identify the codec: try Theora.*/
+ if(!_th->theora_p){
+ nheaders_left=th_decode_headerin(&_th->ti,&_th->tc,_ts,&op);
+ if(nheaders_left>=0){
+ /*It is Theora.*/
+ memcpy(&_th->to,&test,sizeof(test));
+ _th->theora_p=1;
+ /*Advance past the successfully processed header.*/
+ if(nheaders_left>0)ogg_stream_packetout(&_th->to,NULL);
+ continue;
+ }
+ }
+ /*Whatever it is, we don't care about it.*/
+ ogg_stream_clear(&test);
+ }
+ }
+ /*We're expecting more header packets.*/
+ while(_th->theora_p&&nheaders_left>0){
+ int ret;
+ while(nheaders_left>0){
+ ret=ogg_stream_packetpeek(&_th->to,&op);
+ if(ret==0)break;
+ if(ret<0)continue;
+ nheaders_left=th_decode_headerin(&_th->ti,&_th->tc,_ts,&op);
+ if(nheaders_left<0){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ return -1;
+ }
+ /*Advance past the successfully processed header.*/
+ else if(nheaders_left>0)ogg_stream_packetout(&_th->to,NULL);
+ _th->theora_p++;
+ }
+ /*Stop now so we don't fail if there aren't enough pages in a short
+ stream.*/
+ if(!(_th->theora_p&&nheaders_left>0))break;
+ /*The header pages/packets will arrive before anything else we care
+ about, or the stream is not obeying spec.*/
+ if(ogg_sync_pageout(&_th->oy,&og)>0)th_input_queue_page(_th,&og);
+ /*We need more data.*/
+ else if(th_input_buffer_data(_th,_fin)==0){
+ fprintf(stderr,"End of file while searching for codec headers.\n");
+ return -1;
+ }
+ }
+ /*And now we have it all.
+ Initialize the decoder.*/
+ if(_th->theora_p){
+ _th->td=th_decode_alloc(&_th->ti,*_ts);
+ if(_th->td!=NULL){
+ fprintf(stderr,"Ogg logical stream %lx is Theora %ix%i %.02f fps video.\n"
+ "Encoded frame content is %ix%i with %ix%i offset.\n",
+ _th->to.serialno,_th->ti.frame_width,_th->ti.frame_height,
+ (double)_th->ti.fps_numerator/_th->ti.fps_denominator,
+ _th->ti.pic_width,_th->ti.pic_height,_th->ti.pic_x,_th->ti.pic_y);
+ return 1;
+ }
+ }
+ return -1;
+}
+
+static void th_input_close(th_input *_th){
+ if(_th->theora_p){
+ ogg_stream_clear(&_th->to);
+ th_decode_free(_th->td);
+ }
+ th_comment_clear(&_th->tc);
+ th_info_clear(&_th->ti);
+ ogg_sync_clear(&_th->oy);
+}
+
+static int th_input_open(th_input *_th,FILE *_fin,char *_sig,int _nsig){
+ th_input th;
+ th_setup_info *ts;
+ int ret;
+ ret=th_input_open_impl(&th,&ts,_fin,_sig,_nsig);
+ th_setup_free(ts);
+ /*Clean up on failure.*/
+ if(ret<0)th_input_close(&th);
+ else memcpy(_th,&th,sizeof(th));
+ return ret;
+}
+
+static void th_input_get_info(th_input *_th,th_info *_ti){
+ memcpy(_ti,&_th->ti,sizeof(*_ti));
+}
+
+static int th_input_fetch_frame(th_input *_th,FILE *_fin,
+ th_ycbcr_buffer _ycbcr){
+ for(;;){
+ ogg_page og;
+ ogg_packet op;
+ if(ogg_stream_packetout(&_th->to,&op)>0){
+ if(th_decode_packetin(_th->td,&op,NULL)>=0){
+ th_decode_ycbcr_out(_th->td,_ycbcr);
+ if(!summary_only&&show_frame_type){
+ printf("%c",th_packet_iskeyframe(&op)?'K':'D');
+ if(op.bytes>0)printf("%02i ",op.packet[0]&0x3F);
+ else printf("-- ");
+ }
+ return 1;
+ }
+ else return -1;
+ }
+ while(ogg_sync_pageout(&_th->oy,&og)<=0){
+ if(th_input_buffer_data(_th,_fin)==0)return feof(_fin)?0:-1;
+ }
+ th_input_queue_page(_th,&og);
+ }
+}
+
+
+
+typedef struct video_input video_input;
+typedef void (*video_input_get_info_func)(void *_ctx,th_info *_ti);
+typedef int (*video_input_fetch_frame_func)(void *_ctx,FILE *_fin,
+ th_ycbcr_buffer _ycbcr);
+typedef void (*video_input_close_func)(void *_ctx);
+
+struct video_input{
+ FILE *fin;
+ video_input_get_info_func get_info;
+ video_input_fetch_frame_func fetch_frame;
+ video_input_close_func close;
+ union{
+ y4m_input y4m;
+ th_input th;
+ }ctx;
+};
+
+static int video_input_open(video_input *_vid,FILE *_fin){
+ char buffer[4];
+ int ret;
+ /* look for magic */
+ ret=fread(buffer,1,4,_fin);
+ if(ret<4)fprintf(stderr,"EOF determining file type of file.\n");
+ else{
+ if(!memcmp(buffer,"YUV4",4)){
+ if(y4m_input_open(&_vid->ctx.y4m,_fin,buffer,4)>=0){
+ /*fprintf(stderr,"Original %s is %dx%d %.02f fps %s video.\n",
+ f,_y4m->pic_w,_y4m->pic_h,(double)_y4m->fps_n/_y4m->fps_d,_y4m->chroma_type);*/
+ _vid->fin=_fin;
+ _vid->get_info=(video_input_get_info_func)y4m_input_get_info;
+ _vid->fetch_frame=(video_input_fetch_frame_func)y4m_input_fetch_frame;
+ _vid->close=(video_input_close_func)y4m_input_close;
+ return 0;
+ }
+ }
+ else if(!memcmp(buffer,"OggS",4)){
+ if(th_input_open(&_vid->ctx.th,_fin,buffer,4)>=0){
+ _vid->fin=_fin;
+ _vid->get_info=(video_input_get_info_func)th_input_get_info;
+ _vid->fetch_frame=(video_input_fetch_frame_func)th_input_fetch_frame;
+ _vid->close=(video_input_close_func)th_input_close;
+ return 0;
+ }
+ }
+ else fprintf(stderr,"Unknown file type.\n");
+ }
+ return -1;
+}
+
+static void video_input_get_info(video_input *_vid,th_info *_ti){
+ (*_vid->get_info)(&_vid->ctx,_ti);
+}
+
+static int video_input_fetch_frame(video_input *_vid,th_ycbcr_buffer _ycbcr){
+ return (*_vid->fetch_frame)(&_vid->ctx,_vid->fin,_ycbcr);
+}
+
+static void video_input_close(video_input *_vid){
+ (*_vid->close)(&_vid->ctx);
+ fclose(_vid->fin);
+}
+
+
+
+static void usage(char *_argv[]){
+ fprintf(stderr,"Usage: %s [options] <video1> <video2>\n"
+ " <video1> and <video1> may be either YUV4MPEG or Ogg Theora files.\n\n"
+ " Options:\n\n"
+ " -f --frame-type Show frame type and QI value for each Theora frame.\n"
+ " -s --summary Only output the summary line.\n"
+ " -y --luma-only Only output values for the luma channel.\n",_argv[0]);
+}
+
+int main(int _argc,char *_argv[]){
+ video_input vid1;
+ th_info ti1;
+ video_input vid2;
+ th_info ti2;
+ ogg_int64_t gsqerr;
+ ogg_int64_t gnpixels;
+ ogg_int64_t gplsqerr[3];
+ ogg_int64_t gplnpixels[3];
+ int frameno;
+ FILE *fin;
+ int long_option_index;
+ int c;
+#ifdef _WIN32
+ /*We need to set stdin/stdout to binary mode on windows.
+ Beware the evil ifdef.
+ We avoid these where we can, but this one we cannot.
+ Don't add any more, you'll probably go to hell if you do.*/
+ _setmode(_fileno(stdin),_O_BINARY);
+#endif
+ /*Process option arguments.*/
+ while((c=getopt_long(_argc,_argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 'f':show_frame_type=1;break;
+ case 's':summary_only=1;break;
+ case 'y':luma_only=1;break;
+ default:usage(_argv);break;
+ }
+ }
+ if(optind+2!=_argc){
+ usage(_argv);
+ exit(1);
+ }
+ fin=strcmp(_argv[optind],"-")==0?stdin:fopen(_argv[optind],"rb");
+ if(fin==NULL){
+ fprintf(stderr,"Unable to open '%s' for extraction.\n",_argv[optind]);
+ exit(1);
+ }
+ fprintf(stderr,"Opening %s...\n",_argv[optind]);
+ if(video_input_open(&vid1,fin)<0)exit(1);
+ video_input_get_info(&vid1,&ti1);
+ fin=strcmp(_argv[optind+1],"-")==0?stdin:fopen(_argv[optind+1],"rb");
+ if(fin==NULL){
+ fprintf(stderr,"Unable to open '%s' for extraction.\n",_argv[optind+1]);
+ exit(1);
+ }
+ fprintf(stderr,"Opening %s...\n",_argv[optind+1]);
+ if(video_input_open(&vid2,fin)<0)exit(1);
+ video_input_get_info(&vid2,&ti2);
+ /*Check to make sure these videos are compatible.*/
+ if(ti1.pic_width!=ti2.pic_width||ti1.pic_height!=ti2.pic_height){
+ fprintf(stderr,"Video resolution does not match.\n");
+ exit(1);
+ }
+ if(ti1.pixel_fmt!=ti2.pixel_fmt){
+ fprintf(stderr,"Pixel formats do not match.\n");
+ exit(1);
+ }
+ if((ti1.pic_x&!(ti1.pixel_fmt&1))!=(ti2.pic_x&!(ti2.pixel_fmt&1))||
+ (ti1.pic_y&!(ti1.pixel_fmt&2))!=(ti2.pic_y&!(ti2.pixel_fmt&2))){
+ fprintf(stderr,"Chroma subsampling offsets do not match.\n");
+ exit(1);
+ }
+ if(ti1.fps_numerator*(ogg_int64_t)ti2.fps_denominator!=
+ ti2.fps_numerator*(ogg_int64_t)ti1.fps_denominator){
+ fprintf(stderr,"Warning: framerates do not match.\n");
+ }
+ if(ti1.aspect_numerator*(ogg_int64_t)ti2.aspect_denominator!=
+ ti2.aspect_numerator*(ogg_int64_t)ti1.aspect_denominator){
+ fprintf(stderr,"Warning: aspect ratios do not match.\n");
+ }
+ gsqerr=gplsqerr[0]=gplsqerr[1]=gplsqerr[2]=0;
+ gnpixels=gplnpixels[0]=gplnpixels[1]=gplnpixels[2]=0;
+ for(frameno=0;;frameno++){
+ th_ycbcr_buffer f1;
+ th_ycbcr_buffer f2;
+ ogg_int64_t plsqerr[3];
+ long plnpixels[3];
+ ogg_int64_t sqerr;
+ long npixels;
+ int ret1;
+ int ret2;
+ int pli;
+ ret1=video_input_fetch_frame(&vid1,f1);
+ ret2=video_input_fetch_frame(&vid2,f2);
+ if(ret1==0&&ret2==0)break;
+ else if(ret1<0||ret2<0)break;
+ else if(ret1==0){
+ fprintf(stderr,"%s ended before %s.\n",
+ _argv[optind],_argv[optind+1]);
+ break;
+ }
+ else if(ret2==0){
+ fprintf(stderr,"%s ended before %s.\n",
+ _argv[optind+1],_argv[optind]);
+ break;
+ }
+ /*Okay, we got one frame from each.*/
+ sqerr=0;
+ npixels=0;
+ for(pli=0;pli<3;pli++){
+ int xdec;
+ int ydec;
+ int y1;
+ int y2;
+ xdec=pli&&!(ti1.pixel_fmt&1);
+ ydec=pli&&!(ti1.pixel_fmt&2);
+ plsqerr[pli]=0;
+ plnpixels[pli]=0;
+ for(y1=ti1.pic_y>>ydec,y2=ti2.pic_y>>ydec;
+ y1<ti1.pic_y+ti1.pic_height+ydec>>ydec;y1++,y2++){
+ int x1;
+ int x2;
+ for(x1=ti1.pic_x>>xdec,x2=ti2.pic_x>>xdec;
+ x1<ti1.pic_x+ti1.pic_width+xdec>>xdec;x1++,x2++){
+ int d;
+ d=*(f1[pli].data+y1*f1[pli].stride+x1)-
+ *(f2[pli].data+y2*f2[pli].stride+x2);
+ plsqerr[pli]+=d*d;
+ plnpixels[pli]++;
+ }
+ }
+ sqerr+=plsqerr[pli];
+ gplsqerr[pli]+=plsqerr[pli];
+ npixels+=plnpixels[pli];
+ gplnpixels[pli]+=plnpixels[pli];
+ }
+ if(!summary_only){
+ if(!luma_only){
+ printf("%08i: %-7lG (Y': %-7lG Cb: %-7lG Cr: %-7lG)\n",frameno,
+ 10*(log10(255*255)+log10(npixels)-log10(sqerr)),
+ 10*(log10(255*255)+log10(plnpixels[0])-log10(plsqerr[0])),
+ 10*(log10(255*255)+log10(plnpixels[1])-log10(plsqerr[1])),
+ 10*(log10(255*255)+log10(plnpixels[2])-log10(plsqerr[2])));
+ }
+ else{
+ printf("%08i: %-7lG\n",frameno,
+ 10*(log10(255*255)+log10(plnpixels[0])-log10(plsqerr[0])));
+ }
+ }
+ gsqerr+=sqerr;
+ gnpixels+=npixels;
+ }
+ if(!luma_only){
+ printf("Total: %-7lG (Y': %-7lG Cb: %-7lG Cr: %-7lG)\n",
+ 10*(log10(255*255)+log10(gnpixels)-log10(gsqerr)),
+ 10*(log10(255*255)+log10(gplnpixels[0])-log10(gplsqerr[0])),
+ 10*(log10(255*255)+log10(gplnpixels[1])-log10(gplsqerr[1])),
+ 10*(log10(255*255)+log10(gplnpixels[2])-log10(gplsqerr[2])));
+ }
+ else{
+ printf("Total: %-7lG\n",
+ 10*(log10(255*255)+log10(gplnpixels[0])-log10(gplsqerr[0])));
+ }
+ video_input_close(&vid1);
+ video_input_close(&vid2);
+ return 0;
+}
diff --git a/examples/dump_video.c b/examples/dump_video.c
new file mode 100644
index 0000000..c4ad468
--- /dev/null
+++ b/examples/dump_video.c
@@ -0,0 +1,496 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example dumpvid application; dumps Theora streams
+ last mod: $Id: dump_video.c,v 1.2 2004/03/24 19:12:42 derf Exp $
+
+ ********************************************************************/
+
+/* By Mauricio Piacentini (mauricio at xiph.org) */
+/* simply dump decoded YUV data, for verification of theora bitstream */
+
+#if !defined(_REENTRANT)
+#define _REENTRANT
+#endif
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+#define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timeb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/*Yes, yes, we're going to hell.*/
+#if defined(_WIN32)
+#include <io.h>
+#endif
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include "getopt.h"
+#include "theora/theoradec.h"
+
+const char *optstring = "o:rf";
+struct option options [] = {
+ {"output",required_argument,NULL,'o'},
+ {"raw",no_argument, NULL,'r'}, /*Disable YUV4MPEG2 headers:*/
+ {"fps-only",no_argument, NULL, 'f'}, /* Only interested in fps of decode loop */
+ {NULL,0,NULL,0}
+};
+
+/* Helper; just grab some more compressed bitstream and sync it for
+ page extraction */
+int buffer_data(FILE *in,ogg_sync_state *oy){
+ char *buffer=ogg_sync_buffer(oy,4096);
+ int bytes=fread(buffer,1,4096,in);
+ ogg_sync_wrote(oy,bytes);
+ return(bytes);
+}
+
+/* never forget that globals are a one-way ticket to Hell */
+/* Ogg and codec state for demux/decode */
+ogg_sync_state oy;
+ogg_page og;
+ogg_stream_state vo;
+ogg_stream_state to;
+th_info ti;
+th_comment tc;
+th_setup_info *ts;
+th_dec_ctx *td;
+
+int theora_p=0;
+int theora_processing_headers;
+int stateflag=0;
+
+/* single frame video buffering */
+int videobuf_ready=0;
+ogg_int64_t videobuf_granulepos=-1;
+double videobuf_time=0;
+int raw=0;
+
+FILE* outfile = NULL;
+
+int got_sigint=0;
+static void sigint_handler (int signal) {
+ got_sigint = 1;
+}
+
+static th_ycbcr_buffer ycbcr;
+
+static void stripe_decoded(th_ycbcr_buffer _dst,th_ycbcr_buffer _src,
+ int _fragy0,int _fragy_end){
+ int pli;
+ for(pli=0;pli<3;pli++){
+ int yshift;
+ int y_end;
+ int y;
+ yshift=pli!=0&&!(ti.pixel_fmt&2);
+ y_end=_fragy_end<<3-yshift;
+ /*An implemention intending to display this data would need to check the
+ crop rectangle before proceeding.*/
+ for(y=_fragy0<<3-yshift;y<y_end;y++){
+ memcpy(_dst[pli].data+y*_dst[pli].stride,
+ _src[pli].data+y*_src[pli].stride,_src[pli].width);
+ }
+ }
+}
+
+static void open_video(void){
+ th_stripe_callback cb;
+ int pli;
+ /*Here we allocate a buffer so we can use the striped decode feature.
+ There's no real reason to do this in this application, because we want to
+ write to the file top-down, but the frame gets decoded bottom up, so we
+ have to buffer it all anyway.
+ But this illustrates how the API works.*/
+ for(pli=0;pli<3;pli++){
+ int xshift;
+ int yshift;
+ xshift=pli!=0&&!(ti.pixel_fmt&1);
+ yshift=pli!=0&&!(ti.pixel_fmt&2);
+ ycbcr[pli].data=(unsigned char *)malloc(
+ (ti.frame_width>>xshift)*(ti.frame_height>>yshift)*sizeof(char));
+ ycbcr[pli].stride=ti.frame_width>>xshift;
+ ycbcr[pli].width=ti.frame_width>>xshift;
+ ycbcr[pli].height=ti.frame_height>>yshift;
+ }
+ /*Similarly, since ycbcr is a global, there's no real reason to pass it as
+ the context.
+ In a more object-oriented decoder, we could pass the "this" pointer
+ instead (though in C++, platform-dependent calling convention differences
+ prevent us from using a real member function pointer).*/
+ cb.ctx=ycbcr;
+ cb.stripe_decoded=(th_stripe_decoded_func)stripe_decoded;
+ th_decode_ctl(td,TH_DECCTL_SET_STRIPE_CB,&cb,sizeof(cb));
+}
+
+/*Write out the planar YUV frame, uncropped.*/
+static void video_write(void){
+ int pli;
+ int i;
+ /*Uncomment the following to do normal, non-striped decoding.
+ th_ycbcr_buffer ycbcr;
+ th_decode_ycbcr_out(td,ycbcr);*/
+ if(outfile){
+ if(!raw)fprintf(outfile, "FRAME\n");
+ for(pli=0;pli<3;pli++){
+ for(i=0;i<ycbcr[pli].height;i++){
+ fwrite(ycbcr[pli].data+ycbcr[pli].stride*i, 1,
+ ycbcr[pli].width, outfile);
+ }
+ }
+ }
+}
+
+/* dump the theora comment header */
+static int dump_comments(th_comment *_tc){
+ int i;
+ int len;
+ FILE *out;
+ out=stderr;
+ fprintf(out,"Encoded by %s\n",_tc->vendor);
+ if(_tc->comments){
+ fprintf(out,"theora comment header:\n");
+ for(i=0;i<_tc->comments;i++){
+ if(_tc->user_comments[i]){
+ len=_tc->comment_lengths[i]<INT_MAX?_tc->comment_lengths[i]:INT_MAX;
+ fprintf(out,"\t%.*s\n",len,_tc->user_comments[i]);
+ }
+ }
+ }
+ return 0;
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+ that doesn't belong to it */
+static int queue_page(ogg_page *page){
+ if(theora_p)ogg_stream_pagein(&to,page);
+ return 0;
+}
+
+static void usage(void){
+ fprintf(stderr,
+ "Usage: dumpvid <file.ogv> > outfile\n"
+ "input is read from stdin if no file is passed on the command line\n"
+ "\n"
+ );
+}
+
+int main(int argc,char *argv[]){
+
+ ogg_packet op;
+
+ int long_option_index;
+ int c;
+
+ struct timeb start;
+ struct timeb after;
+ struct timeb last;
+ int fps_only=0;
+ int frames = 0;
+
+ FILE *infile = stdin;
+ outfile = stdout;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
+ /* Beware the evil ifdef. We avoid these where we can, but this one we
+ cannot. Don't add any more, you'll probably go to hell if you do. */
+ _setmode( _fileno( stdin ), _O_BINARY );
+ _setmode( _fileno( stdout ), _O_BINARY );
+#endif
+
+ /* Process option arguments. */
+ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 'o':
+ if(strcmp(optarg,"-")!=0){
+ outfile=fopen(optarg,"wb");
+ if(outfile==NULL){
+ fprintf(stderr,"Unable to open output file '%s'\n", optarg);
+ exit(1);
+ }
+ }else{
+ outfile=stdout;
+ }
+ break;
+
+ case 'r':
+ raw=1;
+ break;
+
+ case 'f':
+ fps_only = 1;
+ outfile = NULL;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ if(optind<argc){
+ infile=fopen(argv[optind],"rb");
+ if(infile==NULL){
+ fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
+ exit(1);
+ }
+ if(++optind<argc){
+ usage();
+ exit(1);
+ }
+ }
+ /*Ok, Ogg parsing.
+ The idea here is we have a bitstream that is made up of Ogg pages.
+ The libogg sync layer will find them for us.
+ There may be pages from several logical streams interleaved; we find the
+ first theora stream and ignore any others.
+ Then we pass the pages for our stream to the libogg stream layer which
+ assembles our original set of packets out of them.
+ It's the packets that libtheora actually knows how to handle.*/
+
+ /* start up Ogg stream synchronization layer */
+ ogg_sync_init(&oy);
+
+ /* init supporting Theora structures needed in header parsing */
+ th_comment_init(&tc);
+ th_info_init(&ti);
+
+ /*Ogg file open; parse the headers.
+ Theora (like Vorbis) depends on some initial header packets for decoder
+ setup and initialization.
+ We retrieve these first before entering the main decode loop.*/
+
+ /* Only interested in Theora streams */
+ while(!stateflag){
+ int ret=buffer_data(infile,&oy);
+ if(ret==0)break;
+ while(ogg_sync_pageout(&oy,&og)>0){
+ int got_packet;
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&og)){
+ /* don't leak the page; get it into the appropriate stream */
+ queue_page(&og);
+ stateflag=1;
+ break;
+ }
+
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ got_packet = ogg_stream_packetpeek(&test,&op);
+
+ /* identify the codec: try theora */
+ if((got_packet==1) && !theora_p && (theora_processing_headers=
+ th_decode_headerin(&ti,&tc,&ts,&op))>=0){
+ /* it is theora -- save this stream state */
+ memcpy(&to,&test,sizeof(test));
+ theora_p=1;
+ /*Advance past the successfully processed header.*/
+ if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+
+ /* we're expecting more header packets. */
+ while(theora_p && theora_processing_headers){
+ int ret;
+
+ /* look for further theora headers */
+ while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
+ if(ret<0)continue;
+ theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op);
+ if(theora_processing_headers<0){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ exit(1);
+ }
+ else if(theora_processing_headers>0){
+ /*Advance past the successfully processed header.*/
+ ogg_stream_packetout(&to,NULL);
+ }
+ theora_p++;
+ }
+
+ /*Stop now so we don't fail if there aren't enough pages in a short
+ stream.*/
+ if(!(theora_p && theora_processing_headers))break;
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og); /* demux into the appropriate stream */
+ }else{
+ int ret=buffer_data(infile,&oy); /* someone needs more data */
+ if(ret==0){
+ fprintf(stderr,"End of file while searching for codec headers.\n");
+ exit(1);
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if(theora_p){
+ dump_comments(&tc);
+ td=th_decode_alloc(&ti,ts);
+ fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
+ "Encoded frame content is %dx%d with %dx%d offset\n",
+ to.serialno,ti.frame_width,ti.frame_height,
+ (double)ti.fps_numerator/ti.fps_denominator,
+ ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y);
+ }else{
+ /* tear down the partial theora setup */
+ th_info_clear(&ti);
+ th_comment_clear(&tc);
+ }
+ /*Either way, we're done with the codec setup data.*/
+ th_setup_free(ts);
+
+ /* open video */
+ if(theora_p)open_video();
+
+ if(!raw && outfile){
+ static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422","444"};
+ if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){
+ fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt);
+ exit(1);
+ }
+ fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n",
+ CHROMA_TYPES[ti.pixel_fmt],ti.frame_width,ti.frame_height,
+ ti.fps_numerator,ti.fps_denominator,'p',
+ ti.aspect_numerator,ti.aspect_denominator);
+ }
+
+ /* install signal handler */
+ signal (SIGINT, sigint_handler);
+
+ /*Finally the main decode loop.
+
+ It's one Theora packet per frame, so this is pretty straightforward if
+ we're not trying to maintain sync with other multiplexed streams.
+
+ The videobuf_ready flag is used to maintain the input buffer in the libogg
+ stream state.
+ If there's no output frame available at the end of the decode step, we must
+ need more input data.
+ We could simplify this by just using the return code on
+ ogg_page_packetout(), but the flag system extends easily to the case where
+ you care about more than one multiplexed stream (like with audio
+ playback).
+ In that case, just maintain a flag for each decoder you care about, and
+ pull data when any one of them stalls.
+
+ videobuf_time holds the presentation time of the currently buffered video
+ frame.
+ We ignore this value.*/
+
+ stateflag=0; /* playback has not begun */
+ /* queue any remaining pages from data we buffered but that did not
+ contain headers */
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+
+ if(fps_only){
+ ftime(&start);
+ ftime(&last);
+ }
+
+ while(!got_sigint){
+
+ while(theora_p && !videobuf_ready){
+ /* theora is one in, one out... */
+ if(ogg_stream_packetout(&to,&op)>0){
+
+ if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
+ videobuf_time=th_granule_time(td,videobuf_granulepos);
+ videobuf_ready=1;
+ frames++;
+ if(fps_only)
+ ftime(&after);
+ }
+
+ }else
+ break;
+ }
+
+ if(fps_only && (videobuf_ready || fps_only==2)){
+ long ms =
+ after.time*1000.+after.millitm-
+ (last.time*1000.+last.millitm);
+
+ if(ms>500 || fps_only==1 ||
+ (feof(infile) && !videobuf_ready)){
+ float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
+ fps_only=2;
+
+ ms = after.time*1000.+after.millitm-
+ (start.time*1000.+start.millitm);
+
+ fprintf(stderr,"\rframe:%d rate:%.2fx ",
+ frames,
+ frames*1000./(ms*file_fps));
+ memcpy(&last,&after,sizeof(last));
+ }
+ }
+
+ if(!videobuf_ready && feof(infile))break;
+
+ if(!videobuf_ready){
+ /* no data yet for somebody. Grab another page */
+ buffer_data(infile,&oy);
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+ }
+ /* dumpvideo frame, and get new one */
+ else if(outfile)video_write();
+
+ videobuf_ready=0;
+ }
+
+ /* end of decoder loop -- close everything */
+
+ if(theora_p){
+ ogg_stream_clear(&to);
+ th_decode_free(td);
+ th_comment_clear(&tc);
+ th_info_clear(&ti);
+ }
+ ogg_sync_clear(&oy);
+
+ if(infile && infile!=stdin)fclose(infile);
+ if(outfile && outfile!=stdout)fclose(outfile);
+
+ fprintf(stderr, "\n\n%d frames\n", frames);
+ fprintf(stderr, "\nDone.\n");
+
+ return(0);
+
+}
diff --git a/examples/encoder_example.c b/examples/encoder_example.c
new file mode 100644
index 0000000..4b73b64
--- /dev/null
+++ b/examples/encoder_example.c
@@ -0,0 +1,1826 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example encoder application; makes an Ogg Theora/Vorbis
+ file from YUV4MPEG2 and WAV input
+ last mod: $Id: encoder_example.c 16517 2009-08-25 19:48:57Z giles $
+
+ ********************************************************************/
+
+#if !defined(_REENTRANT)
+#define _REENTRANT
+#endif
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+#define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#include <stdio.h>
+#if !defined(_WIN32)
+#include <getopt.h>
+#include <unistd.h>
+#else
+#include "getopt.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include "theora/theoraenc.h"
+#include "vorbis/codec.h"
+#include "vorbis/vorbisenc.h"
+
+#ifdef _WIN32
+/*supply missing headers and functions to Win32. going to hell, I know*/
+#include <fcntl.h>
+#include <io.h>
+
+static double rint(double x)
+{
+ if (x < 0.0)
+ return (double)(int)(x - 0.5);
+ else
+ return (double)(int)(x + 0.5);
+}
+#endif
+
+const char *optstring = "b:e:o:a:A:v:V:s:S:f:F:ck:d:z:\1\2\3\4";
+struct option options [] = {
+ {"begin-time",required_argument,NULL,'b'},
+ {"end-time",required_argument,NULL,'e'},
+ {"output",required_argument,NULL,'o'},
+ {"audio-rate-target",required_argument,NULL,'A'},
+ {"video-rate-target",required_argument,NULL,'V'},
+ {"audio-quality",required_argument,NULL,'a'},
+ {"video-quality",required_argument,NULL,'v'},
+ {"aspect-numerator",required_argument,NULL,'s'},
+ {"aspect-denominator",required_argument,NULL,'S'},
+ {"framerate-numerator",required_argument,NULL,'f'},
+ {"framerate-denominator",required_argument,NULL,'F'},
+ {"vp3-compatible",no_argument,NULL,'c'},
+ {"speed",required_argument,NULL,'z'},
+ {"soft-target",no_argument,NULL,'\1'},
+ {"keyframe-freq",required_argument,NULL,'k'},
+ {"buf-delay",required_argument,NULL,'d'},
+ {"two-pass",no_argument,NULL,'\2'},
+ {"first-pass",required_argument,NULL,'\3'},
+ {"second-pass",required_argument,NULL,'\4'},
+ {NULL,0,NULL,0}
+};
+
+/* You'll go to Hell for using globals. */
+
+FILE *audio=NULL;
+FILE *video=NULL;
+
+int audio_ch=0;
+int audio_hz=0;
+
+float audio_q=.1f;
+int audio_r=-1;
+int vp3_compatible=0;
+
+int frame_w=0;
+int frame_h=0;
+int pic_w=0;
+int pic_h=0;
+int pic_x=0;
+int pic_y=0;
+int video_fps_n=-1;
+int video_fps_d=-1;
+int video_par_n=-1;
+int video_par_d=-1;
+char interlace;
+int src_c_dec_h=2;
+int src_c_dec_v=2;
+int dst_c_dec_h=2;
+int dst_c_dec_v=2;
+char chroma_type[16];
+
+/*The size of each converted frame buffer.*/
+size_t y4m_dst_buf_sz;
+/*The amount to read directly into the converted frame buffer.*/
+size_t y4m_dst_buf_read_sz;
+/*The size of the auxilliary buffer.*/
+size_t y4m_aux_buf_sz;
+/*The amount to read into the auxilliary buffer.*/
+size_t y4m_aux_buf_read_sz;
+
+/*The function used to perform chroma conversion.*/
+typedef void (*y4m_convert_func)(unsigned char *_dst,unsigned char *_aux);
+
+y4m_convert_func y4m_convert=NULL;
+
+int video_r=-1;
+int video_q=-1;
+ogg_uint32_t keyframe_frequency=0;
+int buf_delay=-1;
+
+long begin_sec=-1;
+long begin_usec=0;
+long end_sec=-1;
+long end_usec=0;
+
+static void usage(void){
+ fprintf(stderr,
+ "Usage: encoder_example [options] [audio_file] video_file\n\n"
+ "Options: \n\n"
+ " -o --output <filename.ogv> file name for encoded output;\n"
+ " If this option is not given, the\n"
+ " compressed data is sent to stdout.\n\n"
+ " -A --audio-rate-target <n> bitrate target for Vorbis audio;\n"
+ " use -a and not -A if at all possible,\n"
+ " as -a gives higher quality for a given\n"
+ " bitrate.\n\n"
+ " -V --video-rate-target <n> bitrate target for Theora video\n\n"
+ " --soft-target Use a large reservoir and treat the rate\n"
+ " as a soft target; rate control is less\n"
+ " strict but resulting quality is usually\n"
+ " higher/smoother overall. Soft target also\n"
+ " allows an optional -v setting to specify\n"
+ " a minimum allowed quality.\n\n"
+ " --two-pass Compress input using two-pass rate control\n"
+ " This option requires that the input to the\n"
+ " to the encoder is seekable and performs\n"
+ " both passes automatically.\n\n"
+ " --first-pass <filename> Perform first-pass of a two-pass rate\n"
+ " controlled encoding, saving pass data to\n"
+ " <filename> for a later second pass\n\n"
+ " --second-pass <filename> Perform second-pass of a two-pass rate\n"
+ " controlled encoding, reading first-pass\n"
+ " data from <filename>. The first pass\n"
+ " data must come from a first encoding pass\n"
+ " using identical input video to work\n"
+ " properly.\n\n"
+ " -a --audio-quality <n> Vorbis quality selector from -1 to 10\n"
+ " (-1 yields smallest files but lowest\n"
+ " fidelity; 10 yields highest fidelity\n"
+ " but large files. '2' is a reasonable\n"
+ " default).\n\n"
+ " -v --video-quality <n> Theora quality selector from 0 to 10\n"
+ " (0 yields smallest files but lowest\n"
+ " video quality. 10 yields highest\n"
+ " fidelity but large files).\n\n"
+ " -s --aspect-numerator <n> Aspect ratio numerator, default is 0\n"
+ " or extracted from YUV input file\n"
+ " -S --aspect-denominator <n> Aspect ratio denominator, default is 0\n"
+ " or extracted from YUV input file\n"
+ " -f --framerate-numerator <n> Frame rate numerator, can be extracted\n"
+ " from YUV input file. ex: 30000000\n"
+ " -F --framerate-denominator <n> Frame rate denominator, can be extracted\n"
+ " from YUV input file. ex: 1000000\n"
+ " The frame rate nominator divided by this\n"
+ " determinates the frame rate in units per tick\n"
+ " -k --keyframe-freq <n> Keyframe frequency\n"
+ " -z --speed <n> Sets the encoder speed level. Higher speed\n"
+ " levels favor quicker encoding over better\n"
+ " quality per bit. Depending on the encoding\n"
+ " mode, and the internal algorithms used,\n"
+ " quality may actually improve with higher\n"
+ " speeds, but in this case bitrate will also\n"
+ " likely increase. The maximum value, and the\n"
+ " meaning of each value, are implementation-\n"
+ " specific and may change depending on the\n"
+ " current encoding mode (rate constrained,\n"
+ " two-pass, etc.).\n"
+ " -d --buf-delay <n> Buffer delay (in frames). Longer delays\n"
+ " allow smoother rate adaptation and provide\n"
+ " better overall quality, but require more\n"
+ " client side buffering and add latency. The\n"
+ " default value is the keyframe interval for\n"
+ " one-pass encoding (or somewhat larger if\n"
+ " --soft-target is used) and infinite for\n"
+ " two-pass encoding.\n"
+ " -b --begin-time <h:m:s.d> Begin encoding at offset into input\n"
+ " -e --end-time <h:m:s.d> End encoding at offset into input\n"
+ "encoder_example accepts only uncompressed RIFF WAV format audio and\n"
+ "YUV4MPEG2 uncompressed video.\n\n");
+ exit(1);
+}
+
+static int y4m_parse_tags(char *_tags){
+ int got_w;
+ int got_h;
+ int got_fps;
+ int got_interlace;
+ int got_par;
+ int got_chroma;
+ int tmp_video_fps_n;
+ int tmp_video_fps_d;
+ int tmp_video_par_n;
+ int tmp_video_par_d;
+ char *p;
+ char *q;
+ got_w=got_h=got_fps=got_interlace=got_par=got_chroma=0;
+ for(p=_tags;;p=q){
+ /*Skip any leading spaces.*/
+ while(*p==' ')p++;
+ /*If that's all we have, stop.*/
+ if(p[0]=='\0')break;
+ /*Find the end of this tag.*/
+ for(q=p+1;*q!='\0'&&*q!=' ';q++);
+ /*Process the tag.*/
+ switch(p[0]){
+ case 'W':{
+ if(sscanf(p+1,"%d",&pic_w)!=1)return -1;
+ got_w=1;
+ }break;
+ case 'H':{
+ if(sscanf(p+1,"%d",&pic_h)!=1)return -1;
+ got_h=1;
+ }break;
+ case 'F':{
+ if(sscanf(p+1,"%d:%d",&tmp_video_fps_n,&tmp_video_fps_d)!=2)return -1;
+ got_fps=1;
+ }break;
+ case 'I':{
+ interlace=p[1];
+ got_interlace=1;
+ }break;
+ case 'A':{
+ if(sscanf(p+1,"%d:%d",&tmp_video_par_n,&tmp_video_par_d)!=2)return -1;
+ got_par=1;
+ }break;
+ case 'C':{
+ if(q-p>16)return -1;
+ memcpy(chroma_type,p+1,q-p-1);
+ chroma_type[q-p-1]='\0';
+ got_chroma=1;
+ }break;
+ /*Ignore unknown tags.*/
+ }
+ }
+ if(!got_w||!got_h||!got_fps||!got_interlace||!got_par)return -1;
+ /*Chroma-type is not specified in older files, e.g., those generated by
+ mplayer.*/
+ if(!got_chroma)strcpy(chroma_type,"420");
+ /*Update fps and aspect ratio globals if not specified in the command line.*/
+ if(video_fps_n==-1)video_fps_n=tmp_video_fps_n;
+ if(video_fps_d==-1)video_fps_d=tmp_video_fps_d;
+ if(video_par_n==-1)video_par_n=tmp_video_par_n;
+ if(video_par_d==-1)video_par_d=tmp_video_par_d;
+ return 0;
+}
+
+/*All anti-aliasing filters in the following conversion functions are based on
+ one of two window functions:
+ The 6-tap Lanczos window (for down-sampling and shifts):
+ sinc(\pi*t)*sinc(\pi*t/3), |t|<3 (sinc(t)==sin(t)/t)
+ 0, |t|>=3
+ The 4-tap Mitchell window (for up-sampling):
+ 7|t|^3-12|t|^2+16/3, |t|<1
+ -(7/3)|x|^3+12|x|^2-20|x|+32/3, |t|<2
+ 0, |t|>=2
+ The number of taps is intentionally kept small to reduce computational
+ overhead and limit ringing.
+
+ The taps from these filters are scaled so that their sum is 1, and the result
+ is scaled by 128 and rounded to integers to create a filter whose
+ intermediate values fit inside 16 bits.
+ Coefficients are rounded in such a way as to ensure their sum is still 128,
+ which is usually equivalent to normal rounding.*/
+
+#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a))
+#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a))
+#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c)))
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420mpeg2 chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ The 4:2:2 modes look exactly the same, except there are twice as many chroma
+ lines, and they are vertically co-sited with the luma samples in both the
+ mpeg2 and jpeg cases (thus requiring no vertical resampling).*/
+static void y4m_convert_42xmpeg2_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+ int c_w;
+ int c_h;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=pic_w*pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
+ c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+ window.*/
+ for(x=0;x<OC_MINI(c_w,2);x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-3;x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ _dst[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[c_w-1]+64>>7,255);
+ }
+ _dst+=c_w;
+ _aux+=c_w;
+ }
+ }
+}
+
+/*This format is only used for interlaced content, but is included for
+ completeness.
+
+ 420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420paldv chroma samples are sited like:
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ Then we use another filter to move the C_r location down one quarter pixel,
+ and the C_b location up one quarter pixel.*/
+static void y4m_convert_42xpaldv_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=pic_w*pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(pic_w+1)/2;
+ c_h=(pic_h+dst_c_dec_h-1)/dst_c_dec_h;
+ c_sz=c_w*c_h;
+ /*First do the horizontal re-sampling.
+ This is the same as the mpeg2 case, except that after the horizontal case,
+ we need to apply a second vertical filter.*/
+ tmp=_aux+2*c_sz;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+ window.*/
+ for(x=0;x<OC_MINI(c_w,2);x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[0]-17*_aux[OC_MAXI(x-1,0)]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[OC_MINI(x+3,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-3;x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[x+1]-9*_aux[x+2]+_aux[x+3]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ tmp[x]=(unsigned char)OC_CLAMPI(0,4*_aux[x-2]-17*_aux[x-1]+
+ 114*_aux[x]+35*_aux[OC_MINI(x+1,c_w-1)]-9*_aux[OC_MINI(x+2,c_w-1)]+
+ _aux[c_w-1]+64>>7,255);
+ }
+ tmp+=c_w;
+ _aux+=c_w;
+ }
+ switch(pli){
+ case 1:{
+ tmp-=c_sz;
+ /*Slide C_b up a quarter-pel.
+ This is the same filter used above, but in the other order.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(c_h,3);y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[0]-
+ 9*tmp[OC_MAXI(y-2,0)*c_w]+35*tmp[OC_MAXI(y-1,0)*c_w]+
+ 114*tmp[y*c_w]-17*tmp[OC_MINI(y+1,c_h-1)*c_w]+
+ 4*tmp[OC_MINI(y+2,c_h-1)*c_w]+64>>7,255);
+ }
+ for(;y<c_h-2;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[(y-3)*c_w]-
+ 9*tmp[(y-2)*c_w]+35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-
+ 17*tmp[(y+1)*c_w]+4*tmp[(y+2)*c_w]+64>>7,255);
+ }
+ for(;y<c_h;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,tmp[(y-3)*c_w]-
+ 9*tmp[(y-2)*c_w]+35*tmp[(y-1)*c_w]+114*tmp[y*c_w]-
+ 17*tmp[OC_MINI(y+1,c_h-1)*c_w]+4*tmp[(c_h-1)*c_w]+64>>7,255);
+ }
+ _dst++;
+ tmp++;
+ }
+ _dst+=c_sz-c_w;
+ tmp-=c_w;
+ }break;
+ case 2:{
+ tmp-=c_sz;
+ /*Slide C_r down a quarter-pel.
+ This is the same as the horizontal filter.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(c_h,2);y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[0]-
+ 17*tmp[OC_MAXI(y-1,0)*c_w]+114*tmp[y*c_w]+
+ 35*tmp[OC_MINI(y+1,c_h-1)*c_w]-9*tmp[OC_MINI(y+2,c_h-1)*c_w]+
+ tmp[OC_MINI(y+3,c_h-1)*c_w]+64>>7,255);
+ }
+ for(;y<c_h-3;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[(y-2)*c_w]-
+ 17*tmp[(y-1)*c_w]+114*tmp[y*c_w]+35*tmp[(y+1)*c_w]-
+ 9*tmp[(y+2)*c_w]+tmp[(y+3)*c_w]+64>>7,255);
+ }
+ for(;y<c_h;y++){
+ _dst[y*c_w]=(unsigned char)OC_CLAMPI(0,4*tmp[(y-2)*c_w]-
+ 17*tmp[(y-1)*c_w]+114*tmp[y*c_w]+35*tmp[OC_MINI(y+1,c_h-1)*c_w]-
+ 9*tmp[OC_MINI(y+2,c_h-1)*c_w]+tmp[(c_h-1)*c_w]+64>>7,255);
+ }
+ _dst++;
+ tmp++;
+ }
+ }break;
+ }
+ /*For actual interlaced material, this would have to be done separately on
+ each field, and the shift amounts would be different.
+ C_r moves down 1/8, C_b up 3/8 in the top field, and C_r moves down 3/8,
+ C_b up 1/8 in the bottom field.
+ The corresponding filters would be:
+ Down 1/8 (reverse order for up): [3 -11 125 15 -4 0]/128
+ Down 3/8 (reverse order for up): [4 -19 98 56 -13 2]/128*/
+ }
+}
+
+/*422jpeg chroma samples are sited like:
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 411 chroma samples are sited like:
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a filter to resample at site locations one eighth pixel (at the source
+ chroma plane's horizontal resolution) and five eighths of a pixel to the
+ right.*/
+static void y4m_convert_411_422jpeg(unsigned char *_dst,
+ unsigned char *_aux){
+ int c_w;
+ int dst_c_w;
+ int c_h;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst+=pic_w*pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w=(pic_w+src_c_dec_h-1)/src_c_dec_h;
+ dst_c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
+ c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
+ for(pli=1;pli<3;pli++){
+ for(y=0;y<c_h;y++){
+ /*Filters: [1 110 18 -1]/128 and [-3 50 86 -5]/128, both derived from a
+ 4-tap Mitchell window.*/
+ for(x=0;x<OC_MINI(c_w,1);x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,111*_aux[0]+
+ 18*_aux[OC_MINI(1,c_w-1)]-_aux[OC_MINI(2,c_w-1)]+64>>7,255);
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,47*_aux[0]+
+ 86*_aux[OC_MINI(1,c_w-1)]-5*_aux[OC_MINI(2,c_w-1)]+64>>7,255);
+ }
+ for(;x<c_w-2;x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+
+ 18*_aux[x+1]-_aux[x+2]+64>>7,255);
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+
+ 86*_aux[x+1]-5*_aux[x+2]+64>>7,255);
+ }
+ for(;x<c_w;x++){
+ _dst[x<<1]=(unsigned char)OC_CLAMPI(0,_aux[x-1]+110*_aux[x]+
+ 18*_aux[OC_MINI(x+1,c_w-1)]-_aux[c_w-1]+64>>7,255);
+ if((x<<1|1)<dst_c_w){
+ _dst[x<<1|1]=(unsigned char)OC_CLAMPI(0,-3*_aux[x-1]+50*_aux[x]+
+ 86*_aux[OC_MINI(x+1,c_w-1)]-5*_aux[c_w-1]+64>>7,255);
+ }
+ }
+ _dst+=dst_c_w;
+ _aux+=c_w;
+ }
+ }
+}
+
+/*The image is padded with empty chroma components at 4:2:0.
+ This costs about 17 bits a frame to code.*/
+static void y4m_convert_mono_420jpeg(unsigned char *_dst,
+ unsigned char *_aux){
+ int c_sz;
+ _dst+=pic_w*pic_h;
+ c_sz=((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
+ memset(_dst,128,c_sz*2);
+}
+
+#if 0
+/*Right now just 444 to 420.
+ Not too hard to generalize.*/
+static void y4m_convert_4xxjpeg_42xjpeg(unsigned char *_dst,
+ unsigned char *_aux){
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int pic_sz;
+ int tmp_sz;
+ int c_sz;
+ int pli;
+ int y;
+ int x;
+ /*Compute the size of each chroma plane.*/
+ c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
+ c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
+ pic_sz=pic_w*pic_h;
+ tmp_sz=c_w*pic_h;
+ c_sz=c_w*c_h;
+ _dst+=pic_sz;
+ for(pli=1;pli<3;pli++){
+ tmp=_aux+pic_sz;
+ /*In reality, the horizontal and vertical steps could be pipelined, for
+ less memory consumption and better cache performance, but we do them
+ separately for simplicity.*/
+ /*First do horizontal filtering (convert to 4:2:2)*/
+ /*Filter: [3 -17 78 78 -17 3]/128, derived from a 6-tap Lanczos window.*/
+ for(y=0;y<pic_h;y++){
+ for(x=0;x<OC_MINI(pic_w,2);x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,64*_aux[0]+78*_aux[OC_MINI(1,pic_w-1)]-
+ 17*_aux[OC_MINI(2,pic_w-1)]+3*_aux[OC_MINI(3,pic_w-1)]+64>>7,255);
+ }
+ for(;x<pic_w-3;x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[x+3])-17*(_aux[x-1]+_aux[x+2])+
+ 78*(_aux[x]+_aux[x+1])+64>>7,255);
+ }
+ for(;x<pic_w;x+=2){
+ tmp[x>>1]=OC_CLAMPI(0,3*(_aux[x-2]+_aux[pic_w-1])-
+ 17*(_aux[x-1]+_aux[OC_MINI(x+2,pic_w-1)])+
+ 78*(_aux[x]+_aux[OC_MINI(x+1,pic_w-1)])+64>>7,255);
+ }
+ tmp+=c_w;
+ _aux+=pic_w;
+ }
+ _aux-=pic_sz;
+ tmp-=tmp_sz;
+ /*Now do the vertical filtering.*/
+ for(x=0;x<c_w;x++){
+ for(y=0;y<OC_MINI(pic_h,2);y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,64*tmp[0]+78*tmp[OC_MINI(1,pic_h-1)*c_w]-
+ 17*tmp[OC_MINI(2,pic_h-1)*c_w]+3*tmp[OC_MINI(3,pic_h-1)*c_w]+
+ 64>>7,255);
+ }
+ for(;y<pic_h-3;y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]+tmp[(y+3)*c_w])-
+ 17*(tmp[(y-1)*c_w]+tmp[(y+2)*c_w])+78*(tmp[y*c_w]+tmp[(y+1)*c_w])+
+ 64>>7,255);
+ }
+ for(;y<pic_h;y+=2){
+ _dst[(y>>1)*c_w]=OC_CLAMPI(0,3*(tmp[(y-2)*c_w]+tmp[(pic_h-1)*c_w])-
+ 17*(tmp[(y-1)*c_w]+tmp[OC_MINI(y+2,pic_h-1)*c_w])+
+ 78*(tmp[y*c_w]+tmp[OC_MINI(y+1,pic_h-1)*c_w])+64>>7,255);
+ }
+ tmp++;
+ _dst++;
+ }
+ _dst-=c_w;
+ }
+}
+#endif
+
+
+/*No conversion function needed.*/
+static void y4m_convert_null(unsigned char *_dst,
+ unsigned char *_aux){
+}
+
+static void id_file(char *f){
+ FILE *test;
+ unsigned char buffer[80];
+ int ret;
+
+ /* open it, look for magic */
+
+ if(!strcmp(f,"-")){
+ /* stdin */
+ test=stdin;
+ }else{
+ test=fopen(f,"rb");
+ if(!test){
+ fprintf(stderr,"Unable to open file %s.\n",f);
+ exit(1);
+ }
+ }
+
+ ret=fread(buffer,1,4,test);
+ if(ret<4){
+ fprintf(stderr,"EOF determining file type of file %s.\n",f);
+ exit(1);
+ }
+
+ if(!memcmp(buffer,"RIFF",4)){
+ /* possible WAV file */
+
+ if(audio){
+ /* umm, we already have one */
+ fprintf(stderr,"Multiple RIFF WAVE files specified on command line.\n");
+ exit(1);
+ }
+
+ /* Parse the rest of the header */
+
+ ret=fread(buffer,1,8,test);
+ if(ret<8)goto riff_err;
+ if(!memcmp(buffer+4,"WAVE",4)){
+
+ while(!feof(test)){
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+ if(!memcmp("fmt",buffer,3)){
+
+ /* OK, this is our audio specs chunk. Slurp it up. */
+
+ ret=fread(buffer,1,20,test);
+ if(ret<20)goto riff_err;
+
+ if(memcmp(buffer+4,"\001\000",2)){
+ fprintf(stderr,"The WAV file %s is in a compressed format; "
+ "can't read it.\n",f);
+ exit(1);
+ }
+
+ audio=test;
+ audio_ch=buffer[6]+(buffer[7]<<8);
+ audio_hz=buffer[8]+(buffer[9]<<8)+
+ (buffer[10]<<16)+(buffer[11]<<24);
+
+ if(buffer[18]+(buffer[19]<<8)!=16){
+ fprintf(stderr,"Can only read 16 bit WAV files for now.\n");
+ exit(1);
+ }
+
+ /* Now, align things to the beginning of the data */
+ /* Look for 'dataxxxx' */
+ while(!feof(test)){
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+ if(!memcmp("data",buffer,4)){
+ /* We're there. Ignore the declared size for now. */
+ ret=fread(buffer,1,4,test);
+ if(ret<4)goto riff_err;
+
+ fprintf(stderr,"File %s is 16 bit %d channel %d Hz RIFF WAV audio.\n",
+ f,audio_ch,audio_hz);
+
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ fprintf(stderr,"Couldn't find WAVE data in RIFF file %s.\n",f);
+ exit(1);
+
+ }
+ if(!memcmp(buffer,"YUV4",4)){
+ /* possible YUV2MPEG2 format file */
+ /* read until newline, or 80 cols, whichever happens first */
+ int i;
+ for(i=0;i<79;i++){
+ ret=fread(buffer+i,1,1,test);
+ if(ret<1)goto yuv_err;
+ if(buffer[i]=='\n')break;
+ }
+ if(i==79){
+ fprintf(stderr,"Error parsing %s header; not a YUV2MPEG2 file?\n",f);
+ }
+ buffer[i]='\0';
+
+ if(!memcmp(buffer,"MPEG",4)){
+
+ if(video){
+ /* umm, we already have one */
+ fprintf(stderr,"Multiple video files specified on command line.\n");
+ exit(1);
+ }
+
+ if(buffer[4]!='2'){
+ fprintf(stderr,"Incorrect YUV input file version; YUV4MPEG2 required.\n");
+ }
+
+ ret=y4m_parse_tags((char *)buffer+5);
+ if(ret<0){
+ fprintf(stderr,"Error parsing YUV4MPEG2 header in file %s.\n",f);
+ exit(1);
+ }
+
+ if(interlace!='p'){
+ fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n");
+ exit(1);
+ }
+
+ if(strcmp(chroma_type,"420")==0||strcmp(chroma_type,"420jpeg")==0){
+ src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+ y4m_dst_buf_read_sz=pic_w*pic_h+2*((pic_w+1)/2)*((pic_h+1)/2);
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+ y4m_convert=y4m_convert_null;
+ }
+ else if(strcmp(chroma_type,"420mpeg2")==0){
+ src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+ y4m_dst_buf_read_sz=pic_w*pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+ y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
+ }
+ else if(strcmp(chroma_type,"420paldv")==0){
+ src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=2;
+ y4m_dst_buf_read_sz=pic_w*pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ y4m_aux_buf_sz=3*((pic_w+1)/2)*((pic_h+1)/2);
+ y4m_aux_buf_read_sz=2*((pic_w+1)/2)*((pic_h+1)/2);
+ y4m_convert=y4m_convert_42xpaldv_42xjpeg;
+ }
+ else if(strcmp(chroma_type,"422")==0){
+ src_c_dec_h=dst_c_dec_h=2;
+ src_c_dec_v=dst_c_dec_v=1;
+ y4m_dst_buf_read_sz=pic_w*pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+1)/2)*pic_h;
+ y4m_convert=y4m_convert_42xmpeg2_42xjpeg;
+ }
+ else if(strcmp(chroma_type,"411")==0){
+ src_c_dec_h=4;
+ /*We don't want to introduce any additional sub-sampling, so we
+ promote 4:1:1 material to 4:2:2, as the closest format Theora can
+ handle.*/
+ dst_c_dec_h=2;
+ src_c_dec_v=dst_c_dec_v=1;
+ y4m_dst_buf_read_sz=pic_w*pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=2*((pic_w+3)/4)*pic_h;
+ y4m_convert=y4m_convert_411_422jpeg;
+ }
+ else if(strcmp(chroma_type,"444")==0){
+ src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+ y4m_dst_buf_read_sz=pic_w*pic_h*3;
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+ y4m_convert=y4m_convert_null;
+ }
+ else if(strcmp(chroma_type,"444alpha")==0){
+ src_c_dec_h=dst_c_dec_h=src_c_dec_v=dst_c_dec_v=1;
+ y4m_dst_buf_read_sz=pic_w*pic_h*3;
+ /*Read the extra alpha plane into the aux buf.
+ It will be discarded.*/
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=pic_w*pic_h;
+ y4m_convert=y4m_convert_null;
+ }
+ else if(strcmp(chroma_type,"mono")==0){
+ src_c_dec_h=src_c_dec_v=0;
+ dst_c_dec_h=dst_c_dec_v=2;
+ y4m_dst_buf_read_sz=pic_w*pic_h;
+ y4m_aux_buf_sz=y4m_aux_buf_read_sz=0;
+ y4m_convert=y4m_convert_mono_420jpeg;
+ }
+ else{
+ fprintf(stderr,"Unknown chroma sampling type: %s\n",chroma_type);
+ exit(1);
+ }
+ /*The size of the final frame buffers is always computed from the
+ destination chroma decimation type.*/
+ y4m_dst_buf_sz=pic_w*pic_h+2*((pic_w+dst_c_dec_h-1)/dst_c_dec_h)*
+ ((pic_h+dst_c_dec_v-1)/dst_c_dec_v);
+
+ video=test;
+
+ fprintf(stderr,"File %s is %dx%d %.02f fps %s video.\n",
+ f,pic_w,pic_h,(double)video_fps_n/video_fps_d,chroma_type);
+
+ return;
+ }
+ }
+ fprintf(stderr,"Input file %s is neither a WAV nor YUV4MPEG2 file.\n",f);
+ exit(1);
+
+ riff_err:
+ fprintf(stderr,"EOF parsing RIFF file %s.\n",f);
+ exit(1);
+ yuv_err:
+ fprintf(stderr,"EOF parsing YUV4MPEG2 file %s.\n",f);
+ exit(1);
+
+}
+
+int spinner=0;
+char *spinascii="|/-\\";
+void spinnit(void){
+ spinner++;
+ if(spinner==4)spinner=0;
+ fprintf(stderr,"\r%c",spinascii[spinner]);
+}
+
+int fetch_and_process_audio(FILE *audio,ogg_page *audiopage,
+ ogg_stream_state *vo,
+ vorbis_dsp_state *vd,
+ vorbis_block *vb,
+ int audioflag){
+ static ogg_int64_t samples_sofar=0;
+ ogg_packet op;
+ int i,j;
+ ogg_int64_t beginsample = audio_hz*begin_sec + audio_hz*begin_usec*.000001;
+ ogg_int64_t endsample = audio_hz*end_sec + audio_hz*end_usec*.000001;
+
+ while(audio && !audioflag){
+ /* process any audio already buffered */
+ spinnit();
+ if(ogg_stream_pageout(vo,audiopage)>0) return 1;
+ if(ogg_stream_eos(vo))return 0;
+
+ {
+ /* read and process more audio */
+ signed char readbuffer[4096];
+ signed char *readptr=readbuffer;
+ int toread=4096/2/audio_ch;
+ int bytesread=fread(readbuffer,1,toread*2*audio_ch,audio);
+ int sampread=bytesread/2/audio_ch;
+ float **vorbis_buffer;
+ int count=0;
+
+ if(bytesread<=0 ||
+ (samples_sofar>=endsample && endsample>0)){
+ /* end of file. this can be done implicitly, but it's
+ easier to see here in non-clever fashion. Tell the
+ library we're at end of stream so that it can handle the
+ last frame and mark end of stream in the output properly */
+ vorbis_analysis_wrote(vd,0);
+ }else{
+ if(samples_sofar < beginsample){
+ if(samples_sofar+sampread > beginsample){
+ readptr += (beginsample-samples_sofar)*2*audio_ch;
+ sampread += samples_sofar-beginsample;
+ samples_sofar = sampread+beginsample;
+ }else{
+ samples_sofar += sampread;
+ sampread = 0;
+ }
+ }else{
+ samples_sofar += sampread;
+ }
+
+ if(samples_sofar > endsample && endsample > 0)
+ sampread-= (samples_sofar - endsample);
+
+ if(sampread>0){
+
+ vorbis_buffer=vorbis_analysis_buffer(vd,sampread);
+ /* uninterleave samples */
+ for(i=0;i<sampread;i++){
+ for(j=0;j<audio_ch;j++){
+ vorbis_buffer[j][i]=((readptr[count+1]<<8)|
+ (0x00ff&(int)readptr[count]))/32768.f;
+ count+=2;
+ }
+ }
+
+ vorbis_analysis_wrote(vd,sampread);
+ }
+ }
+
+ while(vorbis_analysis_blockout(vd,vb)==1){
+
+ /* analysis, assume we want to use bitrate management */
+ vorbis_analysis(vb,NULL);
+ vorbis_bitrate_addblock(vb);
+
+ /* weld packets into the bitstream */
+ while(vorbis_bitrate_flushpacket(vd,&op))
+ ogg_stream_packetin(vo,&op);
+
+ }
+ }
+ }
+
+ return audioflag;
+}
+
+static int frame_state=-1;
+static ogg_int64_t frames=0;
+static unsigned char *yuvframe[3];
+static th_ycbcr_buffer ycbcr;
+
+int fetch_and_process_video_packet(FILE *video,FILE *twopass_file,int passno,
+ th_enc_ctx *td,ogg_packet *op){
+ int ret;
+ int pic_sz;
+ int frame_c_w;
+ int frame_c_h;
+ int c_w;
+ int c_h;
+ int c_sz;
+ ogg_int64_t beginframe;
+ ogg_int64_t endframe;
+ spinnit();
+ beginframe=(video_fps_n*begin_sec+video_fps_n*begin_usec*.000001)/video_fps_d;
+ endframe=(video_fps_n*end_sec+video_fps_n*end_usec*.000001)/video_fps_d;
+ if(frame_state==-1){
+ /* initialize the double frame buffer */
+ yuvframe[0]=(unsigned char *)malloc(y4m_dst_buf_sz);
+ yuvframe[1]=(unsigned char *)malloc(y4m_dst_buf_sz);
+ yuvframe[2]=(unsigned char *)malloc(y4m_aux_buf_sz);
+ frame_state=0;
+ }
+ pic_sz=pic_w*pic_h;
+ frame_c_w=frame_w/dst_c_dec_h;
+ frame_c_h=frame_h/dst_c_dec_v;
+ c_w=(pic_w+dst_c_dec_h-1)/dst_c_dec_h;
+ c_h=(pic_h+dst_c_dec_v-1)/dst_c_dec_v;
+ c_sz=c_w*c_h;
+ /* read and process more video */
+ /* video strategy reads one frame ahead so we know when we're
+ at end of stream and can mark last video frame as such
+ (vorbis audio has to flush one frame past last video frame
+ due to overlap and thus doesn't need this extra work */
+
+ /* have two frame buffers full (if possible) before
+ proceeding. after first pass and until eos, one will
+ always be full when we get here */
+ for(;frame_state<2 && (frames<endframe || endframe<0);){
+ char c,frame[6];
+ int ret=fread(frame,1,6,video);
+ /* match and skip the frame header */
+ if(ret<6)break;
+ if(memcmp(frame,"FRAME",5)){
+ fprintf(stderr,"Loss of framing in YUV input data\n");
+ exit(1);
+ }
+ if(frame[5]!='\n'){
+ int j;
+ for(j=0;j<79;j++)
+ if(fread(&c,1,1,video)&&c=='\n')break;
+ if(j==79){
+ fprintf(stderr,"Error parsing YUV frame header\n");
+ exit(1);
+ }
+ }
+ /*Read the frame data that needs no conversion.*/
+ if(fread(yuvframe[frame_state],1,y4m_dst_buf_read_sz,video)!=
+ y4m_dst_buf_read_sz){
+ fprintf(stderr,"Error reading YUV frame data.\n");
+ exit(1);
+ }
+ /*Read the frame data that does need conversion.*/
+ if(fread(yuvframe[2],1,y4m_aux_buf_read_sz,video)!=y4m_aux_buf_read_sz){
+ fprintf(stderr,"Error reading YUV frame data.\n");
+ exit(1);
+ }
+ /*Now convert the just read frame.*/
+ (*y4m_convert)(yuvframe[frame_state],yuvframe[2]);
+ frames++;
+ if(frames>=beginframe)
+ frame_state++;
+ }
+ /* check to see if there are dupes to flush */
+ if(th_encode_packetout(td,frame_state<1,op)>0)return 1;
+ if(frame_state<1){
+ /* can't get here unless YUV4MPEG stream has no video */
+ fprintf(stderr,"Video input contains no frames.\n");
+ exit(1);
+ }
+ /* Theora is a one-frame-in,one-frame-out system; submit a frame
+ for compression and pull out the packet */
+ /* in two-pass mode's second pass, we need to submit first-pass data */
+ if(passno==2){
+ for(;;){
+ static unsigned char buffer[80];
+ static int buf_pos;
+ int bytes;
+ /*Ask the encoder how many bytes it would like.*/
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0);
+ if(bytes<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If it's got enough, stop.*/
+ if(bytes==0)break;
+ /*Read in some more bytes, if necessary.*/
+ if(bytes>80-buf_pos)bytes=80-buf_pos;
+ if(bytes>0&&fread(buffer+buf_pos,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Could not read frame data from two-pass data file!\n");
+ exit(1);
+ }
+ /*And pass them off.*/
+ ret=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,buffer,bytes);
+ if(ret<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If the encoder consumed the whole buffer, reset it.*/
+ if(ret>=bytes)buf_pos=0;
+ /*Otherwise remember how much it used.*/
+ else buf_pos+=ret;
+ }
+ }
+ /*We submit the buffer to the library as if it were padded, but we do not
+ actually allocate space for the padding.
+ This is okay, because with the 1.0 API the library will never read data from the padded
+ region.*/
+ ycbcr[0].width=frame_w;
+ ycbcr[0].height=frame_h;
+ ycbcr[0].stride=pic_w;
+ ycbcr[0].data=yuvframe[0]-pic_x-pic_y*pic_w;
+ ycbcr[1].width=frame_c_w;
+ ycbcr[1].height=frame_c_h;
+ ycbcr[1].stride=c_w;
+ ycbcr[1].data=yuvframe[0]+pic_sz-(pic_x/dst_c_dec_h)-(pic_y/dst_c_dec_v)*c_w;
+ ycbcr[2].width=frame_c_w;
+ ycbcr[2].height=frame_c_h;
+ ycbcr[2].stride=c_w;
+ ycbcr[2].data=ycbcr[1].data+c_sz;
+ th_encode_ycbcr_in(td,ycbcr);
+ {
+ unsigned char *temp=yuvframe[0];
+ yuvframe[0]=yuvframe[1];
+ yuvframe[1]=temp;
+ frame_state--;
+ }
+ /* in two-pass mode's first pass we need to extract and save the pass data */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass data from encoder.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ /* if there was only one frame, it's the last in the stream */
+ ret = th_encode_packetout(td,frame_state<1,op);
+ if(passno==1 && frame_state<1){
+ /* need to read the final (summary) packet */
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass summary data from encoder.\n");
+ exit(1);
+ }
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ return ret;
+}
+
+
+int fetch_and_process_video(FILE *video,ogg_page *videopage,
+ ogg_stream_state *to,th_enc_ctx *td,FILE *twopass_file,int passno,
+ int videoflag){
+ ogg_packet op;
+ int ret;
+ /* is there a video page flushed? If not, work until there is. */
+ while(!videoflag){
+ if(ogg_stream_pageout(to,videopage)>0) return 1;
+ if(ogg_stream_eos(to)) return 0;
+ ret=fetch_and_process_video_packet(video,twopass_file,passno,td,&op);
+ if(ret<=0)return 0;
+ ogg_stream_packetin(to,&op);
+ }
+ return videoflag;
+}
+
+static int ilog(unsigned _v){
+ int ret;
+ for(ret=0;_v;ret++)_v>>=1;
+ return ret;
+}
+
+int main(int argc,char *argv[]){
+ int c,long_option_index,ret;
+
+ ogg_stream_state to; /* take physical pages, weld into a logical
+ stream of packets */
+ ogg_stream_state vo; /* take physical pages, weld into a logical
+ stream of packets */
+ ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
+ ogg_packet op; /* one raw packet of data for decode */
+
+ th_enc_ctx *td;
+ th_info ti;
+ th_comment tc;
+
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream
+ settings */
+ vorbis_comment vc; /* struct that stores all the user comments */
+
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ int speed=-1;
+ int audioflag=0;
+ int videoflag=0;
+ int akbps=0;
+ int vkbps=0;
+ int soft_target=0;
+
+ ogg_int64_t audio_bytesout=0;
+ ogg_int64_t video_bytesout=0;
+ double timebase;
+
+ FILE *outfile = stdout;
+
+ FILE *twopass_file = NULL;
+ fpos_t video_rewind_pos;
+ int twopass=0;
+ int passno;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
+ /* if we were reading/writing a file, it would also need to in
+ binary mode, eg, fopen("file.wav","wb"); */
+ /* Beware the evil ifdef. We avoid these where we can, but this one we
+ cannot. Don't add any more, you'll probably go to hell if you do. */
+ _setmode( _fileno( stdin ), _O_BINARY );
+ _setmode( _fileno( stdout ), _O_BINARY );
+#endif
+
+ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 'o':
+ outfile=fopen(optarg,"wb");
+ if(outfile==NULL){
+ fprintf(stderr,"Unable to open output file '%s'\n", optarg);
+ exit(1);
+ }
+ break;;
+
+ case 'a':
+ audio_q=(float)(atof(optarg)*.099);
+ if(audio_q<-.1 || audio_q>1){
+ fprintf(stderr,"Illegal audio quality (choose -1 through 10)\n");
+ exit(1);
+ }
+ audio_r=-1;
+ break;
+
+ case 'v':
+ video_q=(int)rint(6.3*atof(optarg));
+ if(video_q<0 || video_q>63){
+ fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+ exit(1);
+ }
+ break;
+
+ case 'A':
+ audio_r=(int)(atof(optarg)*1000);
+ if(audio_q<0){
+ fprintf(stderr,"Illegal audio quality (choose > 0 please)\n");
+ exit(1);
+ }
+ audio_q=-99;
+ break;
+
+ case 'V':
+ video_r=(int)rint(atof(optarg)*1000);
+ if(video_r<1){
+ fprintf(stderr,"Illegal video bitrate (choose > 0 please)\n");
+ exit(1);
+ }
+ break;
+
+ case '\1':
+ soft_target=1;
+ break;
+
+ case 's':
+ video_par_n=(int)rint(atof(optarg));
+ break;
+
+ case 'S':
+ video_par_d=(int)rint(atof(optarg));
+ break;
+
+ case 'f':
+ video_fps_n=(int)rint(atof(optarg));
+ break;
+
+ case 'F':
+ video_fps_d=(int)rint(atof(optarg));
+ break;
+
+ case 'c':
+ vp3_compatible=1;
+ break;
+
+ case 'k':
+ keyframe_frequency=rint(atof(optarg));
+ if(keyframe_frequency<1 || keyframe_frequency>2147483647){
+ fprintf(stderr,"Illegal keyframe frequency\n");
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ buf_delay=atoi(optarg);
+ if(buf_delay<=0){
+ fprintf(stderr,"Illegal buffer delay\n");
+ exit(1);
+ }
+ break;
+
+ case 'z':
+ speed=atoi(optarg);
+ if(speed<0){
+ fprintf(stderr,"Illegal speed level\n");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ {
+ char *pos=strchr(optarg,':');
+ begin_sec=atol(optarg);
+ if(pos){
+ char *pos2=strchr(++pos,':');
+ begin_sec*=60;
+ begin_sec+=atol(pos);
+ if(pos2){
+ pos2++;
+ begin_sec*=60;
+ begin_sec+=atol(pos2);
+ pos=pos2;
+ }
+ }else
+ pos=optarg;
+ pos=strchr(pos,'.');
+ if(pos){
+ int digits = strlen(++pos);
+ begin_usec=atol(pos);
+ while(digits++ < 6)
+ begin_usec*=10;
+ }
+ }
+ break;
+ case 'e':
+ {
+ char *pos=strchr(optarg,':');
+ end_sec=atol(optarg);
+ if(pos){
+ char *pos2=strchr(++pos,':');
+ end_sec*=60;
+ end_sec+=atol(pos);
+ if(pos2){
+ pos2++;
+ end_sec*=60;
+ end_sec+=atol(pos2);
+ pos=pos2;
+ }
+ }else
+ pos=optarg;
+ pos=strchr(pos,'.');
+ if(pos){
+ int digits = strlen(++pos);
+ end_usec=atol(pos);
+ while(digits++ < 6)
+ end_usec*=10;
+ }
+ }
+ break;
+ case '\2':
+ twopass=3; /* perform both passes */
+ twopass_file=tmpfile();
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open temporary file for twopass data\n");
+ exit(1);
+ }
+ break;
+ case '\3':
+ twopass=1; /* perform first pass */
+ twopass_file=fopen(optarg,"wb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open \'%s\' for twopass data\n",optarg);
+ exit(1);
+ }
+ break;
+ case '\4':
+ twopass=2; /* perform second pass */
+ twopass_file=fopen(optarg,"rb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open twopass data file \'%s\'",optarg);
+ exit(1);
+ }
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if(soft_target){
+ if(video_r<=0){
+ fprintf(stderr,"Soft rate target (--soft-target) requested without a bitrate (-V).\n");
+ exit(1);
+ }
+ if(video_q==-1)
+ video_q=0;
+ }else{
+ if(video_q==-1){
+ if(video_r>0)
+ video_q=0;
+ else
+ video_q=48;
+ }
+ }
+
+ if(keyframe_frequency<=0){
+ /*Use a default keyframe frequency of 64 for 1-pass (streaming) mode, and
+ 256 for two-pass mode.*/
+ keyframe_frequency=twopass?256:64;
+ }
+
+ while(optind<argc){
+ /* assume that anything following the options must be a filename */
+ id_file(argv[optind]);
+ optind++;
+ }
+
+ if(twopass==3){
+ /* verify that the input is seekable! */
+ if(video){
+ if(fseek(video,0,SEEK_CUR)){
+ fprintf(stderr,"--two-pass (automatic two-pass) requires the video input\n"
+ "to be seekable. For non-seekable input, encoder_example\n"
+ "must be run twice, first with the --first-pass option, then\n"
+ "with the --second-pass option.\n\n");
+ exit(1);
+ }
+ if(fgetpos(video,&video_rewind_pos)<0){
+ fprintf(stderr,"Unable to determine start position of video data.\n");
+ exit(1);
+ }
+ }
+ }
+
+ /* Set up Ogg output stream */
+ srand(time(NULL));
+ ogg_stream_init(&to,rand()); /* oops, add one ot the above */
+
+ /* initialize Vorbis assuming we have audio to compress. */
+ if(audio && twopass!=1){
+ ogg_stream_init(&vo,rand());
+ vorbis_info_init(&vi);
+ if(audio_q>-99)
+ ret = vorbis_encode_init_vbr(&vi,audio_ch,audio_hz,audio_q);
+ else
+ ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,
+ (int)(64870*(ogg_int64_t)audio_r>>16),-1);
+ if(ret){
+ fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n"
+ "the requested quality or bitrate.\n\n");
+ exit(1);
+ }
+
+ vorbis_comment_init(&vc);
+ vorbis_analysis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+ }
+
+ for(passno=(twopass==3?1:twopass);passno<=(twopass==3?2:twopass);passno++){
+ /* Set up Theora encoder */
+ if(!video){
+ fprintf(stderr,"No video files submitted for compression?\n");
+ exit(1);
+ }
+ /* Theora has a divisible-by-sixteen restriction for the encoded frame size */
+ /* scale the picture size up to the nearest /16 and calculate offsets */
+ frame_w=pic_w+15&~0xF;
+ frame_h=pic_h+15&~0xF;
+ /*Force the offsets to be even so that chroma samples line up like we
+ expect.*/
+ pic_x=frame_w-pic_w>>1&~1;
+ pic_y=frame_h-pic_h>>1&~1;
+ th_info_init(&ti);
+ ti.frame_width=frame_w;
+ ti.frame_height=frame_h;
+ ti.pic_width=pic_w;
+ ti.pic_height=pic_h;
+ ti.pic_x=pic_x;
+ ti.pic_y=pic_y;
+ ti.fps_numerator=video_fps_n;
+ ti.fps_denominator=video_fps_d;
+ ti.aspect_numerator=video_par_n;
+ ti.aspect_denominator=video_par_d;
+ ti.colorspace=TH_CS_UNSPECIFIED;
+ /*Account for the Ogg page overhead.
+ This is 1 byte per 255 for lacing values, plus 26 bytes per 4096 bytes for
+ the page header, plus approximately 1/2 byte per packet (not accounted for
+ here).*/
+ ti.target_bitrate=(int)(64870*(ogg_int64_t)video_r>>16);
+ ti.quality=video_q;
+ ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
+ if(dst_c_dec_h==2){
+ if(dst_c_dec_v==2)ti.pixel_fmt=TH_PF_420;
+ else ti.pixel_fmt=TH_PF_422;
+ }
+ else ti.pixel_fmt=TH_PF_444;
+ td=th_encode_alloc(&ti);
+ th_info_clear(&ti);
+ /* setting just the granule shift only allows power-of-two keyframe
+ spacing. Set the actual requested spacing. */
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
+ &keyframe_frequency,sizeof(keyframe_frequency-1));
+ if(ret<0){
+ fprintf(stderr,"Could not set keyframe interval to %d.\n",(int)keyframe_frequency);
+ }
+ if(vp3_compatible){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_VP3_COMPATIBLE,&vp3_compatible,
+ sizeof(vp3_compatible));
+ if(ret<0||!vp3_compatible){
+ fprintf(stderr,"Could not enable strict VP3 compatibility.\n");
+ if(ret>=0){
+ fprintf(stderr,"Ensure your source format is supported by VP3.\n");
+ fprintf(stderr,
+ "(4:2:0 pixel format, width and height multiples of 16).\n");
+ }
+ }
+ }
+ if(soft_target){
+ /* reverse the rate control flags to favor a 'long time' strategy */
+ int arg = TH_RATECTL_CAP_UNDERFLOW;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_FLAGS,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set encoder flags for --soft-target\n");
+ /* Default buffer control is overridden on two-pass */
+ if(!twopass&&buf_delay<0){
+ if((keyframe_frequency*7>>1) > 5*video_fps_n/video_fps_d)
+ arg=keyframe_frequency*7>>1;
+ else
+ arg=5*video_fps_n/video_fps_d;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set rate control buffer for --soft-target\n");
+ }
+ }
+ /* set up two-pass if needed */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes;
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_OUT,&buffer,sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not set up the first pass of two-pass mode.\n");
+ fprintf(stderr,"Did you remember to specify an estimated bitrate?\n");
+ exit(1);
+ }
+ /*Perform a seek test to ensure we can overwrite this placeholder data at
+ the end; this is better than letting the user sit through a whole
+ encode only to find out their pass 1 file is useless at the end.*/
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ if(passno==2){
+ /*Enable the second pass here.
+ We make this call just to set the encoder into 2-pass mode, because
+ by default enabling two-pass sets the buffer delay to the whole file
+ (because there's no way to explicitly request that behavior).
+ If we waited until we were actually encoding, it would overwite our
+ settings.*/
+ if(th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0)<0){
+ fprintf(stderr,"Could not set up the second pass of two-pass mode.\n");
+ exit(1);
+ }
+ if(twopass==3){
+ /* 'automatic' second pass */
+ if(fsetpos(video,&video_rewind_pos)<0){
+ fprintf(stderr,"Could not rewind video input file for second pass!\n");
+ exit(1);
+ }
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ frame_state=0;
+ frames=0;
+ }
+ }
+ /*Now we can set the buffer delay if the user requested a non-default one
+ (this has to be done after two-pass is enabled).*/
+ if(passno!=1&&buf_delay>=0){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,
+ &buf_delay,sizeof(buf_delay));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not set desired buffer delay.\n");
+ }
+ }
+ /*Speed should also be set after the current encoder mode is established,
+ since the available speed levels may change depending.*/
+ if(speed>=0){
+ int speed_max;
+ int ret;
+ ret=th_encode_ctl(td,TH_ENCCTL_GET_SPLEVEL_MAX,
+ &speed_max,sizeof(speed_max));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not determine maximum speed level.\n");
+ speed_max=0;
+ }
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_SPLEVEL,&speed,sizeof(speed));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not set speed level to %i of %i\n",
+ speed,speed_max);
+ if(speed>speed_max){
+ fprintf(stderr,"Setting it to %i instead\n",speed_max);
+ }
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_SPLEVEL,
+ &speed_max,sizeof(speed_max));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not set speed level to %i of %i\n",
+ speed_max,speed_max);
+ }
+ }
+ }
+ /* write the bitstream header packets with proper page interleave */
+ th_comment_init(&tc);
+ /* first packet will get its own page automatically */
+ if(th_encode_flushheader(td,&tc,&op)<=0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ if(passno!=1){
+ ogg_stream_packetin(&to,&op);
+ if(ogg_stream_pageout(&to,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ }
+ /* create the remaining theora headers */
+ for(;;){
+ ret=th_encode_flushheader(td,&tc,&op);
+ if(ret<0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ else if(!ret)break;
+ if(passno!=1)ogg_stream_packetin(&to,&op);
+ }
+ if(audio && passno!=1){
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+ vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
+ ogg_stream_packetin(&vo,&header); /* automatically placed in its own
+ page */
+ if(ogg_stream_pageout(&vo,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ /* remaining vorbis header packets */
+ ogg_stream_packetin(&vo,&header_comm);
+ ogg_stream_packetin(&vo,&header_code);
+ }
+ /* Flush the rest of our headers. This ensures
+ the actual data in each stream will start
+ on a new page, as per spec. */
+ if(passno!=1){
+ for(;;){
+ int result = ogg_stream_flush(&to,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ }
+ }
+ if(audio && passno!=1){
+ for(;;){
+ int result=ogg_stream_flush(&vo,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,outfile);
+ fwrite(og.body,1,og.body_len,outfile);
+ }
+ }
+ /* setup complete. Raw processing loop */
+ switch(passno){
+ case 0: case 2:
+ fprintf(stderr,"\rCompressing.... \n");
+ break;
+ case 1:
+ fprintf(stderr,"\rScanning first pass.... \n");
+ break;
+ }
+ for(;;){
+ int audio_or_video=-1;
+ if(passno==1){
+ ogg_packet op;
+ int ret=fetch_and_process_video_packet(video,twopass_file,passno,td,&op);
+ if(ret<0)break;
+ if(op.e_o_s)break; /* end of stream */
+ timebase=th_granule_time(td,op.granulepos);
+ audio_or_video=1;
+ }else{
+ double audiotime;
+ double videotime;
+ ogg_page audiopage;
+ ogg_page videopage;
+ /* is there an audio page flushed? If not, fetch one if possible */
+ audioflag=fetch_and_process_audio(audio,&audiopage,&vo,&vd,&vb,audioflag);
+ /* is there a video page flushed? If not, fetch one if possible */
+ videoflag=fetch_and_process_video(video,&videopage,&to,td,twopass_file,passno,videoflag);
+ /* no pages of either? Must be end of stream. */
+ if(!audioflag && !videoflag)break;
+ /* which is earlier; the end of the audio page or the end of the
+ video page? Flush the earlier to stream */
+ audiotime=
+ audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
+ videotime=
+ videoflag?th_granule_time(td,ogg_page_granulepos(&videopage)):-1;
+ if(!audioflag){
+ audio_or_video=1;
+ } else if(!videoflag) {
+ audio_or_video=0;
+ } else {
+ if(audiotime<videotime)
+ audio_or_video=0;
+ else
+ audio_or_video=1;
+ }
+ if(audio_or_video==1){
+ /* flush a video page */
+ video_bytesout+=fwrite(videopage.header,1,videopage.header_len,outfile);
+ video_bytesout+=fwrite(videopage.body,1,videopage.body_len,outfile);
+ videoflag=0;
+ timebase=videotime;
+ }else{
+ /* flush an audio page */
+ audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,outfile);
+ audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,outfile);
+ audioflag=0;
+ timebase=audiotime;
+ }
+ }
+ if(timebase > 0){
+ int hundredths=(int)(timebase*100-(long)timebase*100);
+ int seconds=(long)timebase%60;
+ int minutes=((long)timebase/60)%60;
+ int hours=(long)timebase/3600;
+ if(audio_or_video)vkbps=(int)rint(video_bytesout*8./timebase*.001);
+ else akbps=(int)rint(audio_bytesout*8./timebase*.001);
+ fprintf(stderr,
+ "\r %d:%02d:%02d.%02d audio: %dkbps video: %dkbps ",
+ hours,minutes,seconds,hundredths,akbps,vkbps);
+ }
+ }
+ if(video)th_encode_free(td);
+ }
+
+ /* clear out state */
+ if(audio && twopass!=1){
+ ogg_stream_clear(&vo);
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+ if(audio!=stdin)fclose(audio);
+ }
+ if(video){
+ ogg_stream_clear(&to);
+ th_comment_clear(&tc);
+ if(video!=stdin)fclose(video);
+ }
+
+ if(outfile && outfile!=stdout)fclose(outfile);
+ if(twopass_file)fclose(twopass_file);
+
+ fprintf(stderr,"\r \ndone.\n\n");
+
+ return(0);
+
+}
diff --git a/examples/getopt.c b/examples/getopt.c
new file mode 100644
index 0000000..9bafa45
--- /dev/null
+++ b/examples/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,98,99,2000,2001
+ 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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; 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. */
+# if defined HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifndef _
+# define _(msgid) gettext (msgid)
+# endif
+# 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
+/* Stored original parameters.
+ 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). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# 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
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#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. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* 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;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (posixly_correct == NULL
+ && argc == __libc_argc && argv == __libc_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;
+
+ if (argc < 1)
+ return -1;
+
+ 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. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# 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 if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* 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/examples/getopt.h b/examples/getopt.h
new file mode 100644
index 0000000..a1b8dd6
--- /dev/null
+++ b/examples/getopt.h
@@ -0,0 +1,180 @@
+/* Declarations for getopt.
+ Copyright (C) 1989-1994, 1996-1999, 2001 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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; 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
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+ standalone, or this is the first header included in the source file.
+ If we are being used with glibc, we need to include <features.h>, but
+ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
+ not defined, include <ctype.h>, which will pull in <features.h> for us
+ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
+ doesn't flood the namespace with stuff the way some other headers do.) */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#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__) || defined __cplusplus
+ 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__) || defined __cplusplus
+# 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/examples/getopt1.c b/examples/getopt1.c
new file mode 100644
index 0000000..22a7efb
--- /dev/null
+++ b/examples/getopt1.c
@@ -0,0 +1,188 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#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__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ 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 'd':
+ printf ("option d 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/examples/player_example.c b/examples/player_example.c
new file mode 100644
index 0000000..dcc02bc
--- /dev/null
+++ b/examples/player_example.c
@@ -0,0 +1,857 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example SDL player application; plays Ogg Theora files (with
+ optional Vorbis audio second stream)
+ last mod: $Id: player_example.c 16551 2009-09-09 17:53:13Z gmaxwell $
+
+ ********************************************************************/
+
+/* far more complex than most Ogg 'example' programs. The complexity
+ of maintaining A/V sync is pretty much unavoidable. It's necessary
+ to actually have audio/video playback to make the hard audio clock
+ sync actually work. If there's audio playback, there might as well
+ be simple video playback as well...
+
+ A simple 'demux and write back streams' would have been easier,
+ it's true. */
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE
+#endif
+#if !defined(_LARGEFILE64_SOURCE)
+#define _LARGEFILE64_SOURCE
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include "theora/theoradec.h"
+#include "vorbis/codec.h"
+#include <SDL.h>
+
+/* yes, this makes us OSS-specific for now. None of SDL, libao, libao2
+ give us any way to determine hardware timing, and since the
+ hard/kernel buffer is going to be most of or > a second, that's
+ just a little bit important */
+#if defined(__FreeBSD__)
+#include <machine/soundcard.h>
+#define AUDIO_DEVICE "/dev/audio"
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <soundcard.h>
+#define AUDIO_DEVICE "/dev/audio"
+#else
+#include <sys/soundcard.h>
+#define AUDIO_DEVICE "/dev/dsp"
+#endif
+#include <sys/ioctl.h>
+
+/* Helper; just grab some more compressed bitstream and sync it for
+ page extraction */
+int buffer_data(FILE *in,ogg_sync_state *oy){
+ char *buffer=ogg_sync_buffer(oy,4096);
+ int bytes=fread(buffer,1,4096,in);
+ ogg_sync_wrote(oy,bytes);
+ return(bytes);
+}
+
+/* never forget that globals are a one-way ticket to Hell */
+/* Ogg and codec state for demux/decode */
+ogg_sync_state oy;
+ogg_page og;
+ogg_stream_state vo;
+ogg_stream_state to;
+th_info ti;
+th_comment tc;
+th_dec_ctx *td;
+th_setup_info *ts;
+vorbis_info vi;
+vorbis_dsp_state vd;
+vorbis_block vb;
+vorbis_comment vc;
+th_pixel_fmt px_fmt;
+
+int theora_p=0;
+int vorbis_p=0;
+int stateflag=0;
+
+/* SDL Video playback structures */
+SDL_Surface *screen;
+SDL_Overlay *yuv_overlay;
+SDL_Rect rect;
+
+/* single frame video buffering */
+int videobuf_ready=0;
+ogg_int64_t videobuf_granulepos=-1;
+double videobuf_time=0;
+
+/* single audio fragment audio buffering */
+int audiobuf_fill=0;
+int audiobuf_ready=0;
+ogg_int16_t *audiobuf;
+ogg_int64_t audiobuf_granulepos=0; /* time position of last sample */
+
+/* audio / video synchronization tracking:
+
+Since this will make it to Google at some point and lots of people
+search for how to do this, a quick rundown of a practical A/V sync
+strategy under Linux [the UNIX where Everything Is Hard]. Naturally,
+this works on other platforms using OSS for sound as well.
+
+In OSS, we don't have reliable access to any precise information on
+the exact current playback position (that, of course would have been
+too easy; the kernel folks like to keep us app people working hard
+doing simple things that should have been solved once and abstracted
+long ago). Hopefully ALSA solves this a little better; we'll probably
+use that once ALSA is the standard in the stable kernel.
+
+We can't use the system clock for a/v sync because audio is hard
+synced to its own clock, and both the system and audio clocks suffer
+from wobble, drift, and a lack of accuracy that can be guaranteed to
+add a reliable percent or so of error. After ten seconds, that's
+100ms. We can't drift by half a second every minute.
+
+Although OSS can't generally tell us where the audio playback pointer
+is, we do know that if we work in complete audio fragments and keep
+the kernel buffer full, a blocking select on the audio buffer will
+give us a writable fragment immediately after playback finishes with
+it. We assume at that point that we know the exact number of bytes in
+the kernel buffer that have not been played (total fragments minus
+one) and calculate clock drift between audio and system then (and only
+then). Damp the sync correction fraction, apply, and walla: A
+reliable A/V clock that even works if it's interrupted. */
+
+long audiofd_totalsize=-1;
+int audiofd_fragsize; /* read and write only complete fragments
+ so that SNDCTL_DSP_GETOSPACE is
+ accurate immediately after a bank
+ switch */
+int audiofd=-1;
+ogg_int64_t audiofd_timer_calibrate=-1;
+
+static void open_audio(){
+ audio_buf_info info;
+ int format=AFMT_S16_NE; /* host endian */
+ int channels=vi.channels;
+ int rate=vi.rate;
+ int ret;
+
+ audiofd=open(AUDIO_DEVICE,O_RDWR);
+ if(audiofd<0){
+ fprintf(stderr,"Could not open audio device " AUDIO_DEVICE ".\n");
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_SETFMT,&format);
+ if(ret){
+ fprintf(stderr,"Could not set 16 bit host-endian playback\n");
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_CHANNELS,&channels);
+ if(ret){
+ fprintf(stderr,"Could not set %d channel playback\n",channels);
+ exit(1);
+ }
+
+ ret=ioctl(audiofd,SNDCTL_DSP_SPEED,&rate);
+ if(ret){
+ fprintf(stderr,"Could not set %d Hz playback\n",rate);
+ exit(1);
+ }
+
+ ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
+ audiofd_fragsize=info.fragsize;
+ audiofd_totalsize=info.fragstotal*info.fragsize;
+
+ audiobuf=malloc(audiofd_fragsize);
+}
+
+static void audio_close(void){
+ if(audiofd>-1){
+ ioctl(audiofd,SNDCTL_DSP_RESET,NULL);
+ close(audiofd);
+ free(audiobuf);
+ }
+}
+
+/* call this only immediately after unblocking from a full kernel
+ having a newly empty fragment or at the point of DMA restart */
+void audio_calibrate_timer(int restart){
+ struct timeval tv;
+ ogg_int64_t current_sample;
+ ogg_int64_t new_time;
+
+ gettimeofday(&tv,0);
+ new_time=tv.tv_sec*1000+tv.tv_usec/1000;
+
+ if(restart){
+ current_sample=audiobuf_granulepos-audiobuf_fill/2/vi.channels;
+ }else
+ current_sample=audiobuf_granulepos-
+ (audiobuf_fill+audiofd_totalsize-audiofd_fragsize)/2/vi.channels;
+
+ new_time-=1000*current_sample/vi.rate;
+
+ audiofd_timer_calibrate=new_time;
+}
+
+/* get relative time since beginning playback, compensating for A/V
+ drift */
+double get_time(){
+ static ogg_int64_t last=0;
+ static ogg_int64_t up=0;
+ ogg_int64_t now;
+ struct timeval tv;
+
+ gettimeofday(&tv,0);
+ now=tv.tv_sec*1000+tv.tv_usec/1000;
+
+ if(audiofd_timer_calibrate==-1)audiofd_timer_calibrate=last=now;
+
+ if(audiofd<0){
+ /* no audio timer to worry about, we can just use the system clock */
+ /* only one complication: If the process is suspended, we should
+ reset timing to account for the gap in play time. Do it the
+ easy/hack way */
+ if(now-last>1000)audiofd_timer_calibrate+=(now-last);
+ last=now;
+ }
+
+ if(now-up>200){
+ double timebase=(now-audiofd_timer_calibrate)*.001;
+ int hundredths=timebase*100-(long)timebase*100;
+ int seconds=(long)timebase%60;
+ int minutes=((long)timebase/60)%60;
+ int hours=(long)timebase/3600;
+
+ fprintf(stderr," Playing: %d:%02d:%02d.%02d \r",
+ hours,minutes,seconds,hundredths);
+ up=now;
+ }
+
+ return (now-audiofd_timer_calibrate)*.001;
+
+}
+
+/* write a fragment to the OSS kernel audio API, but only if we can
+ stuff in a whole fragment without blocking */
+void audio_write_nonblocking(void){
+
+ if(audiobuf_ready){
+ audio_buf_info info;
+ long bytes;
+
+ ioctl(audiofd,SNDCTL_DSP_GETOSPACE,&info);
+ bytes=info.bytes;
+ if(bytes>=audiofd_fragsize){
+ if(bytes==audiofd_totalsize)audio_calibrate_timer(1);
+
+ while(1){
+ bytes=write(audiofd,audiobuf+(audiofd_fragsize-audiobuf_fill),
+ audiofd_fragsize);
+
+ if(bytes>0){
+
+ if(bytes!=audiobuf_fill){
+ /* shouldn't actually be possible... but eh */
+ audiobuf_fill-=bytes;
+ }else
+ break;
+ }
+ }
+
+ audiobuf_fill=0;
+ audiobuf_ready=0;
+
+ }
+ }
+}
+
+/* clean quit on Ctrl-C for SDL and thread shutdown as per SDL example
+ (we don't use any threads, but libSDL does) */
+int got_sigint=0;
+static void sigint_handler (int signal) {
+ got_sigint = 1;
+}
+
+static void open_video(void){
+ int w;
+ int h;
+ w=(ti.pic_x+ti.frame_width+1&~1)-(ti.pic_x&~1);
+ h=(ti.pic_y+ti.frame_height+1&~1)-(ti.pic_y&~1);
+ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+ fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ screen = SDL_SetVideoMode(w, h, 0, SDL_SWSURFACE);
+ if ( screen == NULL ) {
+ fprintf(stderr, "Unable to set %dx%d video: %s\n",
+ w,h,SDL_GetError());
+ exit(1);
+ }
+
+ if (px_fmt==TH_PF_422)
+ yuv_overlay = SDL_CreateYUVOverlay(w, h,
+ SDL_YUY2_OVERLAY,
+ screen);
+ else
+ yuv_overlay = SDL_CreateYUVOverlay(w, h,
+ SDL_YV12_OVERLAY,
+ screen);
+
+ if ( yuv_overlay == NULL ) {
+ fprintf(stderr, "SDL: Couldn't create SDL_yuv_overlay: %s\n",
+ SDL_GetError());
+ exit(1);
+ }
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = w;
+ rect.h = h;
+
+ SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+}
+
+static void video_write(void){
+ int i;
+ th_ycbcr_buffer yuv;
+ int y_offset, uv_offset;
+ th_decode_ycbcr_out(td,yuv);
+ /* Lock SDL_yuv_overlay */
+ if ( SDL_MUSTLOCK(screen) ) {
+ if ( SDL_LockSurface(screen) < 0 ) return;
+ }
+ if (SDL_LockYUVOverlay(yuv_overlay) < 0) return;
+
+ /* let's draw the data on a SDL screen (*screen) */
+ /* deal with border stride */
+ /* reverse u and v for SDL */
+ /* and crop input properly, respecting the encoded frame rect */
+ /* problems may exist for odd frame rect for some encodings */
+
+ y_offset=(ti.pic_x&~1)+yuv[0].stride*(ti.pic_y&~1);
+
+ if (px_fmt==TH_PF_422) {
+ uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y);
+ /* SDL doesn't have a planar 4:2:2 */
+ for(i=0;i<yuv_overlay->h;i++) {
+ int j;
+ char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ char *out = (char *)(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i);
+ for (j=0;j<yuv_overlay->w;j++)
+ out[j*2] = in_y[j];
+ char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*i;
+ char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*i;
+ for (j=0;j<yuv_overlay->w>>1;j++) {
+ out[j*4+1] = in_u[j];
+ out[j*4+3] = in_v[j];
+ }
+ }
+ } else {
+ uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y/2);
+ for(i=0;i<yuv_overlay->h;i++)
+ memcpy(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i,
+ yuv[0].data+y_offset+yuv[0].stride*i,
+ yuv_overlay->w);
+ for(i=0;i<yuv_overlay->h/2;i++){
+ memcpy(yuv_overlay->pixels[1]+yuv_overlay->pitches[1]*i,
+ yuv[2].data+uv_offset+yuv[2].stride*i,
+ yuv_overlay->w/2);
+ memcpy(yuv_overlay->pixels[2]+yuv_overlay->pitches[2]*i,
+ yuv[1].data+uv_offset+yuv[1].stride*i,
+ yuv_overlay->w/2);
+ }
+ }
+
+ /* Unlock SDL_yuv_overlay */
+ if ( SDL_MUSTLOCK(screen) ) {
+ SDL_UnlockSurface(screen);
+ }
+ SDL_UnlockYUVOverlay(yuv_overlay);
+
+
+ /* Show, baby, show! */
+ SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+
+}
+/* dump the theora (or vorbis) comment header */
+static int dump_comments(th_comment *tc){
+ int i, len;
+ char *value;
+ FILE *out=stdout;
+
+ fprintf(out,"Encoded by %s\n",tc->vendor);
+ if(tc->comments){
+ fprintf(out, "theora comment header:\n");
+ for(i=0;i<tc->comments;i++){
+ if(tc->user_comments[i]){
+ len=tc->comment_lengths[i];
+ value=malloc(len+1);
+ memcpy(value,tc->user_comments[i],len);
+ value[len]='\0';
+ fprintf(out, "\t%s\n", value);
+ free(value);
+ }
+ }
+ }
+ return(0);
+}
+
+/* Report the encoder-specified colorspace for the video, if any.
+ We don't actually make use of the information in this example;
+ a real player should attempt to perform color correction for
+ whatever display device it supports. */
+static void report_colorspace(th_info *ti)
+{
+ switch(ti->colorspace){
+ case TH_CS_UNSPECIFIED:
+ /* nothing to report */
+ break;;
+ case TH_CS_ITU_REC_470M:
+ fprintf(stderr," encoder specified ITU Rec 470M (NTSC) color.\n");
+ break;;
+ case TH_CS_ITU_REC_470BG:
+ fprintf(stderr," encoder specified ITU Rec 470BG (PAL) color.\n");
+ break;;
+ default:
+ fprintf(stderr,"warning: encoder specified unknown colorspace (%d).\n",
+ ti->colorspace);
+ break;;
+ }
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+ that doesn't belong to it */
+static int queue_page(ogg_page *page){
+ if(theora_p)ogg_stream_pagein(&to,page);
+ if(vorbis_p)ogg_stream_pagein(&vo,page);
+ return 0;
+}
+
+static void usage(void){
+ fprintf(stderr,
+ "Usage: player_example <file.ogv>\n"
+ "input is read from stdin if no file is passed on the command line\n"
+ "\n"
+ );
+}
+
+int main(int argc,char *const *argv){
+
+ int pp_level_max;
+ int pp_level;
+ int pp_inc;
+ int i,j;
+ ogg_packet op;
+
+ FILE *infile = stdin;
+
+ int frames = 0;
+ int dropped = 0;
+
+#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
+ /* Beware the evil ifdef. We avoid these where we can, but this one we
+ cannot. Don't add any more, you'll probably go to hell if you do. */
+ _setmode( _fileno( stdin ), _O_BINARY );
+#endif
+
+ /* open the input file if any */
+ if(argc==2){
+ infile=fopen(argv[1],"rb");
+ if(infile==NULL){
+ fprintf(stderr,"Unable to open '%s' for playback.\n", argv[1]);
+ exit(1);
+ }
+ }
+ if(argc>2){
+ usage();
+ exit(1);
+ }
+
+ /* start up Ogg stream synchronization layer */
+ ogg_sync_init(&oy);
+
+ /* init supporting Vorbis structures needed in header parsing */
+ vorbis_info_init(&vi);
+ vorbis_comment_init(&vc);
+
+ /* init supporting Theora structures needed in header parsing */
+ th_comment_init(&tc);
+ th_info_init(&ti);
+
+ /* Ogg file open; parse the headers */
+ /* Only interested in Vorbis/Theora streams */
+ while(!stateflag){
+ int ret=buffer_data(infile,&oy);
+ if(ret==0)break;
+ while(ogg_sync_pageout(&oy,&og)>0){
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&og)){
+ /* don't leak the page; get it into the appropriate stream */
+ queue_page(&og);
+ stateflag=1;
+ break;
+ }
+
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ ogg_stream_packetout(&test,&op);
+
+
+ /* identify the codec: try theora */
+ if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){
+ /* it is theora */
+ memcpy(&to,&test,sizeof(test));
+ theora_p=1;
+ }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
+ /* it is vorbis */
+ memcpy(&vo,&test,sizeof(test));
+ vorbis_p=1;
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+
+ /* we're expecting more header packets. */
+ while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
+ int ret;
+
+ /* look for further theora headers */
+ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
+ if(ret<0){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ exit(1);
+ }
+ if(!th_decode_headerin(&ti,&tc,&ts,&op)){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ exit(1);
+ }
+ theora_p++;
+ }
+
+ /* look for more vorbis header packets */
+ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
+ if(ret<0){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ if(vorbis_synthesis_headerin(&vi,&vc,&op)){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ exit(1);
+ }
+ vorbis_p++;
+ if(vorbis_p==3)break;
+ }
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og); /* demux into the appropriate stream */
+ }else{
+ int ret=buffer_data(infile,&oy); /* someone needs more data */
+ if(ret==0){
+ fprintf(stderr,"End of file while searching for codec headers.\n");
+ exit(1);
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if(theora_p){
+ td=th_decode_alloc(&ti,ts);
+ printf("Ogg logical stream %lx is Theora %dx%d %.02f fps",
+ to.serialno,ti.pic_width,ti.pic_height,
+ (double)ti.fps_numerator/ti.fps_denominator);
+ px_fmt=ti.pixel_fmt;
+ switch(ti.pixel_fmt){
+ case TH_PF_420: printf(" 4:2:0 video\n"); break;
+ case TH_PF_422: printf(" 4:2:2 video\n"); break;
+ case TH_PF_444: printf(" 4:4:4 video\n"); break;
+ case TH_PF_RSVD:
+ default:
+ printf(" video\n (UNKNOWN Chroma sampling!)\n");
+ break;
+ }
+ if(ti.pic_width!=ti.frame_width || ti.pic_height!=ti.frame_height)
+ printf(" Frame content is %dx%d with offset (%d,%d).\n",
+ ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y);
+ report_colorspace(&ti);
+ dump_comments(&tc);
+ th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,
+ sizeof(pp_level_max));
+ pp_level=pp_level_max;
+ th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
+ pp_inc=0;
+
+ /*{
+ int arg = 0xffff;
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
+ arg=10;
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
+ }*/
+ }else{
+ /* tear down the partial theora setup */
+ th_info_clear(&ti);
+ th_comment_clear(&tc);
+ }
+
+ th_setup_free(ts);
+
+ if(vorbis_p){
+ vorbis_synthesis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+ fprintf(stderr,"Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n",
+ vo.serialno,vi.channels,vi.rate);
+ }else{
+ /* tear down the partial vorbis setup */
+ vorbis_info_clear(&vi);
+ vorbis_comment_clear(&vc);
+ }
+
+ /* open audio */
+ if(vorbis_p)open_audio();
+
+ /* open video */
+ if(theora_p)open_video();
+
+ /* install signal handler as SDL clobbered the default */
+ signal (SIGINT, sigint_handler);
+
+ /* on to the main decode loop. We assume in this example that audio
+ and video start roughly together, and don't begin playback until
+ we have a start frame for both. This is not necessarily a valid
+ assumption in Ogg A/V streams! It will always be true of the
+ example_encoder (and most streams) though. */
+
+ stateflag=0; /* playback has not begun */
+ while(!got_sigint){
+
+ /* we want a video and audio frame ready to go at all times. If
+ we have to buffer incoming, buffer the compressed data (ie, let
+ ogg do the buffering) */
+ while(vorbis_p && !audiobuf_ready){
+ int ret;
+ float **pcm;
+
+ /* if there's pending, decoded audio, grab it */
+ if((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0){
+ int count=audiobuf_fill/2;
+ int maxsamples=(audiofd_fragsize-audiobuf_fill)/2/vi.channels;
+ for(i=0;i<ret && i<maxsamples;i++)
+ for(j=0;j<vi.channels;j++){
+ int val=rint(pcm[j][i]*32767.f);
+ if(val>32767)val=32767;
+ if(val<-32768)val=-32768;
+ audiobuf[count++]=val;
+ }
+ vorbis_synthesis_read(&vd,i);
+ audiobuf_fill+=i*vi.channels*2;
+ if(audiobuf_fill==audiofd_fragsize)audiobuf_ready=1;
+ if(vd.granulepos>=0)
+ audiobuf_granulepos=vd.granulepos-ret+i;
+ else
+ audiobuf_granulepos+=i;
+
+ }else{
+
+ /* no pending audio; is there a pending packet to decode? */
+ if(ogg_stream_packetout(&vo,&op)>0){
+ if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
+ vorbis_synthesis_blockin(&vd,&vb);
+ }else /* we need more data; break out to suck in another page */
+ break;
+ }
+ }
+
+ while(theora_p && !videobuf_ready){
+ /* theora is one in, one out... */
+ if(ogg_stream_packetout(&to,&op)>0){
+
+ if(pp_inc){
+ pp_level+=pp_inc;
+ th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,
+ sizeof(pp_level));
+ pp_inc=0;
+ }
+ /*HACK: This should be set after a seek or a gap, but we might not have
+ a granulepos for the first packet (we only have them for the last
+ packet on a page), so we just set it as often as we get it.
+ To do this right, we should back-track from the last packet on the
+ page and compute the correct granulepos for the first packet after
+ a seek or a gap.*/
+ if(op.granulepos>=0){
+ th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos,
+ sizeof(op.granulepos));
+ }
+ if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){
+ videobuf_time=th_granule_time(td,videobuf_granulepos);
+ frames++;
+
+ /* is it already too old to be useful? This is only actually
+ useful cosmetically after a SIGSTOP. Note that we have to
+ decode the frame even if we don't show it (for now) due to
+ keyframing. Soon enough libtheora will be able to deal
+ with non-keyframe seeks. */
+
+ if(videobuf_time>=get_time())
+ videobuf_ready=1;
+ else{
+ /*If we are too slow, reduce the pp level.*/
+ pp_inc=pp_level>0?-1:0;
+ dropped++;
+ }
+ }
+
+ }else
+ break;
+ }
+
+ if(!videobuf_ready && !audiobuf_ready && feof(infile))break;
+
+ if(!videobuf_ready || !audiobuf_ready){
+ /* no data yet for somebody. Grab another page */
+ buffer_data(infile,&oy);
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+ }
+
+ /* If playback has begun, top audio buffer off immediately. */
+ if(stateflag) audio_write_nonblocking();
+
+ /* are we at or past time for this video frame? */
+ if(stateflag && videobuf_ready && videobuf_time<=get_time()){
+ video_write();
+ videobuf_ready=0;
+ }
+
+ if(stateflag &&
+ (audiobuf_ready || !vorbis_p) &&
+ (videobuf_ready || !theora_p) &&
+ !got_sigint){
+ /* we have an audio frame ready (which means the audio buffer is
+ full), it's not time to play video, so wait until one of the
+ audio buffer is ready or it's near time to play video */
+
+ /* set up select wait on the audiobuffer and a timeout for video */
+ struct timeval timeout;
+ fd_set writefs;
+ fd_set empty;
+ int n=0;
+
+ FD_ZERO(&writefs);
+ FD_ZERO(&empty);
+ if(audiofd>=0){
+ FD_SET(audiofd,&writefs);
+ n=audiofd+1;
+ }
+
+ if(theora_p){
+ double tdiff;
+ long milliseconds;
+ tdiff=videobuf_time-get_time();
+ /*If we have lots of extra time, increase the post-processing level.*/
+ if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){
+ pp_inc=pp_level<pp_level_max?1:0;
+ }
+ else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){
+ pp_inc=pp_level>0?-1:0;
+ }
+ milliseconds=tdiff*1000-5;
+ if(milliseconds>500)milliseconds=500;
+ if(milliseconds>0){
+ timeout.tv_sec=milliseconds/1000;
+ timeout.tv_usec=(milliseconds%1000)*1000;
+
+ n=select(n,&empty,&writefs,&empty,&timeout);
+ if(n)audio_calibrate_timer(0);
+ }
+ }else{
+ select(n,&empty,&writefs,&empty,NULL);
+ }
+ }
+
+ /* if our buffers either don't exist or are ready to go,
+ we can begin playback */
+ if((!theora_p || videobuf_ready) &&
+ (!vorbis_p || audiobuf_ready))stateflag=1;
+ /* same if we've run out of input */
+ if(feof(infile))stateflag=1;
+
+ }
+
+ /* tear it all down */
+
+ audio_close();
+ SDL_Quit();
+
+ if(vorbis_p){
+ ogg_stream_clear(&vo);
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+ }
+ if(theora_p){
+ ogg_stream_clear(&to);
+ th_decode_free(td);
+ th_comment_clear(&tc);
+ th_info_clear(&ti);
+ }
+ ogg_sync_clear(&oy);
+
+ if(infile && infile!=stdin)fclose(infile);
+
+ fprintf(stderr,
+ "\r \r");
+ fprintf(stderr, "%d frames", frames);
+ if (dropped) fprintf(stderr, " (%d dropped)", dropped);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "\nDone.\n");
+
+ return(0);
+
+}
diff --git a/examples/png2theora.c b/examples/png2theora.c
new file mode 100644
index 0000000..71a0d0f
--- /dev/null
+++ b/examples/png2theora.c
@@ -0,0 +1,912 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009,2009 *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: example encoder application; makes an Ogg Theora
+ file from a sequence of png images
+ last mod: $Id: png2theora.c 16503 2009-08-22 18:14:02Z giles $
+ based on code from Vegard Nossum
+
+ ********************************************************************/
+
+#define _FILE_OFFSET_BITS 64
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+#include <libgen.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <png.h>
+#include <ogg/ogg.h>
+#include "theora/theoraenc.h"
+
+#define PROGRAM_NAME "png2theora"
+#define PROGRAM_VERSION "1.1"
+
+static const char *option_output = NULL;
+static int video_fps_numerator = 24;
+static int video_fps_denominator = 1;
+static int video_aspect_numerator = 0;
+static int video_aspect_denominator = 0;
+static int video_rate = -1;
+static int video_quality = -1;
+ogg_uint32_t keyframe_frequency=0;
+int buf_delay=-1;
+int vp3_compatible=0;
+static int chroma_format = TH_PF_420;
+
+static FILE *twopass_file = NULL;
+static int twopass=0;
+static int passno;
+
+static FILE *ogg_fp = NULL;
+static ogg_stream_state ogg_os;
+static ogg_packet op;
+static ogg_page og;
+
+static th_enc_ctx *td;
+static th_info ti;
+
+static char *input_filter;
+
+const char *optstring = "o:hv:\4:\2:V:s:S:f:F:ck:d:\1\2\3\4\5\6";
+struct option options [] = {
+ {"output",required_argument,NULL,'o'},
+ {"help",no_argument,NULL,'h'},
+ {"chroma-444",no_argument,NULL,'\5'},
+ {"chroma-422",no_argument,NULL,'\6'},
+ {"video-rate-target",required_argument,NULL,'V'},
+ {"video-quality",required_argument,NULL,'v'},
+ {"aspect-numerator",required_argument,NULL,'s'},
+ {"aspect-denominator",required_argument,NULL,'S'},
+ {"framerate-numerator",required_argument,NULL,'f'},
+ {"framerate-denominator",required_argument,NULL,'F'},
+ {"vp3-compatible",no_argument,NULL,'c'},
+ {"soft-target",no_argument,NULL,'\1'},
+ {"keyframe-freq",required_argument,NULL,'k'},
+ {"buf-delay",required_argument,NULL,'d'},
+ {"two-pass",no_argument,NULL,'\2'},
+ {"first-pass",required_argument,NULL,'\3'},
+ {"second-pass",required_argument,NULL,'\4'},
+ {NULL,0,NULL,0}
+};
+
+static void usage(void){
+ fprintf(stderr,
+ "%s %s\n"
+ "Usage: %s [options] <input>\n\n"
+ "The input argument uses C printf format to represent a list of files,\n"
+ " i.e. file-%%06d.png to look for files file000001.png to file9999999.png \n\n"
+ "Options: \n\n"
+ " -o --output <filename.ogv> file name for encoded output (required);\n"
+ " -v --video-quality <n> Theora quality selector fro 0 to 10\n"
+ " (0 yields smallest files but lowest\n"
+ " video quality. 10 yields highest\n"
+ " fidelity but large files)\n\n"
+ " -V --video-rate-target <n> bitrate target for Theora video\n\n"
+ " --soft-target Use a large reservoir and treat the rate\n"
+ " as a soft target; rate control is less\n"
+ " strict but resulting quality is usually\n"
+ " higher/smoother overall. Soft target also\n"
+ " allows an optional -v setting to specify\n"
+ " a minimum allowed quality.\n\n"
+ " --two-pass Compress input using two-pass rate control\n"
+ " This option performs both passes automatically.\n\n"
+ " --first-pass <filename> Perform first-pass of a two-pass rate\n"
+ " controlled encoding, saving pass data to\n"
+ " <filename> for a later second pass\n\n"
+ " --second-pass <filename> Perform second-pass of a two-pass rate\n"
+ " controlled encoding, reading first-pass\n"
+ " data from <filename>. The first pass\n"
+ " data must come from a first encoding pass\n"
+ " using identical input video to work\n"
+ " properly.\n\n"
+ " -k --keyframe-freq <n> Keyframe frequency\n"
+ " -d --buf-delay <n> Buffer delay (in frames). Longer delays\n"
+ " allow smoother rate adaptation and provide\n"
+ " better overall quality, but require more\n"
+ " client side buffering and add latency. The\n"
+ " default value is the keyframe interval for\n"
+ " one-pass encoding (or somewhat larger if\n"
+ " --soft-target is used) and infinite for\n"
+ " two-pass encoding.\n"
+ " --chroma-444 Use 4:4:4 chroma subsampling\n"
+ " --chroma-422 Use 4:2:2 chroma subsampling\n"
+ " (4:2:0 is default)\n\n"
+ " -s --aspect-numerator <n> Aspect ratio numerator, default is 0\n"
+ " -S --aspect-denominator <n> Aspect ratio denominator, default is 0\n"
+ " -f --framerate-numerator <n> Frame rate numerator\n"
+ " -F --framerate-denominator <n> Frame rate denominator\n"
+ " The frame rate nominator divided by this\n"
+ " determines the frame rate in units per tick\n"
+ ,PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_NAME
+ );
+ exit(0);
+}
+
+#ifdef WIN32
+int
+alphasort (const void *a, const void *b)
+{
+ return strcoll ((*(const struct dirent **) a)->d_name,
+ (*(const struct dirent **) b)->d_name);
+}
+
+int
+scandir (const char *dir, struct dirent ***namelist,
+ int (*select)(const struct dirent *), int (*compar)(const void *, const void *))
+{
+ DIR *d;
+ struct dirent *entry;
+ register int i=0;
+ size_t entrysize;
+
+ if ((d=opendir(dir)) == NULL)
+ return(-1);
+
+ *namelist=NULL;
+ while ((entry=readdir(d)) != NULL)
+ {
+ if (select == NULL || (select != NULL && (*select)(entry)))
+ {
+ *namelist=(struct dirent **)realloc((void *)(*namelist),
+ (size_t)((i+1)*sizeof(struct dirent *)));
+ if (*namelist == NULL) return(-1);
+ entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1;
+ (*namelist)[i]=(struct dirent *)malloc(entrysize);
+ if ((*namelist)[i] == NULL) return(-1);
+ memcpy((*namelist)[i], entry, entrysize);
+ i++;
+ }
+ }
+ if (closedir(d)) return(-1);
+ if (i == 0) return(-1);
+ if (compar != NULL)
+ qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar);
+
+ return(i);
+}
+#endif
+
+static int
+theora_write_frame(unsigned long w, unsigned long h, unsigned char *yuv, int last)
+{
+ th_ycbcr_buffer ycbcr;
+ ogg_packet op;
+ ogg_page og;
+
+ unsigned long yuv_w;
+ unsigned long yuv_h;
+
+ unsigned char *yuv_y;
+ unsigned char *yuv_u;
+ unsigned char *yuv_v;
+
+ unsigned int x;
+ unsigned int y;
+
+ /* Must hold: yuv_w >= w */
+ yuv_w = (w + 15) & ~15;
+
+ /* Must hold: yuv_h >= h */
+ yuv_h = (h + 15) & ~15;
+
+ ycbcr[0].width = yuv_w;
+ ycbcr[0].height = yuv_h;
+ ycbcr[0].stride = yuv_w;
+ ycbcr[1].width = (chroma_format == TH_PF_444) ? yuv_w : (yuv_w >> 1);
+ ycbcr[1].stride = ycbcr[1].width;
+ ycbcr[1].height = (chroma_format == TH_PF_420) ? (yuv_h >> 1) : yuv_h;
+ ycbcr[2].width = ycbcr[1].width;
+ ycbcr[2].stride = ycbcr[1].stride;
+ ycbcr[2].height = ycbcr[1].height;
+
+ ycbcr[0].data = yuv_y = malloc(ycbcr[0].stride * ycbcr[0].height);
+ ycbcr[1].data = yuv_u = malloc(ycbcr[1].stride * ycbcr[1].height);
+ ycbcr[2].data = yuv_v = malloc(ycbcr[2].stride * ycbcr[2].height);
+
+ for(y = 0; y < h; y++) {
+ for(x = 0; x < w; x++) {
+ yuv_y[x + y * yuv_w] = yuv[3 * (x + y * w) + 0];
+ }
+ }
+
+ if (chroma_format == TH_PF_420) {
+ for(y = 0; y < h; y += 2) {
+ for(x = 0; x < w; x += 2) {
+ yuv_u[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
+ yuv[3 * (x + y * w) + 1];
+ yuv_v[(x >> 1) + (y >> 1) * (yuv_w >> 1)] =
+ yuv[3 * (x + y * w) + 2];
+ }
+ }
+ } else if (chroma_format == TH_PF_444) {
+ for(y = 0; y < h; y++) {
+ for(x = 0; x < w; x++) {
+ yuv_u[x + y * ycbcr[1].stride] = yuv[3 * (x + y * w) + 1];
+ yuv_v[x + y * ycbcr[2].stride] = yuv[3 * (x + y * w) + 2];
+ }
+ }
+ } else { /* TH_PF_422 */
+ for(y = 0; y < h; y += 1) {
+ for(x = 0; x < w; x += 2) {
+ yuv_u[(x >> 1) + y * ycbcr[1].stride] =
+ yuv[3 * (x + y * w) + 1];
+ yuv_v[(x >> 1) + y * ycbcr[2].stride] =
+ yuv[3 * (x + y * w) + 2];
+ }
+ }
+ }
+
+ /* Theora is a one-frame-in,one-frame-out system; submit a frame
+ for compression and pull out the packet */
+ /* in two-pass mode's second pass, we need to submit first-pass data */
+ if(passno==2){
+ int ret;
+ for(;;){
+ static unsigned char buffer[80];
+ static int buf_pos;
+ int bytes;
+ /*Ask the encoder how many bytes it would like.*/
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0);
+ if(bytes<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If it's got enough, stop.*/
+ if(bytes==0)break;
+ /*Read in some more bytes, if necessary.*/
+ if(bytes>80-buf_pos)bytes=80-buf_pos;
+ if(bytes>0&&fread(buffer+buf_pos,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Could not read frame data from two-pass data file!\n");
+ exit(1);
+ }
+ /*And pass them off.*/
+ ret=th_encode_ctl(td,TH_ENCCTL_2PASS_IN,buffer,bytes);
+ if(ret<0){
+ fprintf(stderr,"Error submitting pass data in second pass.\n");
+ exit(1);
+ }
+ /*If the encoder consumed the whole buffer, reset it.*/
+ if(ret>=bytes)buf_pos=0;
+ /*Otherwise remember how much it used.*/
+ else buf_pos+=ret;
+ }
+ }
+
+ if(th_encode_ycbcr_in(td, ycbcr)) {
+ fprintf(stderr, "%s: error: could not encode frame\n",
+ option_output);
+ return 1;
+ }
+
+ /* in two-pass mode's first pass we need to extract and save the pass data */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass data from encoder.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+
+ if(!th_encode_packetout(td, last, &op)) {
+ fprintf(stderr, "%s: error: could not read packets\n",
+ option_output);
+ return 1;
+ }
+
+ if (passno!=1) {
+ ogg_stream_packetin(&ogg_os, &op);
+ while(ogg_stream_pageout(&ogg_os, &og)) {
+ fwrite(og.header, og.header_len, 1, ogg_fp);
+ fwrite(og.body, og.body_len, 1, ogg_fp);
+ }
+ }
+
+ free(yuv_y);
+ free(yuv_u);
+ free(yuv_v);
+
+ return 0;
+}
+
+static unsigned char
+clamp(double d)
+{
+ if(d < 0)
+ return 0;
+
+ if(d > 255)
+ return 255;
+
+ return d;
+}
+
+static void
+rgb_to_yuv(png_bytep *png,
+ unsigned char *yuv,
+ unsigned int w, unsigned int h)
+{
+ unsigned int x;
+ unsigned int y;
+
+ for(y = 0; y < h; y++) {
+ for(x = 0; x < w; x++) {
+ png_byte r;
+ png_byte g;
+ png_byte b;
+
+ r = png[y][3 * x + 0];
+ g = png[y][3 * x + 1];
+ b = png[y][3 * x + 2];
+
+ /* XXX: Cringe. */
+ yuv[3 * (x + w * y) + 0] = clamp(
+ 0.299 * r
+ + 0.587 * g
+ + 0.114 * b);
+ yuv[3 * (x + w * y) + 1] = clamp((0.436 * 255
+ - 0.14713 * r
+ - 0.28886 * g
+ + 0.436 * b) / 0.872);
+ yuv[3 * (x + w * y) + 2] = clamp((0.615 * 255
+ + 0.615 * r
+ - 0.51499 * g
+ - 0.10001 * b) / 1.230);
+ }
+ }
+}
+
+static int
+png_read(const char *pathname, unsigned int *w, unsigned int *h, unsigned char **yuv)
+{
+ FILE *fp;
+ unsigned char header[8];
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_ptr;
+ png_bytep row_data;
+ png_bytep *row_pointers;
+ png_color_16p bkgd;
+ png_uint_32 width;
+ png_uint_32 height;
+ int bit_depth;
+ int color_type;
+ int interlace_type;
+ int compression_type;
+ int filter_method;
+ png_uint_32 y;
+
+ fp = fopen(pathname, "rb");
+ if(!fp) {
+ fprintf(stderr, "%s: error: %s\n",
+ pathname, strerror(errno));
+ return 1;
+ }
+
+ fread(header, 1, 8, fp);
+ if(png_sig_cmp(header, 0, 8)) {
+ fprintf(stderr, "%s: error: %s\n",
+ pathname, "not a PNG");
+ fclose(fp);
+ return 1;
+ }
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if(!png_ptr) {
+ fprintf(stderr, "%s: error: %s\n",
+ pathname, "couldn't create png read structure");
+ fclose(fp);
+ return 1;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if(!info_ptr) {
+ fprintf(stderr, "%s: error: %s\n",
+ pathname, "couldn't create png info structure");
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(fp);
+ return 1;
+ }
+
+ end_ptr = png_create_info_struct(png_ptr);
+ if(!end_ptr) {
+ fprintf(stderr, "%s: error: %s\n",
+ pathname, "couldn't create png info structure");
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(fp);
+ return 1;
+ }
+
+ png_init_io(png_ptr, fp);
+ png_set_sig_bytes(png_ptr, 8);
+ png_read_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ &interlace_type, &compression_type, &filter_method);
+ png_set_expand(png_ptr);
+ if(bit_depth<8)png_set_packing(png_ptr);
+ if(bit_depth==16)png_set_strip_16(png_ptr);
+ if(!(color_type&PNG_COLOR_MASK_COLOR))png_set_gray_to_rgb(png_ptr);
+ if(png_get_bKGD(png_ptr, info_ptr, &bkgd)){
+ png_set_background(png_ptr, bkgd, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+ }
+ /*Note that color_type 2 and 3 can also have alpha, despite not setting the
+ PNG_COLOR_MASK_ALPHA bit.
+ We always strip it to prevent libpng from overrunning our buffer.*/
+ png_set_strip_alpha(png_ptr);
+
+ row_data = (png_bytep)png_malloc(png_ptr,
+ 3*height*width*png_sizeof(*row_data));
+ row_pointers = (png_bytep *)png_malloc(png_ptr,
+ height*png_sizeof(*row_pointers));
+ for(y = 0; y < height; y++) {
+ row_pointers[y] = row_data + y*(3*width);
+ }
+ png_read_image(png_ptr, row_pointers);
+ png_read_end(png_ptr, end_ptr);
+
+ *w = width;
+ *h = height;
+ *yuv = malloc(*w * *h * 3);
+ rgb_to_yuv(row_pointers, *yuv, *w, *h);
+
+ png_free(png_ptr, row_pointers);
+ png_free(png_ptr, row_data);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
+
+ fclose(fp);
+ return 0;
+}
+
+static int include_files (const struct dirent *de)
+{
+ char name[1024];
+ int number = -1;
+ sscanf(de->d_name, input_filter, &number);
+ sprintf(name, input_filter, number);
+ return !strcmp(name, de->d_name);
+}
+
+static int ilog(unsigned _v){
+ int ret;
+ for(ret=0;_v;ret++)_v>>=1;
+ return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c,long_option_index;
+ int i, n;
+ char *input_mask;
+ char *input_directory;
+ char *scratch;
+ th_comment tc;
+ struct dirent **png_files;
+ int soft_target=0;
+ int ret;
+
+ while(1) {
+
+ c=getopt_long(argc,argv,optstring,options,&long_option_index);
+ if(c == EOF)
+ break;
+
+ switch(c) {
+ case 'h':
+ usage();
+ break;
+ case 'o':
+ option_output = optarg;
+ break;;
+ case 'v':
+ video_quality=rint(atof(optarg)*6.3);
+ if(video_quality<0 || video_quality>63){
+ fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+ exit(1);
+ }
+ video_rate=0;
+ break;
+ case 'V':
+ video_rate=rint(atof(optarg)*1000);
+ if(video_rate<1){
+ fprintf(stderr,"Illegal video bitrate (choose > 0 please)\n");
+ exit(1);
+ }
+ video_quality=0;
+ break;
+ case '\1':
+ soft_target=1;
+ break;
+ case 'c':
+ vp3_compatible=1;
+ break;
+ case 'k':
+ keyframe_frequency=rint(atof(optarg));
+ if(keyframe_frequency<1 || keyframe_frequency>2147483647){
+ fprintf(stderr,"Illegal keyframe frequency\n");
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ buf_delay=atoi(optarg);
+ if(buf_delay<=0){
+ fprintf(stderr,"Illegal buffer delay\n");
+ exit(1);
+ }
+ break;
+ case 's':
+ video_aspect_numerator=rint(atof(optarg));
+ break;
+ case 'S':
+ video_aspect_denominator=rint(atof(optarg));
+ break;
+ case 'f':
+ video_fps_numerator=rint(atof(optarg));
+ break;
+ case 'F':
+ video_fps_denominator=rint(atof(optarg));
+ break;
+ case '\5':
+ chroma_format=TH_PF_444;
+ break;
+ case '\6':
+ chroma_format=TH_PF_422;
+ break;
+ case '\2':
+ twopass=3; /* perform both passes */
+ twopass_file=tmpfile();
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open temporary file for twopass data\n");
+ exit(1);
+ }
+ break;
+ case '\3':
+ twopass=1; /* perform first pass */
+ twopass_file=fopen(optarg,"wb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open \'%s\' for twopass data\n",optarg);
+ exit(1);
+ }
+ break;
+ case '\4':
+ twopass=2; /* perform second pass */
+ twopass_file=fopen(optarg,"rb");
+ if(!twopass_file){
+ fprintf(stderr,"Unable to open twopass data file \'%s\'",optarg);
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(argc < 3) {
+ usage();
+ }
+
+ if(soft_target){
+ if(video_rate<=0){
+ fprintf(stderr,"Soft rate target (--soft-target) requested without a bitrate (-V).\n");
+ exit(1);
+ }
+ if(video_quality==-1)
+ video_quality=0;
+ }else{
+ if(video_rate>0)
+ video_quality=0;
+ if(video_quality==-1)
+ video_quality=48;
+ }
+
+ if(keyframe_frequency<=0){
+ /*Use a default keyframe frequency of 64 for 1-pass (streaming) mode, and
+ 256 for two-pass mode.*/
+ keyframe_frequency=twopass?256:64;
+ }
+
+ input_mask = argv[optind];
+ if (!input_mask) {
+ fprintf(stderr, "no input files specified; run with -h for help.\n");
+ exit(1);
+ }
+ /* dirname and basename must operate on scratch strings */
+ scratch = strdup(input_mask);
+ input_directory = strdup(dirname(scratch));
+ free(scratch);
+ scratch = strdup(input_mask);
+ input_filter = strdup(basename(scratch));
+ free(scratch);
+
+#ifdef DEBUG
+ fprintf(stderr, "scanning %s with filter '%s'\n",
+ input_directory, input_filter);
+#endif
+ n = scandir (input_directory, &png_files, include_files, alphasort);
+
+ if (!n) {
+ fprintf(stderr, "no input files found; run with -h for help.\n");
+ exit(1);
+ }
+
+ ogg_fp = fopen(option_output, "wb");
+ if(!ogg_fp) {
+ fprintf(stderr, "%s: error: %s\n",
+ option_output, "couldn't open output file");
+ return 1;
+ }
+
+ srand(time(NULL));
+ if(ogg_stream_init(&ogg_os, rand())) {
+ fprintf(stderr, "%s: error: %s\n",
+ option_output, "couldn't create ogg stream state");
+ return 1;
+ }
+
+ for(passno=(twopass==3?1:twopass);passno<=(twopass==3?2:twopass);passno++){
+ unsigned int w;
+ unsigned int h;
+ unsigned char *yuv;
+ char input_png[1024];
+ int last = 0;
+
+ snprintf(input_png, 1023,"%s/%s", input_directory, png_files[0]->d_name);
+ if(png_read(input_png, &w, &h, &yuv)) {
+ fprintf(stderr, "could not read %s\n", input_png);
+ exit(1);
+ }
+
+ if (passno!=2) fprintf(stderr,"%d frames, %dx%d\n",n,w,h);
+
+ /* setup complete. Raw processing loop */
+ switch(passno){
+ case 0: case 2:
+ fprintf(stderr,"\rCompressing.... \n");
+ break;
+ case 1:
+ fprintf(stderr,"\rScanning first pass.... \n");
+ break;
+ }
+
+ fprintf(stderr, "%s\n", input_png);
+
+ th_info_init(&ti);
+ ti.frame_width = ((w + 15) >>4)<<4;
+ ti.frame_height = ((h + 15)>>4)<<4;
+ ti.pic_width = w;
+ ti.pic_height = h;
+ ti.pic_x = 0;
+ ti.pic_y = 0;
+ ti.fps_numerator = video_fps_numerator;
+ ti.fps_denominator = video_fps_denominator;
+ ti.aspect_numerator = video_aspect_numerator;
+ ti.aspect_denominator = video_aspect_denominator;
+ ti.colorspace = TH_CS_UNSPECIFIED;
+ ti.pixel_fmt = chroma_format;
+ ti.target_bitrate = video_rate;
+ ti.quality = video_quality;
+ ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
+
+ td=th_encode_alloc(&ti);
+ th_info_clear(&ti);
+ /* setting just the granule shift only allows power-of-two keyframe
+ spacing. Set the actual requested spacing. */
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
+ &keyframe_frequency,sizeof(keyframe_frequency-1));
+ if(ret<0){
+ fprintf(stderr,"Could not set keyframe interval to %d.\n",(int)keyframe_frequency);
+ }
+ if(vp3_compatible){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_VP3_COMPATIBLE,&vp3_compatible,
+ sizeof(vp3_compatible));
+ if(ret<0||!vp3_compatible){
+ fprintf(stderr,"Could not enable strict VP3 compatibility.\n");
+ if(ret>=0){
+ fprintf(stderr,"Ensure your source format is supported by VP3.\n");
+ fprintf(stderr,
+ "(4:2:0 pixel format, width and height multiples of 16).\n");
+ }
+ }
+ }
+ if(soft_target){
+ /* reverse the rate control flags to favor a 'long time' strategy */
+ int arg = TH_RATECTL_CAP_UNDERFLOW;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_FLAGS,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set encoder flags for --soft-target\n");
+ /* Default buffer control is overridden on two-pass */
+ if(!twopass&&buf_delay<0){
+ if((keyframe_frequency*7>>1) > 5*video_fps_numerator/video_fps_denominator)
+ arg=keyframe_frequency*7>>1;
+ else
+ arg=5*video_fps_numerator/video_fps_denominator;
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,&arg,sizeof(arg));
+ if(ret<0)
+ fprintf(stderr,"Could not set rate control buffer for --soft-target\n");
+ }
+ }
+ /* set up two-pass if needed */
+ if(passno==1){
+ unsigned char *buffer;
+ int bytes;
+ bytes=th_encode_ctl(td,TH_ENCCTL_2PASS_OUT,&buffer,sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not set up the first pass of two-pass mode.\n");
+ fprintf(stderr,"Did you remember to specify an estimated bitrate?\n");
+ exit(1);
+ }
+ /*Perform a seek test to ensure we can overwrite this placeholder data at
+ the end; this is better than letting the user sit through a whole
+ encode only to find out their pass 1 file is useless at the end.*/
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ if(passno==2){
+ /*Enable the second pass here.
+ We make this call just to set the encoder into 2-pass mode, because
+ by default enabling two-pass sets the buffer delay to the whole file
+ (because there's no way to explicitly request that behavior).
+ If we waited until we were actually encoding, it would overwite our
+ settings.*/
+ if(th_encode_ctl(td,TH_ENCCTL_2PASS_IN,NULL,0)<0){
+ fprintf(stderr,"Could not set up the second pass of two-pass mode.\n");
+ exit(1);
+ }
+ if(twopass==3){
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ }
+ }
+ /*Now we can set the buffer delay if the user requested a non-default one
+ (this has to be done after two-pass is enabled).*/
+ if(passno!=1&&buf_delay>=0){
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_RATE_BUFFER,
+ &buf_delay,sizeof(buf_delay));
+ if(ret<0){
+ fprintf(stderr,"Warning: could not set desired buffer delay.\n");
+ }
+ }
+ /* write the bitstream header packets with proper page interleave */
+ th_comment_init(&tc);
+ /* first packet will get its own page automatically */
+ if(th_encode_flushheader(td,&tc,&op)<=0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ th_comment_clear(&tc);
+ if(passno!=1){
+ ogg_stream_packetin(&ogg_os,&op);
+ if(ogg_stream_pageout(&ogg_os,&og)!=1){
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ fwrite(og.header,1,og.header_len,ogg_fp);
+ fwrite(og.body,1,og.body_len,ogg_fp);
+ }
+ /* create the remaining theora headers */
+ for(;;){
+ ret=th_encode_flushheader(td,&tc,&op);
+ if(ret<0){
+ fprintf(stderr,"Internal Theora library error.\n");
+ exit(1);
+ }
+ else if(!ret)break;
+ if(passno!=1)ogg_stream_packetin(&ogg_os,&op);
+ }
+ /* Flush the rest of our headers. This ensures
+ the actual data in each stream will start
+ on a new page, as per spec. */
+ if(passno!=1){
+ for(;;){
+ int result = ogg_stream_flush(&ogg_os,&og);
+ if(result<0){
+ /* can't get here */
+ fprintf(stderr,"Internal Ogg library error.\n");
+ exit(1);
+ }
+ if(result==0)break;
+ fwrite(og.header,1,og.header_len,ogg_fp);
+ fwrite(og.body,1,og.body_len,ogg_fp);
+ }
+ }
+
+ i=0; last=0;
+ do {
+ if(i >= n-1) last = 1;
+ if(theora_write_frame(w, h, yuv, last)) {
+ fprintf(stderr,"Encoding error.\n");
+ exit(1);
+ }
+ free(yuv);
+ i++;
+ if (!last) {
+ snprintf(input_png, 1023,"%s/%s", input_directory, png_files[i]->d_name);
+ if(png_read(input_png, &w, &h, &yuv)) {
+ fprintf(stderr, "could not read %s\n", input_png);
+ exit(1);
+ }
+ fprintf(stderr, "%s\n", input_png);
+ }
+ } while (!last);
+
+ if(passno==1){
+ /* need to read the final (summary) packet */
+ unsigned char *buffer;
+ int bytes = th_encode_ctl(td, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+ if(bytes<0){
+ fprintf(stderr,"Could not read two-pass summary data from encoder.\n");
+ exit(1);
+ }
+ if(fseek(twopass_file,0,SEEK_SET)<0){
+ fprintf(stderr,"Unable to seek in two-pass data file.\n");
+ exit(1);
+ }
+ if(fwrite(buffer,1,bytes,twopass_file)<bytes){
+ fprintf(stderr,"Unable to write to two-pass data file.\n");
+ exit(1);
+ }
+ fflush(twopass_file);
+ }
+ th_encode_free(td);
+ }
+
+ if(ogg_stream_flush(&ogg_os, &og)) {
+ fwrite(og.header, og.header_len, 1, ogg_fp);
+ fwrite(og.body, og.body_len, 1, ogg_fp);
+ }
+
+ free(input_directory);
+ free(input_filter);
+
+ while (n--) free(png_files[n]);
+ free(png_files);
+
+ if(ogg_fp){
+ fflush(ogg_fp);
+ if(ogg_fp!=stdout)fclose(ogg_fp);
+ }
+
+ ogg_stream_clear(&ogg_os);
+ if(twopass_file)fclose(twopass_file);
+ fprintf(stderr,"\r \ndone.\n\n");
+
+ return 0;
+}