[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