[xiph-cvs] r6565 - in trunk/theora-tools: . debian doc theora123 theora_dumpvideo theora_splayer theora_transcoder theoraenc

giles at xiph.org giles at xiph.org
Tue May 4 21:24:43 PDT 2004



Author: giles
Date: 2004-04-21 14:26:11 -0400 (Wed, 21 Apr 2004)
New Revision: 6565

Added:
   trunk/theora-tools/Makefile.am
   trunk/theora-tools/autogen.sh
   trunk/theora-tools/configure.ac
   trunk/theora-tools/debian/
   trunk/theora-tools/debian/.cvsignore
   trunk/theora-tools/debian/Makefile
   trunk/theora-tools/debian/Makefile.am
   trunk/theora-tools/debian/Makefile.in
   trunk/theora-tools/debian/changelog
   trunk/theora-tools/debian/compat
   trunk/theora-tools/debian/control
   trunk/theora-tools/debian/copyright
   trunk/theora-tools/debian/rules
   trunk/theora-tools/debian/stamp-autotools-files
   trunk/theora-tools/debian/theora-tools.install
   trunk/theora-tools/doc/
   trunk/theora-tools/doc/README_SPLAYER
   trunk/theora-tools/doc/transcoder_example.sh
   trunk/theora-tools/theora123/
   trunk/theora-tools/theora123/Makefile.am
   trunk/theora-tools/theora123/theora123.c
   trunk/theora-tools/theora_dumpvideo/
   trunk/theora-tools/theora_dumpvideo/Makefile.am
   trunk/theora-tools/theora_dumpvideo/theora_dumpvideo.c
   trunk/theora-tools/theora_splayer/
   trunk/theora-tools/theora_splayer/Makefile.am
   trunk/theora-tools/theora_splayer/theora_splayer.c
   trunk/theora-tools/theora_transcoder/
   trunk/theora-tools/theora_transcoder/Makefile.am
   trunk/theora-tools/theora_transcoder/avi2vp3.c
   trunk/theora-tools/theora_transcoder/avilib.c
   trunk/theora-tools/theora_transcoder/avilib.h
   trunk/theora-tools/theora_transcoder/readme.txt
   trunk/theora-tools/theora_transcoder/theora_transcoder.c
   trunk/theora-tools/theora_transcoder/vp31.avi
   trunk/theora-tools/theoraenc/
   trunk/theora-tools/theoraenc/Makefile.am
   trunk/theora-tools/theoraenc/getopt.c
   trunk/theora-tools/theoraenc/getopt.h
   trunk/theora-tools/theoraenc/getopt1.c
   trunk/theora-tools/theoraenc/theoraenc.c
Log:
Import the 0.1 source release of Jan's theora-tools. This is a 
repackaging of some of the example code in the theora module.

<p>Added: trunk/theora-tools/Makefile.am
===================================================================
--- trunk/theora-tools/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign dist-zip
+
+SUBDIRS = debian theora123 theora_splayer theoraenc theora_transcoder theora_dumpvideo
+
+EXTRA_DIST = COPYING autogen.sh win32
+
+dist-hook:
+	rm -rf `find $(distdir)/macos -name CVS`
+	rm -rf `find $(distdir)/win32 -name CVS`
+
+debug:
+	$(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+	$(MAKE) all CFLAGS="@PROFILE@"

Added: trunk/theora-tools/autogen.sh
===================================================================
--- trunk/theora-tools/autogen.sh	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/autogen.sh	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,87 @@
+#!/bin/sh
+# Run this to set up the build system: configure, makefiles, etc.
+# (based on the version in enlightenment's cvs)
+
+package="theora-tools"
+
+olddir=`pwd`
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+cd "$srcdir"
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have autoconf installed to compile $package."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+        DIE=1
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have automake installed to compile $package."
+	echo "Download the appropriate package for your system,"
+	echo "or get the source from one of the GNU ftp sites"
+	echo "listed in http://www.gnu.org/order/ftp.html"
+        DIE=1
+}
+
+(libtoolize --version) < /dev/null > /dev/null 2>&1 || {
+	echo
+	echo "You must have libtool installed to compile $package."
+	echo "Download the appropriate package for your system,"
+	echo "or get the source from one of the GNU ftp sites"
+	echo "listed in http://www.gnu.org/order/ftp.html"
+	DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+echo "Generating configuration files for $package, please wait...."
+
+test -f aclocal.m4 && rm aclocal.m4
+echo -n "looking for our m4 macros... "
+aclocaldirs="$srcdir `aclocal --print-ac-dir` \
+        /usr/local/share/aclocal* /usr/local/aclocal* \
+        /sw/share/aclocal* /usr/share/aclocal*"
+for dir in $aclocaldirs; do
+  test -d "$dir" && if ! test -z "`ls $dir | grep .m4`"; then
+    if test ! -z "`fgrep XIPH_PATH_OGG $dir/*.m4`"; then
+      echo $dir
+      ACLOCAL_FLAGS="-I $dir $ACLOCAL_FLAGS"
+      break
+    fi
+  fi
+done
+if test -z "$ACLOCAL_FLAGS"; then
+  echo "nope."
+  echo
+  echo "Please install the ogg and vorbis libraries, or add the"
+  echo "location of ogg.m4 to ACLOCAL_FLAGS in the environment."
+  echo
+  exit 1
+fi
+
+
+echo "  aclocal $ACLOCAL_FLAGS"
+aclocal $ACLOCAL_FLAGS
+echo "  autoheader"
+autoheader
+echo "  libtoolize --automake"
+libtoolize --automake
+echo "  automake --add-missing $AUTOMAKE_FLAGS"
+automake --add-missing $AUTOMAKE_FLAGS 
+echo "  autoconf"
+autoconf
+
+cd $olddir
+$srcdir/configure "$@" && echo

<p>Property changes on: trunk/theora-tools/autogen.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/theora-tools/configure.ac
===================================================================
--- trunk/theora-tools/configure.ac	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/configure.ac	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,147 @@
+dnl Process this file with autoconf to produce a configure script
+
+dnl ------------------------------------------------
+dnl Initialization and Versioning
+dnl ------------------------------------------------
+
+AC_INIT(theora-tools,[unreleased-snapshot])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE
+
+dnl --------------------------------------------------  
+dnl Check for programs
+dnl --------------------------------------------------  
+
+dnl save $CFLAGS since AC_PROG_CC likes to insert "-g -O2"
+dnl if $CFLAGS is blank
+cflags_save="$CFLAGS"
+AC_PROG_CC
+AC_PROG_CPP
+CFLAGS="$cflags_save"
+
+dnl no shared linking for us
+AM_DISABLE_SHARED
+AM_ENABLE_STATIC
+AM_PROG_LIBTOOL
+
+dnl --------------------------------------------------
+dnl Set build flags based on environment
+dnl --------------------------------------------------
+
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+dnl Set some target options
+
+cflags_save="$CFLAGS"
+ldflags_save="$LDFLAGS"
+if test -z "$GCC"; then
+        case $host in 
+        *)
+                DEBUG="-g"
+                CFLAGS="-O"
+                PROFILE="-g -p" ;;
+        esac
+else
+
+        case $host in 
+        *)
+                DEBUG="-g -W -D__NO_MATH_INLINES"
+                CFLAGS="-O2 -Wall"
+                PROFILE="-W -pg -g -O2 -fno-inline-functions";;
+        esac
+fi
+CFLAGS="$CFLAGS $cflags_save"
+LDFLAGS="$LDFLAGS $ldflags_save"
+
+dnl --------------------------------------------------
+dnl Check for headers
+dnl --------------------------------------------------
+
+dnl none here
+
+dnl --------------------------------------------------
+dnl Check for typedefs, structures, etc
+dnl --------------------------------------------------
+
+dnl none
+
+dnl --------------------------------------------------
+dnl Check for library functions
+dnl --------------------------------------------------
+
+dnl substitue the included getopt if the system doesn't support long options
+AC_CHECK_FUNC(getopt_long, [GETOPT_OBJS=''], [GETOPT_OBJS='getopt.$(OBJEXT) getopt1.$(OBJEXT)'])
+AC_SUBST(GETOPT_OBJS)
+
+XIPH_PATH_OGG(, AC_MSG_ERROR([
+    libogg is required to build this package!
+    please see http://www.xiph.org/ for how to
+    obtain a copy.
+]))
+CFLAGS="$CFLAGS $OGG_CFLAGS"
+LIBS="$LIBS $OGG_LIBS"
+AC_CHECK_FUNC(oggpackB_read, , [
+  AC_MSG_ERROR([newer libogg version (>1.0) required])
+])
+
+AC_CHECK_LIB(theora, theora_version_string, [
+  AC_CHECK_HEADER(theora/theora.h, [HAVE_THEORA=yes])
+  ],,[-ltheora]
+)
+if test "x$HAVE_THEORA" != xyes; then
+	AC_MSG_ERROR([
+    	libtheora is required to build this package!
+    	please see http://www.xiph.org/ for how to
+    	obtain a copy.
+	])
+fi
+
+
+AM_PATH_SDL(,[
+	HAVE_SDL=yes
+	SDL_LIBS=`$SDL_CONFIG --libs`
+],AC_MSG_WARN([*** Unable to find SDL -- Not compiling example players ***]))
+
+AC_CHECK_HEADERS([sys/soundcard.h soundcard.h machine/soundcard.h],[
+  HAVE_OSS=yes
+  break
+])
+
+AC_CHECK_LIB(portaudio, Pa_OpenStream, [
+  AC_CHECK_HEADER(portaudio.h, [HAVE_PORTAUDIO=yes])
+  ],,[-lpthread]
+)
+if test "x$HAVE_PORTAUDIO" != xyes; then
+  AC_MSG_WARN([portaudio not found -- not compiling splayer example])
+fi
+
+if test x$HAVE_SDL = xyes -a x$HAVE_OSS = xyes; then
+  THEORA123="theora123"
+  AC_SUBST(THEORA123)
+fi
+if test x$HAVE_SDL = xyes -a x$HAVE_PORTAUDIO = xyes; then
+  SPLAYER="theora_splayer"
+  AC_SUBST(SPLAYER)
+fi
+
+dnl --------------------------------------------------
+dnl Do substitutions
+dnl --------------------------------------------------
+
+LIBS="$LIBS"
+
+AC_SUBST(LIBS)
+AC_SUBST(DEBUG)
+AC_SUBST(PROFILE)
+
+AC_CONFIG_FILES([
+  Makefile
+  theora123/Makefile
+  theora_splayer/Makefile
+  theoraenc/Makefile
+  theora_transcoder/Makefile
+  theora_dumpvideo/Makefile
+  debian/Makefile
+])
+AC_OUTPUT

Added: trunk/theora-tools/debian/.cvsignore
===================================================================
--- trunk/theora-tools/debian/.cvsignore	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/.cvsignore	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in

Added: trunk/theora-tools/debian/Makefile
===================================================================
--- trunk/theora-tools/debian/Makefile	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/Makefile	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,298 @@
+# Makefile.in generated by automake 1.7.9 from Makefile.am.
+# debian/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+# 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.
+
+
+
+srcdir = .
+top_srcdir = ..
+
+pkgdatadir = $(datadir)/theora-tools
+pkglibdir = $(libdir)/theora-tools
+pkgincludedir = $(includedir)/theora-tools
+top_builddir = ..
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = /usr/bin/install -c
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_triplet = i686-pc-linux-gnu
+ACLOCAL = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run aclocal-1.7
+AMDEP_FALSE = #
+AMDEP_TRUE = 
+AMTAR = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run autoconf
+AUTOHEADER = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run autoheader
+AUTOMAKE = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run automake-1.7
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -O2 -Wall  
+CPP = gcc -E
+CPPFLAGS = 
+CXX = g++
+CXXCPP = g++ -E
+CXXDEPMODE = depmode=gcc3
+CXXFLAGS = -g -O2
+CYGPATH_W = echo
+DEBUG = -g -W -D__NO_MATH_INLINES
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+ECHO = echo
+ECHO_C = 
+ECHO_N = -n
+ECHO_T = 
+EGREP = grep -E
+EXEEXT = 
+F77 = g77
+FFLAGS = -g -O2
+GETOPT_OBJS = 
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s
+LDFLAGS =  
+LIBOBJS = 
+LIBS =   -logg
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LN_S = ln -s
+LTLIBOBJS = 
+MAKEINFO = ${SHELL} /usr/local/src/xiph/theora-tools/missing --run makeinfo
+OBJEXT = o
+OGG_CFLAGS = 
+OGG_LIBS =  -logg
+PACKAGE = theora-tools
+PACKAGE_BUGREPORT = 
+PACKAGE_NAME = theora-tools
+PACKAGE_STRING = theora-tools unreleased-snapshot
+PACKAGE_TARNAME = theora-tools
+PACKAGE_VERSION = unreleased-snapshot
+PATH_SEPARATOR = :
+PROFILE = -W -pg -g -O2 -fno-inline-functions
+RANLIB = ranlib
+SDL_CFLAGS = -I/usr/include/SDL -D_REENTRANT
+SDL_CONFIG = /usr/bin/sdl-config
+SDL_LIBS = -lSDL -lpthread
+SET_MAKE = 
+SHELL = /bin/sh
+SPLAYER = theora_splayer
+STRIP = strip
+THEORA123 = theora123
+VERSION = unreleased-snapshot
+ac_ct_AR = ar
+ac_ct_CC = gcc
+ac_ct_CXX = g++
+ac_ct_F77 = g77
+ac_ct_RANLIB = ranlib
+ac_ct_STRIP = strip
+am__fastdepCC_FALSE = #
+am__fastdepCC_TRUE = 
+am__fastdepCXX_FALSE = #
+am__fastdepCXX_TRUE = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+bindir = ${exec_prefix}/bin
+build = i686-pc-linux-gnu
+build_alias = 
+build_cpu = i686
+build_os = linux-gnu
+build_vendor = pc
+datadir = ${prefix}/share
+exec_prefix = ${prefix}
+host = i686-pc-linux-gnu
+host_alias = 
+host_cpu = i686
+host_os = linux-gnu
+host_vendor = pc
+includedir = ${prefix}/include
+infodir = ${prefix}/info
+install_sh = /usr/local/src/xiph/theora-tools/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localstatedir = ${prefix}/var
+mandir = ${prefix}/man
+oldincludedir = /usr/include
+prefix = /usr/local
+program_transform_name = s,x,x,
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+sysconfdir = ${prefix}/etc
+target = i686-pc-linux-gnu
+target_alias = 
+target_cpu = i686
+target_os = linux-gnu
+target_vendor = pc
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = changelog control copyright rules
+subdir = debian
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+DIST_SOURCES =
+DIST_COMMON = $(srcdir)/Makefile.in Makefile.am
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  Makefile.am  $(top_srcdir)/configure.ac $(ACLOCAL_M4)
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  debian/Makefile
+Makefile:  $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  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
+
+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_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-libtool
+
+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
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+	distclean distclean-generic distclean-libtool 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-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \
+	uninstall-info-am
+
+# 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:

Added: trunk/theora-tools/debian/Makefile.am
===================================================================
--- trunk/theora-tools/debian/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,5 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = changelog control copyright rules

Added: trunk/theora-tools/debian/Makefile.in
===================================================================
--- trunk/theora-tools/debian/Makefile.in	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/Makefile.in	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,298 @@
+# Makefile.in generated by automake 1.7.9 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+# 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.
+
+ at SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_triplet = @host@
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEBUG = @DEBUG@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GETOPT_OBJS = @GETOPT_OBJS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+OGG_CFLAGS = @OGG_CFLAGS@
+OGG_LIBS = @OGG_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PROFILE = @PROFILE@
+RANLIB = @RANLIB@
+SDL_CFLAGS = @SDL_CFLAGS@
+SDL_CONFIG = @SDL_CONFIG@
+SDL_LIBS = @SDL_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPLAYER = @SPLAYER@
+STRIP = @STRIP@
+THEORA123 = @THEORA123@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_DIST = changelog control copyright rules
+subdir = debian
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+DIST_SOURCES =
+DIST_COMMON = $(srcdir)/Makefile.in Makefile.am
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  Makefile.am  $(top_srcdir)/configure.ac $(ACLOCAL_M4)
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  debian/Makefile
+Makefile:  $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  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
+
+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_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-libtool
+
+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
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+	distclean distclean-generic distclean-libtool 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-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \
+	uninstall-info-am
+
+# 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:

Added: trunk/theora-tools/debian/changelog
===================================================================
--- trunk/theora-tools/debian/changelog	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/changelog	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,9 @@
+theora-tools (0.0.0-0) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Christopher L Cheney <ccheney at debian.org>  Wed, 1 Apr 2004 21:00:00 -0500
+
+Local variables:
+mode: debian-changelog
+End:

Added: trunk/theora-tools/debian/compat
===================================================================
--- trunk/theora-tools/debian/compat	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/compat	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1 @@
+4

Added: trunk/theora-tools/debian/control
===================================================================
--- trunk/theora-tools/debian/control	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/control	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,17 @@
+Source: theora-tools
+Section: libs
+Priority: optional
+Maintainer: Christopher L Cheney <ccheney at debian.org>
+Build-Depends: cdbs, autotools-dev, debhelper (>= 4.1.0), libogg-dev (>= 1.1.0)
+Standards-Version: 3.6.0
+
+Package: theora-tools
+Architecture: any
+Section: libs
+Depends: ${shlibs:Depends}
+Description:  Some basic Ogg Theora tools
+ theoraenc - encode Ogg Theora files from YUV4MPEG streams
+ theora123 - simple player to play theora streams
+ theora_dumpvideo - dumps the video stream
+ theora_transcoder - transcode vp3 avi files without recompression
+ avi2vp3    - dump the vp3 stream of an avi file

Added: trunk/theora-tools/debian/copyright
===================================================================
--- trunk/theora-tools/debian/copyright	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/copyright	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,38 @@
+This package was debianized by Christopher L Cheney <ccheney at debian.org> on
+Wed, 25 Sep 2002 21:00:00 -0500.
+
+It was downloaded from CVS
+
+Upstream Author(s): Xiph.Org
+
+Copyright:
+
+Copyright (c) 2002-2004, Xiph.org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

Added: trunk/theora-tools/debian/rules
===================================================================
--- trunk/theora-tools/debian/rules	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/rules	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+

<p>Property changes on: trunk/theora-tools/debian/rules
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/theora-tools/debian/stamp-autotools-files
===================================================================

Added: trunk/theora-tools/debian/theora-tools.install
===================================================================
--- trunk/theora-tools/debian/theora-tools.install	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/debian/theora-tools.install	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1 @@
+debian/tmp/usr/bin/.*

Added: trunk/theora-tools/doc/README_SPLAYER
===================================================================
--- trunk/theora-tools/doc/README_SPLAYER	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/doc/README_SPLAYER	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,21 @@
+06/09/03 - by mau (mauricio at xiph.org)
+
+The splayer example is a experimental simple Theora player using SDL and 
+Portaudio for cross platform compatibility. To build the windows version 
+please use the Visual C++ project available at the 
+win32/experimental/splayer directory.
+
+By default this sample  will not be built on Linux, unless Portaudio is 
+properly installed. Follow these steps to install Portaudio:
+
+1) Get portaudio tar package from www.portaudio.com
+2) Uncompress it to a local directory
+3) Run './configure' in the portaudio directory
+4) Run 'make install'
+
+This will install Portaudio libraries in /usr/local/lib, and the Portaudio 
+include file at /usr/local/include. On linux you may need to manually 
+execute 'ldconfig' to rebuild the library cache.
+
+Switch to theora's directory, re-run theora's autogen.sh script to 
+re-generate the appropriate makefiles, and 'make install' to build.

Added: trunk/theora-tools/doc/transcoder_example.sh
===================================================================
--- trunk/theora-tools/doc/transcoder_example.sh	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/doc/transcoder_example.sh	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,4 @@
+mplayer -aofile tmp.wav -ao pcm clip.avi
+avi2vp3
+transcoder tmp.wav output.vp3
+

Added: trunk/theora-tools/theora123/Makefile.am
===================================================================
--- trunk/theora-tools/theora123/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora123/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES = -I$(top_srcdir)/include 
+
+bin_PROGRAMS = $(THEORA123)
+
+# possible contents of BUILDABLE_PLAYERS:
+EXTRA_PROGRAMS = theora123
+
+CFLAGS = $(SDL_CFLAGS)
+LDADD = -ltheora -logg -lvorbis -lm
+
+theora123_SOURCES = theora123.c
+theora123_LDADD = $(LDADD) $(SDL_LIBS)

Added: trunk/theora-tools/theora123/theora123.c
===================================================================
--- trunk/theora-tools/theora123/theora123.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora123/theora123.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,747 @@
+/********************************************************************
+ *                                                                  *
+ * 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-2003                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example SDL player application; plays Ogg Theora files (with
+            optional Vorbis audio second stream)
+  last mod: $Id: theora_player.c,v 1.29 2004/03/08 06:44:26 giles Exp $
+
+ ********************************************************************/
+
+/* 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. */
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#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/theora.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;
+theora_info      ti;
+theora_comment   tc;
+theora_state     td;
+vorbis_info      vi;
+vorbis_dsp_state vd;
+vorbis_block     vb;
+vorbis_comment   vc;
+
+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){
+  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+    fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
+    exit(1);
+  }
+
+  screen = SDL_SetVideoMode(ti.frame_width, ti.frame_height, 0, SDL_SWSURFACE);
+  if ( screen == NULL ) {
+    fprintf(stderr, "Unable to set %dx%d video: %s\n",
+            ti.frame_width,ti.frame_height,SDL_GetError());
+    exit(1);
+  }
+
+  yuv_overlay = SDL_CreateYUVOverlay(ti.frame_width, ti.frame_height,
+                                     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 = ti.frame_width;
+  rect.h = ti.frame_height;
+
+  SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+}
+
+static void video_write(void){
+  int i;
+  yuv_buffer yuv;
+  int crop_offset;
+  theora_decode_YUVout(&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 (*yuv[3]) on a SDL screen (*screen) */
+  /* deal with border stride */
+  /* reverse u and v for SDL */
+  /* and crop input properly, respecting the encoded frame rect */
+  crop_offset=ti.offset_x+yuv.y_stride*ti.offset_y;
+  for(i=0;i<yuv_overlay->h;i++)
+    memcpy(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i,
+           yuv.y+crop_offset+yuv.y_stride*i,
+           yuv_overlay->w);
+  crop_offset=(ti.offset_x/2)+(yuv.uv_stride)*(ti.offset_y/2);
+  for(i=0;i<yuv_overlay->h/2;i++){
+    memcpy(yuv_overlay->pixels[1]+yuv_overlay->pitches[1]*i,
+           yuv.v+crop_offset+yuv.uv_stride*i,
+           yuv_overlay->w/2);
+    memcpy(yuv_overlay->pixels[2]+yuv_overlay->pitches[2]*i,
+           yuv.u+crop_offset+yuv.uv_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(theora_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(theora_info *ti)
+{
+    switch(ti->colorspace){
+      case OC_CS_UNSPECIFIED:
+        /* nothing to report */
+        break;;
+      case OC_CS_ITU_REC_470M:
+        fprintf(stderr,"  encoder specified ITU Rec 470M (NTSC) color.\n");
+        break;;
+      case OC_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,&og);
+  if(vorbis_p)ogg_stream_pagein(&vo,&og);
+  return 0;
+}
+
+static void usage(void){
+  fprintf(stderr,
+          "Usage: theora_player <file.ogg>\n"
+          "input is read from stdin if no file is passed on the command line\n"
+          "\n"
+  );
+}
+
+int main(int argc,char *argv[]){
+
+  int i,j;
+  ogg_packet op;
+
+  FILE *infile = stdin;
+
+#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 */
+  theora_comment_init(&tc);
+  theora_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 && theora_decode_header(&ti,&tc,&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(theora_decode_header(&ti,&tc,&op)){
+        printf("Error parsing Theora stream headers; corrupt stream?\n");
+        exit(1);
+      }
+      theora_p++;
+      if(theora_p==3)break;
+    }
+
+    /* 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){
+    theora_decode_init(&td,&ti);
+    printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n",
+           to.serialno,ti.width,ti.height, 
+           (double)ti.fps_numerator/ti.fps_denominator);
+    if(ti.width!=ti.frame_width || ti.height!=ti.frame_height)
+      printf("  Frame content is %dx%d with offset (%d,%d).\n",
+           ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y);
+    report_colorspace(&ti);
+    dump_comments(&tc);
+  }else{
+    /* tear down the partial theora setup */
+    theora_info_clear(&ti);
+    theora_comment_clear(&tc);
+  }
+  if(vorbis_p){
+    vorbis_synthesis_init(&vd,&vi);
+    vorbis_block_init(&vd,&vb);
+    fprintf(stderr,"Ogg logical stream %x is Vorbis %d channel %d 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){
+
+        theora_decode_packetin(&td,&op);
+        videobuf_granulepos=td.granulepos;
+        
+        videobuf_time=theora_granule_time(&td,videobuf_granulepos);
+
+        /* 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
+        break;
+    }
+
+    if(!videobuf_ready && !audiobuf_ready && feof(infile))break;
+
+    if(!videobuf_ready || !audiobuf_ready){
+      /* no data yet for somebody.  Grab another page */
+      int ret=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){
+        long milliseconds=(videobuf_time-get_time())*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);
+    theora_clear(&td);
+    theora_comment_clear(&tc);
+    theora_info_clear(&ti);
+  }
+  ogg_sync_clear(&oy);
+
+  if(infile && infile!=stdin)fclose(infile);
+
+  fprintf(stderr,
+          "\r                                                              "
+          "\nDone.\n");
+  return(0);
+
+}

Added: trunk/theora-tools/theora_dumpvideo/Makefile.am
===================================================================
--- trunk/theora-tools/theora_dumpvideo/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_dumpvideo/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = theora_dumpvideo
+
+CFLAGS = $(SDL_CFLAGS)
+LDADD = -ltheora -logg -lvorbis -lm
+
+theora_dumpvideo_SOURCES = theora_dumpvideo.c
+theora_dumpvideo_LDADD = $(LDADD)

Added: trunk/theora-tools/theora_dumpvideo/theora_dumpvideo.c
===================================================================
--- trunk/theora-tools/theora_dumpvideo/theora_dumpvideo.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_dumpvideo/theora_dumpvideo.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,341 @@
+/********************************************************************
+ *                                                                  *
+ * 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-2003                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example dumpvid application; dumps  Theora streams
+  last mod: $Id: dump_video.c,v 1.10 2004/03/08 06:44:26 giles Exp $
+
+ ********************************************************************/
+
+/* By Mauricio Piacentini (mauricio at xiph.org) */
+/*  simply dump decoded YUV data, for verification of theora bitstream */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+/*#include <sys/time.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 "getopt.h"
+#include "theora/theora.h"
+
+
+
+const char *optstring = "o:";
+struct option options [] = {
+  {"output",required_argument,NULL,'o'},
+  {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;
+theora_info      ti;
+theora_comment   tc;
+theora_state     td;
+
+int              theora_p=0;
+int              stateflag=0;
+
+/* single frame video buffering */
+int          videobuf_ready=0;
+ogg_int64_t  videobuf_granulepos=-1;
+double       videobuf_time=0;
+
+FILE* outfile = NULL;
+
+int got_sigint=0;
+static void sigint_handler (int signal) {
+  got_sigint = 1;
+}
+
+static void open_video(void){
+
+}
+
+static void video_write(void){
+  int i;
+
+  yuv_buffer yuv;
+  theora_decode_YUVout(&td,&yuv);
+
+  for(i=0;i<yuv.y_height;i++)
+    fwrite(yuv.y+yuv.y_stride*i, 1, yuv.y_width, outfile);
+  for(i=0;i<yuv.uv_height;i++)
+    fwrite(yuv.v+yuv.uv_stride*i, 1, yuv.uv_width, outfile);
+  for(i=0;i<yuv.uv_height;i++)
+    fwrite(yuv.u+yuv.uv_stride*i, 1, yuv.uv_width, outfile);
+
+}
+/* dump the theora (or vorbis) comment header */
+static int dump_comments(theora_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);
+}
+
+/* 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,&og);
+  return 0;
+}
+
+static void usage(void){
+  fprintf(stderr,
+          "Usage: dumpvid <file.ogg> > 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;
+
+  FILE *infile = stdin;
+  outfile = stdout;
+
+
+#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 );
+  _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':
+      outfile=fopen(optarg,"wb");
+      if(outfile==NULL){
+        fprintf(stderr,"Unable to open output file '%s'\n", optarg);
+        exit(1);
+      }
+      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);
+    }
+  }
+
+  /* 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 */
+  theora_comment_init(&tc);
+  theora_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 && theora_decode_header(&ti,&tc,&op)>=0){
+        /* it is theora */
+        memcpy(&to,&test,sizeof(test));
+        theora_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){
+    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(theora_decode_header(&ti,&tc,&op)){
+        printf("Error parsing Theora stream headers; corrupt stream?\n");
+        exit(1);
+      }
+      theora_p++;
+      if(theora_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){
+    theora_decode_init(&td,&ti);
+    fprintf(stderr,"Ogg logical stream %x is Theora %dx%d %.02f fps video\nEncoded frame content is %dx%d with %dx%d offset\n",
+            to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator,
+            ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y);
+  }else{
+    /* tear down the partial theora setup */
+    theora_info_clear(&ti);
+    theora_comment_clear(&tc);
+  }
+
+  /* open video */
+  if(theora_p)open_video();
+
+  /* install signal handler */
+  signal (SIGINT, sigint_handler);
+
+  /* on to the main decode loop.*/
+
+  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);
+  }
+  while(!got_sigint){
+
+    while(theora_p && !videobuf_ready){
+      /* theora is one in, one out... */
+      if(ogg_stream_packetout(&to,&op)>0){
+
+        theora_decode_packetin(&td,&op);
+        videobuf_granulepos=td.granulepos;
+        videobuf_time=theora_granule_time(&td,videobuf_granulepos);
+        videobuf_ready=1;
+
+      }else
+        break;
+    }
+
+    if(!videobuf_ready  && feof(infile))break;
+
+    if(!videobuf_ready ){
+      /* no data yet for somebody.  Grab another page */
+      int ret=buffer_data(infile,&oy);
+      while(ogg_sync_pageout(&oy,&og)>0){
+        queue_page(&og);
+      }
+    }
+    /* dumpvideo frame, and get new one */
+    else video_write();
+
+    videobuf_ready=0;
+  }
+
+  /* close everything */
+
+  if(theora_p){
+    ogg_stream_clear(&to);
+    theora_clear(&td);
+    theora_comment_clear(&tc);
+    theora_info_clear(&ti);
+  }
+  ogg_sync_clear(&oy);
+
+  if(infile && infile!=stdin)fclose(infile);
+
+  fprintf(stderr,
+          "\r                                                              "
+          "\nDone.\n");
+  return(0);
+
+}

Added: trunk/theora-tools/theora_splayer/Makefile.am
===================================================================
--- trunk/theora-tools/theora_splayer/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_splayer/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = $(SPLAYER)
+
+# possible contents of BUILDABLE_PLAYERS:
+EXTRA_PROGRAMS = theora_splayer
+
+CFLAGS = $(SDL_CFLAGS)
+LDADD = -ltheora -logg -lvorbis -lm
+
+theora_splayer_SOURCES = theora_splayer.c
+theora_splayer_LDADD = $(LDADD) $(SDL_LIBS) -lportaudio -lpthread

Added: trunk/theora-tools/theora_splayer/theora_splayer.c
===================================================================
--- trunk/theora-tools/theora_splayer/theora_splayer.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_splayer/theora_splayer.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,1103 @@
+/********************************************************************
+ *                                                                  *
+ * 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-2003                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example SDL player application; plays Ogg Theora files (with
+            optional Vorbis audio second stream)
+ * Modified by M. Piacentini http://www.tabuleiro.com
+ * from the original Theora Alpha player_sample files 
+ *
+ * Modified to build on Windows and use PortAudio as the audio
+ * and synchronization layer, calculating license.
+ *
+ * With SDL PortAudio it should be easy to compile on other platforms and
+ * sound providers like DirectSound
+ * just include the corresponding .c file (see PortAudio main documentation
+ * for additional information)
+
+ ********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include "theora/theora.h"
+#include "vorbis/codec.h"
+
+#ifdef WIN32
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include <portaudio.h>
+#include <SDL.h>
+
+/* for portaudio  */
+#define FRAMES_PER_BUFFER (256)
+
+/*start of portaudio helper functions, extracted from pablio directory*/
+
+/*Pa_streamio routines modified by mauricio at xiph.org
+ * Modified version of Portable Audio Blocking read/write utility.
+ * from the original PABLIO files
+ * Modified to support only playback buffers, direct access
+ * to the underlying stream time and remove blocking operations*/
+
+/* PortAudio copyright notice follows */
+
+/*
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ */
+
+typedef struct
+{
+    long   bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
+/* These are declared volatile because they are written by a different thread than the reader. */
+    volatile long   writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
+    volatile long   readIndex;  /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
+    long   bigMask;    /* Used for wrapping indices with extra bit to distinguish full/empty. */
+    long   smallMask;  /* Used for fitting indices to buffer. */
+    char *buffer;
+}
+RingBuffer;
+
+typedef struct
+{
+    RingBuffer   outFIFO;
+    PortAudioStream *stream;
+    int          bytesPerFrame;
+    int          samplesPerFrame;
+}
+PASTREAMIO_Stream;
+
+/* Values for flags for OpenAudioStream(). */
+/* Keep PABLIO ones*/
+
+#define PASTREAMIO_READ     (1<<0)
+#define PASTREAMIO_WRITE    (1<<1)
+#define PASTREAMIO_READ_WRITE    (PABLIO_READ|PABLIO_WRITE)
+#define PASTREAMIO_MONO     (1<<2)
+#define PASTREAMIO_STEREO   (1<<3)
+
+ /***************************************************************************
+** Helper function added to report stream time. */
+
+PaTimestamp GetAudioStreamTime( PASTREAMIO_Stream *aStream ){
+  return Pa_StreamTime( aStream->stream ) ;
+}
+
+ /***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+    rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+ * Initialize FIFO.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+    if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
+    rbuf->bufferSize = numBytes;
+    rbuf->buffer = (char *)dataPtr;
+    RingBuffer_Flush( rbuf );
+    rbuf->bigMask = (numBytes*2)-1;
+    rbuf->smallMask = (numBytes)-1;
+    return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+    return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+    return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+                                 void **dataPtr1, long *sizePtr1,
+                                 void **dataPtr2, long *sizePtr2 )
+{
+    long   index;
+    long   available = RingBuffer_GetWriteAvailable( rbuf );
+    if( numBytes > available ) numBytes = available;
+    /* Check to see if write is not contiguous. */
+    index = rbuf->writeIndex & rbuf->smallMask;
+    if( (index + numBytes) > rbuf->bufferSize )
+    {
+        /* Write data in two blocks that wrap the buffer. */
+        long   firstHalf = rbuf->bufferSize - index;
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = firstHalf;
+        *dataPtr2 = &rbuf->buffer[0];
+        *sizePtr2 = numBytes - firstHalf;
+    }
+    else
+    {
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = numBytes;
+        *dataPtr2 = NULL;
+        *sizePtr2 = 0;
+    }
+    return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+    return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+                                void **dataPtr1, long *sizePtr1,
+                                void **dataPtr2, long *sizePtr2 )
+{
+    long   index;
+    long   available = RingBuffer_GetReadAvailable( rbuf );
+    if( numBytes > available ) numBytes = available;
+    /* Check to see if read is not contiguous. */
+    index = rbuf->readIndex & rbuf->smallMask;
+    if( (index + numBytes) > rbuf->bufferSize )
+    {
+        /* Write data in two blocks that wrap the buffer. */
+        long firstHalf = rbuf->bufferSize - index;
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = firstHalf;
+        *dataPtr2 = &rbuf->buffer[0];
+        *sizePtr2 = numBytes - firstHalf;
+    }
+    else
+    {
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = numBytes;
+        *dataPtr2 = NULL;
+        *sizePtr2 = 0;
+    }
+    return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+    return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+    long size1, size2, numWritten;
+    void *data1, *data2;
+    numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+    if( size2 > 0 )
+    {
+
+        memcpy( data1, data, size1 );
+        data = ((char *)data) + size1;
+        memcpy( data2, data, size2 );
+    }
+    else
+    {
+        memcpy( data1, data, size1 );
+    }
+    RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+    return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+    long size1, size2, numRead;
+    void *data1, *data2;
+    numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+    if( size2 > 0 )
+    {
+        memcpy( data, data1, size1 );
+        data = ((char *)data) + size1;
+        memcpy( data, data2, size2 );
+    }
+    else
+    {
+        memcpy( data, data1, size1 );
+    }
+    RingBuffer_AdvanceReadIndex( rbuf, numRead );
+    return numRead;
+}
+
+
+/************************************************************************/
+/******** Functions *****************************************************/
+/************************************************************************/
+
+/* Called from PortAudio.
+ * Read and write data only if there is room in FIFOs.
+ */
+static int audioIOCallback( void *inputBuffer, void *outputBuffer,
+                               unsigned long framesPerBuffer,
+                               PaTimestamp outTime, void *userData )
+{
+    PASTREAMIO_Stream *data = (PASTREAMIO_Stream*)userData;
+    long numBytes = data->bytesPerFrame * framesPerBuffer;
+    (void) outTime;
+    (void) inputBuffer;
+
+    if( outputBuffer != NULL )
+    {
+        int i;
+        int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
+        /* Zero out remainder of buffer if we run out of data. */
+        for( i=numRead; i<numBytes; i++ )
+        {
+            ((char *)outputBuffer)[i] = 0;
+        }
+    }
+
+    return 0;
+}
+
+/* Allocate buffer. */
+static PaError PASTREAMIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
+{
+    long numBytes = numFrames * bytesPerFrame;
+    char *buffer = (char *) malloc( numBytes );
+    if( buffer == NULL ) return paInsufficientMemory;
+    memset( buffer, 0, numBytes );
+    return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
+}
+
+/* Free buffer. */
+static PaError PASTREAMIO_TermFIFO( RingBuffer *rbuf )
+{
+    if( rbuf->buffer ) free( rbuf->buffer );
+    rbuf->buffer = NULL;
+    return paNoError;
+}
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PASTREAMIO_Stream *aStream, void *data, long numFrames )
+{
+    long bytesWritten;
+    char *p = (char *) data;
+    long numBytes = aStream->bytesPerFrame * numFrames;
+    while( numBytes > 0)
+    {
+        bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
+        numBytes -= bytesWritten;
+        p += bytesWritten;
+        if( numBytes > 0) Pa_Sleep(10);
+    }
+    return numFrames;
+}
+
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PASTREAMIO_Stream *aStream )
+{
+    int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+    return bytesEmpty / aStream->bytesPerFrame;
+}
+
+
+
+/************************************************************/
+unsigned long RoundUpToNextPowerOf2( unsigned long n )
+{
+    long numBits = 0;
+    if( ((n-1) & n) == 0) return n; /* Already Power of two. */
+    while( n > 0 )
+    {
+        n= n>>1;
+        numBits++;
+    }
+    return (1<<numBits);
+}
+
+/* forward prototype */
+PaError CloseAudioStream( PASTREAMIO_Stream *aStream );
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PASTREAMIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ *    PABLIO_WRITE, 
+ *    and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PASTREAMIO_Stream **rwblPtr, double sampleRate,
+                         PaSampleFormat format, long flags )
+{
+    long   bytesPerSample;
+    long   doWrite = 0;
+    PaError err;
+    PASTREAMIO_Stream *aStream;
+    long   minNumBuffers;
+    long   numFrames;
+
+    /* Allocate PASTREAMIO_Stream structure for caller. */
+    aStream = (PASTREAMIO_Stream *) malloc( sizeof(PASTREAMIO_Stream) );
+    if( aStream == NULL ) return paInsufficientMemory;
+    memset( aStream, 0, sizeof(PASTREAMIO_Stream) );
+
+    /* Determine size of a sample. */
+    bytesPerSample = Pa_GetSampleSize( format );
+    if( bytesPerSample < 0 )
+    {
+        err = (PaError) bytesPerSample;
+        goto error;
+    }
+    aStream->samplesPerFrame = ((flags&PASTREAMIO_MONO) != 0) ? 1 : 2;
+    aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
+
+    /* Initialize PortAudio  */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    /* Warning: numFrames must be larger than amount of data processed per interrupt
+     *    inside PA to prevent glitches. Just to be safe, adjust size upwards.
+     */
+    minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+    numFrames = minNumBuffers * FRAMES_PER_BUFFER;
+    numFrames = RoundUpToNextPowerOf2( numFrames );
+
+    /* Initialize Ring Buffer */
+    doWrite = ((flags & PASTREAMIO_WRITE) != 0);
+
+    if(doWrite)
+    {
+        err = PASTREAMIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
+        if( err != paNoError ) goto error;
+        /* Make Write FIFO appear full initially. 
+        numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );*/
+    }
+
+    /* Open a PortAudio stream that we will use to communicate with the underlying
+     * audio drivers. */
+    err = Pa_OpenStream(
+              &aStream->stream,
+              paNoDevice,
+              0 ,
+              format,
+              NULL,
+              Pa_GetDefaultOutputDeviceID() ,
+              aStream->samplesPerFrame ,
+              format,
+              NULL,
+              sampleRate,
+              FRAMES_PER_BUFFER,
+              minNumBuffers,
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              audioIOCallback,
+              aStream );
+    if( err != paNoError ) goto error;
+
+    *rwblPtr = aStream;
+    return paNoError;
+
+error:
+    CloseAudioStream( aStream );
+    *rwblPtr = NULL;
+    return err;
+}
+
+PaError StartAudioStream( PASTREAMIO_Stream *aStream)
+{
+	PaError err;
+	err = Pa_StartStream( aStream->stream );
+    if( err != paNoError ) goto error;
+
+    return paNoError;
+error:
+    CloseAudioStream( aStream );
+    return err;
+}
+
+
+/************************************************************/
+PaError CloseAudioStream( PASTREAMIO_Stream *aStream )
+{
+    PaError err;
+    int bytesEmpty;
+    int byteSize = aStream->outFIFO.bufferSize;
+
+    /* If we are writing data, make sure we play everything written. */
+    if( byteSize > 0 )
+    {
+        bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        while( bytesEmpty < byteSize )
+        {
+            Pa_Sleep( 10 );
+            bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        }
+    }
+
+    err = Pa_StopStream( aStream->stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( aStream->stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+
+error:
+    PASTREAMIO_TermFIFO( &aStream->outFIFO );
+    free( aStream );
+    return err;
+}
+/*end of portaudio specific routines*/
+
+/*portaudio related global types  */
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#define SAMPLE_SILENCE  (0)
+
+PASTREAMIO_Stream  *aOutStream; /* our modified stream buffer*/
+SAMPLE      *samples; /*local buffer for samples*/
+double latency_sec = 0;
+
+/*ticks information to be used if the audio stream is not present*/
+int    currentTicks = -1;
+
+/*initial state of the audio stream*/
+int isPlaying = 0;
+PaError err;
+
+/* this should go in a header file */
+int theora_decode_tables(theora_info *c, ogg_packet *op);
+
+
+/* Ogg and codec state for demux/decode */
+ogg_sync_state   oy; 
+ogg_page         og;
+ogg_stream_state vo;
+ogg_stream_state to;
+theora_info      ti;
+theora_comment   tc;
+theora_state     td;
+vorbis_info      vi;
+vorbis_dsp_state vd;
+vorbis_block     vb;
+vorbis_comment   vc;
+
+int              theora_p=0;
+int              vorbis_p=0;
+int              stateflag=0;
+
+FILE * infile = NULL;
+
+/* SDL Video playback structures */
+SDL_Surface *screen;
+SDL_Overlay *yuv_overlay;
+int          audiobuf_ready=0;
+SDL_Rect rect;
+
+/* single frame video buffering */
+int          videobuf_ready=0;
+ogg_int64_t  videobuf_granulepos=-1;
+double       videobuf_time=0;
+
+ogg_int64_t  audiobuf_granulepos=0; /* time position of last sample */
+
+
+short initialticks, endticks;
+
+/* Helper; just grab some more compressed bitstream and sync it for
+   page extraction */
+int buffer_data(ogg_sync_state *oy){
+  char *buffer=ogg_sync_buffer(oy,4096);
+  int bytes=fread(buffer,1,4096,infile);
+  ogg_sync_wrote(oy,bytes);
+  return(bytes);
+}
+
+static void usage(void){
+  printf("Usage: splayer ogg_file\n\n"
+         "or drag and drop an ogg file over the .exe\n\n");
+  exit(1);
+}
+
+static int open_audio(){
+    /* this will open one circular audio stream*/
+    /*build on top of portaudio routines*/
+    /*implementation on fille pastreamio.c*/
+
+    int        numSamples;
+    int        numBytes;
+
+    int minNumBuffers;
+    int numFrames;
+
+    minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, vi.rate );
+    numFrames = minNumBuffers * FRAMES_PER_BUFFER;
+    numFrames = RoundUpToNextPowerOf2( numFrames );
+
+    numSamples = numFrames * vi.channels;
+    numBytes = numSamples * sizeof(SAMPLE);
+
+    samples = (SAMPLE *) malloc( numBytes );
+
+    /*store our latency calculation here*/
+    latency_sec =  (double) numFrames / vi.rate / vi.channels;
+    printf( "Latency: %.04f\n", latency_sec );
+
+    err = OpenAudioStream( &aOutStream, vi.rate, PA_SAMPLE_TYPE,
+                           (PASTREAMIO_WRITE | PASTREAMIO_STEREO) );
+    if( err != paNoError ) goto error;
+    return err;
+error:
+    CloseAudioStream( aOutStream );
+    printf( "An error occured while opening the portaudio stream\n" );
+    printf( "Error number: %d\n", err );
+    printf( "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+
+}
+
+static int start_audio(){
+    err = StartAudioStream(aOutStream);
+    if( err != paNoError ) goto error;
+
+    return err;
+error:
+    CloseAudioStream( aOutStream );
+    printf( "An error occured while opening the portaudio stream\n" );
+    printf( "Error number: %d\n", err );
+    printf( "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
+
+static int audio_close(void){
+    err = CloseAudioStream( aOutStream );
+    if( err != paNoError ) goto error;
+
+    free(samples);
+    return err;
+
+error:
+    Pa_Terminate();
+    printf( "An error occured while closing the portaudio stream\n" );
+    printf( "Error number: %d\n", err );
+    printf( "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
+
+
+double get_time(){
+    double curtime;
+    if (vorbis_p){
+     /*not entirely accurate with the WAVE OUT device, but good enough
+     at this stage. Needs to be reworked to account for blank audio
+     data written to the stream...*/
+      curtime = (double) (GetAudioStreamTime( aOutStream ) / vi.rate) -  latency_sec;
+      if (curtime<0) curtime = 0;
+	} else {
+        /*initialize timer variable if not set yet*/
+        if (currentTicks==-1) {
+            currentTicks = SDL_GetTicks();
+            return currentTicks;
+        }
+		curtime = (double) (SDL_GetTicks() - currentTicks)/1000.0F;
+	}
+    return  curtime  ;
+}
+
+
+static void open_video(void){
+  /*taken from player_sample.c test file for theora alpha*/
+
+  if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+    printf("Unable to init SDL: %s\n", SDL_GetError());
+    exit(1);
+  }
+  
+  screen = SDL_SetVideoMode(ti.frame_width, ti.frame_height, 0, SDL_SWSURFACE);
+  if ( screen == NULL ) {
+    printf("Unable to set %dx%d video: %s\n", 
+           ti.frame_width,ti.frame_height,SDL_GetError());
+    exit(1);
+  }
+  
+  yuv_overlay = SDL_CreateYUVOverlay(ti.frame_width, ti.frame_height,
+				     SDL_YV12_OVERLAY,
+				     screen);
+  if ( yuv_overlay == NULL ) {
+    printf("SDL: Couldn't create SDL_yuv_overlay: %s\n", 
+	   SDL_GetError());
+    exit(1);
+  }
+  rect.x = 0;
+  rect.y = 0;
+  rect.w = ti.frame_width;
+  rect.h = ti.frame_height;
+
+  SDL_DisplayYUVOverlay(yuv_overlay, &rect);
+}
+
+static void video_write(void){
+  /*taken from player_sample.c test file for theora alpha*/
+  int i;
+  yuv_buffer yuv;
+  int crop_offset;
+  theora_decode_YUVout(&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 (*yuv[3]) on a SDL screen (*screen) */
+  /* deal with border stride */
+  /* reverse u and v for SDL */
+  /* and crop input properly, respecting the encoded frame rect */
+  crop_offset=ti.offset_x+yuv.y_stride*ti.offset_y;
+  for(i=0;i<yuv_overlay->h;i++)
+    memcpy(yuv_overlay->pixels[0]+yuv_overlay->pitches[0]*i, 
+	   yuv.y+crop_offset+yuv.y_stride*i, 
+	   yuv_overlay->w);
+  crop_offset=(ti.offset_x/2)+(yuv.uv_stride)*(ti.offset_y/2);
+  for(i=0;i<yuv_overlay->h/2;i++){
+    memcpy(yuv_overlay->pixels[1]+yuv_overlay->pitches[1]*i, 
+	   yuv.v+crop_offset+yuv.uv_stride*i, 
+	   yuv_overlay->w/2);
+    memcpy(yuv_overlay->pixels[2]+yuv_overlay->pitches[2]*i, 
+	   yuv.u+crop_offset+yuv.uv_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(theora_comment *tc){
+  int i, len;
+  char *value;
+  
+  printf("Encoded by %s\n",tc->vendor);
+  if(tc->comments){
+    printf("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';
+      	printf("\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(theora_info *ti)
+{
+    switch(ti->colorspace){
+      case OC_CS_UNSPECIFIED:
+        /* nothing to report */
+        break;;
+      case OC_CS_ITU_REC_470M:
+        fprintf(stderr,"  encoder specified ITU Rec 470M color.\n");
+        break;;
+      case OC_CS_ITU_REC_470BG:
+        fprintf(stderr,"  encoder specified ITU Rec 470BG 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,&og);
+  if(vorbis_p)ogg_stream_pagein(&vo,&og);
+  return 0;
+}                                   
+
+
+void parseHeaders(){
+  /*extracted from player_sample.c test file for theora alpha*/
+  ogg_packet op;
+  /* Parse the headers */
+  /* Only interested in Vorbis/Theora streams */
+  while(!stateflag){
+    int ret=buffer_data(&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 && theora_decode_header(&ti,&tc,&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));
+	/* there will be more vorbis headers later... */
+	vorbis_p=1;
+      }else{
+	/* whatever it is, we don't care about it */
+	ogg_stream_clear(&test);
+      }
+    }
+  }
+
+  /* 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){
+      	printf("Error parsing Theora stream headers; corrupt stream?\n");
+      	exit(1);
+      }
+      if(theora_decode_header(&ti,&tc,&op)){
+        printf("Error parsing Theora stream headers; corrupt stream?\n");
+        exit(1);
+      }
+      theora_p++;
+      if(theora_p==3)break;
+    }
+
+    /* look for more vorbis header packets */  
+    while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
+      if(ret<0){
+	printf("Error parsing Vorbis stream headers; corrupt stream?\n");
+	exit(1);
+      }
+      if(vorbis_synthesis_headerin(&vi,&vc,&op)){
+	printf("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(&oy);
+      if(ret==0){
+	fprintf(stderr,"End of file while searching for Vorbis headers.\n");
+	exit(1);
+      }
+    }
+  }
+
+}
+
+int main( int argc, char* argv[] ){
+
+  int i,j;
+  ogg_packet op;
+  SDL_Event event;
+  int hasdatatobuffer = 1;
+  int playbackdone = 0;
+
+  int frameNum=0;
+
+  /*takes first argument as file to play*/
+  /*this works better on Windows and is more convenient
+  for drag and drop ogg files over the .exe*/
+
+  if( argc != 2 )
+  {
+    usage();
+    exit(0);
+  }
+
+  infile  = fopen( argv[1], "rb" );
+
+  /* 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 */
+  theora_comment_init(&tc);
+  theora_info_init(&ti);
+
+  parseHeaders();
+
+  /* initialize decoders */
+  if(theora_p){
+    theora_decode_init(&td,&ti);
+    printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n"
+           "  Frame content is %dx%d with offset (%d,%d).\n",
+	   to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator,
+	   ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y);
+    report_colorspace(&ti);
+    dump_comments(&tc);
+  }else{
+    /* tear down the partial theora setup */
+    theora_info_clear(&ti);
+    theora_comment_clear(&tc);
+  }
+  if(vorbis_p){
+    vorbis_synthesis_init(&vd,&vi);
+    vorbis_block_init(&vd,&vb);  
+    printf("Ogg logical stream %x is Vorbis %d channel %d 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();
+
+  /*initialticks = GetTickCount();*/
+  /*our main loop*/
+  while(1){
+
+    SDL_Delay(5);
+
+    if (playbackdone == 1 ) break;
+
+    /*break out on SDL quit event*/
+    if ( SDL_PollEvent ( &event ) )
+    {
+      if ( event.type == SDL_QUIT ) break ;
+
+    }
+
+    /*get some audio data*/
+    while(vorbis_p && !audiobuf_ready){
+      int ret;
+      float **pcm;
+      int count = 0;
+      int maxBytesToWrite;
+
+      /* is there pending audio? does it fit our circular buffer without blocking?*/
+      ret=vorbis_synthesis_pcmout(&vd,&pcm);
+      maxBytesToWrite = GetAudioStreamWriteable(aOutStream);
+	  
+      if (maxBytesToWrite<=FRAMES_PER_BUFFER)
+      {
+	/*break out until there is a significant amount of
+	data to avoid a series of small write operations*/
+	break;
+      }
+      /* if there's pending, decoded audio, grab it */
+      if((ret>0)&&(maxBytesToWrite>0)){
+	for(i=0;i<ret && i<(maxBytesToWrite/vi.channels);i++)
+	  for(j=0;j<vi.channels;j++){
+	    int val=(int)(pcm[j][i]*32767.f);
+	    if(val>32767)val=32767;
+	    if(val<-32768)val=-32768;
+	    samples[count]=val;
+	    count++;
+	  }
+	if(WriteAudioStream( aOutStream, samples, i )) {
+        vorbis_synthesis_read(&vd,i);
+		if(count==maxBytesToWrite) {
+			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;
+      }
+    }/*end audio cycle*/
+
+    while(theora_p && !videobuf_ready){
+      /* get one video packet... */
+      if(ogg_stream_packetout(&to,&op)>0){
+	  theora_decode_packetin(&td,&op);
+
+	  videobuf_granulepos=td.granulepos;
+	  videobuf_time=theora_granule_time(&td,videobuf_granulepos);
+	  /*update the frame counter*/
+	  //printf("Frame\n");
+	  frameNum++;
+
+	  /*check if this frame time has not passed yet.
+	  If the frame is late we need to decode additonal
+	  ones and keep looping, since theora at
+	  this stage needs to decode all frames*/
+
+	   if(videobuf_time>= get_time())
+		/*got a good frame, not late, ready to break out*/
+		videobuf_ready=1;
+
+      }else
+	/*already have a good frame in the buffer*/
+      {
+        if (isPlaying == 1)
+        {
+	  printf("end\n");
+	  /*endticks = GetTickCount();*/
+	  isPlaying = 0;
+	  playbackdone = 1;
+	}
+	break;
+      }
+    }
+ 
+    if(stateflag && videobuf_ready && audiobuf_ready && (videobuf_time<= get_time())){
+      /*time to write our cached frame*/
+      video_write();
+      videobuf_ready=0;
+	  audiobuf_ready=0;
+      /*if audio has not started (first frame) then start it*/
+      if ((!isPlaying)&&(vorbis_p)){
+	start_audio();
+	isPlaying = 1;
+      }
+    }
+
+    /*buffer compressed data every loop */
+    if (hasdatatobuffer)
+    {
+      hasdatatobuffer=buffer_data(&oy);
+      if(hasdatatobuffer==0){
+	printf("Ogg buffering stopped, end of file reached.\n");
+
+      }
+    }
+	/* audio is already in portaudio buffer */
+	else if (!audiobuf_ready && !playbackdone)audiobuf_ready=1;
+
+
+
+    if(ogg_sync_pageout(&oy,&og)>0){
+      queue_page(&og);
+    }
+	
+  }
+
+  /*show number of video frames decoded*/
+  printf( "Frames decoded: %d\n", frameNum );
+  /*printf( "Milliseconds: %d\n", endticks-initialticks);
+  printf("Average fps (%.02f)\n", ((float) frameNum)/((endticks-initialticks)/1000.0f));*/
+
+  /* tear it all down */
+  fclose( infile );
+
+  SDL_Quit();
+
+  if(vorbis_p){
+    audio_close();
+
+    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);
+    theora_clear(&td);
+    theora_comment_clear(&tc);
+    theora_info_clear(&ti);
+  }
+  ogg_sync_clear(&oy);
+
+  printf("\r                                                              "
+	 "\nDone.\n");
+  return(0);
+
+}

Added: trunk/theora-tools/theora_transcoder/Makefile.am
===================================================================
--- trunk/theora-tools/theora_transcoder/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = theora_transcoder avi2vp3
+
+LDADD = -ltheora -logg -lvorbis -lm
+
+theora_transcoder_SOURCES = theora_transcoder.c
+theora_transcoder_LDADD = $(LDADD) -lvorbisenc
+
+avi2vp3_SOURCES = avi2vp3.c avilib.c avilib.h

Added: trunk/theora-tools/theora_transcoder/avi2vp3.c
===================================================================
--- trunk/theora-tools/theora_transcoder/avi2vp3.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/avi2vp3.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+
+/*extremely crude app to dump vp3 frames from an avi file*/
+/*filenames are hardcoded*/
+
+#include "avilib.h"
+
+int main(int argc, const char **argv)
+{
+    FILE * f = fopen("outfile.vp3", "wb");
+
+    char * buffer = malloc(32768);
+    int olength;
+    int length;
+    avi_t *avifile;
+    int frame;
+    int frames;
+    int keyframegap = 0;
+    int maxkeyframegap = 0;
+    
+    
+    int framew = 0;
+    int frameh = 0;
+    double framerate = 0.0f;
+    double fps_numerator, fps_denominator;
+
+    avifile = AVI_open_input_file("vp31.avi", 1);
+    frames = AVI_video_frames(avifile);
+    framew = AVI_video_width(avifile);
+    frameh = AVI_video_height(avifile);
+    framerate = AVI_frame_rate(avifile);
+    printf("Frames(%d) Video(%dx%d) %3.2f fps\n",frames,framew, frameh,framerate);
+    printf("Video Compressor: %s", AVI_video_compressor(avifile));
+    fps_denominator = 1000000.0F;
+    fps_numerator = framerate * fps_denominator;
+    
+    sprintf(buffer,"AVI2VP31R W%d H%d F%.0f:%.0f Ip A0:0\n", framew, frameh, fps_numerator, fps_denominator);
+    fwrite(buffer, strlen(buffer), 1, f);
+    
+    for (frame = 0; frame < frames;) {
+	int keyframe;
+	olength = length;
+	length = AVI_frame_size(avifile, frame++);
+	if( !length ) {
+	    length = olength;
+	}
+	AVI_read_frame(avifile, (char *) buffer, &keyframe);
+	fwrite("FRAME\n", 6, 1, f);
+	fwrite(&length, sizeof(int), 1, f);
+	fwrite(&keyframe, sizeof(int), 1, f);
+	printf("Frame size(%d) IsKeyframe(%d)\n", length, keyframe);
+	fwrite(buffer, 1, length, f);
+	if (!keyframe){
+	  keyframegap++;
+	} else {
+	  if (keyframegap>maxkeyframegap) maxkeyframegap=keyframegap;	
+	  keyframegap = 0;
+	}
+	}
+
+	fclose(f);
+	printf("Max keyframegap (%d)\n", maxkeyframegap);
+	
+	free(buffer);
+
+    exit(0);
+}

Added: trunk/theora-tools/theora_transcoder/avilib.c
===================================================================
--- trunk/theora-tools/theora_transcoder/avilib.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/avilib.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,1833 @@
+/*
+ *  avilib.c
+ *
+ *  Copyright (C) Thomas Östreich - June 2001
+ *  multiple audio track support Copyright (C) 2002 Thomas Östreich 
+ *
+ *  Original code:
+ *  Copyright (C) 1999 Rainer Johanni <Rainer at Johanni.de> 
+ *
+ *  This file is part of transcode, a linux video stream processing tool
+ *      
+ *  transcode is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  transcode is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ */
+
+#include "avilib.h"
+//#include <time.h>
+
+#define INFO_LIST
+
+/* The following variable indicates the kind of error */
+
+long AVI_errno;
+
+#define MAX_INFO_STRLEN 64
+static char id_str[MAX_INFO_STRLEN];
+
+#define FRAME_RATE_SCALE 1000000
+
+#ifndef PACKAGE
+#define PACKAGE "my"
+#define VERSION "0.00"
+#endif
+
+/*******************************************************************
+ *                                                                 *
+ *    Utilities for writing an AVI File                            *
+ *                                                                 *
+ *******************************************************************/
+
+static size_t avi_read(int fd, char *buf, size_t len)
+{
+   size_t n = 0;
+   size_t r = 0;
+
+   while (r < len) {
+      n = read (fd, buf + r, len - r);
+
+      if (n <= 0)
+	  return r;
+      r += n;
+   }
+
+   return r;
+}
+
+static size_t avi_write (int fd, char *buf, size_t len)
+{
+   size_t n = 0;
+   size_t r = 0;
+
+   while (r < len) {
+      n = write (fd, buf + r, len - r);
+      if (n < 0)
+         return n;
+      
+      r += n;
+   }
+   return r;
+}
+
+/* HEADERBYTES: The number of bytes to reserve for the header */
+
+#define HEADERBYTES 2048
+
+/* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below
+    the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */
+
+#define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES)
+
+#define PAD_EVEN(x) ( ((x)+1) & ~1 )
+
+
+/* Copy n into dst as a 4 byte, little endian number.
+   Should also work on big endian machines */
+
+static void long2str(unsigned char *dst, int n)
+{
+   dst[0] = (n    )&0xff;
+   dst[1] = (n>> 8)&0xff;
+   dst[2] = (n>>16)&0xff;
+   dst[3] = (n>>24)&0xff;
+}
+
+/* Convert a string of 4 or 2 bytes to a number,
+   also working on big endian machines */
+
+static unsigned long str2ulong(unsigned char *str)
+{
+   return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
+}
+static unsigned long str2ushort(unsigned char *str)
+{
+   return ( str[0] | (str[1]<<8) );
+}
+
+/* Calculate audio sample size from number of bits and number of channels.
+   This may have to be adjusted for eg. 12 bits and stereo */
+
+static int avi_sampsize(avi_t *AVI, int j)
+{
+   int s;
+   s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans;
+   //   if(s==0) s=1; /* avoid possible zero divisions */
+   if(s<4) s=4; /* avoid possible zero divisions */ 
+   return s;
+}
+
+/* Add a chunk (=tag and data) to the AVI file,
+   returns -1 on write error, 0 on success */
+
+static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, int length)
+{
+   unsigned char c[8];
+
+   /* Copy tag and length int c, so that we need only 1 write system call
+      for these two values */
+
+   memcpy(c,tag,4);
+   long2str(c+4,length);
+
+   /* Output tag, length and data, restore previous position
+      if the write fails */
+
+   length = PAD_EVEN(length);
+
+   if( avi_write(AVI->fdes,(char *)c,8) != 8 ||
+       avi_write(AVI->fdes,(char *)data,length) != length )
+   {
+      lseek(AVI->fdes,AVI->pos,SEEK_SET);
+      AVI_errno = AVI_ERR_WRITE;
+      return -1;
+   }
+
+   /* Update file position */
+
+   AVI->pos += 8 + length;
+
+   //fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag);
+
+   return 0;
+}
+
+static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, unsigned long pos, unsigned long len)
+{
+   void *ptr;
+
+   if(AVI->n_idx>=AVI->max_idx) {
+     ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16);
+     
+     if(ptr == 0) {
+       AVI_errno = AVI_ERR_NO_MEM;
+       return -1;
+     }
+     AVI->max_idx += 4096;
+     AVI->idx = (unsigned char((*)[16]) ) ptr;
+   }
+   
+   /* Add index entry */
+
+   //   fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len);
+
+   memcpy(AVI->idx[AVI->n_idx],tag,4);
+   long2str(AVI->idx[AVI->n_idx]+ 4,flags);
+   long2str(AVI->idx[AVI->n_idx]+ 8, pos);
+   long2str(AVI->idx[AVI->n_idx]+12, len);
+   
+   /* Update counter */
+
+   AVI->n_idx++;
+
+   if(len>AVI->max_len) AVI->max_len=len;
+
+   return 0;
+}
+
+/*
+   AVI_open_output_file: Open an AVI File and write a bunch
+                         of zero bytes as space for the header.
+
+   returns a pointer to avi_t on success, a zero pointer on error
+*/
+
+avi_t* AVI_open_output_file(char * filename)
+{
+   avi_t *AVI;
+   int i;
+
+   int mask = 0;
+   
+   unsigned char AVI_header[HEADERBYTES];
+
+   /* Allocate the avi_t struct and zero it */
+
+   AVI = (avi_t *) malloc(sizeof(avi_t));
+   if(AVI==0)
+   {
+      AVI_errno = AVI_ERR_NO_MEM;
+      return 0;
+   }
+   memset((void *)AVI,0,sizeof(avi_t));
+
+   /* Since Linux needs a long time when deleting big files,
+      we do not truncate the file when we open it.
+      Instead it is truncated when the AVI file is closed */
+
+  /* mask = umask (0);
+   umask (mask);*/
+
+   AVI->fdes = open(filename, O_RDWR|O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) &~ mask);
+   if (AVI->fdes < 0)
+   {
+      AVI_errno = AVI_ERR_OPEN;
+      free(AVI);
+      return 0;
+   }
+
+   /* Write out HEADERBYTES bytes, the header will go here
+      when we are finished with writing */
+
+   for (i=0;i<HEADERBYTES;i++) AVI_header[i] = 0;
+   i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES);
+   if (i != HEADERBYTES)
+   {
+      close(AVI->fdes);
+      AVI_errno = AVI_ERR_WRITE;
+      free(AVI);
+      return 0;
+   }
+
+   AVI->pos  = HEADERBYTES;
+   AVI->mode = AVI_MODE_WRITE; /* open for writing */
+
+   //init
+   AVI->anum = 0;
+   AVI->aptr = 0;
+
+   return AVI;
+}
+
+void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor)
+{
+   /* may only be called if file is open for writing */
+
+   if(AVI->mode==AVI_MODE_READ) return;
+
+   AVI->width  = width;
+   AVI->height = height;
+   AVI->fps    = fps;
+   
+   if(strncmp(compressor, "RGB", 3)==0) {
+     memset(AVI->compressor, 0, 4);
+   } else {
+     memcpy(AVI->compressor,compressor,4);
+   }     
+   
+   AVI->compressor[4] = 0;
+
+   avi_update_header(AVI);
+}
+
+void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate)
+{
+   /* may only be called if file is open for writing */
+
+   if(AVI->mode==AVI_MODE_READ) return;
+
+   //inc audio tracks
+   AVI->aptr=AVI->anum;
+   ++AVI->anum;
+
+   if(AVI->anum > AVI_MAX_TRACKS) {
+     fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
+     exit(1);
+   }
+
+   AVI->track[AVI->aptr].a_chans = channels;
+   AVI->track[AVI->aptr].a_rate  = rate;
+   AVI->track[AVI->aptr].a_bits  = bits;
+   AVI->track[AVI->aptr].a_fmt   = format;
+   AVI->track[AVI->aptr].mp3rate = mp3rate;
+
+   avi_update_header(AVI);
+}
+
+#define OUT4CC(s) \
+   if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4
+
+#define OUTLONG(n) \
+   if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb,n); nhb += 4
+
+#define OUTSHRT(n) \
+   if(nhb<=HEADERBYTES-2) { \
+      AVI_header[nhb  ] = (n   )&0xff; \
+      AVI_header[nhb+1] = (n>>8)&0xff; \
+   } \
+   nhb += 2
+
+
+//ThOe write preliminary AVI file header: 0 frames, max vid/aud size
+int avi_update_header(avi_t *AVI)
+{
+   int njunk, sampsize, hasIndex, ms_per_frame, frate, flag;
+   int movi_len, hdrl_start, strl_start, j;
+   unsigned char AVI_header[HEADERBYTES];
+   long nhb;
+
+   //assume max size
+   movi_len = AVI_MAX_LEN - HEADERBYTES + 4;
+
+   //assume index will be written
+   hasIndex=1;
+
+   if(AVI->fps < 0.001) {
+     frate=0;
+     ms_per_frame=0;
+   } else {
+     frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
+     ms_per_frame=(int) (1000000/AVI->fps + 0.5);
+   }
+
+   /* Prepare the file header */
+
+   nhb = 0;
+
+   /* The RIFF header */
+
+   OUT4CC ("RIFF");
+   OUTLONG(movi_len);    // assume max size
+   OUT4CC ("AVI ");
+
+   /* Start the header list */
+
+   OUT4CC ("LIST");
+   OUTLONG(0);        /* Length of list in bytes, don't know yet */
+   hdrl_start = nhb;  /* Store start position */
+   OUT4CC ("hdrl");
+
+   /* The main AVI header */
+
+   /* The Flags in AVI File header */
+
+#define AVIF_HASINDEX           0x00000010      /* Index at end of file */
+#define AVIF_MUSTUSEINDEX       0x00000020
+#define AVIF_ISINTERLEAVED      0x00000100
+#define AVIF_TRUSTCKTYPE        0x00000800      /* Use CKType to find key frames */
+#define AVIF_WASCAPTUREFILE     0x00010000
+#define AVIF_COPYRIGHTED        0x00020000
+
+   OUT4CC ("avih");
+   OUTLONG(56);                 /* # of bytes to follow */
+   OUTLONG(ms_per_frame);       /* Microseconds per frame */
+   //ThOe ->0 
+   //   OUTLONG(10000000);           /* MaxBytesPerSec, I hope this will never be used */
+   OUTLONG(0);
+   OUTLONG(0);                  /* PaddingGranularity (whatever that might be) */
+                                /* Other sources call it 'reserved' */
+   flag = AVIF_ISINTERLEAVED;
+   if(hasIndex) flag |= AVIF_HASINDEX;
+   if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
+   OUTLONG(flag);               /* Flags */
+   OUTLONG(0);                  // no frames yet
+   OUTLONG(0);                  /* InitialFrames */
+
+   OUTLONG(AVI->anum+1);
+
+   OUTLONG(0);                  /* SuggestedBufferSize */
+   OUTLONG(AVI->width);         /* Width */
+   OUTLONG(AVI->height);        /* Height */
+                                /* MS calls the following 'reserved': */
+   OUTLONG(0);                  /* TimeScale:  Unit used to measure time */
+   OUTLONG(0);                  /* DataRate:   Data rate of playback     */
+   OUTLONG(0);                  /* StartTime:  Starting time of AVI data */
+   OUTLONG(0);                  /* DataLength: Size of AVI data chunk    */
+
+
+   /* Start the video stream list ---------------------------------- */
+
+   OUT4CC ("LIST");
+   OUTLONG(0);        /* Length of list in bytes, don't know yet */
+   strl_start = nhb;  /* Store start position */
+   OUT4CC ("strl");
+
+   /* The video stream header */
+
+   OUT4CC ("strh");
+   OUTLONG(56);                 /* # of bytes to follow */
+   OUT4CC ("vids");             /* Type */
+   OUT4CC (AVI->compressor);    /* Handler */
+   OUTLONG(0);                  /* Flags */
+   OUTLONG(0);                  /* Reserved, MS says: wPriority, wLanguage */
+   OUTLONG(0);                  /* InitialFrames */
+   OUTLONG(FRAME_RATE_SCALE);              /* Scale */
+   OUTLONG(frate);              /* Rate: Rate/Scale == samples/second */
+   OUTLONG(0);                  /* Start */
+   OUTLONG(0);                  // no frames yet
+   OUTLONG(0);                  /* SuggestedBufferSize */
+   OUTLONG(-1);                 /* Quality */
+   OUTLONG(0);                  /* SampleSize */
+   OUTLONG(0);                  /* Frame */
+   OUTLONG(0);                  /* Frame */
+   //   OUTLONG(0);                  /* Frame */
+   //OUTLONG(0);                  /* Frame */
+
+   /* The video stream format */
+
+   OUT4CC ("strf");
+   OUTLONG(40);                 /* # of bytes to follow */
+   OUTLONG(40);                 /* Size */
+   OUTLONG(AVI->width);         /* Width */
+   OUTLONG(AVI->height);        /* Height */
+   OUTSHRT(1); OUTSHRT(24);     /* Planes, Count */
+   OUT4CC (AVI->compressor);    /* Compression */
+   // ThOe (*3)
+   OUTLONG(AVI->width*AVI->height*3);  /* SizeImage (in bytes?) */
+   OUTLONG(0);                  /* XPelsPerMeter */
+   OUTLONG(0);                  /* YPelsPerMeter */
+   OUTLONG(0);                  /* ClrUsed: Number of colors used */
+   OUTLONG(0);                  /* ClrImportant: Number of colors important */
+
+   /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+
+   long2str(AVI_header+strl_start-4,nhb-strl_start);
+
+   
+   /* Start the audio stream list ---------------------------------- */
+   
+   for(j=0; j<AVI->anum; ++j) {
+       
+       sampsize = avi_sampsize(AVI, j);
+   
+       OUT4CC ("LIST");
+       OUTLONG(0);        /* Length of list in bytes, don't know yet */
+       strl_start = nhb;  /* Store start position */
+       OUT4CC ("strl");
+       
+       /* The audio stream header */
+       
+       OUT4CC ("strh");
+       OUTLONG(56);            /* # of bytes to follow */
+       OUT4CC ("auds");
+       
+       // -----------
+       // ThOe
+       OUTLONG(0);             /* Format (Optionally) */
+       // -----------
+       
+       OUTLONG(0);             /* Flags */
+       OUTLONG(0);             /* Reserved, MS says: wPriority, wLanguage */
+       OUTLONG(0);             /* InitialFrames */
+       
+       // ThOe /4
+       OUTLONG(sampsize/4);      /* Scale */
+       OUTLONG(1000*AVI->track[j].mp3rate/8);
+       OUTLONG(0);             /* Start */
+       OUTLONG(4*AVI->track[j].audio_bytes/sampsize);   /* Length */
+       OUTLONG(0);             /* SuggestedBufferSize */
+       OUTLONG(-1);            /* Quality */
+       
+       // ThOe /4
+       OUTLONG(sampsize/4);    /* SampleSize */
+       
+       OUTLONG(0);             /* Frame */
+       OUTLONG(0);             /* Frame */
+       //       OUTLONG(0);             /* Frame */
+       //OUTLONG(0);             /* Frame */
+       
+       /* The audio stream format */
+       
+       OUT4CC ("strf");
+       OUTLONG(16);                   /* # of bytes to follow */
+       OUTSHRT(AVI->track[j].a_fmt);           /* Format */
+       OUTSHRT(AVI->track[j].a_chans);         /* Number of channels */
+       OUTLONG(AVI->track[j].a_rate);          /* SamplesPerSec */
+       // ThOe
+       OUTLONG(1000*AVI->track[j].mp3rate/8);
+       //ThOe (/4)
+       
+       OUTSHRT(sampsize/4);           /* BlockAlign */
+       
+       
+       OUTSHRT(AVI->track[j].a_bits);          /* BitsPerSample */
+       
+       /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+       
+       long2str(AVI_header+strl_start-4,nhb-strl_start);
+   }
+   
+   /* Finish header list */
+   
+   long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
+   
+   
+   /* Calculate the needed amount of junk bytes, output junk */
+   
+   njunk = HEADERBYTES - nhb - 8 - 12;
+   
+   /* Safety first: if njunk <= 0, somebody has played with
+      HEADERBYTES without knowing what (s)he did.
+      This is a fatal error */
+   
+   if(njunk<=0)
+     {
+       fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
+       exit(1);
+     }
+   
+   OUT4CC ("JUNK");
+   OUTLONG(njunk);
+   memset(AVI_header+nhb,0,njunk);
+   
+   //11/14/01 added id string 
+
+   if(njunk > strlen(id_str)+8) {
+     sprintf(id_str, "%s-%s", PACKAGE, VERSION);
+     memcpy(AVI_header+nhb, id_str, strlen(id_str));
+   }
+   
+   nhb += njunk;
+
+   /* Start the movi list */
+
+   OUT4CC ("LIST");
+   OUTLONG(movi_len); /* Length of list in bytes */
+   OUT4CC ("movi");
+
+   /* Output the header, truncate the file to the number of bytes
+      actually written, report an error if someting goes wrong */
+
+   if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
+        avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
+	lseek(AVI->fdes,AVI->pos,SEEK_SET)<0)
+     {
+       AVI_errno = AVI_ERR_CLOSE;
+       return -1;
+     }
+
+   return 0;
+}
+
+/*
+  Write the header of an AVI file and close it.
+  returns 0 on success, -1 on write error.
+*/
+
+static int avi_close_output_file(avi_t *AVI)
+{
+
+   int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag;
+   unsigned long movi_len;
+   int hdrl_start, strl_start, j;
+   unsigned char AVI_header[HEADERBYTES];
+   long nhb;
+
+#ifdef INFO_LIST
+   long info_len;
+//   time_t calptr;
+#endif
+
+   /* Calculate length of movi list */
+
+   movi_len = AVI->pos - HEADERBYTES + 4;
+
+   /* Try to ouput the index entries. This may fail e.g. if no space
+      is left on device. We will report this as an error, but we still
+      try to write the header correctly (so that the file still may be
+      readable in the most cases */
+
+   idxerror = 0;
+   //   fprintf(stderr, "pos=%lu, index_len=%ld             \n", AVI->pos, AVI->n_idx*16);
+   ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (void*)AVI->idx, AVI->n_idx*16);
+   hasIndex = (ret==0);
+   //fprintf(stderr, "pos=%lu, index_len=%d\n", AVI->pos, hasIndex);
+
+   if(ret) {
+     idxerror = 1;
+     AVI_errno = AVI_ERR_WRITE_INDEX;
+   }
+   
+   /* Calculate Microseconds per frame */
+
+   if(AVI->fps < 0.001) {
+     frate=0;
+     ms_per_frame=0;
+   } else {
+     frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5);
+     ms_per_frame=(int) (1000000/AVI->fps + 0.5);
+   }
+
+   /* Prepare the file header */
+
+   nhb = 0;
+
+   /* The RIFF header */
+
+   OUT4CC ("RIFF");
+   OUTLONG(AVI->pos - 8);    /* # of bytes to follow */
+   OUT4CC ("AVI ");
+
+   /* Start the header list */
+
+   OUT4CC ("LIST");
+   OUTLONG(0);        /* Length of list in bytes, don't know yet */
+   hdrl_start = nhb;  /* Store start position */
+   OUT4CC ("hdrl");
+
+   /* The main AVI header */
+
+   /* The Flags in AVI File header */
+
+#define AVIF_HASINDEX           0x00000010      /* Index at end of file */
+#define AVIF_MUSTUSEINDEX       0x00000020
+#define AVIF_ISINTERLEAVED      0x00000100
+#define AVIF_TRUSTCKTYPE        0x00000800      /* Use CKType to find key frames */
+#define AVIF_WASCAPTUREFILE     0x00010000
+#define AVIF_COPYRIGHTED        0x00020000
+
+   OUT4CC ("avih");
+   OUTLONG(56);                 /* # of bytes to follow */
+   OUTLONG(ms_per_frame);       /* Microseconds per frame */
+   //ThOe ->0 
+   //   OUTLONG(10000000);           /* MaxBytesPerSec, I hope this will never be used */
+   OUTLONG(0);
+   OUTLONG(0);                  /* PaddingGranularity (whatever that might be) */
+                                /* Other sources call it 'reserved' */
+   flag = AVIF_ISINTERLEAVED;
+   if(hasIndex) flag |= AVIF_HASINDEX;
+   if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX;
+   OUTLONG(flag);               /* Flags */
+   OUTLONG(AVI->video_frames);  /* TotalFrames */
+   OUTLONG(0);                  /* InitialFrames */
+
+   OUTLONG(AVI->anum+1);
+//   if (AVI->track[0].audio_bytes)
+//      { OUTLONG(2); }           /* Streams */
+//   else
+//      { OUTLONG(1); }           /* Streams */
+
+   OUTLONG(0);                  /* SuggestedBufferSize */
+   OUTLONG(AVI->width);         /* Width */
+   OUTLONG(AVI->height);        /* Height */
+                                /* MS calls the following 'reserved': */
+   OUTLONG(0);                  /* TimeScale:  Unit used to measure time */
+   OUTLONG(0);                  /* DataRate:   Data rate of playback     */
+   OUTLONG(0);                  /* StartTime:  Starting time of AVI data */
+   OUTLONG(0);                  /* DataLength: Size of AVI data chunk    */
+
+
+   /* Start the video stream list ---------------------------------- */
+
+   OUT4CC ("LIST");
+   OUTLONG(0);        /* Length of list in bytes, don't know yet */
+   strl_start = nhb;  /* Store start position */
+   OUT4CC ("strl");
+
+   /* The video stream header */
+
+   OUT4CC ("strh");
+   OUTLONG(56);                 /* # of bytes to follow */
+   OUT4CC ("vids");             /* Type */
+   OUT4CC (AVI->compressor);    /* Handler */
+   OUTLONG(0);                  /* Flags */
+   OUTLONG(0);                  /* Reserved, MS says: wPriority, wLanguage */
+   OUTLONG(0);                  /* InitialFrames */
+   OUTLONG(FRAME_RATE_SCALE);              /* Scale */
+   OUTLONG(frate);              /* Rate: Rate/Scale == samples/second */
+   OUTLONG(0);                  /* Start */
+   OUTLONG(AVI->video_frames);  /* Length */
+   OUTLONG(0);                  /* SuggestedBufferSize */
+   OUTLONG(-1);                 /* Quality */
+   OUTLONG(0);                  /* SampleSize */
+   OUTLONG(0);                  /* Frame */
+   OUTLONG(0);                  /* Frame */
+   //   OUTLONG(0);                  /* Frame */
+   //OUTLONG(0);                  /* Frame */
+
+   /* The video stream format */
+
+   OUT4CC ("strf");
+   OUTLONG(40);                 /* # of bytes to follow */
+   OUTLONG(40);                 /* Size */
+   OUTLONG(AVI->width);         /* Width */
+   OUTLONG(AVI->height);        /* Height */
+   OUTSHRT(1); OUTSHRT(24);     /* Planes, Count */
+   OUT4CC (AVI->compressor);    /* Compression */
+   // ThOe (*3)
+   OUTLONG(AVI->width*AVI->height*3);  /* SizeImage (in bytes?) */
+   OUTLONG(0);                  /* XPelsPerMeter */
+   OUTLONG(0);                  /* YPelsPerMeter */
+   OUTLONG(0);                  /* ClrUsed: Number of colors used */
+   OUTLONG(0);                  /* ClrImportant: Number of colors important */
+
+   /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+
+   long2str(AVI_header+strl_start-4,nhb-strl_start);
+
+   /* Start the audio stream list ---------------------------------- */
+
+   for(j=0; j<AVI->anum; ++j) {
+     
+     //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes)
+       {
+	   
+	 sampsize = avi_sampsize(AVI, j);
+	   
+	 OUT4CC ("LIST");
+	 OUTLONG(0);        /* Length of list in bytes, don't know yet */
+	 strl_start = nhb;  /* Store start position */
+	 OUT4CC ("strl");
+	   
+	 /* The audio stream header */
+	 
+	 OUT4CC ("strh");
+	 OUTLONG(56);            /* # of bytes to follow */
+	 OUT4CC ("auds");
+	 
+	 // -----------
+	 // ThOe
+	 OUTLONG(0);             /* Format (Optionally) */
+	   // -----------
+	   
+	 OUTLONG(0);             /* Flags */
+	 OUTLONG(0);             /* Reserved, MS says: wPriority, wLanguage */
+	 OUTLONG(0);             /* InitialFrames */
+	   
+	 // ThOe /4
+	 OUTLONG(sampsize/4);      /* Scale */
+	 OUTLONG(1000*AVI->track[j].mp3rate/8);
+	 OUTLONG(0);             /* Start */
+	 OUTLONG(4*AVI->track[j].audio_bytes/sampsize);   /* Length */
+	 OUTLONG(0);             /* SuggestedBufferSize */
+	 OUTLONG(-1);            /* Quality */
+	   
+	 // ThOe /4
+	 OUTLONG(sampsize/4);    /* SampleSize */
+	   
+	 OUTLONG(0);             /* Frame */
+	 OUTLONG(0);             /* Frame */
+	 //	 OUTLONG(0);             /* Frame */
+	 //OUTLONG(0);             /* Frame */
+	   
+	 /* The audio stream format */
+	 
+	 OUT4CC ("strf");
+	 OUTLONG(16);                   /* # of bytes to follow */
+	 OUTSHRT(AVI->track[j].a_fmt);           /* Format */
+	 OUTSHRT(AVI->track[j].a_chans);         /* Number of channels */
+	 OUTLONG(AVI->track[j].a_rate);          /* SamplesPerSec */
+	 // ThOe
+	 OUTLONG(1000*AVI->track[j].mp3rate/8);
+	 //ThOe (/4)
+	 
+	 OUTSHRT(sampsize/4);           /* BlockAlign */
+	 
+	 
+	 OUTSHRT(AVI->track[j].a_bits);          /* BitsPerSample */
+	 
+	 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
+       }
+       long2str(AVI_header+strl_start-4,nhb-strl_start);
+   }
+   
+   /* Finish header list */
+   
+   long2str(AVI_header+hdrl_start-4,nhb-hdrl_start);
+
+
+   // add INFO list --- (0.6.0pre4)
+
+#ifdef INFO_LIST
+   OUT4CC ("LIST");
+   
+   //FIXME
+   info_len = MAX_INFO_STRLEN + 12;
+   OUTLONG(info_len);
+   OUT4CC ("INFO");
+
+//   OUT4CC ("INAM");
+//   OUTLONG(MAX_INFO_STRLEN);
+
+//   sprintf(id_str, "\t");
+//   memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
+//   memcpy(AVI_header+nhb, id_str, strlen(id_str));
+//   nhb += MAX_INFO_STRLEN;
+
+   OUT4CC ("ISFT");
+   OUTLONG(MAX_INFO_STRLEN);
+
+   sprintf(id_str, "%s-%s", PACKAGE, VERSION);
+   memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
+   memcpy(AVI_header+nhb, id_str, strlen(id_str));
+   nhb += MAX_INFO_STRLEN;
+
+//   OUT4CC ("ICMT");
+//   OUTLONG(MAX_INFO_STRLEN);
+
+//   calptr=time(NULL); 
+//   sprintf(id_str, "\t%s %s", ctime(&calptr), "");
+//   memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
+//   memcpy(AVI_header+nhb, id_str, 25);
+//   nhb += MAX_INFO_STRLEN;
+#endif
+
+   // ----------------------------
+   
+   /* Calculate the needed amount of junk bytes, output junk */
+   
+   njunk = HEADERBYTES - nhb - 8 - 12;
+   
+   /* Safety first: if njunk <= 0, somebody has played with
+      HEADERBYTES without knowing what (s)he did.
+      This is a fatal error */
+   
+   if(njunk<=0)
+   {
+      fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
+      exit(1);
+   }
+
+   OUT4CC ("JUNK");
+   OUTLONG(njunk);
+   memset(AVI_header+nhb,0,njunk);
+   
+   nhb += njunk;
+
+   /* Start the movi list */
+
+   OUT4CC ("LIST");
+   OUTLONG(movi_len); /* Length of list in bytes */
+   OUT4CC ("movi");
+
+   /* Output the header, truncate the file to the number of bytes
+      actually written, report an error if someting goes wrong */
+
+   if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
+        avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES 
+        //|| ftruncate(AVI->fdes,AVI->pos)<0 
+        )
+   {
+      AVI_errno = AVI_ERR_CLOSE;
+      return -1;
+   }
+
+   if(idxerror) return -1;
+
+   return 0;
+}
+
+/*
+   AVI_write_data:
+   Add video or audio data to the file;
+
+   Return values:
+    0    No error;
+   -1    Error, AVI_errno is set appropriatly;
+
+*/
+
+static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe)
+{
+   int n;
+
+   unsigned char astr[5];
+
+   /* Check for maximum file length */
+   
+   if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) {
+     AVI_errno = AVI_ERR_SIZELIM;
+     return -1;
+   }
+   
+   /* Add index entry */
+
+   //set tag for current audio track
+   sprintf((char *)astr, "0%1dwb", AVI->aptr+1);
+
+   if(audio)
+     n = avi_add_index_entry(AVI,astr,0x00,AVI->pos,length);
+   else
+     n = avi_add_index_entry(AVI,(unsigned char *) "00db",((keyframe)?0x10:0x0),AVI->pos,length);
+   
+   if(n) return -1;
+   
+   /* Output tag and data */
+   
+   if(audio)
+     n = avi_add_chunk(AVI,(unsigned char *) astr, (unsigned char *)data,length);
+   else
+     n = avi_add_chunk(AVI,(unsigned char *)"00db",(unsigned char *)data,length);
+   
+   if (n) return -1;
+   
+   return 0;
+}
+
+int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe)
+{
+  unsigned long pos;
+  
+  if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+  
+  pos = AVI->pos;
+
+  if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1;
+   
+  AVI->last_pos = pos;
+  AVI->last_len = bytes;
+  AVI->video_frames++;
+  return 0;
+}
+
+int AVI_dup_frame(avi_t *AVI)
+{
+   if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+   if(AVI->last_pos==0) return 0; /* No previous real frame */
+   if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1;
+   AVI->video_frames++;
+   AVI->must_use_index = 1;
+   return 0;
+}
+
+int AVI_write_audio(avi_t *AVI, char *data, long bytes)
+{
+   if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+   if( avi_write_data(AVI,data,bytes,1,0) ) return -1;
+   AVI->track[AVI->aptr].audio_bytes += bytes;
+   return 0;
+}
+
+
+int AVI_append_audio(avi_t *AVI, char *data, long bytes)
+{
+
+  long i, length, pos;
+  unsigned char c[4];
+
+  if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+  
+  // update last index entry:
+  
+  --AVI->n_idx;
+  length = str2ulong(AVI->idx[AVI->n_idx]+12);
+  pos    = str2ulong(AVI->idx[AVI->n_idx]+8);
+
+  //update;
+  long2str(AVI->idx[AVI->n_idx]+12,length+bytes);   
+
+  ++AVI->n_idx;
+
+  AVI->track[AVI->aptr].audio_bytes += bytes;
+
+  //update chunk header
+  lseek(AVI->fdes, pos+4, SEEK_SET);
+  long2str(c, length+bytes);     
+  avi_write(AVI->fdes,(char *) c, 4);
+
+  lseek(AVI->fdes, pos+8+length, SEEK_SET);
+
+  i=PAD_EVEN(length + bytes);
+
+  bytes = i - length;
+  avi_write(AVI->fdes, data, bytes);
+  AVI->pos = pos + 8 + i;
+
+  return 0;
+}
+
+
+long AVI_bytes_remain(avi_t *AVI)
+{
+   if(AVI->mode==AVI_MODE_READ) return 0;
+
+   return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx));
+}
+
+long AVI_bytes_written(avi_t *AVI)
+{
+   if(AVI->mode==AVI_MODE_READ) return 0;
+
+   return (AVI->pos + 8 + 16*AVI->n_idx);
+}
+
+int AVI_set_audio_track(avi_t *AVI, int track)
+{
+  
+  if(track < 0 || track + 1 > AVI->anum) return(-1);
+
+  //this info is not written to file anyway
+  AVI->aptr=track;
+  return 0;
+}
+
+int AVI_get_audio_track(avi_t *AVI)
+{
+    return(AVI->aptr);
+}
+
+
+/*******************************************************************
+ *                                                                 *
+ *    Utilities for reading video and audio from an AVI File       *
+ *                                                                 *
+ *******************************************************************/
+
+int AVI_close(avi_t *AVI)
+{
+   int ret;
+
+   /* If the file was open for writing, the header and index still have
+      to be written */
+
+   if(AVI->mode == AVI_MODE_WRITE)
+      ret = avi_close_output_file(AVI);
+   else
+      ret = 0;
+
+   /* Even if there happened an error, we first clean up */
+
+   close(AVI->fdes);
+   if(AVI->idx) free(AVI->idx);
+   if(AVI->video_index) free(AVI->video_index);
+   //FIXME
+   //if(AVI->audio_index) free(AVI->audio_index);
+   free(AVI);
+
+   return ret;
+}
+
+
+#define ERR_EXIT(x) \
+{ \
+   AVI_close(AVI); \
+   AVI_errno = x; \
+   return 0; \
+}
+
+avi_t *AVI_open_input_file(char *filename, int getIndex)
+{
+  avi_t *AVI=NULL;
+  
+  /* Create avi_t structure */
+  
+  AVI = (avi_t *) malloc(sizeof(avi_t));
+  if(AVI==NULL)
+    {
+      AVI_errno = AVI_ERR_NO_MEM;
+      return 0;
+    }
+  memset((void *)AVI,0,sizeof(avi_t));
+  
+  AVI->mode = AVI_MODE_READ; /* open for reading */
+  
+  /* Open the file */
+  
+  AVI->fdes = open(filename,O_RDONLY);
+  if(AVI->fdes < 0)
+    {
+      AVI_errno = AVI_ERR_OPEN;
+      free(AVI);
+      return 0;
+    }
+  
+  avi_parse_input_file(AVI, getIndex);
+
+  AVI->aptr=0; //reset  
+
+  return AVI;
+}
+
+avi_t *AVI_open_fd(int fd, int getIndex)
+{
+  avi_t *AVI=NULL;
+  
+  /* Create avi_t structure */
+  
+  AVI = (avi_t *) malloc(sizeof(avi_t));
+  if(AVI==NULL)
+    {
+      AVI_errno = AVI_ERR_NO_MEM;
+      return 0;
+    }
+  memset((void *)AVI,0,sizeof(avi_t));
+  
+  AVI->mode = AVI_MODE_READ; /* open for reading */
+  
+  // file alread open
+  AVI->fdes = fd;
+  
+  avi_parse_input_file(AVI, getIndex);
+
+  AVI->aptr=0; //reset
+  
+  return AVI;
+}
+
+int avi_parse_input_file(avi_t *AVI, int getIndex)
+{
+  long i, n, rate, scale, idx_type;
+  unsigned char *hdrl_data;
+  long header_offset=0, hdrl_len=0;
+  long nvi, nai[AVI_MAX_TRACKS], ioff;
+  long tot[AVI_MAX_TRACKS];
+  int j;
+  int lasttag = 0;
+  int vids_strh_seen = 0;
+  int vids_strf_seen = 0;
+  int auds_strh_seen = 0;
+  //  int auds_strf_seen = 0;
+  int num_stream = 0;
+  char data[256];
+  
+  /* Read first 12 bytes and check that this is an AVI file */
+
+   if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ)
+
+   if( strncasecmp(data  ,"RIFF",4) !=0 ||
+       strncasecmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI)
+
+   /* Go through the AVI file and extract the header list,
+      the start position of the 'movi' list and an optionally
+      present idx1 tag */
+
+   hdrl_data = 0;
+
+   while(1)
+   {
+      if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */
+
+      n = str2ulong((unsigned char *) data+4);
+      n = PAD_EVEN(n);
+
+      if(strncasecmp(data,"LIST",4) == 0)
+      {
+         if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ)
+         n -= 4;
+         if(strncasecmp(data,"hdrl",4) == 0)
+         {
+            hdrl_len = n;
+            hdrl_data = (unsigned char *) malloc(n);
+            if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM);
+				 
+	    // offset of header
+	    
+	    header_offset = lseek(AVI->fdes,0,SEEK_CUR);
+				 
+            if( avi_read(AVI->fdes,(char *)hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ)
+         }
+         else if(strncasecmp(data,"movi",4) == 0)
+         {
+            AVI->movi_start = lseek(AVI->fdes,0,SEEK_CUR);
+            lseek(AVI->fdes,n,SEEK_CUR);
+         }
+         else
+            lseek(AVI->fdes,n,SEEK_CUR);
+      }
+      else if(strncasecmp(data,"idx1",4) == 0)
+      {
+         /* n must be a multiple of 16, but the reading does not
+            break if this is not the case */
+
+         AVI->n_idx = AVI->max_idx = n/16;
+         AVI->idx = (unsigned  char((*)[16]) ) malloc(n);
+         if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM)
+         if(avi_read(AVI->fdes, (char *) AVI->idx, n) != n ) ERR_EXIT(AVI_ERR_READ)
+      }
+      else
+         lseek(AVI->fdes,n,SEEK_CUR);
+   }
+
+   if(!hdrl_data      ) ERR_EXIT(AVI_ERR_NO_HDRL)
+   if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI)
+
+   /* Interpret the header list */
+
+   for(i=0;i<hdrl_len;)
+   {
+      /* List tags are completly ignored */
+
+      if(strncasecmp((char *) hdrl_data+i, "LIST",4)==0) { i+= 12; continue; }
+
+      n = str2ulong(hdrl_data+i+4);
+      n = PAD_EVEN(n);
+
+      /* Interpret the tag and its args */
+
+      if(strncasecmp((char *)hdrl_data+i,"strh",4)==0)
+      {
+         i += 8;
+         if(strncasecmp((char *)hdrl_data+i,"vids",4) == 0 && !vids_strh_seen)
+         {
+            memcpy(AVI->compressor,hdrl_data+i+4,4);
+            AVI->compressor[4] = 0;
+
+	    // ThOe
+	    AVI->v_codech_off = header_offset + i+4;
+
+            scale = str2ulong((unsigned char *)hdrl_data+i+20);
+            rate  = str2ulong(hdrl_data+i+24);
+            if(scale!=0) AVI->fps = (double)rate/(double)scale;
+            AVI->video_frames = str2ulong(hdrl_data+i+32);
+            AVI->video_strn = num_stream;
+	    AVI->max_len = 0;
+            vids_strh_seen = 1;
+            lasttag = 1; /* vids */
+         }
+         else if (strncasecmp ((char *) hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen)
+         {
+
+	   //inc audio tracks
+	   AVI->aptr=AVI->anum;
+	   ++AVI->anum;
+	   
+	   if(AVI->anum > AVI_MAX_TRACKS) {
+	     fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);
+	     return(-1);
+	   }
+	   
+	   AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0);
+	   AVI->track[AVI->aptr].audio_strn = num_stream;
+	   //	   auds_strh_seen = 1;
+	   lasttag = 2; /* auds */
+	   
+	   // ThOe
+	   AVI->track[AVI->aptr].a_codech_off = header_offset + i;
+	   
+         }
+         else
+            lasttag = 0;
+         num_stream++;
+      }
+      else if(strncasecmp((char *) hdrl_data+i,"strf",4)==0)
+      {
+         i += 8;
+         if(lasttag == 1)
+         {
+            AVI->width  = str2ulong(hdrl_data+i+4);
+            AVI->height = str2ulong(hdrl_data+i+8);
+            vids_strf_seen = 1;
+	    //ThOe
+	    AVI->v_codecf_off = header_offset + i+16;
+
+	    memcpy(AVI->compressor2, hdrl_data+i+16, 4);
+            AVI->compressor2[4] = 0;
+
+         }
+         else if(lasttag == 2)
+         {
+            AVI->track[AVI->aptr].a_fmt   = str2ushort(hdrl_data+i  );
+
+	    //ThOe
+	    AVI->track[AVI->aptr].a_codecf_off = header_offset + i;
+	    
+            AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2);
+            AVI->track[AVI->aptr].a_rate  = str2ulong (hdrl_data+i+4);
+	    //ThOe: read mp3bitrate
+	    AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000;
+	    //:ThOe
+            AVI->track[AVI->aptr].a_bits  = str2ushort(hdrl_data+i+14);
+	    //            auds_strf_seen = 1;
+         }
+         lasttag = 0;
+      }
+      else
+      {
+         i += 8;
+         lasttag = 0;
+      }
+
+      i += n;
+   }
+
+   free(hdrl_data);
+
+   if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS)
+
+   AVI->video_tag[0] = AVI->video_strn/10 + '0';
+   AVI->video_tag[1] = AVI->video_strn%10 + '0';
+   AVI->video_tag[2] = 'd';
+   AVI->video_tag[3] = 'b';
+
+   /* Audio tag is set to "99wb" if no audio present */
+   if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99;
+
+   for(j=0; j<AVI->anum; ++j) {
+     AVI->track[j].audio_tag[0] = (j+1)/10 + '0';
+     AVI->track[j].audio_tag[1] = (j+1)%10 + '0';
+     AVI->track[j].audio_tag[2] = 'w';
+     AVI->track[j].audio_tag[3] = 'b';
+   }
+
+   lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
+
+   /* get index if wanted */
+
+   if(!getIndex) return(0);
+
+   /* if the file has an idx1, check if this is relative
+      to the start of the file or to the start of the movi list */
+
+   idx_type = 0;
+
+   if(AVI->idx)
+   {
+      long pos, len;
+
+      /* Search the first videoframe in the idx1 and look where
+         it is in the file */
+
+      for(i=0;i<AVI->n_idx;i++)
+         if( strncasecmp((char *) AVI->idx[i],(char *) AVI->video_tag,3)==0 ) break;
+      if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS)
+
+      pos = str2ulong(AVI->idx[i]+ 8);
+      len = str2ulong(AVI->idx[i]+12);
+
+      lseek(AVI->fdes,pos,SEEK_SET);
+      if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
+      if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 && 
+      str2ulong((unsigned char *)data+4)==len )
+      {
+         idx_type = 1; /* Index from start of file */
+      }
+      else
+      {
+         lseek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET);
+         if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ)
+         if( strncasecmp((char *)data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len )
+         {
+            idx_type = 2; /* Index from start of movi list */
+         }
+      }
+      /* idx_type remains 0 if neither of the two tests above succeeds */
+   }
+
+   if(idx_type == 0)
+   {
+      /* we must search through the file to get the index */
+
+      lseek(AVI->fdes, AVI->movi_start, SEEK_SET);
+
+      AVI->n_idx = 0;
+
+      while(1)
+      {
+         if( avi_read(AVI->fdes,data,8) != 8 ) break;
+         n = str2ulong((unsigned char *)data+4);
+
+         /* The movi list may contain sub-lists, ignore them */
+
+         if(strncasecmp(data,"LIST",4)==0)
+         {
+            lseek(AVI->fdes,4,SEEK_CUR);
+            continue;
+         }
+
+         /* Check if we got a tag ##db, ##dc or ##wb */
+	 
+         if( ( (data[2]=='d' || data[2]=='D') &&
+               (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') )
+	     || ( (data[2]=='w' || data[2]=='W') &&
+		  (data[3]=='b' || data[3]=='B') ) )
+	   {
+	   avi_add_index_entry(AVI,(unsigned char *) data,0,lseek(AVI->fdes,0,SEEK_CUR)-8,n);
+         }
+	 
+         lseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR);
+      }
+      idx_type = 1;
+   }
+
+   /* Now generate the video index and audio index arrays */
+
+   nvi = 0;
+   for(j=0; j<AVI->anum; ++j) nai[j] = 0;
+
+   for(i=0;i<AVI->n_idx;i++) {
+     
+     if(strncasecmp((char *)AVI->idx[i],(char *) AVI->video_tag,3) == 0) nvi++;
+     
+     for(j=0; j<AVI->anum; ++j) if(strncasecmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++;
+   }
+   
+   AVI->video_frames = nvi;
+   for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = nai[j];
+
+//   fprintf(stderr, "chunks = %ld %d %s\n", AVI->track[0].audio_chunks, AVI->anum, AVI->track[0].audio_tag);
+
+   if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS);
+   AVI->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry));
+   if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
+   
+   for(j=0; j<AVI->anum; ++j) {
+       if(AVI->track[j].audio_chunks) {
+	   AVI->track[j].audio_index = (audio_index_entry *) malloc(nai[j]*sizeof(audio_index_entry));
+	   if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM);
+       }
+   }   
+   
+   nvi = 0;
+   for(j=0; j<AVI->anum; ++j) nai[j] = tot[j] = 0;
+   
+   ioff = idx_type == 1 ? 8 : AVI->movi_start+4;
+   
+   for(i=0;i<AVI->n_idx;i++) {
+
+     //video
+     if(strncasecmp((char *)AVI->idx[i],(char *)AVI->video_tag,3) == 0) {
+       AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4);
+       AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
+       AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12);
+       nvi++;
+     }
+     
+     //audio
+     for(j=0; j<AVI->anum; ++j) {
+	 
+       if(strncasecmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) {
+	 AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff;
+	 AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12);
+	 AVI->track[j].audio_index[nai[j]].tot = tot[j];
+	 tot[j] += AVI->track[j].audio_index[nai[j]].len;
+	 nai[j]++;
+       }
+     }
+   }
+   
+   
+   for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j];
+   
+   /* Reposition the file */
+   
+   lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
+   AVI->video_pos = 0;
+
+   return(0);
+}
+
+long AVI_video_frames(avi_t *AVI)
+{
+   return AVI->video_frames;
+}
+int  AVI_video_width(avi_t *AVI)
+{
+   return AVI->width;
+}
+int  AVI_video_height(avi_t *AVI)
+{
+   return AVI->height;
+}
+double AVI_frame_rate(avi_t *AVI)
+{
+   return AVI->fps;
+}
+char* AVI_video_compressor(avi_t *AVI)
+{
+   return AVI->compressor2;
+}
+
+long AVI_max_video_chunk(avi_t *AVI)
+{
+   return AVI->max_len; 
+}
+
+int AVI_audio_tracks(avi_t *AVI)
+{
+    return(AVI->anum);
+}
+
+int AVI_audio_channels(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_chans;
+}
+
+long AVI_audio_mp3rate(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].mp3rate;
+}
+
+int AVI_audio_bits(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_bits;
+}
+
+int AVI_audio_format(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_fmt;
+}
+
+long AVI_audio_rate(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_rate;
+}
+
+long AVI_audio_bytes(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].audio_bytes;
+}
+
+long AVI_audio_chunks(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].audio_chunks;
+}
+
+long AVI_audio_codech_offset(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_codech_off;
+}
+
+long AVI_audio_codecf_offset(avi_t *AVI)
+{
+   return AVI->track[AVI->aptr].a_codecf_off;
+}
+
+long  AVI_video_codech_offset(avi_t *AVI)
+{
+    return AVI->v_codech_off;
+}
+
+long  AVI_video_codecf_offset(avi_t *AVI)
+{
+    return AVI->v_codecf_off;
+}
+
+long AVI_frame_size(avi_t *AVI, long frame)
+{
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->video_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   if(frame < 0 || frame >= AVI->video_frames) return 0;
+   return(AVI->video_index[frame].len);
+}
+
+long AVI_audio_size(avi_t *AVI, long frame)
+{
+  if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+  if(!AVI->track[AVI->aptr].audio_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+  
+  if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return 0;
+  return(AVI->track[AVI->aptr].audio_index[frame].len);
+}
+
+long AVI_get_video_position(avi_t *AVI, long frame)
+{
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->video_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   if(frame < 0 || frame >= AVI->video_frames) return 0;
+   return(AVI->video_index[frame].pos);
+}
+
+
+int AVI_seek_start(avi_t *AVI)
+{
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+   lseek(AVI->fdes,AVI->movi_start,SEEK_SET);
+   AVI->video_pos = 0;
+   return 0;
+}
+
+int AVI_set_video_position(avi_t *AVI, long frame)
+{
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->video_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   if (frame < 0 ) frame = 0;
+   AVI->video_pos = frame;
+   return 0;
+}
+
+int AVI_set_audio_bitrate(avi_t *AVI, long bitrate)
+{
+   if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+
+   AVI->track[AVI->aptr].mp3rate = bitrate;
+   return 0;
+}
+      
+
+long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe)
+{
+   long n;
+
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->video_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1;
+   n = AVI->video_index[AVI->video_pos].len;
+
+   *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0;
+
+   lseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET);
+
+   if (avi_read(AVI->fdes,vidbuf,n) != n)
+   {
+      AVI_errno = AVI_ERR_READ;
+      return -1;
+   }
+
+   AVI->video_pos++;
+
+   return n;
+}
+
+int AVI_set_audio_position(avi_t *AVI, long byte)
+{
+   long n0, n1, n;
+
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->track[AVI->aptr].audio_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   if(byte < 0) byte = 0;
+
+   /* Binary search in the audio chunks */
+
+   n0 = 0;
+   n1 = AVI->track[AVI->aptr].audio_chunks;
+
+   while(n0<n1-1)
+   {
+      n = (n0+n1)/2;
+      if(AVI->track[AVI->aptr].audio_index[n].tot>byte)
+         n1 = n;
+      else
+         n0 = n;
+   }
+
+   AVI->track[AVI->aptr].audio_posc = n0;
+   AVI->track[AVI->aptr].audio_posb = byte - AVI->track[AVI->aptr].audio_index[n0].tot;
+
+   return 0;
+}
+
+long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes)
+{
+   long nr, pos, left, todo;
+
+   if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; }
+   if(!AVI->track[AVI->aptr].audio_index)         { AVI_errno = AVI_ERR_NO_IDX;   return -1; }
+
+   nr = 0; /* total number of bytes read */
+
+   while(bytes>0)
+   {
+      left = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb;
+      if(left==0)
+      {
+         if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr;
+         AVI->track[AVI->aptr].audio_posc++;
+         AVI->track[AVI->aptr].audio_posb = 0;
+         continue;
+      }
+      if(bytes<left)
+         todo = bytes;
+      else
+         todo = left;
+      pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb;
+      lseek(AVI->fdes, pos, SEEK_SET);
+      if (avi_read(AVI->fdes,audbuf+nr,todo) != todo)
+      {
+         AVI_errno = AVI_ERR_READ;
+         return -1;
+      }
+      bytes -= todo;
+      nr    += todo;
+      AVI->track[AVI->aptr].audio_posb += todo;
+   }
+
+   return nr;
+}
+
+/* AVI_read_data: Special routine for reading the next audio or video chunk
+                  without having an index of the file. */
+
+int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf,
+                              char *audbuf, long max_audbuf,
+                              long *len)
+{
+
+/*
+ * Return codes:
+ *
+ *    1 = video data read
+ *    2 = audio data read
+ *    0 = reached EOF
+ *   -1 = video buffer too small
+ *   -2 = audio buffer too small
+ */
+
+   int n;
+   char data[8];
+ 
+   if(AVI->mode==AVI_MODE_WRITE) return 0;
+
+   while(1)
+   {
+      /* Read tag and length */
+
+      if( avi_read(AVI->fdes,data,8) != 8 ) return 0;
+
+      /* if we got a list tag, ignore it */
+
+      if(strncasecmp(data,"LIST",4) == 0)
+      {
+         lseek(AVI->fdes,4,SEEK_CUR);
+         continue;
+      }
+
+      n = PAD_EVEN(str2ulong((unsigned char *)data+4));
+
+      if(strncasecmp(data,AVI->video_tag,3) == 0)
+      {
+         *len = n;
+         AVI->video_pos++;
+         if(n>max_vidbuf)
+         {
+            lseek(AVI->fdes,n,SEEK_CUR);
+            return -1;
+         }
+         if(avi_read(AVI->fdes,vidbuf,n) != n ) return 0;
+         return 1;
+      }
+      else if(strncasecmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0)
+      {
+         *len = n;
+         if(n>max_audbuf)
+         {
+            lseek(AVI->fdes,n,SEEK_CUR);
+            return -2;
+         }
+         if(avi_read(AVI->fdes,audbuf,n) != n ) return 0;
+         return 2;
+         break;
+      }
+      else
+         if(lseek(AVI->fdes,n,SEEK_CUR)<0)  return 0;
+   }
+}
+
+/* AVI_print_error: Print most recent error (similar to perror) */
+
+char *(avi_errors[]) =
+{
+  /*  0 */ "avilib - No Error",
+  /*  1 */ "avilib - AVI file size limit reached",
+  /*  2 */ "avilib - Error opening AVI file",
+  /*  3 */ "avilib - Error reading from AVI file",
+  /*  4 */ "avilib - Error writing to AVI file",
+  /*  5 */ "avilib - Error writing index (file may still be useable)",
+  /*  6 */ "avilib - Error closing AVI file",
+  /*  7 */ "avilib - Operation (read/write) not permitted",
+  /*  8 */ "avilib - Out of memory (malloc failed)",
+  /*  9 */ "avilib - Not an AVI file",
+  /* 10 */ "avilib - AVI file has no header list (corrupted?)",
+  /* 11 */ "avilib - AVI file has no MOVI list (corrupted?)",
+  /* 12 */ "avilib - AVI file has no video data",
+  /* 13 */ "avilib - operation needs an index",
+  /* 14 */ "avilib - Unkown Error"
+};
+static int num_avi_errors = sizeof(avi_errors)/sizeof(char*);
+
+static char error_string[4096];
+
+void AVI_print_error(char *str)
+{
+   int aerrno;
+
+   aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
+
+   fprintf(stderr,"%s: %s\n",str,avi_errors[aerrno]);
+
+   /* for the following errors, perror should report a more detailed reason: */
+
+   if(AVI_errno == AVI_ERR_OPEN ||
+      AVI_errno == AVI_ERR_READ ||
+      AVI_errno == AVI_ERR_WRITE ||
+      AVI_errno == AVI_ERR_WRITE_INDEX ||
+      AVI_errno == AVI_ERR_CLOSE )
+   {
+      perror("REASON");
+   }
+}
+
+char *AVI_strerror()
+{
+   int aerrno;
+
+   aerrno = (AVI_errno>=0 && AVI_errno<num_avi_errors) ? AVI_errno : num_avi_errors-1;
+
+   if(AVI_errno == AVI_ERR_OPEN ||
+      AVI_errno == AVI_ERR_READ ||
+      AVI_errno == AVI_ERR_WRITE ||
+      AVI_errno == AVI_ERR_WRITE_INDEX ||
+      AVI_errno == AVI_ERR_CLOSE )
+   {
+      sprintf(error_string,"%s - %s",avi_errors[aerrno],strerror(errno));
+      return error_string;
+   }
+   else
+   {
+      return avi_errors[aerrno];
+   }
+}
+
+uint64_t AVI_max_size()
+{
+  return((uint64_t) AVI_MAX_LEN);
+}
+

Added: trunk/theora-tools/theora_transcoder/avilib.h
===================================================================
--- trunk/theora-tools/theora_transcoder/avilib.h	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/avilib.h	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,309 @@
+/*
+ *  avilib.h
+ *
+ *  Copyright (C) Thomas Östreich - June 2001
+ *  multiple audio track support Copyright (C) 2002 Thomas Östreich
+ *
+ *  Original code:
+ *  Copyright (C) 1999 Rainer Johanni <Rainer at Johanni.de> 
+ *
+ *  This file is part of transcode, a linux video stream processing tool
+ *      
+ *  transcode is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  transcode is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef AVILIB_H
+#define AVILIB_H
+
+#define AVI_MAX_TRACKS 8
+
+typedef struct
+{
+  unsigned long key;
+  unsigned long pos;
+  unsigned long len;
+} video_index_entry;
+
+typedef struct
+{
+   unsigned long pos;
+   unsigned long len;
+   unsigned long tot;
+} audio_index_entry;
+
+typedef struct track_s
+{
+
+    long   a_fmt;             /* Audio format, see #defines below */
+    long   a_chans;           /* Audio channels, 0 for no audio */
+    long   a_rate;            /* Rate in Hz */
+    long   a_bits;            /* bits per audio sample */
+    long   mp3rate;           /* mp3 bitrate kbs*/
+
+    long   audio_strn;        /* Audio stream number */
+    long   audio_bytes;       /* Total number of bytes of audio data */
+    long   audio_chunks;      /* Chunks of audio data in the file */
+
+    char   audio_tag[4];      /* Tag of audio data */
+    long   audio_posc;        /* Audio position: chunk */
+    long   audio_posb;        /* Audio position: byte within chunk */
+ 
+    long  a_codech_off;       /* absolut offset of audio codec information */ 
+    long  a_codecf_off;       /* absolut offset of audio codec information */ 
+
+    audio_index_entry *audio_index;
+
+} track_t;
+
+typedef struct
+{
+  
+  long   fdes;              /* File descriptor of AVI file */
+  long   mode;              /* 0 for reading, 1 for writing */
+  
+  long   width;             /* Width  of a video frame */
+  long   height;            /* Height of a video frame */
+  double fps;               /* Frames per second */
+  char   compressor[8];     /* Type of compressor, 4 bytes + padding for 0 byte */
+  char   compressor2[8];     /* Type of compressor, 4 bytes + padding for 0 byte */
+  long   video_strn;        /* Video stream number */
+  long   video_frames;      /* Number of video frames */
+  char   video_tag[4];      /* Tag of video data */
+  long   video_pos;         /* Number of next frame to be read
+			       (if index present) */
+  
+  unsigned long max_len;    /* maximum video chunk present */
+  
+  track_t track[AVI_MAX_TRACKS];  // up to AVI_MAX_TRACKS audio tracks supported
+  
+  unsigned long pos;        /* position in file */
+  long   n_idx;             /* number of index entries actually filled */
+  long   max_idx;           /* number of index entries actually allocated */
+  
+  long  v_codech_off;       /* absolut offset of video codec (strh) info */ 
+  long  v_codecf_off;       /* absolut offset of video codec (strf) info */ 
+  
+  unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */
+  video_index_entry *video_index;
+  
+  unsigned long last_pos;          /* Position of last frame written */
+  unsigned long last_len;          /* Length of last frame written */
+  int must_use_index;              /* Flag if frames are duplicated */
+  unsigned long   movi_start;
+  
+  int anum;            // total number of audio tracks 
+  int aptr;            // current audio working track 
+  
+} avi_t;
+
+#define AVI_MODE_WRITE  0
+#define AVI_MODE_READ   1
+
+/* The error codes delivered by avi_open_input_file */
+
+#define AVI_ERR_SIZELIM      1     /* The write of the data would exceed
+                                      the maximum size of the AVI file.
+                                      This is more a warning than an error
+                                      since the file may be closed safely */
+
+#define AVI_ERR_OPEN         2     /* Error opening the AVI file - wrong path
+                                      name or file nor readable/writable */
+
+#define AVI_ERR_READ         3     /* Error reading from AVI File */
+
+#define AVI_ERR_WRITE        4     /* Error writing to AVI File,
+                                      disk full ??? */
+
+#define AVI_ERR_WRITE_INDEX  5     /* Could not write index to AVI file
+                                      during close, file may still be
+                                      usable */
+
+#define AVI_ERR_CLOSE        6     /* Could not write header to AVI file
+                                      or not truncate the file during close,
+                                      file is most probably corrupted */
+
+#define AVI_ERR_NOT_PERM     7     /* Operation not permitted:
+                                      trying to read from a file open
+                                      for writing or vice versa */
+
+#define AVI_ERR_NO_MEM       8     /* malloc failed */
+
+#define AVI_ERR_NO_AVI       9     /* Not an AVI file */
+
+#define AVI_ERR_NO_HDRL     10     /* AVI file has no has no header list,
+                                      corrupted ??? */
+
+#define AVI_ERR_NO_MOVI     11     /* AVI file has no has no MOVI list,
+                                      corrupted ??? */
+
+#define AVI_ERR_NO_VIDS     12     /* AVI file contains no video data */
+
+#define AVI_ERR_NO_IDX      13     /* The file has been opened with
+                                      getIndex==0, but an operation has been
+                                      performed that needs an index */
+
+/* Possible Audio formats */
+
+#ifndef WAVE_FORMAT_PCM
+#define WAVE_FORMAT_UNKNOWN             (0x0000)
+#define WAVE_FORMAT_PCM                 (0x0001)
+#define WAVE_FORMAT_ADPCM               (0x0002)
+#define WAVE_FORMAT_IBM_CVSD            (0x0005)
+#define WAVE_FORMAT_ALAW                (0x0006)
+#define WAVE_FORMAT_MULAW               (0x0007)
+#define WAVE_FORMAT_OKI_ADPCM           (0x0010)
+#define WAVE_FORMAT_DVI_ADPCM           (0x0011)
+#define WAVE_FORMAT_DIGISTD             (0x0015)
+#define WAVE_FORMAT_DIGIFIX             (0x0016)
+#define WAVE_FORMAT_YAMAHA_ADPCM        (0x0020)
+#define WAVE_FORMAT_DSP_TRUESPEECH      (0x0022)
+#define WAVE_FORMAT_GSM610              (0x0031)
+#define IBM_FORMAT_MULAW                (0x0101)
+#define IBM_FORMAT_ALAW                 (0x0102)
+#define IBM_FORMAT_ADPCM                (0x0103)
+#endif
+
+avi_t* AVI_open_output_file(char * filename);
+void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor);
+void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate);
+int  AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe);
+int  AVI_dup_frame(avi_t *AVI);
+int  AVI_write_audio(avi_t *AVI, char *data, long bytes);
+int  AVI_append_audio(avi_t *AVI, char *data, long bytes);
+long AVI_bytes_remain(avi_t *AVI);
+int  AVI_close(avi_t *AVI);
+long AVI_bytes_written(avi_t *AVI);
+
+avi_t *AVI_open_input_file(char *filename, int getIndex);
+avi_t *AVI_open_fd(int fd, int getIndex);
+int avi_parse_input_file(avi_t *AVI, int getIndex);
+long AVI_audio_mp3rate(avi_t *AVI);
+long AVI_video_frames(avi_t *AVI);
+int  AVI_video_width(avi_t *AVI);
+int  AVI_video_height(avi_t *AVI);
+double AVI_frame_rate(avi_t *AVI);
+char* AVI_video_compressor(avi_t *AVI);
+
+int  AVI_audio_channels(avi_t *AVI);
+int  AVI_audio_bits(avi_t *AVI);
+int  AVI_audio_format(avi_t *AVI);
+long AVI_audio_rate(avi_t *AVI);
+long AVI_audio_bytes(avi_t *AVI);
+long AVI_audio_chunks(avi_t *AVI);
+
+long AVI_max_video_chunk(avi_t *AVI);
+
+long AVI_frame_size(avi_t *AVI, long frame);
+long AVI_audio_size(avi_t *AVI, long frame);
+int  AVI_seek_start(avi_t *AVI);
+int  AVI_set_video_position(avi_t *AVI, long frame);
+long AVI_get_video_position(avi_t *AVI, long frame);
+long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe);
+
+int  AVI_set_audio_position(avi_t *AVI, long byte);
+int  AVI_set_audio_bitrate(avi_t *AVI, long bitrate);
+
+long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes);
+
+long AVI_audio_codech_offset(avi_t *AVI);
+long AVI_audio_codecf_offset(avi_t *AVI);
+long AVI_video_codech_offset(avi_t *AVI);
+long AVI_video_codecf_offset(avi_t *AVI);
+
+int  AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf,
+                               char *audbuf, long max_audbuf,
+                               long *len);
+
+void AVI_print_error(char *str);
+char *AVI_strerror();
+char *AVI_syserror();
+
+int AVI_scan(char *name);
+int AVI_dump(char *name, int mode);
+
+char *AVI_codec2str(short cc);
+int AVI_file_check(char *import_file);
+
+void AVI_info(avi_t *avifile);
+uint64_t AVI_max_size();
+int avi_update_header(avi_t *AVI);
+
+int AVI_set_audio_track(avi_t *AVI, int track);
+int AVI_get_audio_track(avi_t *AVI);
+int AVI_audio_tracks(avi_t *AVI);
+
+
+struct riff_struct 
+{
+  unsigned char id[4];   /* RIFF */
+  unsigned long len;
+  unsigned char wave_id[4]; /* WAVE */
+};
+
+
+struct chunk_struct 
+{
+	unsigned char id[4];
+	unsigned long len;
+};
+
+struct common_struct 
+{
+	unsigned short wFormatTag;
+	unsigned short wChannels;
+	unsigned long dwSamplesPerSec;
+	unsigned long dwAvgBytesPerSec;
+	unsigned short wBlockAlign;
+	unsigned short wBitsPerSample;  /* Only for PCM */
+};
+
+struct wave_header 
+{
+	struct riff_struct   riff;
+	struct chunk_struct  format;
+	struct common_struct common;
+	struct chunk_struct  data;
+};
+
+
+
+struct AVIStreamHeader {
+  long  fccType;
+  long  fccHandler;
+  long  dwFlags;
+  long  dwPriority;
+  long  dwInitialFrames;
+  long  dwScale;
+  long  dwRate;
+  long  dwStart;
+  long  dwLength;
+  long  dwSuggestedBufferSize;
+  long  dwQuality;
+  long  dwSampleSize;
+};
+
+#endif

Added: trunk/theora-tools/theora_transcoder/readme.txt
===================================================================
--- trunk/theora-tools/theora_transcoder/readme.txt	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/readme.txt	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,17 @@
+Quick hack at a transcoder tool from VP3 to Theora
+
+I actually built the avi2vp3 tool with codeWarrior, but it should compile under
+VC as well. I have included a source avi file and the converted .vp3 output.
+Output is a file with some header info matching YUVMPEG, and for each frame: 
+
+FRAME header block matching YUV2MPEG
+long (Intel aligned) keyframeflag describing in frame is a keyframe
+long (Intel aligned) fsize storing frame size in bytes
+bytes[fsize] with binary frame data
+
+The transcode tool is a modification of the current encoder. PUt it into the
+win32/experimental subdirectory, and the paths should be correct. It produces
+an apparently valid theora stream, but outputs garbage data. The code is packing
+the binary frame data in a way that SHOULD work at least imo, but I am probably
+missing some initialization issue (or vp3 is not transcodable to theora).
+

Added: trunk/theora-tools/theora_transcoder/theora_transcoder.c
===================================================================
--- trunk/theora-tools/theora_transcoder/theora_transcoder.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theora_transcoder/theora_transcoder.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -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-2003                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example encoder application; makes an Ogg Theora/Vorbis
+            file from YUV4MPEG2 and WAV input
+  last mod: $Id: transcoder_example.c,v 1.2 2003/10/28 18:18:42 mauricio Exp $
+
+ ********************************************************************/
+
+#define _GNU_SOURCE
+#define _REENTRANT
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <time.h>
+#include <math.h>
+#include <theora/theora.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>
+
+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 = "o:a:A:v:V:";
+struct option options [] = {
+  {"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'},
+  {NULL,0,NULL,0}
+};
+
+typedef struct TC_INSTANCE {
+  ogg_uint32_t     LastKeyFrame ;
+  ogg_int64_t      KeyFrameCount;
+  int              ThisIsFirstFrame;
+  int              ThisIsKeyFrame;
+  ogg_uint32_t     CurrentFrame;
+  ogg_int64_t      granulepos;
+  int              keyframe_granule_shift;
+  char *           in_bytes;
+  long             in_bytecount;
+  ogg_uint32_t     fps_denominator;
+  ogg_uint32_t     fps_numerator;
+  oggpack_buffer   opb_in;
+  oggpack_buffer   opb_out;
+} TC_INSTANCE;
+
+/* 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=.1;
+int audio_r=-1;
+
+int video_x=0;
+int video_y=0;
+int frame_x=0;
+int frame_y=0;
+int frame_x_offset=0;
+int frame_y_offset=0;
+int video_hzn=0;
+int video_hzd=0;
+int video_an=0;
+int video_ad=0;
+
+int video_r=-1;
+int video_q=16;
+
+  char *vp3frame[2];
+  int framebytecount[2];
+  int frameiskey[2];
+
+    ogg_page audiopage;
+    ogg_page videopage;
+
+static void usage(void){
+  fprintf(stderr,
+          "Usage: encoder_example [options] [audio_file] video_file\n\n"
+          "Options: \n\n"
+          "  -o --output <filename.ogg>  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"
+          "  -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 fro 0 to 10\n"
+          "                              (0 yields smallest files but lowest\n"
+          "                              video quality. 10 yields highest\n"
+          "                              fidelity but large files).\n\n"
+          "encoder_example accepts only uncompressed RIFF WAV format audio and\n"
+          "YUV4MPEG2 uncompressed video.\n\n");
+  exit(1);
+}
+
+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,4,test);
+    ret=fread(buffer,1,4,test);
+    if(ret<4)goto riff_err;
+    if(!memcmp(buffer,"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,"AVI2",4)){
+    /* possible AVI2VP31 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 VP31 raw frames file?\n",f);
+    }
+    buffer[i]='\0';
+
+    if(!memcmp(buffer,"VP31",4)){
+      char interlace;
+
+      if(video){
+        /* umm, we already have one */
+        fprintf(stderr,"Multiple video files specified on command line.\n");
+        exit(1);
+      }
+
+      if(buffer[4]!='R'){
+        fprintf(stderr,"Incorrect file ; VP31 raw frames required.\n");
+      }
+
+      ret=sscanf(buffer,"VP31R W%d H%d F%d:%d I%c A%d:%d",
+                 &frame_x,&frame_y,&video_hzn,&video_hzd,&interlace,
+                 &video_an,&video_ad);
+      if(ret<7){
+        fprintf(stderr,"Error parsing AVI2VP31R 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);
+      }
+
+      video=test;
+
+      fprintf(stderr,"File %s is %dx%d %.02f fps VP31 video.\n",
+              f,frame_x,frame_y,(double)video_hzn/video_hzd);
+
+      return;
+    }
+  }
+  fprintf(stderr,"Input file %s is neither a WAV nor VP31 file.\n",f);
+  exit(1);
+
+ riff_err:
+  fprintf(stderr,"EOF parsing RIFF file %s.\n",f);
+  exit(1);
+ yuv_err:
+  fprintf(stderr,"EOF parsing VP31 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){
+  ogg_packet op;
+  int i,j;
+
+  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];
+      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){
+        /* 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{
+        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]=((readbuffer[count+1]<<8)|
+                                 (0x00ff&(int)readbuffer[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;
+}
+
+int theora_transcode_packetout( TC_INSTANCE *ttc, int last_p, ogg_packet *op){
+
+  long bytes=ttc->in_bytecount;
+
+  if(!bytes)return(0);
+
+  op->packet=ttc->in_bytes;
+  op->bytes=bytes;
+  op->b_o_s=0;
+  op->e_o_s=last_p;
+
+  op->packetno=ttc->CurrentFrame;
+  op->granulepos=ttc->granulepos;
+
+  return 1;
+}
+
+void TranscodeKeyFrame(TC_INSTANCE *ttc){
+    /* Keep track of the total number of Key Frames Coded */
+    ttc->KeyFrameCount += 1;
+    ttc->LastKeyFrame = 1;
+}
+
+void TranscodeFrame(TC_INSTANCE *ttc){
+    ttc->LastKeyFrame++;
+}
+
+void TranscodeFirstFrame(TC_INSTANCE *ttc){
+  /* Keep track of the total number of Key Frames Coded. */
+  ttc->KeyFrameCount = 1;
+  ttc->LastKeyFrame = 1;
+}
+
+int theora_transcode_bufferin( TC_INSTANCE *ttc, int isKeyFrame, char * bytes, int bytecount){
+
+  /*transcode: record keyframe flag*/
+  ttc->ThisIsKeyFrame = isKeyFrame;
+
+  /* Special case for first frame */
+  if ( ttc->ThisIsFirstFrame ){
+    ttc->ThisIsFirstFrame = 0;
+    ttc->ThisIsKeyFrame = 0;
+  } else if ( ttc->ThisIsKeyFrame ) {
+    TranscodeKeyFrame(ttc);
+    ttc->ThisIsKeyFrame = 0;
+  } else  {
+    /* Compress the frame. */
+    TranscodeFrame( ttc );
+  }
+  /*need to pack info here*/
+  {
+
+    int            frame_type;
+    long           total_bits;
+    long           total_words;
+    int            frac_bits;
+
+    oggpackB_readinit(&ttc->opb_in,bytes,bytecount);
+    oggpackB_reset(&ttc->opb_out);
+
+    /*Mark as video frame.*/
+    oggpackB_write(&ttc->opb_out,0,1);
+    /*Copy frame type.*/
+    frame_type=oggpackB_read1(&ttc->opb_in);
+    oggpackB_write(&ttc->opb_out,frame_type,1);
+    /*Skip an unused bit in the VP32 header.*/
+    oggpackB_adv1(&ttc->opb_in);
+    /*Copy Q multiplier.*/
+    oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,6),6);
+    /*If the frame is a base/key/golden frame, copy a few extra bits.*/
+    if(frame_type==0){
+      /*These 13 bits are not included in a Theora frame header.
+        They were 0's and VP3 version info in VP32.*/
+      oggpackB_adv(&ttc->opb_in,13);
+      /*Copy the key frame type and the spare configuration bits.*/
+      oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,3),3);
+    }
+
+    /*Copy the rest of the bits over.*/
+    total_bits=bytecount*8-oggpack_bits(&ttc->opb_in);
+    frac_bits=(int)(total_bits&31);
+    if(frac_bits){
+      oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,frac_bits),
+       frac_bits);
+    }
+    total_words=total_bits>>5;
+    while(total_words-->0){
+      oggpackB_write(&ttc->opb_out,oggpackB_read(&ttc->opb_in,32),32);
+    }
+
+    ttc->in_bytecount = oggpackB_bytes(&ttc->opb_out);
+    ttc->in_bytes = oggpackB_get_buffer(&ttc->opb_out);
+  }
+
+  /* Update stats variables. */
+  ttc->CurrentFrame++;
+
+  ttc->granulepos=
+    ((ttc->CurrentFrame-ttc->LastKeyFrame-1)<<ttc->keyframe_granule_shift)+
+    ttc->LastKeyFrame-1;
+
+  return 0;
+}
+
+//static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len)
+
+int theora_transcoder_init(theora_info * ti, TC_INSTANCE * ttc){
+  memset(ttc, 0, sizeof(*ttc));
+  ttc->granulepos = -1;
+  ttc->keyframe_granule_shift=_ilog(ti->keyframe_frequency_force-1);
+  ttc->LastKeyFrame  = 0;
+  ttc->KeyFrameCount = 0;
+  ttc->ThisIsFirstFrame = 1;
+  ttc->ThisIsKeyFrame = 0;
+  ttc->CurrentFrame = 1; 
+  ttc->in_bytes = 0;
+  ttc->in_bytecount  = 0;
+  ttc->fps_denominator = ti->fps_denominator;
+  ttc->fps_numerator = ti->fps_numerator;
+  oggpackB_writeinit(&ttc->opb_out);
+  return 0;
+}
+
+int fetch_and_process_video(FILE *video,ogg_page *videopage,
+                            ogg_stream_state *to,
+                            TC_INSTANCE *ttc,
+                            int videoflag){
+  /* You'll go to Hell for using static variables */
+  static int          state=-1;
+  ogg_packet          op;
+  int i;
+  int keyframeflag, framelength;
+
+
+  if(state==-1){
+        /* initialize the double frame buffer */
+    state=0;
+  }
+
+  /* is there a video page flushed?  If not, work until there is. */
+  while(!videoflag){
+    spinnit();
+
+    if(ogg_stream_pageout(to,videopage)>0) return 1;
+    if(ogg_stream_eos(to)) return 0;
+
+    {
+      /* 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(i=state;i<2;i++){
+        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 VP31 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 VP31 frame header\n");
+            exit(1);
+          }
+        }
+
+        /*read the length*/
+        ret=fread(&framelength, sizeof(int), 1, video);
+
+        /*read the keyframeflag*/
+        ret=fread(&keyframeflag, sizeof(int), 1, video);
+
+        vp3frame[i] = malloc(framelength);
+        framebytecount[i] = framelength;
+        frameiskey[i] = keyframeflag;
+
+        /* read the frame */
+        ret=fread((char *) vp3frame[i], sizeof(char), framelength, video);
+            if(ret!=framelength) break;
+        
+        state++;
+      }
+
+      if(state<1){
+        /* can't get here unless VP31 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 */
+
+      //theora_encode_YUVin(td,&yuv);
+      theora_transcode_bufferin( ttc, frameiskey[0], vp3frame[0], framebytecount[0]);
+
+      /* if there's only one frame, it's the last in the stream */
+      if(state<2)
+        theora_transcode_packetout(ttc,1,&op);
+      else
+        theora_transcode_packetout(ttc,0,&op);
+
+      ogg_stream_packetin(to,&op);
+
+      {
+        signed char *temp=vp3frame[0];
+        vp3frame[0]=vp3frame[1];
+        vp3frame[1] = temp;
+        free(temp);
+        
+        framebytecount[0]= framebytecount[1];
+        frameiskey[0] = frameiskey[1];
+        state--;
+      }
+    }
+  }
+  return videoflag;
+}
+
+/* returns, in seconds, absolute time of current packet in given
+   logical stream */
+double transcode_granule_time(TC_INSTANCE *ttc,ogg_int64_t granulepos){
+  if(granulepos>=0){
+    ogg_int64_t iframe=granulepos>>ttc->keyframe_granule_shift;
+    ogg_int64_t pframe=granulepos-(iframe<<ttc->keyframe_granule_shift);
+
+    return (iframe+pframe)*
+      ((double)ttc->fps_denominator/ttc->fps_numerator);
+
+  }
+  return(-1);
+}
+
+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 */
+
+  theora_state     td;
+  theora_info      ti;
+  theora_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 audioflag=0;
+  int videoflag=0;
+  int akbps=0;
+  int vkbps=0;
+
+  ogg_int64_t audio_bytesout=0;
+  ogg_int64_t video_bytesout=0;
+  double timebase;
+
+  FILE* outfile = stdout;
+
+  TC_INSTANCE ttc;
+
+#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=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=rint(atof(optarg)*6.3);
+      if(video_q<0 || video_q>63){
+        fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+        exit(1);
+      }
+      video_r=0;
+      break;
+
+    case 'A':
+      audio_r=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=rint(atof(optarg)*1000);
+      if(video_r<45000 || video_r>2000000){
+        fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n");
+        exit(1);
+      }
+      video_q=0;
+     break;
+    default:
+      usage();
+    }
+  }
+
+  while(optind<argc){
+    /* assume that anything following the options must be a filename */
+    id_file(argv[optind]);
+    optind++;
+  }
+
+  /* yayness.  Set up Ogg output stream */
+  srand(time(NULL));
+  ogg_stream_init(&vo,rand());
+  ogg_stream_init(&to,rand()); /* oops, add one ot the above */
+
+  /* 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 video size */
+  /* scale the frame size up to the nearest /16 and calculate offsets */
+  video_x=((frame_x + 15) >>4)<<4;
+  video_y=((frame_y + 15) >>4)<<4;
+  frame_x_offset=(video_x-frame_x)/2;
+  frame_y_offset=(video_y-frame_y)/2;
+
+  theora_info_init(&ti);
+  ti.width=video_x;
+  ti.height=video_y;
+  ti.frame_width=frame_x;
+  ti.frame_height=frame_y;
+  ti.offset_x=frame_x_offset;
+  ti.offset_y=frame_y_offset;
+  ti.fps_numerator=video_hzn;
+  ti.fps_denominator=video_hzd;
+  ti.aspect_numerator=video_an;
+  ti.aspect_denominator=video_ad;
+  ti.colorspace=OC_CS_UNSPECIFIED;
+  ti.target_bitrate=video_r;
+  ti.quality=video_q;
+
+  ti.dropframes_p=0;
+  ti.quick_p=1;
+  ti.keyframe_auto_p=1;
+  ti.keyframe_frequency=32768;
+  ti.keyframe_frequency_force=32768;
+  ti.keyframe_data_target_bitrate=video_r*1.5;
+  ti.keyframe_auto_threshold=80;
+  ti.keyframe_mindistance=8;
+  ti.noise_sensitivity=1;
+
+  theora_encode_init(&td,&ti);
+  theora_transcoder_init(&ti, &ttc);
+  theora_info_clear(&ti);
+
+  /* initialize Vorbis too, assuming we have audio to compress. */
+  if(audio){
+    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,audio_r,-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);
+  }
+
+  /* write the bitstream header packets with proper page interleave */
+
+  /* first packet will get its own page automatically */
+  theora_encode_header(&td,&op);
+  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 */
+  theora_comment_init(&tc);
+  theora_encode_comment(&tc,&op);
+  ogg_stream_packetin(&to,&op);
+  theora_encode_tables(&td,&op);
+  ogg_stream_packetin(&to,&op);
+
+  if(audio){
+    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. */
+  while(1){
+    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){
+    while(1){
+      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 */
+  fprintf(stderr,"Compressing....\n");
+  while(1){
+
+
+    /* 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,&ttc,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 */
+    {
+      int audio_or_video=-1;
+      double audiotime=
+        audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
+      double videotime=
+        videoflag?transcode_granule_time(&ttc,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;
+      }
+      {
+        int hundredths=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=rint(video_bytesout*8./timebase*.001);
+        else
+          akbps=rint(audio_bytesout*8./timebase*.001);
+        
+        fprintf(stderr,
+                "\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps                 ",
+                hours,minutes,seconds,hundredths,akbps,vkbps);
+      }
+    }
+
+  }
+
+  /* clear out state */
+
+  if(audio){
+    ogg_stream_clear(&vo);
+    vorbis_block_clear(&vb);
+    vorbis_dsp_clear(&vd);
+    vorbis_comment_clear(&vc);
+    vorbis_info_clear(&vi);
+  }
+  if(video){
+    ogg_stream_clear(&to);
+    theora_clear(&td);
+  }
+
+  if(outfile && outfile!=stdout)fclose(outfile);
+
+  fprintf(stderr,"\r   \ndone.\n\n");
+
+  return(0);
+
+}

Added: trunk/theora-tools/theora_transcoder/vp31.avi
===================================================================
(Binary files differ)

<p>Property changes on: trunk/theora-tools/theora_transcoder/vp31.avi
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/theora-tools/theoraenc/Makefile.am
===================================================================
--- trunk/theora-tools/theoraenc/Makefile.am	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theoraenc/Makefile.am	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+bin_PROGRAMS = theoraenc
+
+CFLAGS = $(SDL_CFLAGS)
+LDADD = -ltheora -logg -lvorbis -lm
+
+theoraenc_SOURCES = theoraenc.c
+EXTRA_theoraenc_SOURCES = getopt.c getopt1.c getopt.h
+theoraenc_LDADD = $(GETOPT_OBJS) $(LDADD) -lvorbisenc
+theoraenc_DEPENDENCIES = $(GETOPT_OBJS)

Added: trunk/theora-tools/theoraenc/getopt.c
===================================================================
--- trunk/theora-tools/theoraenc/getopt.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theoraenc/getopt.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -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 at 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 */

Added: trunk/theora-tools/theoraenc/getopt.h
===================================================================
--- trunk/theora-tools/theoraenc/getopt.h	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theoraenc/getopt.h	2004-04-21 18:26:11 UTC (rev 6565)
@@ -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 */

Added: trunk/theora-tools/theoraenc/getopt1.c
===================================================================
--- trunk/theora-tools/theoraenc/getopt1.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theoraenc/getopt1.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -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 */

Added: trunk/theora-tools/theoraenc/theoraenc.c
===================================================================
--- trunk/theora-tools/theoraenc/theoraenc.c	2004-04-21 18:23:03 UTC (rev 6564)
+++ trunk/theora-tools/theoraenc/theoraenc.c	2004-04-21 18:26:11 UTC (rev 6565)
@@ -0,0 +1,825 @@
+/********************************************************************
+ *                                                                  *
+ * 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-2003                *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+  function: example encoder application; makes an Ogg Theora/Vorbis
+            file from YUV4MPEG2 and WAV input
+  last mod: $Id: theoraenc.c,v 1.28 2004/03/08 06:44:26 giles Exp $
+
+ ********************************************************************/
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#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 <getopt.h>
+#include <time.h>
+#include <math.h>
+#include "theora/theora.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>
+
+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 = "o:a:A:v:V:s:S:f:F:";
+struct option options [] = {
+  {"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",optional_argument,NULL,'s'},
+  {"aspect-denominator",optional_argument,NULL,'S'},
+  {"framerate-numerator",optional_argument,NULL,'f'},
+  {"framerate-denominator",optional_argument,NULL,'F'},
+  {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=.1;
+int audio_r=-1;
+
+int video_x=0;
+int video_y=0;
+int frame_x=0;
+int frame_y=0;
+int frame_x_offset=0;
+int frame_y_offset=0;
+int video_hzn=-1;
+int video_hzd=-1;
+int video_an=-1;
+int video_ad=-1;
+
+int video_r=-1;
+int video_q=16;
+
+static void usage(void){
+  fprintf(stderr,
+          "Usage: theoraenc [options] [audio_file] video_file\n\n"
+          "Options: \n\n"
+          "  -o --output <filename.ogg>     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"
+          "  -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 fro 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"
+          "theoraenc accepts only uncompressed RIFF WAV format audio and\n"
+          "YUV4MPEG2 uncompressed video.\n\n");
+  exit(1);
+}
+
+static void id_file(char *f){
+  FILE *test;
+  unsigned char buffer[80];
+  int ret;
+  int tmp_video_hzn, tmp_video_hzd, tmp_video_an, tmp_video_ad;
+
+  /* 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,4,test);
+    ret=fread(buffer,1,4,test);
+    if(ret<4)goto riff_err;
+    if(!memcmp(buffer,"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)){
+      char interlace;
+
+      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=sscanf(buffer,"MPEG2 W%d H%d F%d:%d I%c A%d:%d",
+                 &frame_x,&frame_y,&tmp_video_hzn,&tmp_video_hzd,&interlace,
+                 &tmp_video_an,&tmp_video_ad);
+      if(ret<7){
+        fprintf(stderr,"Error parsing YUV4MPEG2 header in file %s.\n",f);
+        exit(1);
+      }
+
+      /*update fps and aspect ratio globals if not specified in the command line*/
+      if (video_hzn==-1) video_hzn = tmp_video_hzn;
+      if (video_hzd==-1) video_hzd = tmp_video_hzd;
+      if (video_an==-1) video_an = tmp_video_an;
+      if (video_ad==-1) video_ad = tmp_video_ad;
+
+      if(interlace!='p'){
+        fprintf(stderr,"Input video is interlaced; Theora handles only progressive scan\n");
+        exit(1);
+      }
+
+      video=test;
+
+      fprintf(stderr,"File %s is %dx%d %.02f fps YUV12 video.\n",
+              f,frame_x,frame_y,(double)video_hzn/video_hzd);
+
+      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){
+  ogg_packet op;
+  int i,j;
+
+  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];
+      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){
+        /* 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{
+        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]=((readbuffer[count+1]<<8)|
+                                 (0x00ff&(int)readbuffer[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;
+}
+
+int fetch_and_process_video(FILE *video,ogg_page *videopage,
+                            ogg_stream_state *to,
+                            theora_state *td,
+                            int videoflag){
+  /* You'll go to Hell for using static variables */
+  static int          state=-1;
+  static signed char *yuvframe[2];
+  signed char        *line;
+  yuv_buffer          yuv;
+  ogg_packet          op;
+  int i, e;
+
+  if(state==-1){
+        /* initialize the double frame buffer */
+    yuvframe[0]=malloc(video_x*video_y*3/2);
+    yuvframe[1]=malloc(video_x*video_y*3/2);
+
+        /* clear initial frame as it may be larger than actual video data */
+        /* fill Y plane with 0x10 and UV planes with 0X80, for black data */
+    memset(yuvframe[0],0x10,video_x*video_y);
+    memset(yuvframe[0]+video_x*video_y,0x80,video_x*video_y/2);
+    memset(yuvframe[1],0x10,video_x*video_y);
+    memset(yuvframe[1]+video_x*video_y,0x80,video_x*video_y/2);
+
+    state=0;
+  }
+
+  /* is there a video page flushed?  If not, work until there is. */
+  while(!videoflag){
+    spinnit();
+
+    if(ogg_stream_pageout(to,videopage)>0) return 1;
+    if(ogg_stream_eos(to)) return 0;
+
+    {
+      /* 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(i=state;i<2;i++){
+        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 Y plane into our frame buffer with centering */
+        line=yuvframe[i]+video_x*frame_y_offset+frame_x_offset;
+        for(e=0;e<frame_y;e++){
+          ret=fread(line,1,frame_x,video);
+            if(ret!=frame_x) break;
+          line+=video_x;
+        }
+        /* now get U plane*/
+        line=yuvframe[i]+(video_x*video_y)
+          +(video_x/2)*(frame_y_offset/2)+frame_x_offset/2;
+        for(e=0;e<frame_y/2;e++){
+          ret=fread(line,1,frame_x/2,video);
+            if(ret!=frame_x/2) break;
+          line+=video_x/2;
+        }
+        /* and the V plane*/
+        line=yuvframe[i]+(video_x*video_y*5/4)
+                  +(video_x/2)*(frame_y_offset/2)+frame_x_offset/2;
+        for(e=0;e<frame_y/2;e++){
+          ret=fread(line,1,frame_x/2,video);
+            if(ret!=frame_x/2) break;
+          line+=video_x/2;
+        }
+        state++;
+      }
+
+      if(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 */
+
+      {
+        yuv.y_width=video_x;
+        yuv.y_height=video_y;
+        yuv.y_stride=video_x;
+
+        yuv.uv_width=video_x/2;
+        yuv.uv_height=video_y/2;
+        yuv.uv_stride=video_x/2;
+
+        yuv.y= yuvframe[0];
+        yuv.u= yuvframe[0]+ video_x*video_y;
+        yuv.v= yuvframe[0]+ video_x*video_y*5/4 ;
+      }
+
+      theora_encode_YUVin(td,&yuv);
+
+      /* if there's only one frame, it's the last in the stream */
+      if(state<2)
+        theora_encode_packetout(td,1,&op);
+      else
+        theora_encode_packetout(td,0,&op);
+
+      ogg_stream_packetin(to,&op);
+
+      {
+        signed char *temp=yuvframe[0];
+        yuvframe[0]=yuvframe[1];
+        yuvframe[1]=temp;
+        state--;
+      }
+
+    }
+  }
+  return videoflag;
+}
+
+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 */
+
+  theora_state     td;
+  theora_info      ti;
+  theora_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 audioflag=0;
+  int videoflag=0;
+  int akbps=0;
+  int vkbps=0;
+
+  ogg_int64_t audio_bytesout=0;
+  ogg_int64_t video_bytesout=0;
+  double timebase;
+
+  FILE* outfile = stdout;
+
+#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=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=rint(atof(optarg)*6.3);
+      if(video_q<0 || video_q>63){
+        fprintf(stderr,"Illegal video quality (choose 0 through 10)\n");
+        exit(1);
+      }
+      video_r=0;
+      break;
+
+    case 'A':
+      audio_r=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=rint(atof(optarg)*1000);
+      if(video_r<45000 || video_r>2000000){
+        fprintf(stderr,"Illegal video bitrate (choose 45kbps through 2000kbps)\n");
+        exit(1);
+      }
+      video_q=0;
+     break;
+
+    case 's':
+      video_an=rint(atof(optarg));
+      break;
+
+    case 'S':
+      video_ad=rint(atof(optarg));
+      break;
+
+    case 'f':
+      video_hzn=rint(atof(optarg));
+      break;
+
+    case 'F':
+      video_hzd=rint(atof(optarg));
+      break;
+
+    default:
+      usage();
+    }
+  }
+
+  while(optind<argc){
+    /* assume that anything following the options must be a filename */
+    id_file(argv[optind]);
+    optind++;
+  }
+
+  /* yayness.  Set up Ogg output stream */
+  srand(time(NULL));
+  ogg_stream_init(&vo,rand());
+  ogg_stream_init(&to,rand()); /* oops, add one ot the above */
+
+  /* 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 video size */
+  /* scale the frame size up to the nearest /16 and calculate offsets */
+  video_x=((frame_x + 15) >>4)<<4;
+  video_y=((frame_y + 15) >>4)<<4;
+  frame_x_offset=(video_x-frame_x)/2;
+  frame_y_offset=(video_y-frame_y)/2;
+
+  theora_info_init(&ti);
+  ti.width=video_x;
+  ti.height=video_y;
+  ti.frame_width=frame_x;
+  ti.frame_height=frame_y;
+  ti.offset_x=frame_x_offset;
+  ti.offset_y=frame_y_offset;
+  ti.fps_numerator=video_hzn;
+  ti.fps_denominator=video_hzd;
+  ti.aspect_numerator=video_an;
+  ti.aspect_denominator=video_ad;
+  ti.colorspace=OC_CS_UNSPECIFIED;
+  ti.target_bitrate=video_r;
+  ti.quality=video_q;
+
+  ti.dropframes_p=0;
+  ti.quick_p=1;
+  ti.keyframe_auto_p=1;
+  ti.keyframe_frequency=64;
+  ti.keyframe_frequency_force=64;
+  ti.keyframe_data_target_bitrate=video_r*1.5;
+  ti.keyframe_auto_threshold=80;
+  ti.keyframe_mindistance=8;
+  ti.noise_sensitivity=1;
+
+  theora_encode_init(&td,&ti);
+  theora_info_clear(&ti);
+
+  /* initialize Vorbis too, assuming we have audio to compress. */
+  if(audio){
+    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,audio_r,-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);
+  }
+
+  /* write the bitstream header packets with proper page interleave */
+
+  /* first packet will get its own page automatically */
+  theora_encode_header(&td,&op);
+  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 */
+  theora_comment_init(&tc);
+  theora_encode_comment(&tc,&op);
+  ogg_stream_packetin(&to,&op);
+  theora_encode_tables(&td,&op);
+  ogg_stream_packetin(&to,&op);
+
+  if(audio){
+    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. */
+  while(1){
+    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){
+    while(1){
+      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 */
+  fprintf(stderr,"Compressing....\n");
+  while(1){
+    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,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 */
+    {
+      int audio_or_video=-1;
+      double audiotime=
+        audioflag?vorbis_granule_time(&vd,ogg_page_granulepos(&audiopage)):-1;
+      double videotime=
+        videoflag?theora_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;
+      }
+      {
+        int hundredths=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=rint(video_bytesout*8./timebase*.001);
+        else
+          akbps=rint(audio_bytesout*8./timebase*.001);
+        
+        fprintf(stderr,
+                "\r      %d:%02d:%02d.%02d audio: %dkbps video: %dkbps                 ",
+                hours,minutes,seconds,hundredths,akbps,vkbps);
+      }
+    }
+
+  }
+
+  /* clear out state */
+
+  if(audio){
+    ogg_stream_clear(&vo);
+    vorbis_block_clear(&vb);
+    vorbis_dsp_clear(&vd);
+    vorbis_comment_clear(&vc);
+    vorbis_info_clear(&vi);
+  }
+  if(video){
+    ogg_stream_clear(&to);
+    theora_clear(&td);
+  }
+
+  if(outfile && outfile!=stdout)fclose(outfile);
+
+  fprintf(stderr,"\r   \ndone.\n\n");
+
+  return(0);
+
+}

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list