diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/src')
38 files changed, 17653 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/src/Makefile.am b/debian/transcode/transcode-1.1.7/src/Makefile.am new file mode 100644 index 00000000..cd163146 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/Makefile.am @@ -0,0 +1,71 @@ +# # Process this file with automake to produce Makefile.in. + +AM_CPPFLAGS = \ + $(PTHREAD_CFLAGS) \ + -DMOD_PATH=\"$(MOD_PATH)\" \ + -DPROF_PATH=\"$(PROF_PATH)\" \ + -I$(top_srcdir) \ + $(LIBAVCODEC_CFLAGS) \ + $(DLDARWIN_CFLAGS) \ + $(X_CFLAGS) + +AM_LDFLAGS = -export-dynamic + +bin_PROGRAMS = transcode + +if HAVE_X11 +TC_X_LIBS = $(X_LIBS) $(X_PRE_LIBS) -lXext -lX11 $(X_EXTRA_LIBS) +endif + +transcode_LDADD = \ + $(DLDARWIN_LIBS) \ + $(LIBTC_LIBS) \ + $(LIBTCAUDIO_LIBS) \ + $(LIBTCVIDEO_LIBS) \ + $(ACLIB_LIBS) \ + $(AVILIB_LIBS) \ + $(WAVLIB_LIBS) \ + $(XIO_LIBS) \ + $(LIBXML2_LIBS) \ + $(PTHREAD_LIBS) \ + $(TC_X_LIBS) \ + -lm + +EXTRA_DIST = \ + audio_trans.h \ + cmdline.h \ + cmdline_def.h \ + counter.h \ + decoder.h \ + dl_loader.h \ + encoder.h \ + encoder-common.h \ + export_profile.h \ + filter.h \ + frame_threads.h \ + framebuffer.h \ + probe.h \ + socket.h \ + split.h \ + tc_defaults.h \ + tcinfo.h \ + transcode.h \ + video_trans.h + +transcode_SOURCES = \ + transcode.c \ + audio_trans.c \ + cmdline.c \ + counter.c \ + decoder.c \ + dl_loader.c \ + encoder.c \ + encoder-common.c \ + encoder-buffer.c \ + filter.c \ + frame_threads.c \ + framebuffer.c \ + probe.c \ + socket.c \ + split.c \ + video_trans.c diff --git a/debian/transcode/transcode-1.1.7/src/Makefile.in b/debian/transcode/transcode-1.1.7/src/Makefile.in new file mode 100644 index 00000000..48bb6d05 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/Makefile.in @@ -0,0 +1,706 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# # Process this file with automake to produce Makefile.in. + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = transcode$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_transcode_OBJECTS = transcode.$(OBJEXT) audio_trans.$(OBJEXT) \ + cmdline.$(OBJEXT) counter.$(OBJEXT) decoder.$(OBJEXT) \ + dl_loader.$(OBJEXT) encoder.$(OBJEXT) encoder-common.$(OBJEXT) \ + encoder-buffer.$(OBJEXT) filter.$(OBJEXT) \ + frame_threads.$(OBJEXT) framebuffer.$(OBJEXT) probe.$(OBJEXT) \ + socket.$(OBJEXT) split.$(OBJEXT) video_trans.$(OBJEXT) +transcode_OBJECTS = $(am_transcode_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_X11_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ +@HAVE_X11_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +transcode_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/autotools/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(transcode_SOURCES) +DIST_SOURCES = $(transcode_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A52_CFLAGS = @A52_CFLAGS@ +A52_LIBS = @A52_LIBS@ +ACLIB_LIBS = @ACLIB_LIBS@ +ACLOCAL = @ACLOCAL@ +ALTIVEC_CFLAGS = @ALTIVEC_CFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AVILIB_LIBS = @AVILIB_LIBS@ +AWK = @AWK@ +BSDAV_CFLAGS = @BSDAV_CFLAGS@ +BSDAV_LIBS = @BSDAV_LIBS@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXXCPP = @CXXCPP@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLDARWIN_CFLAGS = @DLDARWIN_CFLAGS@ +DLDARWIN_LIBS = @DLDARWIN_LIBS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FAAC_CFLAGS = @FAAC_CFLAGS@ +FAAC_LIBS = @FAAC_LIBS@ +FGREP = @FGREP@ +FREETYPE2_CFLAGS = @FREETYPE2_CFLAGS@ +FREETYPE2_LIBS = @FREETYPE2_LIBS@ +GREP = @GREP@ +IBP_LIBS = @IBP_LIBS@ +ICONV_CFLAGS = @ICONV_CFLAGS@ +ICONV_LIBS = @ICONV_LIBS@ +IMAGEMAGICK_CFLAGS = @IMAGEMAGICK_CFLAGS@ +IMAGEMAGICK_LIBS = @IMAGEMAGICK_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LAME_CFLAGS = @LAME_CFLAGS@ +LAME_LIBS = @LAME_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBAVCODEC_CFLAGS = @LIBAVCODEC_CFLAGS@ +LIBAVCODEC_LIBS = @LIBAVCODEC_LIBS@ +LIBAVFORMAT_CFLAGS = @LIBAVFORMAT_CFLAGS@ +LIBAVFORMAT_LIBS = @LIBAVFORMAT_LIBS@ +LIBDVDREAD_CFLAGS = @LIBDVDREAD_CFLAGS@ +LIBDVDREAD_LIBS = @LIBDVDREAD_LIBS@ +LIBDV_CFLAGS = @LIBDV_CFLAGS@ +LIBDV_LIBS = @LIBDV_LIBS@ +LIBJPEG_CFLAGS = @LIBJPEG_CFLAGS@ +LIBJPEG_LIBS = @LIBJPEG_LIBS@ +LIBMPEG2CONVERT_CFLAGS = @LIBMPEG2CONVERT_CFLAGS@ +LIBMPEG2CONVERT_LIBS = @LIBMPEG2CONVERT_LIBS@ +LIBMPEG2_CFLAGS = @LIBMPEG2_CFLAGS@ +LIBMPEG2_LIBS = @LIBMPEG2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBPOSTPROC_CFLAGS = @LIBPOSTPROC_CFLAGS@ +LIBPOSTPROC_LIBS = @LIBPOSTPROC_LIBS@ +LIBQUICKTIME_CFLAGS = @LIBQUICKTIME_CFLAGS@ +LIBQUICKTIME_LIBS = @LIBQUICKTIME_LIBS@ +LIBS = @LIBS@ +LIBTCAUDIO_LIBS = @LIBTCAUDIO_LIBS@ +LIBTCVIDEO_LIBS = @LIBTCVIDEO_LIBS@ +LIBTC_LIBS = @LIBTC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBV4L2_CFLAGS = @LIBV4L2_CFLAGS@ +LIBV4L2_LIBS = @LIBV4L2_LIBS@ +LIBV4LCONVERT_CFLAGS = @LIBV4LCONVERT_CFLAGS@ +LIBV4LCONVERT_LIBS = @LIBV4LCONVERT_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LZO_CFLAGS = @LZO_CFLAGS@ +LZO_LIBS = @LZO_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MJPEGTOOLS_CFLAGS = @MJPEGTOOLS_CFLAGS@ +MJPEGTOOLS_LIBS = @MJPEGTOOLS_LIBS@ +MKDIR_P = @MKDIR_P@ +MOD_PATH = @MOD_PATH@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OGG_CFLAGS = @OGG_CFLAGS@ +OGG_LIBS = @OGG_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PATH_TO_AWK = @PATH_TO_AWK@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROF_PATH = @PROF_PATH@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PVM3_CFLAGS = @PVM3_CFLAGS@ +PVM3_LIBS = @PVM3_LIBS@ +PVM3_PVMGS = @PVM3_PVMGS@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_LIBS = @SDL_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIMD_FLAGS = @SIMD_FLAGS@ +STRIP = @STRIP@ +THEORA_CFLAGS = @THEORA_CFLAGS@ +THEORA_LIBS = @THEORA_LIBS@ +USE_DLDARWIN = @USE_DLDARWIN@ +VERSION = @VERSION@ +VORBIS_CFLAGS = @VORBIS_CFLAGS@ +VORBIS_LIBS = @VORBIS_LIBS@ +WAVLIB_LIBS = @WAVLIB_LIBS@ +X264_CFLAGS = @X264_CFLAGS@ +X264_LIBS = @X264_LIBS@ +XIO_CFLAGS = @XIO_CFLAGS@ +XIO_LIBS = @XIO_LIBS@ +XMKMF = @XMKMF@ +XVID_CFLAGS = @XVID_CFLAGS@ +XVID_LIBS = @XVID_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +a52_config = @a52_config@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +bsdav_config = @bsdav_config@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +faac_config = @faac_config@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +iconv_config = @iconv_config@ +imagemagick_config = @imagemagick_config@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +lame_config = @lame_config@ +libdir = @libdir@ +libdvdread_config = @libdvdread_config@ +libexecdir = @libexecdir@ +libjpeg_config = @libjpeg_config@ +libjpegmmx_config = @libjpegmmx_config@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lzo_config = @lzo_config@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pvm3_config = @pvm3_config@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +x_includes = @x_includes@ +x_libraries = @x_libraries@ +xvid_config = @xvid_config@ +AM_CPPFLAGS = \ + $(PTHREAD_CFLAGS) \ + -DMOD_PATH=\"$(MOD_PATH)\" \ + -DPROF_PATH=\"$(PROF_PATH)\" \ + -I$(top_srcdir) \ + $(LIBAVCODEC_CFLAGS) \ + $(DLDARWIN_CFLAGS) \ + $(X_CFLAGS) + +AM_LDFLAGS = -export-dynamic +@HAVE_X11_TRUE@TC_X_LIBS = $(X_LIBS) $(X_PRE_LIBS) -lXext -lX11 $(X_EXTRA_LIBS) +transcode_LDADD = \ + $(DLDARWIN_LIBS) \ + $(LIBTC_LIBS) \ + $(LIBTCAUDIO_LIBS) \ + $(LIBTCVIDEO_LIBS) \ + $(ACLIB_LIBS) \ + $(AVILIB_LIBS) \ + $(WAVLIB_LIBS) \ + $(XIO_LIBS) \ + $(LIBXML2_LIBS) \ + $(PTHREAD_LIBS) \ + $(TC_X_LIBS) \ + -lm + +EXTRA_DIST = \ + audio_trans.h \ + cmdline.h \ + cmdline_def.h \ + counter.h \ + decoder.h \ + dl_loader.h \ + encoder.h \ + encoder-common.h \ + export_profile.h \ + filter.h \ + frame_threads.h \ + framebuffer.h \ + probe.h \ + socket.h \ + split.h \ + tc_defaults.h \ + tcinfo.h \ + transcode.h \ + video_trans.h + +transcode_SOURCES = \ + transcode.c \ + audio_trans.c \ + cmdline.c \ + counter.c \ + decoder.c \ + dl_loader.c \ + encoder.c \ + encoder-common.c \ + encoder-buffer.c \ + filter.c \ + frame_threads.c \ + framebuffer.c \ + probe.c \ + socket.c \ + split.c \ + video_trans.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +transcode$(EXEEXT): $(transcode_OBJECTS) $(transcode_DEPENDENCIES) + @rm -f transcode$(EXEEXT) + $(LINK) $(transcode_OBJECTS) $(transcode_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audio_trans.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/counter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decoder.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dl_loader.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder-buffer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder-common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frame_threads.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/framebuffer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/probe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/split.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transcode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/video_trans.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/debian/transcode/transcode-1.1.7/src/audio_trans.c b/debian/transcode/transcode-1.1.7/src/audio_trans.c new file mode 100644 index 00000000..019e5212 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/audio_trans.c @@ -0,0 +1,176 @@ +/* + * audio_trans.c - audio frame transformation routines + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "framebuffer.h" +#include "audio_trans.h" +#include "libtcaudio/tcaudio.h" + +/*************************************************************************/ + +/* Handle for calling tcaudio functions. */ +static TCAHandle handle = 0; + +/*************************************************************************/ +/*************************************************************************/ + +/** + * do_process_audio: Perform actual audio processing. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to audio frame buffer. + * Return value: + * 1 on success, 0 on failure. + */ + +static int do_process_audio(vob_t *vob, aframe_list_t *ptr) +{ + int srcfmt, nsamples; + + /* First convert audio to destination format (also handles -d) */ + if (vob->a_bits == 8) { + srcfmt = TCA_U8; + nsamples = ptr->audio_size; + } else if (vob->a_bits == 16) { + srcfmt = pcmswap ? TCA_S16BE : TCA_S16LE; + nsamples = ptr->audio_size / 2; + } else { + tc_log_error(__FILE__, "Sorry, source audio format not supported"); + return 0; + } + tca_convert_from(handle, ptr->audio_buf, nsamples, srcfmt); + + /* Convert between stereo and mono */ + if (vob->a_chan == 1 && vob->dm_chan == 2) { + tca_mono_to_stereo(handle, ptr->audio_buf, nsamples); + nsamples *= 2; + } else if (vob->a_chan == 2 && vob->dm_chan == 1) { + nsamples /= 2; + tca_stereo_to_mono(handle, ptr->audio_buf, nsamples); + } + + /* Update audio buffer size */ + ptr->audio_size = nsamples * (vob->dm_bits/8); + + /* -s: Amplify volume */ + if (vob->volume > 0) { + int nclip = 0; + tca_amplify(handle, ptr->audio_buf, nsamples, vob->volume, &nclip); + vob->clip_count += nclip; + } + + /* --av_fine_ms: Shift audio */ + if (vob->sync_ms != 0) { + /* This is the first time here: convert time (ms) to samples. + * Note that we adjust based on the source rate */ + vob->sync_samples = (vob->sync_ms * vob->a_rate / 1000) * vob->dm_chan; + if (verbose & TC_DEBUG) { + if (vob->sync_samples < 0) { + tc_log_info(__FILE__, "inserting %d PCM samples (%d ms)", + -vob->sync_samples, -vob->sync_ms); + } else { + tc_log_info(__FILE__, "deleting %d PCM samples (%d ms)", + vob->sync_samples, vob->sync_ms); + } + } + vob->sync_ms = 0; // Clear it so we don't come here again + } + if (vob->sync_samples < 0) { + int new_samples = -vob->sync_samples; + int new_bytes = new_samples * (vob->dm_bits/8); + memmove((uint8_t *)ptr->audio_buf + new_bytes, ptr->audio_buf, + ptr->audio_size); + memset(ptr->audio_buf, 0, new_bytes); + nsamples += new_samples; + ptr->audio_size += new_bytes; + vob->sync_samples = 0; + } else if (vob->sync_samples > 0) { + int del_samples = vob->sync_samples; + if (del_samples >= nsamples) { + del_samples = nsamples; + nsamples = 0; + ptr->audio_size = 0; + } else { + int del_bytes = del_samples * (vob->dm_bits/8); + memmove(ptr->audio_buf, (uint8_t *)ptr->audio_buf + del_bytes, + ptr->audio_size - del_bytes); + nsamples -= del_samples; + ptr->audio_size -= del_bytes; + } + vob->sync_samples -= del_samples; + } + + /* All done */ + return 1; +} + +/*************************************************************************/ + +/** + * process_aud_frame: Main audio frame processing routine. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to audio frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +int process_aud_frame(vob_t *vob, aframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Allocate tcaudio handle if necessary */ + if (!handle) { + AudioFormat format; + if (vob->dm_bits == 8) { + format = TCA_U8; + } else if (vob->dm_bits == 16) { + format = TCA_S16LE; + } else { + tc_log_error(__FILE__, "Sorry, output audio format not supported"); + return -1; + } + handle = tca_init(format); + if (!handle) { + tc_log_error(__FILE__, "tca_init() failed!"); + return -1; + } + } + + /* Check for pass-through mode */ + if (vob->pass_flag & TC_AUDIO) + return 0; + + /* Check audio format */ + if (vob->im_a_codec != CODEC_PCM) { + tc_log_error(__FILE__, + "Sorry, only PCM audio is supported for processing"); + return -1; + } + + /* Actually perform processing */ + return do_process_audio(vob, ptr) ? 0 : -1; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/audio_trans.h b/debian/transcode/transcode-1.1.7/src/audio_trans.h new file mode 100644 index 00000000..1060ee8a --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/audio_trans.h @@ -0,0 +1,18 @@ +/* + * audio_trans.h - header for audio frame transformation routines + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef _AUDIO_TRANS_H +#define _AUDIO_TRANS_H + +#include "framebuffer.h" + +int process_aud_frame(vob_t *vob, aframe_list_t *ptr); + +#endif diff --git a/debian/transcode/transcode-1.1.7/src/cmdline.c b/debian/transcode/transcode-1.1.7/src/cmdline.c new file mode 100644 index 00000000..dec44bff --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/cmdline.c @@ -0,0 +1,241 @@ +/* + * cmdline.c -- parse transcode command line + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "decoder.h" +#include "probe.h" +#include "libtc/libtc.h" +#include "libtc/cfgfile.h" +#include "libtc/ratiocodes.h" +#include "libtc/tccodecs.h" +#include "libtc/xio.h" + +#include "cmdline.h" + +#include <ctype.h> +#ifdef HAVE_GETOPT_LONG_ONLY +#include <getopt.h> +#else +#include "libtc/getopt.h" +#endif + + +/* Global variables from transcode.c that should eventually go away. */ +int core_mode=TC_MODE_DEFAULT; +char *im_aud_mod = NULL, *im_vid_mod = NULL; +char *ex_aud_mod = NULL, *ex_vid_mod = NULL, *ex_mplex_mod = NULL; +char *plugins_string = NULL; +char + *nav_seek_file=NULL, *socket_file=NULL, + *chbase=NULL, //*dirbase=NULL, + base[TC_BUF_MIN]; +int psu_frame_threshold=12; //psu with less/equal frames are skipped. +int + no_vin_codec=1, no_ain_codec=1, + no_v_out_codec=1, no_a_out_codec=1; +int + frame_a=TC_FRAME_FIRST, // defaults to all frames + frame_b=TC_FRAME_LAST, + splitavi_frames=0, + psu_mode=TC_FALSE; +int preset_flag=0, auto_probe=1, seek_range=1; +int no_audio_adjust=TC_FALSE, no_split=TC_FALSE; +char *fc_ttime_string = NULL; +int sync_seconds=0; +pid_t writepid = 0; + + +/*************************************************************************/ + +/* Utility routines used by option processing. */ + + +/** + * print_option_help: Print a help line for a given option. + * + * Parameters: + * name: Option name (long name). + * shortopt: Character used for the short form of the option, 0 if none. + * argname: String describing the option's argument, NULL if none. + * helptext: Help text for the option. May include newlines (\n). + * optwidth: Number of columns to use for the long name and argument. + * Return value: + * None. + */ + +#define MAX_LINELEN 79 /* Maximum line length */ +#define MAX_OPTWIDTH 35 /* Maximum space to allocate to options */ + +static void print_option_help(const char *name, char shortopt, + const char *argname, const char *helptext, + int optwidth) +{ + int helpmax; + char optbuf[MAX_LINELEN+1]; + const char *s; + + if (optwidth > MAX_OPTWIDTH) + optwidth = MAX_OPTWIDTH; + snprintf(optbuf, sizeof(optbuf), "--%s%s%s", + name, + argname ? " " : "", + argname ? argname : ""); + printf(" %c%c%c%-*s ", + shortopt ? '-' : ' ', + shortopt ? shortopt : ' ', + shortopt ? '/' : ' ', + optwidth, optbuf); + if (strlen(optbuf) > optwidth) { + /* If the option overflowed the given width, skip to the next line */ + printf("\n%-*s", 5 + optwidth + 2, ""); + } + /* Break help text into lines at whitespace or \n */ + helpmax = MAX_LINELEN - 5 - optwidth - 2; + s = helptext; + s += strspn(s, " \t"); + do { + const char *t, *next; + t = s + helpmax; + if (t > s + strlen(s)) + t = s + strlen(s); + if (t > s + strcspn(s, "\n")) + t = s + strcspn(s, "\n"); + /* Don't try to break text with no whitespace */ + if (s + strcspn(s, " \t") < t) { + while (t > s+1 && *t && !isspace(*t)) + t--; + } + if (*t == '\n') { + /* Preserve whitespace after a newline */ + next = t + 1; + } else { + next = t + strspn(t, " \t\n"); + } + /* Only print indent if there's more text */ + printf("%.*s\n%-*s", + (int)(t-s), s, + *next ? optwidth+7+3 : 0, ""); + s = next; + /* Indent subsequent lines 3 spaces (the +3 above is also for this) */ + helpmax = 79 - 5 - optwidth - 2 - 3; + } while (*s); +} + +/*************************************************************************/ + +/* The actual option definitions are located in cmdline_def.h using macros; + * see that file for details. */ + +enum { + DUMMY = 0x100, +#define TC_OPTIONS_TO_ENUM +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_ENUM +}; + +static struct option tc_options[] = { +#define TC_OPTIONS_TO_STRUCT_OPTION +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_STRUCT_OPTION +}; + + +/** + * usage: Print a command-line help message. + * + * Parameters: + * None. + * Return value: + * None. + */ + +static void usage(void) +{ + int optwidth = 0; + +#define TC_OPTIONS_TO_OPTWIDTH optwidth +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_OPTWIDTH + + version(); + printf("\n"); + printf("Usage: transcode [options...]\n"); + printf("\n"); + printf("Options:\n"); +#define TC_OPTIONS_TO_HELP optwidth +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_HELP + printf("\n"); + printf("use tcmodinfo to discover module properties and configurable options.\n"); +} + + +/** + * parse_cmdline: Parse all options on the transcode command line, storing + * appropriate values in the global "vob" data structure. + * + * Parameters: + * argc: Command line argument count. + * argv: Command line argument vector. + * vob: Global data structure. + * Return value: + * Nonzero on success, zero on error. + */ + +int parse_cmdline(int argc, char **argv, vob_t *vob) +{ + const char *shortopts; + int option; + +#define TC_OPTIONS_TO_SHORTOPTS shortopts +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_SHORTOPTS + + while (-1 != (option = getopt_long_only(argc, argv, shortopts, + tc_options, NULL)) + ) { + switch (option) { +#define TC_OPTIONS_TO_CODE +#include "cmdline_def.h" +#undef TC_OPTIONS_TO_CODE + default: + short_usage: /* error-handling label */ + fprintf(stderr, "'transcode -h | more' shows a list of available" + " command line options.\n"); + return 0; + } + } + + if (optind == 1) + goto short_usage; + +#ifndef __APPLE__ + if (optind < argc) { + int n; + tc_warn("unused command line argument detected (%d/%d)", optind, argc); + for (n = optind; n < argc; n++) + tc_warn("argc[%d]=%s (unused)", n, argv[n]); + } +#endif + + return 1; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/cmdline.h b/debian/transcode/transcode-1.1.7/src/cmdline.h new file mode 100644 index 00000000..94a3bfb2 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/cmdline.h @@ -0,0 +1,47 @@ +/* + * cmdline.h -- header for transcode command line parser + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef _CMDLINE_H +#define _CMDLINE_H + +/*************************************************************************/ + +/* The parsing routine: */ +extern int parse_cmdline(int argc, char **argv, vob_t *vob); + +/* Global variables from transcode.c that should eventually go away. */ +extern int core_mode; +extern char *im_aud_mod, *im_vid_mod; +extern char *ex_aud_mod, *ex_vid_mod, *ex_mplex_mod; +extern char *plugins_string; +extern char *nav_seek_file, *socket_file, *chbase, //*dirbase, + base[TC_BUF_MIN]; +extern int psu_frame_threshold; +extern int no_vin_codec, no_ain_codec, no_v_out_codec, no_a_out_codec; +extern int frame_a, frame_b, splitavi_frames, psu_mode; +extern int preset_flag, auto_probe, seek_range; +extern int no_audio_adjust, no_split; +extern char *fc_ttime_string; +extern int sync_seconds; +extern pid_t writepid; + +/*************************************************************************/ + +#endif /* _CMDLINE_H */ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/cmdline_def.h b/debian/transcode/transcode-1.1.7/src/cmdline_def.h new file mode 100644 index 00000000..1f0d23ab --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/cmdline_def.h @@ -0,0 +1,1602 @@ +/* + * cmdline_def.h -- transcode command line option definitions + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +/* This file contains definitions of all command-line options supported by + * transcode. The options are defined using macros which evaluate to + * different things depending on which of the following symbols is defined + * (exactly one must be defined before including this file): + * + * TC_OPTIONS_TO_ENUM + * Output a constant name for each long option, for use in defining an + * enum for the "val" field in "struct option". + * + * TC_OPTIONS_TO_STRUCT_OPTION + * Output a "struct option" definition for each option, for use in + * initializing a long-option array. + * + * TC_OPTIONS_TO_SHORTOPTS + * Output code to create a string of the form "x" or "x:" for each + * option which has a short option equivalent, for use in creating the + * short-option list passed to getopt(). The resulting string pointer + * is assigned to the variable named by the TC_OPTIONS_TO_SHORTOPTS + * symbol. + * + * TC_OPTIONS_TO_CODE + * Output code to process each option, for use in a switch statement. + * + * TC_OPTIONS_TO_OPTWIDTH + * Output code to compute the maximum width of any long option name + * (including the leading "--"), storing the result in the variable + * named by the TC_OPTIONS_TO_OPTWIDTH symbol, which must have been + * initialized to zero. + * + * TC_OPTIONS_TO_HELP + * Output a printf() statement to print the help text for each option. + * Also output printf() statements for option group headers (defined + * with the TC_HEADER macro). The variable named by the + * TC_OPTIONS_TO_HELP symbol is used as the option name field width + * (presumed to have been computed with TC_OPTIONS_TO_OPTWIDTH). + * + * If none of the above are defined, this file simply declares the + * parse_cmdline() prototype. + * + * Note that we explicitly do not use a #ifdef/#endif pair to avoid + * multiple inclusion of this file, because it is included multiple times + * by transcode.c, each time with a different one of the above symbols + * defined. + */ + +/*************************************************************************/ + +/* Check what we've been included for, and define macros appropriately. + * The macros defined are: + * + * TC_OPTION(name, shortopt, argname, helptext, code) + * Defines an option "--name" (name should _not_ be quoted). The name + * must be a valid C identifier, except that it may begin with a digit. + * * "shortopt" should be the character constant for the option's + * option's short form (e.g. 'x' if the short form is "-x"), or + * zero if the option has no short form. + * * "argname" should be a string naming the option's argument (for + * display in help text) if the option takes an argument, zero + * (0, not the string "0") if it does not. + * * "helptext" should be the option's help text, as a string. + * * "code" should be the code to process the option (which will be in + * its own block, so local variables are allowed--however, a comma in + * variable declarations will be interpreted as a macro argument + * delimiter, so each variable must go in its own statement; see the + * -x code for an example). + * + * TC_HEADER(name) + * Defines an option group header, which will be printed as part of the + * help message. + * + * _TCO_INIT + * _TCO_FINI + * Local macros containing any appropriate header and trailer code for + * the requested output. Defined only when necessary. + */ + +#ifdef TC_OPTION +# error TC_OPTION is already defined! Check for symbol name conflicts. +#endif +#ifdef TC_HEADER +# error TC_HEADER is already defined! Check for symbol name conflicts. +#endif +#ifdef _TCO_INIT +# error _TCO_INIT is already defined! Check for symbol name conflicts. +#endif +#ifdef _TCO_FINI +# error _TCO_FINI is already defined! Check for symbol name conflicts. +#endif + +#ifdef TC_OPTIONS_TO_ENUM +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +/* Define the option value to either the character value of the short form + * or a unique enum constant, while not disturbing the enum sequence */ +# define TC_OPTION(name,shortopt,argname,helptext,code) \ + OPT_TMP1_##name, \ + OPT_##name = shortopt + (!shortopt * OPT_TMP1_##name),\ + OPT_TMP2_##name = OPT_TMP1_##name, +# define TC_HEADER(name) /* nothing */ +#endif + +#ifdef TC_OPTIONS_TO_STRUCT_OPTION +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +# define TC_OPTION(name,shortopt,argname,helptext,code) \ + { #name, (argname) ? required_argument : no_argument, NULL, OPT_##name }, +# define TC_HEADER(name) /* nothing */ +# define _TCO_FINI {0,0,0,0} +#endif + +#ifdef TC_OPTIONS_TO_SHORTOPTS +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +# define _TCO_INIT { \ + static char shortbuf[513]; \ + char *ptr = shortbuf; \ + *shortbuf = 0; +# define TC_OPTION(name,shortopt,argname,helptext,code) \ + if (shortopt && ptr-shortbuf < sizeof(shortbuf)-3) { \ + *ptr++ = shortopt; \ + if (argname) \ + *ptr++ = ':'; \ + } +# define TC_HEADER(name) /* nothing */ +# define _TCO_FINI \ + *ptr = 0; \ + TC_OPTIONS_TO_SHORTOPTS = shortbuf; \ +} +#endif + +#ifdef TC_OPTIONS_TO_CODE +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +# define TC_OPTION(name,shortopt,argname,helptext,code) \ + case OPT_##name: { code; break; } +# define TC_HEADER(name) /* nothing */ +#endif + +#ifdef TC_OPTIONS_TO_OPTWIDTH +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +# define TC_OPTION(name,shortopt,argname,helptext,code) { \ + int len = (sizeof(#name)-1)+2; \ + if (argname) \ + len += (sizeof(argname)-1)+1; \ + if (len > TC_OPTIONS_TO_OPTWIDTH) \ + TC_OPTIONS_TO_OPTWIDTH = len; \ +} +# define TC_HEADER(name) /* nothing */ +#endif + +#ifdef TC_OPTIONS_TO_HELP +# ifdef TC_OPTION +# error More than one TC_OPTIONS symbol defined! +# endif +# define TC_OPTION(name,shortopt,argname,helptext,code) \ + print_option_help(#name, shortopt, argname, helptext, TC_OPTIONS_TO_HELP); +# define TC_HEADER(name) \ + printf("\n ======== %s ========\n\n", name); +#endif + +#ifndef TC_OPTION +# define TC_OPTION(name,shortopt,argname,helptext,code) /* nothing */ +# define TC_HEADER(name) /* nothing */ +/* The parsing routine: */ +extern int parse_cmdline(int argc, char **argv, vob_t *vob); +#endif + + +/* Output header code, if any. */ +#ifdef _TCO_INIT +_TCO_INIT +#endif + +/*************************************************************************/ + +/* The actual option definitions. */ +TC_OPTION(help, 'h', 0, + "print this usage message and exit", + usage(); + return 0; +) +TC_OPTION(version, 'v', 0, + "print version and exit", + version(); + return 0; +) +TC_OPTION(verbose, 'q', "level", + "verbosity (0=quiet,1=info,2=debug) [1]", + verbose = strtol(optarg, &optarg, 10); + if (*optarg) { + tc_error("Invalid argument for -q/--verbose"); + goto short_usage; + } + if (verbose) // ensure TC_INFO is always set if not silent + verbose |= TC_INFO; + vob->verbose = verbose; +) + +/********/ TC_HEADER("Input, output, and control files") /********/ + +TC_OPTION(input, 'i', "file", +#ifdef HAVE_LIBDVDREAD + "input file/directory/device/mountpoint name", +#else + "input file/directory name", +#endif + vob->video_in_file = optarg; +) + +TC_OPTION(multi_input, 0, 0, + "enable EXPERIMENTAL multiple input mode (see manpage)", + core_mode = TC_MODE_DIRECTORY; +) +TC_OPTION(output, 'o', "file", + "output file name", + vob->video_out_file = optarg; +) +TC_OPTION(avi_limit, 0, "size", + "split output AVI file after \"size\" MB [2048]", + tc_avi_limit = strtol(optarg, &optarg, 10); + if (*optarg) { + tc_error("Invalid argument for --avi_limit"); + goto short_usage; + } + if (tc_avi_limit <= 0) + tc_avi_limit = (unsigned int)-1; +) +TC_OPTION(avi_comments, 0, "file", + "read AVI header comments from file [off]", + vob->avi_comment_fd = xio_open(optarg, O_RDONLY); + if (vob->avi_comment_fd == -1) { + tc_error("Cannot open comment file \"%s\"", optarg); + goto short_usage; + } +) +TC_OPTION(split, 't', "n,base", + "split output to base%03d.avi with n frames [off]", + /* DANGER WILL ROBINSON! scanf("%1000s") will read up to + * 1000 characters--EXCLUDING the trailing null, so a + * buffer of 1001 bytes is needed! */ + char buf[1001] = ""; + if (sscanf(optarg, "%d,%1000[^,]", &splitavi_frames, buf) != 2 + || splitavi_frames <= 0 + ) { + tc_error("Invalid argument for -t/--split"); + goto short_usage; + } + if (strlen(buf) > sizeof(base)-1) { + tc_error("Base name too long for -t/--split"); + goto short_usage; + } + memcpy(base, buf, strlen(buf)+1); + core_mode = TC_MODE_AVI_SPLIT; +) +TC_OPTION(audio_input, 'p', "file", + "read audio stream from separate file [off]", + vob->audio_in_file = optarg; +) +TC_OPTION(audio_output, 'm', "file", + "write audio stream to separate file [off]", + if (*optarg == '-') + goto short_usage; + vob->audio_out_file = optarg; + vob->audio_file_flag = 1; +) +TC_OPTION(nav_seek, 0, "file", + "use VOB navigation file [off]", + if (*optarg == '-') + goto short_usage; + vob->nav_seek_file = optarg; + nav_seek_file = optarg; +) +TC_OPTION(socket, 0, "file", + "socket file for run-time control [off]", + if (*optarg == '-') + goto short_usage; + socket_file = optarg; +) +TC_OPTION(write_pid, 0, "file", + "write pid of transcode process to \"file\" [off]", + FILE *f; + if (*optarg == '-') + goto short_usage; + f = fopen(optarg, "w"); + if (f) { + fprintf(f, "%d\n", writepid); + fclose(f); + } +) +TC_OPTION(config_dir, 0, "dir", + "assume config files are in this dir [off]", + if (*optarg == '-') + goto short_usage; + tc_set_config_dir(optarg); +) + +/********/ TC_HEADER("Input stream selection") /********/ + +TC_OPTION(extract_track, 'a', "a[,v]", + "extract audio[,video] track [0,0]", + if (sscanf(optarg, "%d,%d", &vob->a_track, &vob->v_track) < 1 + || vob->a_track < 0 + || vob->v_track < 0 + ) { + tc_error("Invalid argument for -a/--extract_track"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_TRACK; +) +TC_OPTION(frames, 'c', "f1-f2[,f3-f4...]", + "encode only given range (frames or HH:MM:SS)," + " f2,f4,... are *not* encoded [all]", + if (*optarg == '-') + goto short_usage; + fc_ttime_string = optarg; +) +TC_OPTION(frame_interval, 0, "N", + "select only every Nth frame to be exported [1]", + vob->frame_interval = strtol(optarg, &optarg, 0); + if (*optarg || vob->frame_interval < 1) { + tc_error("Invalid argument for --frame_interval"); + goto short_usage; + } +) +TC_OPTION(title, 'T', "t[,c[-d][,a]]", + "select DVD title[,chapters[,angle]] [1,all,1]", + if (sscanf(optarg, "%d,%d-%d,%d", &vob->dvd_title, + &vob->dvd_chapter1, &vob->dvd_chapter2, + &vob->dvd_angle) >= 3 + ) { + /* Chapter range given */ + } else if (sscanf(optarg, "%d,%d,%d", &vob->dvd_title, + &vob->dvd_chapter1, &vob->dvd_angle) >= 1 + ) { + /* Single (or no) chapter given */ + vob->dvd_chapter2 = -1; /* indicate single chapter */ + } else { + tc_error("Invalid argument for -T/--title"); + goto short_usage; + } + if (vob->dvd_title < 1) { + tc_error("Invalid title for -T/--title"); + goto short_usage; + } + if (vob->dvd_chapter1 != -1) { + if (vob->dvd_chapter1 < 1 + || (vob->dvd_chapter2 != -1 + && vob->dvd_chapter2 < vob->dvd_chapter1) + ) { + tc_error("Invalid chapter(s) for -T/--title"); + goto short_usage; + } + } + if (vob->dvd_angle < 1) { + tc_error("Invalid angle for -T/--title"); + goto short_usage; + } +) +TC_OPTION(psu, 'S', "unit[,s1-s2]", + "process program stream unit[,s1-s2] sequences [0,all]", + if (sscanf(optarg, "%d,%d-%d", &vob->ps_unit, + &vob->ps_seq1, &vob->ps_seq2) < 0 + || vob->ps_unit < 0 + || vob->ps_seq1 < 0 + || vob->ps_seq2 < 0 + || vob->ps_seq1 > vob->ps_seq2 + ) { + tc_error("Invalid argument for -S/--psu"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_SEEK; +) +TC_OPTION(vob_seek, 'L', "N", + "seek to VOB stream offset Nx2kB [0]", + vob->vob_offset = strtol(optarg, &optarg, 10); + if (*optarg || vob->vob_offset < 0) { + tc_error("Invalid argument for -L/--vob_seek"); + goto short_usage; + } +) +TC_OPTION(ts_pid, 0, "0xNN", + "transport video stream pid [0]", + vob->ts_pid1 = strtol(optarg, &optarg, 16); + if (*optarg) { + tc_error("Invalid argument for --ts_pid"); + goto short_usage; + } + vob->ts_pid2 = vob->ts_pid1; +) + +/********/ TC_HEADER("Input stream format options") /********/ + +TC_OPTION(probe, 'H', "n", + "auto-probe n MB of source (0=off) [1]", + seek_range = strtol(optarg, &optarg, 10); + if (*optarg || seek_range < 0) { + tc_error("Invalid argument for -H/--probe"); + goto short_usage; + } + if (seek_range == 0) + auto_probe = 0; +) +TC_OPTION(mplayer_probe, 0, 0, + "use (external) mplayer to probe source [off]", + preset_flag |= TC_PROBE_NO_BUILTIN; +) +TC_OPTION(import_with, 'x', "vmod[,amod]", + "video[,audio] import modules [null]", + /* Careful here! "static char vbuf[1001], abuf[1001]" will + * be treated as two separate macro arguments by the + * preprocessor, so we have to declare each variable in a + * separate statement. */ + static char vbuf[1001]; + static char abuf[1001]; + char quote; + char *s; + int n; + /* Scan the string ourselves, rather than using scanf(), + * so we can handle internal quotes properly for -x mplayer */ + if (!*optarg) { + tc_error("Invalid argument for -x/--import_with"); + goto short_usage; + } + *vbuf = *abuf = 0; + quote = 0; + n = 0; + s = vbuf; + while (*optarg) { + if (*optarg == quote) { + quote = 0; + } else if (!quote && (*optarg == '"' || *optarg == '\'')) { + quote = *optarg; + } else if (!quote && *optarg == ',') { + if (s == vbuf) { + s = abuf; + n = 0; + } else { + tc_error("Invalid argument for -x/--import_with"); + goto short_usage; + } + } else { + if (n < (s==vbuf ? sizeof(vbuf) : sizeof(abuf))-1) { + s[n++] = *optarg; + s[n] = 0; + } + } + optarg++; + } + if (quote) { + tc_error("Invalid argument for -x/--import_with" + " (unbalanced quotes)"); + } + s[n] = 0; + n = (s==vbuf ? 1 : 2); + im_vid_mod = vbuf; + // FIXME: vin -> v_in to match no_v_out_codec (same w/audio) + no_vin_codec = 0; + if ((s = strchr(im_vid_mod, '=')) != NULL) { + *s++ = 0; + if (!*s) { + tc_error("Invalid option string for video import" + " module"); + goto short_usage; + } + vob->im_v_string = s; + } + if (n >= 2) { + im_aud_mod = abuf; + no_ain_codec = 0; + if ((s = strchr(im_aud_mod, '=')) != NULL) { + *s++ = 0; + if (!*s) { + tc_error("Invalid option string for audio import" + " module"); + goto short_usage; + } + vob->im_a_string = s; + } + } else { + im_aud_mod = im_vid_mod; + } + /* "auto" checks have to come here, to catch "auto=..." */ + if (strcmp(im_vid_mod, "auto") == 0) { + im_vid_mod = NULL; + no_vin_codec = 1; + } + if (strcmp(im_aud_mod, "auto") == 0) { + im_aud_mod = NULL; + no_ain_codec = 1; + } +) +TC_OPTION(frame_size, 'g', "WxH", + "video frame size [720x576]", + if (sscanf(optarg, "%dx%d", &vob->im_v_width, + &vob->im_v_height) != 2 + || vob->im_v_width <= 0 + || vob->im_v_height <= 0 + ) { + tc_error("Invalid argument for -g/--frame_size"); + goto short_usage; + } + if (vob->im_v_width > TC_MAX_V_FRAME_WIDTH + || vob->im_v_height > TC_MAX_V_FRAME_HEIGHT + ) { + tc_error("Video frame size out of range (max %dx%d)", + TC_MAX_V_FRAME_WIDTH, TC_MAX_V_FRAME_HEIGHT); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_FRAMESIZE; +) +TC_OPTION(import_asr, 0, "C", + "set import display aspect ratio code C [auto]", + vob->im_asr = strtol(optarg, &optarg, 10); + if (*optarg || vob->im_asr < 0) { + tc_error("Invalid argument for --import_asr"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_IMASR; +) +TC_OPTION(import_fps, 'f', "rate[,frc]", + "input video frame rate[,frc] [25.000,0]", + int n = sscanf(optarg, "%lf,%d", &vob->fps, &vob->im_frc); + if (n == 2) { + if (vob->im_frc < 0 || vob->im_frc > 15) { + tc_error("invalid frame rate code for option -f"); + goto short_usage; + } + tc_frc_code_to_value(vob->im_frc, &vob->fps); + } else { + if (n < 1 || vob->fps < MIN_FPS) { + tc_error("invalid frame rate for option -f"); + goto short_usage; + } + } + preset_flag |= TC_PROBE_NO_FPS; +) +TC_OPTION(hard_fps, 0, 0, + "disable smooth dropping (for variable fps clips) [enabled]", + vob->hard_fps_flag = TC_TRUE; +) +TC_OPTION(import_afmt, 'e', "r[,b[,c]]", + "import audio sample format [48000,16,2]", + int n = sscanf(optarg, "%d,%d,%d", &vob->a_rate, + &vob->a_bits, &vob->a_chan); + switch (n) { + case 3: + if (vob->a_chan != 0 + && vob->a_chan != 1 + && vob->a_chan != 2 + && vob->a_chan != 6 + ) { + tc_error("Invalid channels argument for" + " -e/--import_afmt"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_CHAN; + /* fall through */ + case 2: + if (vob->a_bits != 8 && vob->a_bits != 16) { + tc_error("Invalid bits argument for" + " -e/--import_afmt"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_BITS; + /* fall through */ + case 1: + if (vob->a_rate <= 0 || vob->a_rate > RATE) { + tc_error("Invalid rate argument for" + " -e/--import_afmt"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_RATE; + break; + default: + tc_error("Invalid argument for -e/--import_afmt"); + break; + } +) +TC_OPTION(import_codec, 'n', "0xNN", + "import audio codec ID [0x2000]", + vob->a_codec_flag = strtol(optarg, &optarg, 16); + if (*optarg) { + tc_error("Invalid argument for -n/--import_format"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_ACODEC; +) +TC_OPTION(no_audio_adjust, 0, 0, + "disable audio frame size adjustment [enabled]", + no_audio_adjust = TC_TRUE; +) + +/********/ TC_HEADER("Output stream format options") /********/ + +TC_OPTION(export_prof, 0, "profile", + "export profile: {vcd|svcd|xvcd|dvd}[-pal|-ntsc|-secam]" + " [none]", + if (strncasecmp(optarg, "vcd", 3) == 0) { + vob->mpeg_profile = VCD; // need to guess NTSC/PAL later + if (strncasecmp(optarg, "vcd-pal", 7) == 0) { + vob->mpeg_profile = VCD_PAL; + } else if (strncasecmp(optarg, "vcd-ntsc", 8) == 0) { + vob->mpeg_profile = VCD_NTSC; + } else if (strncasecmp(optarg, "vcd-secam", 9) == 0) { + vob->mpeg_profile = VCD_PAL; + } + } else if (strncasecmp(optarg, "svcd", 4) == 0) { + vob->mpeg_profile = SVCD; + if (strncasecmp(optarg, "svcd-pal", 8) == 0) { + vob->mpeg_profile = SVCD_PAL; + } else if (strncasecmp(optarg, "svcd-ntsc", 9) == 0) { + vob->mpeg_profile = SVCD_NTSC; + } else if (strncasecmp(optarg, "svcd-secam", 10) == 0) { + vob->mpeg_profile = SVCD_PAL; + } + } else if (strncasecmp(optarg, "dvd", 3) == 0) { + vob->mpeg_profile = DVD; + if (strncasecmp(optarg, "dvd-pal", 7) == 0) { + vob->mpeg_profile = DVD_PAL; + } else if (strncasecmp(optarg, "dvd-ntsc", 8) == 0) { + vob->mpeg_profile = DVD_NTSC; + } else if (strncasecmp(optarg, "dvd-secam", 9) == 0) { + vob->mpeg_profile = DVD_PAL; + } + } else if (strncasecmp(optarg, "xvcd", 4) == 0) { + vob->mpeg_profile = XVCD; + if (strncasecmp(optarg, "xvcd-pal", 8) == 0) { + vob->mpeg_profile = XVCD_PAL; + } else if (strncasecmp(optarg, "xvcd-ntsc", 9) == 0) { + vob->mpeg_profile = XVCD_NTSC; + } else if (strncasecmp(optarg, "xvcd-secam", 10) == 0) { + vob->mpeg_profile = XVCD_PAL; + } + } else { + tc_error("Invalid argument for --export_prof"); + } +) +TC_OPTION(export_with, 'y', "vm[,am[,mm]]", + "video[,audio[,mplex]] export modules [null]", + static char vbuf[1001]; + static char abuf[1001]; + static char mbuf[1001]; + char *s; + int n; + /* FIXME: handle embedded quotes like -x? */ + if ((n = sscanf(optarg, "%1000[^,],%1000[^,],%1000[^,]", + vbuf, abuf, mbuf)) < 1 + ) { + tc_error("Invalid argument for -y/--export_with"); + goto short_usage; + } + ex_vid_mod = vbuf; + no_v_out_codec = 0; + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VMODULE; + if ((s = strchr(ex_vid_mod, '=')) != NULL) { + *s++ = 0; + if (!*s) { + tc_error("Invalid option string for video export" + " module"); + goto short_usage; + } + vob->ex_v_string = s; + } + if (n >= 2) { + ex_aud_mod = abuf; + no_a_out_codec = 0; + if ((s = strchr(ex_aud_mod, '=')) != NULL) { + *s++ = 0; + if (!*s) { + tc_error("Invalid option string for audio export" + " module"); + goto short_usage; + } + vob->ex_a_string = s; + } + } else { + ex_aud_mod = ex_vid_mod; + } + if (n >= 3) { + ex_mplex_mod = mbuf; + if ((s = strchr(ex_mplex_mod, '=')) != NULL) { + *s++ = 0; + if (!*s) { + tc_error("Invalid option string for multiplex" + " module"); + goto short_usage; + } + vob->ex_m_string = s; + } + } +) +TC_OPTION(export_param, 'F', "string", + "encoder parameter strings [module dependent]", + char *s; + if ((s = strchr(optarg, ',')) != NULL) { + char *s2; + *s = 0; + vob->ex_a_fcc = s+1; + if ((s2 = strchr(vob->ex_a_fcc,',')) != NULL) { + *s2 = 0; + vob->ex_profile_name = s2+1; + } + } + vob->ex_v_fcc = optarg; + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VCODEC; +) +TC_OPTION(export_codec, 'N', "format", + "export audio codec [mp3]", + if (*optarg == '-') + goto short_usage; + if (optarg[0] == '0' && optarg[1] == 'x') { + /* old behaviour */ + vob->ex_a_codec = strtol(optarg, &optarg, 16); + if (*optarg) { + tc_error("Invalid argument for -N/--export_format"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACODEC; + } else { + char acodec[33]; + char vcodec[33]; + int n; + /* new behaviour */ + n = sscanf(optarg, "%32[^,],%32[^,]", vcodec, acodec); + /* codecs in reversed order for backward compatibility */ + switch (n) { + case 2: /* audio AND video codec */ + vob->ex_v_codec = tc_codec_from_string(vcodec); + if (vob->ex_v_codec == TC_CODEC_ERROR) { + tc_error("Unknown video format for" + " -N/--export_format"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VCODEC; + /* move acodec to vcodec */ + memcpy(vcodec, acodec, sizeof(vcodec)); + /* fallthrough */ + case 1: /* audio codec */ + vob->ex_a_codec = tc_codec_from_string(acodec); + if (vob->ex_a_codec == TC_CODEC_ERROR) { + tc_error("Unknown audio format for" + " -N/--export_format"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACODEC; + break; + default: + tc_error("Invalid argument for -N/--export_format"); + goto short_usage; + } + } +) +TC_OPTION(multipass, 'R', "N[,vf[,af]]", + "enable multi-pass encoding (0-3) [0,divx4.log,pcm.log]", + static char vlogfile[1001] = "divx4.log"; + static char alogfile[1001] = "pcm.log"; + if (sscanf(optarg, "%d,%1000[^,],%1000[^,]", + &vob->divxmultipass, vlogfile, alogfile) < 1 + || vob->divxmultipass < 0 || vob->divxmultipass > 3 + || !*vlogfile + || !*alogfile + ) { + tc_error("Invalid argument for -R/--multipass"); + goto short_usage; + } + vob->divxlogfile = vlogfile; + vob->audiologfile = alogfile; +) +TC_OPTION(vbitrate, 'w', "r[,k[,c]]", + "encoder bitrate[,keyframes[,crispness]] [1800,250,100]", + float ratefact = 1.0f; + int n = sscanf(optarg, "%f,%d,%d", &ratefact, + &vob->divxkeyframes, &vob->divxcrispness); + switch (n) { + case 3: + if (vob->divxcrispness < 0 || vob->divxcrispness > 100) { + tc_error("Invalid crispness argument for" + " -w/--vbitrate"); + goto short_usage; + } + case 2: + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_GOP; + case 1: + if (vob->divxbitrate <= 0) { + tc_error("Invalid bitrate argument for" + " -w/--vbitrate"); + goto short_usage; + } + vob->divxbitrate = (int)ratefact; + vob->m2v_requant = ratefact; + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VBITRATE; + break; + default: + tc_error("Invalid argument for -w/--vbitrate"); + goto short_usage; + } +) +TC_OPTION(video_max_bitrate, 0, "r", + "maximum bitrate when encoding variable bitrate MPEG-2" + " streams [same as -w]", + vob->video_max_bitrate = strtol(optarg, &optarg, 10); + if (*optarg || vob->video_max_bitrate < 0) { + tc_error("Invalid argument for --video_max_bitrate"); + goto short_usage; + } +) +TC_OPTION(export_fps, 0, "f[,c]", + "output video frame rate[,code] [as input]", + int n = sscanf(optarg, "%lf,%d", &vob->ex_fps, &vob->ex_frc); + if (n < 1 || n > 2) { + tc_error("Invalid argument for --export_fps"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FPS; + if (n == 2) { + if (vob->ex_frc < 0 || vob->ex_frc > 15) { + tc_error("Invalid frc value for --export_fps"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FRC; + tc_frc_code_to_value(vob->ex_frc, &vob->ex_fps); + } else { + if (vob->ex_fps < MIN_FPS) { + tc_error("Invalid fps value for --export_fps"); + goto short_usage; + } + vob->ex_frc = 0; + } +) +TC_OPTION(export_frc, 0, "C", + "set export frame rate code C independently of actual" + " frame rate [derived from export FPS]", + vob->ex_frc = strtol(optarg, &optarg, 10); + if (*optarg || vob->ex_frc < 0 || vob->ex_frc > 15) { + tc_error("Invalid frc value for --export_frc"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FRC; +) +TC_OPTION(export_asr, 0, "C", + "set export display aspect ratio code C [as input]", + vob->ex_asr = strtol(optarg, &optarg, 10); + if (*optarg || vob->ex_asr < 0 || vob->ex_asr > 4) { + tc_error("Invalid argument for --export_asr"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ASR; +) +TC_OPTION(export_par, 0, "{C | N,D}", + "set export pixel aspect ratio [auto]", + int n = sscanf(optarg, "%d,%d", &vob->ex_par_width, + &vob->ex_par_height); + if (n == 1) { + /* Only one argument: PAR code */ + vob->ex_par = vob->ex_par_width; + if (vob->ex_par < 0 || vob->ex_par > 5) { + tc_error("--export_par must be between 0 and 5"); + goto short_usage; + } + tc_par_code_to_ratio(vob->ex_par, + &vob->ex_par_width, + &vob->ex_par_height); + } else if (n == 2) { + /* Two arguments: use nonstandard PAR */ + vob->ex_par = 0; + if (vob->ex_par_width <= 0 || vob->ex_par_height <= 0) { + tc_error("bad PAR values for --export_par:" + " %d/%d not [>0]/[>0]", + vob->ex_par_width, vob->ex_par_height); + goto short_usage; + } + /* correct common misbehaviour */ + if (vob->ex_par_width == 1 && vob->ex_par_height == 1) { + vob->ex_par = 1; + tc_info("given PAR values of 1/1, reset PAR code" + " to 1"); + } + } else { + /* Bad number of arguments (<1 || >2) */ + tc_error("Invalid argument for --export_par"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_PAR; +) +TC_OPTION(encode_fields, 0, "C", + "enable field-based encoding if supported [off]\n" + "C can be t (top-first), b (bottom-first),\n" + " p (progressive), u (unknown)", + switch (*optarg) { + case 't': + vob->encode_fields = TC_ENCODE_FIELDS_TOP_FIRST; break; + case 'b': + vob->encode_fields = TC_ENCODE_FIELDS_BOTTOM_FIRST; break; + case 'p': + vob->encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE; break; + case 'u': + vob->encode_fields = TC_ENCODE_FIELDS_UNKNOWN; break; + default: + tc_error("Invalid argument for --encode_fields"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FIELDS; +) +TC_OPTION(pulldown, 0, 0, + "set MPEG 3:2 pulldown flags on export [off]", + vob->pulldown = TC_TRUE; +) +TC_OPTION(abitrate, 'b', "r[,v[,q[,m]]]", + "audio encoder bitrate kBits/s[,vbr[,quality[,mode]]]" + " [128,0,5,0]", + if (sscanf(optarg, "%d,%d,%f,%d", &vob->mp3bitrate, + &vob->a_vbr, &vob->mp3quality, &vob->mp3mode) < 1 + || vob->mp3bitrate < 0 + || vob->a_vbr < 0 + || vob->mp3quality < -1.00001 + || vob->mp3mode < 0 + ) { + tc_error("Invalid argument for -b/--abitrate"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ABITRATE; +) +TC_OPTION(export_afmt, 'E', "r[,b[,c]]", + "audio output samplerate, bits, channels [as input]", + int n = sscanf(optarg, "%d,%d,%d", &vob->mp3frequency, + &vob->dm_bits, &vob->dm_chan); + switch (n) { + case 3: + if (vob->dm_chan < 0 || vob->dm_chan > 6) { + tc_error("Invalid channels argument for" + " -E/--export_afmt"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACHANS; + /* fall through */ + case 2: + if (vob->dm_bits != 0 + && vob->dm_bits != 8 + && vob->dm_bits != 16 + && vob->dm_bits != 24 + ) { + tc_error("Invalid bits argument for -E/--export_afmt"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ABITS; + /* fall through */ + case 1: + if (vob->mp3frequency < 0) { + tc_error("Invalid rate argument for -E/--export_afmt"); + goto short_usage; + } + vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ARATE; + break; + default: + tc_error("Invalid argument for -E/--export_afmt"); + break; + } +) + +/********/ TC_HEADER("Video processing options") /********/ + +TC_OPTION(pre_clip, 0, "t[,l[,b[,r]]]", + "select initial frame region by clipping [off]", + int n = sscanf(optarg, "%d,%d,%d,%d", + &vob->pre_im_clip_top, + &vob->pre_im_clip_left, + &vob->pre_im_clip_bottom, + &vob->pre_im_clip_right); + if (n < 1) { + tc_error("Invalid argument for --pre_clip"); + goto short_usage; + } + pre_im_clip = TC_TRUE; + // Symmetrical clipping for only 1-3 arguments + if (n < 2) + vob->pre_im_clip_left = 0; + if (n < 3) + vob->pre_im_clip_bottom = vob->pre_im_clip_top; + if (n < 4) + vob->pre_im_clip_right = vob->pre_im_clip_left; +) +TC_OPTION(im_clip, 'j', "t[,l[,b[,r]]]", + "clip or add frame border before filters [off]", + int n = sscanf(optarg, "%d,%d,%d,%d", + &vob->im_clip_top, + &vob->im_clip_left, + &vob->im_clip_bottom, + &vob->im_clip_right); + if (n < 1) { + tc_error("Invalid argument for -j/--im_clip"); + goto short_usage; + } + im_clip = TC_TRUE; + // Symmetrical clipping for only 1-3 arguments + if (n < 2) + vob->im_clip_left = 0; + if (n < 3) + vob->im_clip_bottom = vob->im_clip_top; + if (n < 4) + vob->im_clip_right = vob->im_clip_left; +) +TC_OPTION(deinterlace, 'I', "mode", + "deinterlace video using given mode (1-5) [off]", + vob->deinterlace = strtol(optarg, &optarg, 10); + if (*optarg || vob->deinterlace < 1 || vob->deinterlace > 5) { + tc_error("Invalid argument for -I/--deinterlace"); + goto short_usage; + } +) +TC_OPTION(expand, 'X', "n[,m[,M]]", + "expand to height+n*M rows, width+m*M columns [0,0,32]", + vob->hori_resize2 = 0; + if (sscanf(optarg, "%d,%d,%d", &vob->vert_resize2, + &vob->hori_resize2, &vob->resize2_mult) < 1 + ) { + tc_error("Invalid argument for -X/--expand"); + goto short_usage; + } + if (vob->resize2_mult != 8 + && vob->resize2_mult != 16 + && vob->resize2_mult != 32 + ) { + tc_error("Invalid multiplier for -X/--expand (must be" + " 8, 16, or 32)"); + goto short_usage; + } + resize2 = TC_TRUE; +) +TC_OPTION(shrink, 'B', "n[,m[,M]]", + "shrink to height-n*M rows, width-m*M columns [0,0,32]", + vob->hori_resize1 = 0; + if (sscanf(optarg, "%d,%d,%d", &vob->vert_resize1, + &vob->hori_resize1, &vob->resize1_mult) < 1 + ) { + tc_error("Invalid argument for -X/--expand"); + goto short_usage; + } + if (vob->resize1_mult != 8 + && vob->resize1_mult != 16 + && vob->resize1_mult != 32 + ) { + tc_error("Invalid multiplier for -B/--shrink (must be" + " 8, 16, or 32)"); + goto short_usage; + } + resize1 = TC_TRUE; +) +TC_OPTION(zoom, 'Z', "[W]x[H][,mode]", + "resize to W columns, H rows w/filtering [off]", + char *s = optarg; + if (isdigit(*s)) { + vob->zoom_width = strtol(s, &s, 10); + if (vob->zoom_width > TC_MAX_V_FRAME_WIDTH) { + tc_error("Invalid width for -Z/--zoom (maximum %d)", + TC_MAX_V_FRAME_WIDTH); + goto short_usage; + } + } else { + vob->zoom_width = 0; + } + if (*s++ != 'x') { + tc_error("Invalid argument for -Z/--zoom"); + goto short_usage; + } + if (isdigit(*s)) { + vob->zoom_height = strtol(s, &s, 10); + if (vob->zoom_height > TC_MAX_V_FRAME_HEIGHT) { + tc_error("Invalid height for -Z/--zoom (maximum %d)", + TC_MAX_V_FRAME_HEIGHT); + goto short_usage; + } + } else { + vob->zoom_height = 0; + } + zoom = TC_TRUE; + if (*s == ',') { + s++; + if (strncmp(s, "fast", strlen(s)) == 0) + fast_resize = TC_TRUE; + else if (strncmp(s, "interlaced", strlen(s)) == 0) + vob->zoom_interlaced = TC_TRUE; + } +) +TC_OPTION(zoom_filter, 0, "filter", + "use given filter for -Z resizing [Lanczos3]", + vob->zoom_filter = tcv_zoom_filter_from_string(optarg); + if (vob->zoom_filter == TCV_ZOOM_NULL) { + tc_error("invalid argument for --zoom_filter\n" + "filter must be one of:\n" + " bell box b_spline hermite lanczos3" + " mitchell triangle cubic_keys4 sinc8"); + goto short_usage; + } +) +TC_OPTION(ex_clip, 'Y', "t[,l[,b[,r]]]", + "clip or add frame border after filters [off]", + int n = sscanf(optarg, "%d,%d,%d,%d", + &vob->ex_clip_top, + &vob->ex_clip_left, + &vob->ex_clip_bottom, + &vob->ex_clip_right); + if (n < 1) { + tc_error("Invalid argument for -Y/--ex_clip"); + goto short_usage; + } + ex_clip = TC_TRUE; + // Symmetrical clipping for only 1-3 arguments + if (n < 2) + vob->ex_clip_left = 0; + if (n < 3) + vob->ex_clip_bottom = vob->ex_clip_top; + if (n < 4) + vob->ex_clip_right = vob->ex_clip_left; +) +TC_OPTION(reduce, 'r', "n[,m]", + "reduce video height/width by n[,m] [off]", + int n = sscanf(optarg, "%d,%d", &vob->reduce_h,&vob->reduce_w); + if (n == 1) + vob->reduce_w = vob->reduce_h; + if (n < 1 || vob->reduce_h <= 0 || vob->reduce_w <= 0) { + tc_error("Invalid argument for -r/--reduce"); + goto short_usage; + } + rescale = TC_TRUE; +) +TC_OPTION(flip, 'z', 0, + "flip video frame upside down [off]", + flip = TC_TRUE; +) +TC_OPTION(mirror, 'l', 0, + "mirror video frame [off]", + mirror = TC_TRUE; +) +TC_OPTION(swap_colors, 'k', 0, + "swap red/blue (Cb/Cr) in video frame [off]", + rgbswap = TC_TRUE; +) +TC_OPTION(grayscale, 'K', 0, + "enable grayscale mode [off]", + decolor = TC_TRUE; + vob->decolor = TC_TRUE; +) +TC_OPTION(gamma, 'G', "val", + "gamma correction (0.0-10.0) [off]", + vob->gamma = strtod(optarg, &optarg); + if (*optarg || vob->gamma < 0) { + tc_error("Invalid argument for -G/--gamma"); + goto short_usage; + } + dgamma = TC_TRUE; +) +TC_OPTION(antialias, 'C', "mode", + "enable anti-aliasing mode (1-3) [off]", + vob->antialias = strtol(optarg, &optarg, 10); + if (*optarg || vob->antialias < 1 || vob->antialias > 3) { + tc_error("Invalid argument for -C/--antialias"); + goto short_usage; + } +) +TC_OPTION(antialias_para, 0, "w,b", + "center pixel weight, xy-bias [0.333,0.500]", + if (sscanf(optarg, "%lf,%lf", + &vob->aa_weight, &vob->aa_bias) != 2) { + tc_error("Invalid argument for --antialias_para"); + goto short_usage; + } + if (vob->aa_weight < 0.0 || vob->aa_weight > 1.0) { + tc_error("Invalid weight for --antlalias_para" + " (0.0 <= w <= 1.0)"); + goto short_usage; + } + if (vob->aa_bias < 0.0 || vob->aa_bias > 1.0) { + tc_error("Invalid bias for --antlalias_para" + " (0.0 <= b <= 1.0)"); + goto short_usage; + } +) +TC_OPTION(post_clip, 0, "t[,l[,b[,r]]]", + "select final frame region by clipping [off]", + int n = sscanf(optarg, "%d,%d,%d,%d", + &vob->post_ex_clip_top, + &vob->post_ex_clip_left, + &vob->post_ex_clip_bottom, + &vob->post_ex_clip_right); + if (n < 1) { + tc_error("Invalid argument for --post_clip"); + goto short_usage; + } + post_ex_clip = TC_TRUE; + // Symmetrical clipping for only 1-3 arguments + if (n < 2) + vob->post_ex_clip_left = 0; + if (n < 3) + vob->post_ex_clip_bottom = vob->post_ex_clip_top; + if (n < 4) + vob->post_ex_clip_right = vob->post_ex_clip_left; +) +TC_OPTION(video_format, 'V', "fmt", + "select internal video format [yuv420p]\n" + "one of: yuv420p, yuv422p, rgb24", + if (strcmp(optarg, "yuv420p") == 0) { + tc_info("yuv420p is already the default for -V"); + /* anyway... */ + vob->im_v_codec = CODEC_YUV; + } else if (strcmp(optarg, "yuv422p") == 0) { + vob->im_v_codec = CODEC_YUV422; + } else if (strcmp(optarg, "rgb24") == 0) { + vob->im_v_codec = CODEC_RGB; + } else { + tc_error("bad argument for -V/--video_format, should" + " be one of: yuv420p (default), yuv422p, rgb24"); + goto short_usage; + } +) +TC_OPTION(keep_asr, 0, 0, + "try to keep aspect ratio (broken)", + tc_error("--keep_asr is unavailable, calculate frame" + " parameters manually"); +) + +/********/ TC_HEADER("Audio processing options") /********/ + +TC_OPTION(audio_swap, 'd', 0, + "swap bytes in audio stream [off]", + pcmswap = TC_TRUE; +) +TC_OPTION(audio_scale, 's', "g[,c,f,r]", + "scale volume by gain[,center,front,rear] [1,1,1,1]", + vob->ac3_gain[0] = 1.0; + vob->ac3_gain[1] = 1.0; + vob->ac3_gain[2] = 1.0; + if (sscanf(optarg, "%lf,%lf,%lf,%lf", &vob->volume, + &vob->ac3_gain[0], &vob->ac3_gain[1], + &vob->ac3_gain[2]) < 1 + || vob->volume < 0 + ) { + tc_error("Invalid argument for -s/--audio_scale"); + goto short_usage; + } +) +TC_OPTION(audio_use_ac3, 'A', 0, + "use AC3 as internal audio codec [off]", + vob->im_a_codec = CODEC_AC3; +) + +/********/ TC_HEADER("Other processing options") /********/ + +TC_OPTION(filter, 'J', "f1[,f2...]", + "apply external audio/video filters [none]", + static int size_plugstr = 0; + int newlen; + if (*optarg == '-') + goto short_usage; + newlen = size_plugstr + strlen(optarg) + 1; // \0 + if (size_plugstr) // it's an append... + newlen++; // ... so add the and ',' separator + plugins_string = tc_realloc(plugins_string, newlen); + if (!plugins_string) + return 0; + snprintf(plugins_string + size_plugstr, + newlen - size_plugstr, + "%s%s", size_plugstr ? "," : "", optarg); + size_plugstr = newlen - 1; + // cut the \0 for the next append (if any) +) +TC_OPTION(quality, 'Q', "enc[,dec]", + "encoding[,decoding] quality (0=fastest-5=best) [5,5]", + if (sscanf(optarg,"%d,%d",&vob->divxquality,&vob->quality) < 1 + || vob->divxquality < 0 + || vob->quality < 0 + ) { + tc_error("Invalid argument for -Q/--quality"); + goto short_usage; + } +) +TC_OPTION(passthrough, 'P', "flag", + "pass-through flag (0=off|1=V|2=A|3=A+V) [0]", + vob->pass_flag = strtol(optarg, &optarg, 10); + if (*optarg || vob->pass_flag < 0 || vob->pass_flag > 3) { + tc_error("Invalid argument for -P/--passthrough"); + goto short_usage; + } +) +TC_OPTION(sync_frame, 'D', "N", + "sync video start with audio frame num [0]", + vob->sync = strtol(optarg, &optarg, 10); + if (*optarg) { + tc_error("Invalid argument for -D/--sync_frame"); + goto short_usage; + } + sync_seconds = vob->sync; + preset_flag |= TC_PROBE_NO_AVSHIFT; +) +TC_OPTION(av_fine_ms, 0, "time", + "AV fine-tuning shift in millisecs [autodetect]", + vob->sync_ms = strtol(optarg, &optarg, 10); + if (*optarg) { + tc_error("Invalid argument for --av_sync_ms"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_AV_FINE; +) +TC_OPTION(demuxer_sync, 'M', "N", + "demuxer PES AV sync mode\n" + "(0=off|1=PTS only|2=full) [1]", + vob->demuxer = strtol(optarg, &optarg, 10); + if (*optarg || vob->demuxer < 0 || vob->demuxer > 4) { + tc_error("Invalid argument for -M/--demuxer_sync"); + goto short_usage; + } + preset_flag |= TC_PROBE_NO_DEMUX; +) + +/********/ TC_HEADER("Codec-specific options") /********/ + +TC_OPTION(dv_yv12_mode, 0, 0, + "(libdv) force YV12 mode for PAL\n" + "Use this option if transcode autodetection fails," + " with DV video.", + vob->dv_yuy2_mode = TC_FALSE; +) +TC_OPTION(dv_yuy2_mode, 0, 0, + "(libdv) use YUY2 mode for PAL [YV12]\n" + "If you experience crashes decoding DV video," + " try this option.", + vob->dv_yuy2_mode = TC_TRUE; +) +TC_OPTION(quantizers, 0, "min,max", + "min/max quantizer, for MPEG-like codecs [2,31]", + if (sscanf(optarg, "%d,%d", &vob->min_quantizer, + &vob->max_quantizer) != 2 + || vob->min_quantizer < 1 || vob->min_quantizer > 31 + || vob->max_quantizer < 1 || vob->max_quantizer > 31 + ) { + tc_error("Invalid argument for --quantizers"); + goto short_usage; + } +) +TC_OPTION(divx_quant, 0, "min,max", + "(DivX) min/max quantizer (deprecated) [2,31]", + tc_warn("--divx_quant option is deprecated and will be removed soon."); + tc_warn("use --quantizers instead."); + if (sscanf(optarg, "%d,%d", &vob->min_quantizer, + &vob->max_quantizer) != 2 + || vob->min_quantizer < 1 || vob->min_quantizer > 31 + || vob->max_quantizer < 1 || vob->max_quantizer > 31 + ) { + tc_error("Invalid argument for --divx_quant"); + goto short_usage; + } +) +TC_OPTION(divx_rc, 0, "p,rp,rr", + "(DivX) rate control parameter [2000,10,20]", + if (sscanf(optarg, "%d,%d,%d", &vob->rc_period, + &vob->rc_reaction_period, + &vob->rc_reaction_ratio) != 3 + ) { + tc_error("Invalid argument for --divx_rc"); + goto short_usage; + } +) +TC_OPTION(divx_vbv_prof, 0, "N", + "(DivX) VBV profile (0=free-5=hiqhq) [3]", + vob->divx5_vbv_prof = strtol(optarg, &optarg, 10); + if (*optarg + || vob->divx5_vbv_prof < 0 + || vob->divx5_vbv_prof > 5 + ) { + tc_error("Invalid argument for --divx_vbv_prof"); + goto short_usage; + } +) +TC_OPTION(divx_vbv, 0, "br,sz,oc", + "(DivX) VBV params (bitrate,size,occupancy) [10000,192,36864]", + if (sscanf(optarg, "%d,%d,%d", &vob->divx5_vbv_bitrate, + &vob->divx5_vbv_size, + &vob->divx5_vbv_occupancy) != 3 + ) { + tc_error("Invalid argument for --divx_vbv"); + goto short_usage; + } +) +TC_OPTION(lame_preset, 0, "N[,fast]", + "(LAME) use preset named N [off]", + if (*optarg == '-') + goto short_usage; + vob->lame_preset = optarg; +) +TC_OPTION(no_bitreservoir, 0, 0, + "(LAME) disable bitreservoir [off]", + vob->bitreservoir = TC_FALSE; +) +TC_OPTION(encoder_noflush, 'O', 0, + "avoid to flush buffer(s) on encoder stop [enabled]", + vob->encoder_flush = TC_FALSE; +) +TC_OPTION(a52_demux, 0, 0, + "(liba52) demux AC3/A52 to separate channels [off]", + vob->a52_mode |= TC_A52_DEMUX; +) +TC_OPTION(a52_drc_off, 0, 0, + "(liba52) disable dynamic range compression [enabled]", + vob->a52_mode |= TC_A52_DRC_OFF; +) +TC_OPTION(a52_dolby_off, 0, 0, + "(liba52) disable Dolby surround [enabled]", + vob->a52_mode |= TC_A52_DOLBY_OFF; +) + +/********/ TC_HEADER("Cluster/PSU/chapter mode processing") /********/ + +TC_OPTION(autosplit, 'W', "n,m[,file]", + "autosplit VOB and process part n of m [off]", + static char vob_logfile[1001] = ""; + if (sscanf(optarg, "%d,%d,%1000[^,]", &vob->vob_chunk, + &vob->vob_chunk_max, vob_logfile) < 2 + || vob->vob_chunk < 0 + || vob->vob_chunk_max <= 0 + || vob->vob_chunk >= vob->vob_chunk_max + 1 + ) { + tc_error("Invalid parameter for -W/--autosplit"); + goto short_usage; + } + if (*vob_logfile) + vob->vob_info_file = vob_logfile; + tc_cluster_mode = TC_TRUE; +) +TC_OPTION(cluster_percentage, 0, 0, + "use percentage mode for cluster encoding [off]", + vob->vob_percentage = TC_TRUE; +) +TC_OPTION(cluster_chunks, 0, "a-b", + "process chunk range instead of selected chunk [off]", + if (sscanf(optarg,"%d-%d", + &vob->vob_chunk_num1, &vob->vob_chunk_num2) != 2 + || vob->vob_chunk_num1 < 0 + || vob->vob_chunk_num2 <= 0 + || vob->vob_chunk_num1 >= vob->vob_chunk_num2 + ) { + tc_error("invalid parameter for --cluster_chunks"); + goto short_usage; + } +) +TC_OPTION(psu_mode, 0, 0, + "process VOB in PSU, -o is a filemask incl. %d [off]", + psu_mode = TC_TRUE; + core_mode = TC_MODE_PSU; + tc_cluster_mode = TC_TRUE; +) +TC_OPTION(psu_chunks, 0, "a-b", + "process only units a-b for PSU mode [all]", + if (sscanf(optarg, "%d-%d,%d", + &vob->vob_psu_num1, &vob->vob_psu_num2, + &psu_frame_threshold) < 2 + || vob->vob_psu_num1 < 0 + || vob->vob_psu_num2 <= 0 + || vob->vob_psu_num1 >= vob->vob_psu_num2 + ) { + tc_error("Invalid parameter for --psu_chunks"); + goto short_usage; + } +) +TC_OPTION(no_split, 0, 0, + "encode to single file in chapter/psu mode [off]", + no_split = TC_TRUE; +) +TC_OPTION(chapter_mode, 'U', "base", + "process DVD in chapter mode to base-ch%02d.avi [off]", + if (*optarg == '-') + goto short_usage; + chbase = optarg; + core_mode = TC_MODE_DVD_CHAPTER; +) + +/********/ TC_HEADER("Miscellaneous options") /********/ + +#ifdef TC_OPTIONS_TO_HELP +/* produce ONLY help messages since this option require special tratment */ +TC_OPTION(log_no_color, 0, 0, + "disable colors in log messages [use colors]", + ; /* nothing */ +) +#endif /* TC_OPTIONS_TO_HELP */ + +TC_OPTION(buffers, 'u', "N", + "use N framebuffers for AV processing [10]", + /* FIXME: threads ought to be a separate option */ + int threads_dummy = 0; + if (sscanf(optarg, "%d,%d,%d,%d", &max_frame_buffer, + &threads_dummy, &tc_buffer_delay_dec, + &tc_buffer_delay_enc) < 1 + || max_frame_buffer < 0 + ) { + tc_error("Invalid argument for -u/--buffers"); + goto short_usage; + } + if (threads_dummy != 0 && threads_dummy != max_frame_threads) { + tc_warn("Use --threads to specify number of threads," + " not -u"); + /* FIXME: deprecated in 1.1.0, remove later */ + max_frame_threads = threads_dummy; + } + preset_flag |= TC_PROBE_NO_BUFFER; +) +TC_OPTION(threads, 0, "N", + "use N threads for AV processing [1]", + max_frame_threads = strtol(optarg, &optarg, 10); + if (*optarg + || max_frame_threads < 0 + || max_frame_threads > TC_FRAME_THREADS_MAX + ) { + tc_error("Invalid argument for -u/--threads"); + goto short_usage; + } +) +TC_OPTION(progress_meter, 0, "N", + "select type of progress meter [1]", + tc_progress_meter = strtol(optarg, &optarg, 0); + if (*optarg || tc_progress_meter < 0) { + tc_error("Invalid argument for --progress_meter"); + goto short_usage; + } +) +TC_OPTION(progress_rate, 0, "N", + "print progress every N frames [1]", + tc_progress_rate = strtol(optarg, &optarg, 0); + if (*optarg || tc_progress_rate <= 0) { + tc_error("Invalid argument for --progress_rate"); + goto short_usage; + } +) +TC_OPTION(nice, 0, "N", + "set niceness to N [off]", + tc_niceness = strtol(optarg, &optarg, 0); + if (*optarg) { + tc_error("Invalid argument for --nice"); + goto short_usage; + } +) +TC_OPTION(accel, 0, "type[,type...]", + "override CPU acceleration flags (for debugging)", +#if defined(ARCH_X86) || defined(ARCH_X86_64) + int parsed = ac_parseflags(optarg, &tc_accel); + if (!parsed) { + tc_error("bad --accel type, valid types: C %s", + ac_flagstotext(AC_ALL)); + goto short_usage; + } +#else + /* Not supported--leave a statement in so the macro doesn't + * complain about a missing argument */ + break; +#endif +) +#if 0 +TC_OPTION(debug, 0, 0, + "enable debugging mode [disabled]", + core_mode = TC_MODE_DEBUG; +) +#endif + +/*************************************************************************/ + +/* Output trailer code, if any. */ +#ifdef _TCO_FINI +_TCO_FINI +#endif + +/* Undefine the macros we defined above. */ + +#undef TC_OPTION +#undef TC_HEADER +#undef _TCO_INIT +#undef _TCO_FINI + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/counter.c b/debian/transcode/transcode-1.1.7/src/counter.c new file mode 100644 index 00000000..3d9a60cd --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/counter.c @@ -0,0 +1,365 @@ +/* + * counter.c - transcode progress counter routines + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "counter.h" +#include "frame_threads.h" +#include <math.h> + +/*************************************************************************/ + +static int counter_active = 0; /* Is the counter active? */ +static int frames_to_encode = 0; /* Total number of frames to encode */ +static int encoded_frames = 0; /* Number of frames encoded so far */ +static double encoded_time = 0; /* Time spent encoding so far */ +static int frames_to_skip = 0; /* Total number of frames to skip */ +static int skipped_frames = 0; /* Number of frames skipped so far */ +static double skipped_time = 0; /* Time spent skipping so far */ +static int highest_frame = 0; /* Highest frame number to be seen */ + +static int printed = 0; /* Have we printed a line? */ + +static void print_counter_line(int encoding, int frame, int first, int last, + double fps, double done, double timestamp, + int secleft, int decodebuf[2], int filterbuf[2], + int encodebuf[2]); + +/*************************************************************************/ +/*************************************************************************/ + +/** + * counter_on: Activate the counter display. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void counter_on(void) +{ + counter_active = 1; +} + +/*************************************************************************/ + +/** + * counter_off: Deactivate the counter display. + * + * Parameters: + * None. + * Return value: + * None. + * Side effects: + * When in human-readable mode (tc_progress_meter == 1), if the counter + * has been displayed at least once, a newline is written to standard + * output. + */ + +void counter_off(void) +{ + if (printed) { + if (tc_progress_meter == 1) + fprintf(stderr, "\n"); + printed = 0; + } + counter_active = 0; +} + +/*************************************************************************/ + +/** + * counter_add_range: Add the given range of frames to the total number of + * frames to be encoded or skipped. + * + * Parameters: + * first: First frame of range. + * last: Last frame of range. + * encode: True (nonzero) if frames are to be encoded. + * False (zero) if frames are being skipped. + * Return value: + * None. + */ + +void counter_add_range(int first, int last, int encode) +{ + if (encode) { + frames_to_encode += last+1 - first; + } else { + frames_to_skip += last+1 - first; + } + if (last > highest_frame) + highest_frame = last; +} + +/*************************************************************************/ + +/** + * counter_reset_ranges: Reset the counter's stored range data. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void counter_reset_ranges(void) +{ + frames_to_encode = 0; + encoded_frames = 0; + encoded_time = 0; + frames_to_skip = 0; + skipped_frames = 0; + skipped_time = 0; + highest_frame = 0; +} + +/*************************************************************************/ + +/** + * counter_print: Display the progress counter, if active. + * + * Parameters: + * encoding: True (nonzero) if frames are being encoded. + * False (zero) if frames are being skipped. + * frame: Current frame being encoded or skipped. + * first: First frame of current range. + * last: Last frame of current range, -1 if unknown. + * Return value: + * None. + */ + +void counter_print(int encoding, int frame, int first, int last) +{ + vob_t *vob = tc_get_vob(); + struct timeval tv; + struct timezone dummy_tz = {0,0}; + double now, timediff, fps, time; + int buf_im[2], buf_fl[2], buf_ex[2]; + /* Values of 'first' and `last' during last call (-1 = not called yet) */ + static int old_first = -1, old_last = -1; + /* Time of first call for this range */ + static double start_time = 0; + /* Time of last call */ + static double old_time = 0; + + if (!tc_progress_meter + || !tc_progress_rate + || !counter_active + || frame % tc_progress_rate != 0 + ) { + return; + } + if (frame < 0 || first < 0) { + static int warned = 0; + if (!warned) { + tc_log_warn(__FILE__, "invalid arguments to counter_print" + " (%d,%d,%d,%d)", encoding, frame, first, last); + warned = 1; + } + return; + } + +#ifdef HAVE_GETTIMEOFDAY + if (gettimeofday(&tv, &dummy_tz) != 0) { + static int warned = 0; + if (!warned) { + tc_log_warn(__FILE__, "gettimeofday() failed!"); + warned = 1; + } + return; + } + now = tv.tv_sec + (double)tv.tv_usec/1000000.0; +#else + now = time(NULL); +#endif + + timediff = now - old_time; + old_time = now; + if (old_first != first || old_last != last) { + /* In human-readable mode, start a new counter line for each range + * if we don't know the total number of frames to be encoded. */ + if (tc_progress_meter == 1 && old_first != -1 && frames_to_encode == 0) + fprintf(stderr, "\n"); + start_time = now; + old_first = first; + old_last = last; + /* We decrement the frame counts here to compensate for this frame + * which took an unknown amount of time to complete. */ + if (encoding && frames_to_encode > 0) + frames_to_encode--; + else if (!encoding && frames_to_skip > 0) + frames_to_skip--; + return; + } + + /* Note that we don't add 1 to the numerator here, since start_time is + * the time we were called for the first frame, so frame first+1 is one + * one frame later than start_time, not two. */ + if (now > start_time) { + fps = (frame - first) / (now - start_time); + } else { + /* No time has passed (maybe we don't have gettimeofday()) */ + fps = 0; + } + + vframe_get_counters(&buf_im[0], &buf_fl[0], &buf_ex[0]); + aframe_get_counters(&buf_im[1], &buf_fl[1], &buf_ex[1]); + + time = (double)frame / ((vob->ex_fps<1.0) ? 1.0 : vob->ex_fps); + + if (last == -1) { + /* Can't calculate ETA, just display current timestamp */ + print_counter_line(encoding, frame, first, -1, fps, -1, time, -1, + buf_im, buf_fl, buf_ex); + + } else if (frames_to_encode == 0) { + /* Total number of frames unknown, just display for current range */ + double done = (double)(frame - first + 1) / (double)(last+1 - first); + int secleft = fps>0 ? ((last+1)-frame) / fps : -1; + print_counter_line(encoding, frame, first, last, fps, done, time, + secleft, buf_im, buf_fl, buf_ex); + + } else { + /* Estimate time remaining for entire run */ + double done; + int secleft; + if (encoding) { + encoded_frames++; + encoded_time += timediff; + } else { + skipped_frames++; + skipped_time += timediff; + } + if (encoded_frames > frames_to_encode) + frames_to_encode = encoded_frames; + if (skipped_frames > frames_to_skip) + frames_to_skip = skipped_frames; + if (encoded_frames == 0) { + /* We don't know how long it will take to encode frames; avoid + * understating the ETA, and just say we don't know */ + secleft = -1; + } else { + double encode_fps, skip_fps, total_time; + /* Find the processing speed for encoding and skipping */ + encode_fps = encoded_time ? encoded_frames / encoded_time : 0; + if (skipped_frames > 0 && skipped_time > 0) { + skip_fps = skipped_frames / skipped_time; + } else { + /* Just assume the same FPS for skipping as for encoding. + * Overstating the ETA isn't as bad as understating it, and + * certainly better than giving the user "unknown" for an + * entire encoding range. */ + skip_fps = encode_fps; + } + if (encode_fps > 0) { + /* Estimate the total processing time required */ + total_time = (frames_to_encode / encode_fps) + + (frames_to_skip / skip_fps); + /* Determine time left (round up) */ + secleft = ceil(total_time - (encoded_time + skipped_time)); + } else { + total_time = -1; + secleft = -1; + } + /* Use the proper overall FPS in the status line */ + fps = encoding ? encode_fps : skip_fps; + } + /* Just use the frame ratio for completion percentage */ + done = (double)(encoded_frames + skipped_frames) + / (double)(frames_to_encode + frames_to_skip); + print_counter_line(encoding, frame, 0, highest_frame, fps, done, + time, secleft, buf_im, buf_fl, buf_ex); + } + + fflush(stdout); +} + +/*************************************************************************/ + +/** + * print_counter_line: Helper function to format display arguments into a + * progress counter line depending on settings. + * + * Parameters: + * encoding: True (nonzero) if frames are being encoded. + * False (zero) if frames are being skipped. + * frame: Current frame being encoded or skipped. + * first: First frame of current range. + * last: Last frame of current range, -1 if unknown. + * fps: Estimated frames processed per second. + * done: Completion ratio (0..1), -1 if unknown. + * timestamp: Timestamp of current frame, in seconds. + * secleft: Estimated time remaining to completion, in seconds (-1 if + * unknown). + * decodebuf: Number of buffered frames awaiting decoding [V, A]. + * filterbuf: Number of buffered frames awaiting filtering [V, A]. + * encodebuf: Number of buffered frames awaiting encoding [V, A]. + * Return value: + * None. + */ + +static void print_counter_line(int encoding, int frame, int first, int last, + double fps, double done, double timestamp, + int secleft, + int decodebuf[2], int filterbuf[2], int encodebuf[2]) +{ + if (tc_progress_meter == 2) { + /* Raw data format */ + printf("encoding=%d frame=%d first=%d last=%d fps=%.3f done=%.6f" + " timestamp=%.3f timeleft=%d decodebuf=%d filterbuf=%d" + " encodebuf=%d\n", + encoding, frame, first, last, fps, done, + timestamp, secleft, decodebuf[0] + decodebuf[1], + filterbuf[0] + filterbuf[1], encodebuf[0] + encodebuf[1]); + } else if (last < 0 || done < 0 || secleft < 0) { + int timeint = floor(timestamp); + fprintf(stderr, "%s frames [%d-%d], %6.2f fps, CFT: %d:%02d:%02d," + " (%2d,%2d|%2d,%2d|%2d,%2d) \r", + encoding ? "encoding" : "skipping", + first, frame, + fps, + timeint/3600, (timeint/60) % 60, timeint % 60, + decodebuf[0], decodebuf[1], filterbuf[0], filterbuf[1], + encodebuf[0], encodebuf[1] + ); + } else { + char eta_buf[100]; + if (secleft < 0) { + snprintf(eta_buf, sizeof(eta_buf), "--:--:--"); + } else { + snprintf(eta_buf, sizeof(eta_buf), "%d:%02d:%02d", + secleft/3600, (secleft/60) % 60, secleft % 60); + } + fprintf(stderr, "%s frame [%d/%d], %6.2f fps, %5.1f%%, ETA: %s," + " (%2d,%2d|%2d,%2d|%2d,%2d) \r", + encoding ? "encoding" : "skipping", + frame, last+1, + fps, + 100*done, + eta_buf, + decodebuf[0], decodebuf[1], filterbuf[0], filterbuf[1], + encodebuf[0], encodebuf[1] + ); + } + printed = 1; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/counter.h b/debian/transcode/transcode-1.1.7/src/counter.h new file mode 100644 index 00000000..15eb0156 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/counter.h @@ -0,0 +1,33 @@ +/* + * counter.h + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef _COUNTER_H +#define _COUNTER_H + +void counter_on(void); +void counter_off(void); +void counter_add_range(int first, int last, int encode); +void counter_reset_ranges(void); +void counter_print(int encoding, int frame, int first, int last); + +#endif diff --git a/debian/transcode/transcode-1.1.7/src/decoder.c b/debian/transcode/transcode-1.1.7/src/decoder.c new file mode 100644 index 00000000..b0c0d180 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/decoder.c @@ -0,0 +1,1113 @@ +/* + * decoder.c -- transcode import layer module, implementation. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - July 2007 + * + * This file is part of transcode, a 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 "transcode.h" +#include "dl_loader.h" +#include "filter.h" +#include "framebuffer.h" +#include "video_trans.h" +#include "audio_trans.h" +#include "decoder.h" +#include "encoder.h" +#include "frame_threads.h" +#include "cmdline.h" +#include "probe.h" + + +/*************************************************************************/ + +/* anonymous since used just internally */ +enum { + TC_IM_THREAD_UNKNOWN = -1, /* halting cause not specified */ + TC_IM_THREAD_DONE = 0, /* import ends as expected */ + TC_IM_THREAD_INTERRUPT, /* external event interrupts import */ + TC_IM_THREAD_EXT_ERROR, /* external (I/O) error */ + TC_IM_THREAD_INT_ERROR, /* internal (core) error */ + TC_IM_THREAD_PROBE_ERROR, /* source is incompatible */ +}; + + +typedef struct tcdecoderdata_ TCDecoderData; +struct tcdecoderdata_ { + const char *tag; /* audio or video? used for logging */ + FILE *fd; /* for stream import */ + void *im_handle; /* import module handle */ + volatile int active_flag; /* active or not? */ + pthread_t thread_id; + pthread_mutex_t lock; +}; + + +/*************************************************************************/ + +static TCDecoderData video_decdata = { + .tag = "video", + .fd = NULL, + .im_handle = NULL, + .active_flag = 0, + .thread_id = (pthread_t)0, + .lock = PTHREAD_MUTEX_INITIALIZER, +}; + +static TCDecoderData audio_decdata = { + .tag = "audio", + .fd = NULL, + .im_handle = NULL, + .active_flag = 0, + .thread_id = (pthread_t)0, + .lock = PTHREAD_MUTEX_INITIALIZER, +}; + +static pthread_t tc_pthread_main = (pthread_t)0; +static long int vframecount = 0; +static long int aframecount = 0; + + +/*************************************************************************/ +/* Old-style compatibility support functions */ +/*************************************************************************/ + +struct modpair { + int codec; /* internal codec/colorspace/format */ + int caps; /* module capabilities */ +}; + +static const struct modpair audpairs[] = { + { CODEC_PCM, TC_CAP_PCM }, + { CODEC_AC3, TC_CAP_AC3 }, + { CODEC_RAW, TC_CAP_AUD }, + { CODEC_NULL, TC_CAP_NONE } /* end marker, must be the last */ +}; + +static const struct modpair vidpairs[] = { + { CODEC_RGB, TC_CAP_RGB }, + { CODEC_YUV, TC_CAP_YUV }, + { CODEC_YUV422, TC_CAP_YUV422 }, + { CODEC_RAW_YUV, TC_CAP_VID }, + { CODEC_RAW, TC_CAP_VID }, + { CODEC_NULL, TC_CAP_NONE } /* end marker, must be the last */ +}; + + +/* + * check_module_caps: verifies if a module is compatible with transcode + * core colorspace/format settings. + * + * Parameters: + * param: data describing (old-style) module capabilities. + * codec: codec/format/colorspace requested by core. + * mpairs: table of formats/capabilities to be used for check. + * Return Value: + * 0: module INcompatible with core format request. + * !0: module can accomplish to the core format request. + */ +static int check_module_caps(const transfer_t *param, int codec, + const struct modpair *mpairs) +{ + int caps = 0; + + if (param->flag == verbose) { + caps = (codec == mpairs[0].codec); + /* legacy: grab the first and stay */ + } else { + int i = 0; + + /* module returned capability flag */ + if (verbose >= TC_DEBUG) { + tc_log_msg(__FILE__, "Capability flag 0x%x | 0x%x", + param->flag, codec); + } + + for (i = 0; mpairs[i].codec != CODEC_NULL; i++) { + if (codec == mpairs[i].codec) { + caps = (param->flag & mpairs[i].caps); + break; + } + } + } + return caps; +} + +/*************************************************************************/ +/* optimized block-wise fread */ +/*************************************************************************/ + +#ifdef PIPE_BUF +#define BLOCKSIZE PIPE_BUF /* 4096 on linux-x86 */ +#else +#define BLOCKSIZE 4096 +#endif + +static int mfread(uint8_t *buf, int size, int nelem, FILE *f) +{ + int fd = fileno(f); + int n = 0, r1 = 0, r2 = 0; + while (n < size*nelem-BLOCKSIZE) { + if ( !(r1 = read (fd, &buf[n], BLOCKSIZE))) return 0; + n += r1; + } + while (size*nelem-n) { + if ( !(r2 = read (fd, &buf[n], size*nelem-n)))return 0; + n += r2; + } + return nelem; +} + +/*************************************************************************/ +/* some macro goodies */ +/*************************************************************************/ + +#define RETURN_IF_NULL(HANDLE, MEDIA) do { \ + if ((HANDLE) == NULL) { \ + tc_log_error(PACKAGE, "Loading %s import module failed", (MEDIA)); \ + tc_log_error(PACKAGE, \ + "Did you enable this module when you ran configure?"); \ + return TC_ERROR; \ + } \ +} while (0) + +#define RETURN_IF_NOT_SUPPORTED(CAPS, MEDIA) do { \ + if (!(CAPS)) { \ + tc_log_error(PACKAGE, "%s format not supported by import module", \ + (MEDIA)); \ + return TC_ERROR; \ + } \ +} while (0) + +#define RETURN_IF_FUNCTION_FAILED(func, ...) do { \ + int ret = func(__VA_ARGS__); \ + if (ret != TC_OK) { \ + return TC_ERROR; \ + } \ +} while (0) + +#define RETURN_IF_REGISTRATION_FAILED(PTR, MEDIA) do { \ + /* ok, that's pure paranoia */ \ + if ((PTR) == NULL) { \ + tc_log_error(__FILE__, "frame registration failed (%s)", (MEDIA)); \ + return TC_IM_THREAD_INT_ERROR; \ + } \ +} while (0) + +/*************************************************************************/ +/* stream-specific functions */ +/*************************************************************************/ +/* status handling functions */ +/*************************************************************************/ + + +/* + * tc_import_thread_stop (Thread safe): mark the import status flag + * as `stopped'; the import thread will stop as soon as is possible. + * + * Parameters: + * decdata: pointer to a TCDecoderData structure representing the + * import thread to stop. + * Return Value: + * None + */ +static void tc_import_thread_stop(TCDecoderData *decdata) +{ + pthread_mutex_lock(&decdata->lock); + decdata->active_flag = TC_FALSE; + pthread_mutex_unlock(&decdata->lock); +} + +/* + * tc_import_thread_start (Thread safe): mark the import status flag + * as `started'; import thread become running and it starts producing data. + * + * Parameters: + * decdata: pointer to a TCDecoderData structure representing the + * import thread to start. + * Return Value: + * None + */ +static void tc_import_thread_start(TCDecoderData *decdata) +{ + pthread_mutex_lock(&decdata->lock); + decdata->active_flag = TC_TRUE; + pthread_mutex_unlock(&decdata->lock); +} + +/* + * tc_import_thread_is_active (Thread safe): poll for the current + * status flag of an import thread. + * + * Parameters: + * decdata: pointer to a TCDecoderData structure representing the + * import thread to query. + * Return Value: + * TC_FALSE: import thread is stopped or stopping. + * TC_TRUE: import thread is running. + */ + +static int tc_import_thread_is_active(TCDecoderData *decdata) +{ + int flag; + pthread_mutex_lock(&decdata->lock); + flag = decdata->active_flag; + pthread_mutex_unlock(&decdata->lock); + return flag; +} + +/*************************************************************************/ +/* stream open/close functions */ +/*************************************************************************/ + +/* + * tc_import_{video,audio}_open: open audio stream for importing. + * + * Parameters: + * vob: vob structure + * Return Value: + * TC_OK: succesfull. + * TC_ERROR: failure; reason was tc_log*()ged out. + */ +static int tc_import_video_open(vob_t *vob) +{ + int ret; + transfer_t import_para; + + memset(&import_para, 0, sizeof(transfer_t)); + + import_para.flag = TC_VIDEO; + + ret = tcv_import(TC_IMPORT_OPEN, &import_para, vob); + if (ret < 0) { + tc_log_error(PACKAGE, "video import module error: OPEN failed"); + return TC_ERROR; + } + + video_decdata.fd = import_para.fd; + + return TC_OK; +} + + +static int tc_import_audio_open(vob_t *vob) +{ + int ret; + transfer_t import_para; + + memset(&import_para, 0, sizeof(transfer_t)); + + import_para.flag = TC_AUDIO; + + ret = tca_import(TC_IMPORT_OPEN, &import_para, vob); + if (ret < 0) { + tc_log_error(PACKAGE, "audio import module error: OPEN failed"); + return TC_ERROR; + } + + audio_decdata.fd = import_para.fd; + + return TC_OK; +} + +/* + * tc_import_{video,audio}_close: close audio stream used for importing. + * + * Parameters: + * None. + * Return Value: + * TC_OK: succesfull. + * TC_ERROR: failure; reason was tc_log*()ged out. + */ + +static int tc_import_audio_close(void) +{ + int ret; + transfer_t import_para; + + memset(&import_para, 0, sizeof(transfer_t)); + + import_para.flag = TC_AUDIO; + import_para.fd = audio_decdata.fd; + + ret = tca_import(TC_IMPORT_CLOSE, &import_para, NULL); + if (ret == TC_IMPORT_ERROR) { + tc_log_warn(PACKAGE, "audio import module error: CLOSE failed"); + return TC_ERROR; + } + audio_decdata.fd = NULL; + + return TC_OK; +} + +static int tc_import_video_close(void) +{ + int ret; + transfer_t import_para; + + memset(&import_para, 0, sizeof(transfer_t)); + + import_para.flag = TC_VIDEO; + import_para.fd = video_decdata.fd; + + ret = tcv_import(TC_IMPORT_CLOSE, &import_para, NULL); + if (ret == TC_IMPORT_ERROR) { + tc_log_warn(PACKAGE, "video import module error: CLOSE failed"); + return TC_ERROR; + } + video_decdata.fd = NULL; + + return TC_OK; +} + + +/*************************************************************************/ +/* the import loops */ +/*************************************************************************/ + + +#define MARK_TIME_RANGE(PTR, VOB) do { \ + /* Set skip attribute based on -c */ \ + if (fc_time_contains((VOB)->ttime, (PTR)->id)) \ + (PTR)->attributes &= ~TC_FRAME_IS_OUT_OF_RANGE; \ + else \ + (PTR)->attributes |= TC_FRAME_IS_OUT_OF_RANGE; \ +} while (0) + + +/* + * stop_cause: specify the cause of an import loop termination. + * + * Parameters: + * ret: termination cause identifier to be specified + * Return Value: + * the most specific recognizable termination cause. + */ +static int stop_cause(int ret) +{ + if (ret == TC_IM_THREAD_UNKNOWN) { + if (tc_interrupted()) { + ret = TC_IM_THREAD_INTERRUPT; + } else if (tc_stopped()) { + ret = TC_IM_THREAD_DONE; + } + } + return ret; +} + +/* + * {video,audio}_import_loop: data import loops. Feed frame FIFOs with + * new data forever until are interrupted or stopped. + * + * Parameters: + * vob: vob structure + * Return Value: + * TC_IM_THREAD_* value reporting operation status. + */ +static int video_import_loop(vob_t *vob) +{ + int ret = 0, vbytes = 0; + vframe_list_t *ptr = NULL; + transfer_t import_para; + TCFrameStatus next = (tc_frame_threads_have_video_workers()) + ?TC_FRAME_WAIT :TC_FRAME_READY; + int im_ret = TC_IM_THREAD_UNKNOWN; + + if (verbose >= TC_DEBUG) + tc_log_msg(__FILE__, "video thread id=%ld", (unsigned long)pthread_self()); + + vbytes = vob->im_v_size; + + while (tc_running() && tc_import_thread_is_active(&video_decdata)) { + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) %10s [%ld] %i bytes", "requesting", + vframecount, vbytes); + + /* stage 1: register new blank frame */ + ptr = vframe_register(vframecount); + if (ptr == NULL) { + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) frame registration interrupted!"); + break; + } + + /* stage 2: fill the frame with data */ + ptr->attributes = 0; + MARK_TIME_RANGE(ptr, vob); + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) new frame registered and marked, now filling..."); + + if (video_decdata.fd != NULL) { + if (vbytes && (ret = mfread(ptr->video_buf, vbytes, 1, video_decdata.fd)) != 1) + ret = -1; + ptr->video_len = vbytes; + ptr->video_size = vbytes; + } else { + import_para.fd = NULL; + import_para.buffer = ptr->video_buf; + import_para.buffer2 = ptr->video_buf2; + import_para.size = vbytes; + import_para.flag = TC_VIDEO; + import_para.attributes = ptr->attributes; + + ret = tcv_import(TC_IMPORT_DECODE, &import_para, vob); + + ptr->video_len = import_para.size; + ptr->video_size = import_para.size; + ptr->attributes |= import_para.attributes; + } + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) new frame filled (%s)", (ret == -1) ?"FAILED" :"OK"); + + if (ret < 0) { + if (verbose >= TC_DEBUG) + tc_log_msg(__FILE__, "(V) data read failed - end of stream"); + + ptr->video_len = 0; + ptr->video_size = 0; + if (!tc_has_more_video_in_file(vob)) { + ptr->attributes = TC_FRAME_IS_END_OF_STREAM; + } else { + ptr->attributes = TC_FRAME_IS_SKIPPED; + } + } + + ptr->v_height = vob->im_v_height; + ptr->v_width = vob->im_v_width; + ptr->v_bpp = BPP; + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) new frame is being processed"); + + /* stage 3: account filled frame and process it if needed */ + if (TC_FRAME_NEED_PROCESSING(ptr)) { + //first stage pre-processing - (synchronous) + preprocess_vid_frame(vob, ptr); + + //filter pre-processing - (synchronous) + ptr->tag = TC_VIDEO|TC_PRE_S_PROCESS; + tc_filter_process((frame_list_t *)ptr); + } + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) new frame ready to be pushed"); + + /* stage 4: push frame to next transcoding layer */ + vframe_push_next(ptr, next); + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) %10s [%ld] %i bytes", "received", + vframecount, ptr->video_size); + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(V) new frame pushed"); + + if (ret < 0) { + /* + * we must delay this stuff in order to properly END_OF_STREAM + * frames _and_ to push them to subsequent stages + */ + tc_import_thread_stop(&audio_decdata); + im_ret = TC_IM_THREAD_DONE; + break; + } + vframecount++; + } + return stop_cause(im_ret); +} + + +#define GET_AUDIO_FRAME do { \ + if (audio_decdata.fd != NULL) { \ + if (abytes && (ret = mfread(ptr->audio_buf, abytes, 1, audio_decdata.fd)) != 1) { \ + ret = -1; \ + } \ + ptr->audio_len = abytes; \ + ptr->audio_size = abytes; \ + } else { \ + import_para.fd = NULL; \ + import_para.buffer = ptr->audio_buf; \ + import_para.size = abytes; \ + import_para.flag = TC_AUDIO; \ + import_para.attributes = ptr->attributes; \ + \ + ret = tca_import(TC_IMPORT_DECODE, &import_para, vob); \ + \ + ptr->audio_len = import_para.size; \ + ptr->audio_size = import_para.size; \ + } \ +} while (0) + +static int audio_import_loop(vob_t *vob) +{ + int ret = 0, abytes; + aframe_list_t *ptr = NULL; + transfer_t import_para; + TCFrameStatus next = (tc_frame_threads_have_audio_workers()) + ?TC_FRAME_WAIT :TC_FRAME_READY; + int im_ret = TC_IM_THREAD_UNKNOWN; + + if (verbose >= TC_DEBUG) + tc_log_msg(__FILE__, "audio thread id=%ld", + (unsigned long)pthread_self()); + + abytes = vob->im_a_size; + + while (tc_running() && tc_import_thread_is_active(&audio_decdata)) { + /* stage 1: audio adjustment for non-PAL frame rates */ + if (aframecount != 0 && aframecount % TC_LEAP_FRAME == 0) { + abytes = vob->im_a_size + vob->a_leap_bytes; + } else { + abytes = vob->im_a_size; + } + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(A) %10s [%ld] %i bytes", + "requesting", aframecount, abytes); + + /* stage 2: register new blank frame */ + ptr = aframe_register(aframecount); + if (ptr == NULL) { + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(A) frame registration interrupted!"); + break; + } + + ptr->attributes = 0; + MARK_TIME_RANGE(ptr, vob); + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(A) new frame registered and marked, now syncing..."); + + /* stage 3: fill the frame with data */ + /* stage 3.1: resync audio by discarding frames, if needed */ + if (vob->sync > 0) { + // discard vob->sync frames + while (vob->sync--) { + GET_AUDIO_FRAME; + + if (ret == -1) + break; + } + vob->sync++; + } + + /* stage 3.2: grab effective audio data */ + if (vob->sync == 0) { + GET_AUDIO_FRAME; + } + + /* stage 3.3: silence at last */ + if (vob->sync < 0) { + if (verbose >= TC_DEBUG) + tc_log_msg(__FILE__, " zero padding %d", vob->sync); + memset(ptr->audio_buf, 0, abytes); + ptr->audio_len = abytes; + ptr->audio_size = abytes; + vob->sync++; + } + /* stage 3.x final note: all this stuff can be done in a cleaner way... */ + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(A) syncing done, new frame ready to be filled..."); + + if (ret < 0) { + if (verbose >= TC_DEBUG) + tc_log_msg(__FILE__, "(A) data read failed - end of stream"); + + ptr->audio_len = 0; + ptr->audio_size = 0; + if (!tc_has_more_audio_in_file(vob)) { + ptr->attributes = TC_FRAME_IS_END_OF_STREAM; + } else { + ptr->attributes = TC_FRAME_IS_SKIPPED; + } + } + + // init frame buffer structure with import frame data + ptr->a_rate = vob->a_rate; + ptr->a_bits = vob->a_bits; + ptr->a_chan = vob->a_chan; + + /* stage 4: account filled frame and process it if needed */ + if (TC_FRAME_NEED_PROCESSING(ptr)) { + ptr->tag = TC_AUDIO|TC_PRE_S_PROCESS; + tc_filter_process((frame_list_t *)ptr); + } + + /* stage 5: push frame to next transcoding layer */ + aframe_push_next(ptr, next); + + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "(A) %10s [%ld] %i bytes", "received", + aframecount, ptr->audio_size); + + if (ret < 0) { + tc_import_thread_stop(&audio_decdata); + im_ret = TC_IM_THREAD_DONE; + break; + } + aframecount++; + } + return stop_cause(im_ret); +} + +#undef GET_AUDIO_FRAME +#undef MARK_TIME_RANGE + +/*************************************************************************/ +/* ladies and gentlemens, the thread routines */ +/*************************************************************************/ + +/* audio decode thread wrapper */ +static void *audio_import_thread(void *_vob) +{ + static int ret = 0; + ret = audio_import_loop(_vob); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "audio decode loop ends with code 0x%i", ret); + pthread_exit(&ret); +} + +/* video decode thread wrapper */ +static void *video_import_thread(void *_vob) +{ + static int ret = 0; + ret = video_import_loop(_vob); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "video decode loop ends with code 0x%i", ret); + pthread_exit(&ret); +} + + +/*************************************************************************/ +/* main API functions */ +/*************************************************************************/ + +int tc_import_status() +{ + return tc_import_video_status() && tc_import_audio_status(); +} + +int tc_import_video_running(void) +{ + return tc_import_thread_is_active(&video_decdata); +} + +int tc_import_audio_running(void) +{ + return tc_import_thread_is_active(&audio_decdata); +} + +int tc_import_video_status(void) +{ + return vframe_have_more() || tc_import_thread_is_active(&video_decdata); +} + +int tc_import_audio_status(void) +{ + return aframe_have_more() || tc_import_thread_is_active(&audio_decdata); +} + + +void tc_import_threads_cancel(void) +{ + void *status = NULL; + int vret, aret; + + if (tc_decoder_delay) + tc_log_info(__FILE__, "sleeping for %i seconds to cool down", tc_decoder_delay); + sleep(tc_decoder_delay); + tc_log_info(__FILE__, "cancelling the import threads"); + + tc_import_thread_stop(&video_decdata); + tc_import_thread_stop(&audio_decdata); + + tc_framebuffer_interrupt_import(); + + vret = pthread_join(video_decdata.thread_id, &status); + if (verbose >= TC_DEBUG) { + int *pst = status; /* avoid explicit cast in log below */ + tc_log_msg(__FILE__, "video thread exit (ret_code=%i)" + " (status_code=%i)", vret, *pst); + } + + aret = pthread_join(audio_decdata.thread_id, &status); + if (verbose >= TC_DEBUG) { + int *pst = status; /* avoid explicit cast in log below */ + tc_log_msg(__FILE__, "audio thread exit (ret_code=%i)" + " (status_code=%i)", aret, *pst); + } + return; +} + + +void tc_import_threads_create(vob_t *vob) +{ + int ret; + + tc_import_thread_start(&audio_decdata); + ret = pthread_create(&audio_decdata.thread_id, NULL, + audio_import_thread, vob); + if (ret != 0) + tc_error("failed to start audio stream import thread"); + + tc_import_thread_start(&video_decdata); + ret = pthread_create(&video_decdata.thread_id, NULL, + video_import_thread, vob); + if (ret != 0) + tc_error("failed to start video stream import thread"); +} + + +int tc_import_init(vob_t *vob, const char *a_mod, const char *v_mod) +{ + transfer_t import_para; + int caps; + + a_mod = (a_mod == NULL) ?TC_DEFAULT_IMPORT_AUDIO :a_mod; + audio_decdata.im_handle = load_module(a_mod, TC_IMPORT+TC_AUDIO); + RETURN_IF_NULL(audio_decdata.im_handle, "audio"); + + v_mod = (v_mod == NULL) ?TC_DEFAULT_IMPORT_VIDEO :v_mod; + video_decdata.im_handle = load_module(v_mod, TC_IMPORT+TC_VIDEO); + RETURN_IF_NULL(video_decdata.im_handle, "video"); + + memset(&import_para, 0, sizeof(transfer_t)); + import_para.flag = verbose; + tca_import(TC_IMPORT_NAME, &import_para, NULL); + + caps = check_module_caps(&import_para, vob->im_a_codec, audpairs); + RETURN_IF_NOT_SUPPORTED(caps, "audio"); + + memset(&import_para, 0, sizeof(transfer_t)); + import_para.flag = verbose; + tcv_import(TC_IMPORT_NAME, &import_para, NULL); + + caps = check_module_caps(&import_para, vob->im_v_codec, vidpairs); + RETURN_IF_NOT_SUPPORTED(caps, "video"); + + tc_pthread_main = pthread_self(); + + return TC_OK; +} + + +int tc_import_open(vob_t *vob) +{ + RETURN_IF_FUNCTION_FAILED(tc_import_audio_open, vob); + RETURN_IF_FUNCTION_FAILED(tc_import_video_open, vob); + + return TC_OK; +} + +int tc_import_close(void) +{ + RETURN_IF_FUNCTION_FAILED(tc_import_audio_close); + RETURN_IF_FUNCTION_FAILED(tc_import_video_close); + + return TC_OK; +} + +void tc_import_shutdown(void) +{ + if (verbose >= TC_DEBUG) { + tc_log_msg(__FILE__, "unloading audio import module"); + } + + unload_module(audio_decdata.im_handle); + audio_decdata.im_handle = NULL; + + if (verbose >= TC_DEBUG) { + tc_log_msg(__FILE__, "unloading video import module"); + } + + unload_module(video_decdata.im_handle); + video_decdata.im_handle = NULL; +} + + +/*************************************************************************/ +/* the new multi-input sequential API */ +/*************************************************************************/ + +static void dump_probeinfo(const ProbeInfo *pi, int i, const char *tag) +{ +#ifdef DEBUG + tc_log_warn(__FILE__, "(%s): %ix%i asr=%i frc=%i codec=0x%lX", + tag, pi->width, pi->height, pi->asr, pi->frc, pi->codec); + + if (i >= 0) { + tc_log_warn(__FILE__, "(%s): #%i %iHz %ich %ibits format=0x%X", + tag, i, + pi->track[i].samplerate, pi->track[i].chan, + pi->track[i].bits, pi->track[i].format); + } +#endif +} + +static int probe_im_stream(const char *src, ProbeInfo *info) +{ + static pthread_mutex_t probe_mutex = PTHREAD_MUTEX_INITIALIZER; /* XXX */ + /* UGLY! */ + int ret = 1; /* be optimistic! */ + + pthread_mutex_lock(&probe_mutex); + ret = probe_stream_data(src, seek_range, info); + pthread_mutex_unlock(&probe_mutex); + + dump_probeinfo(info, 0, "probed"); + + return ret; +} + +static int probe_matches(const ProbeInfo *ref, const ProbeInfo *cand, int i) +{ + if (ref->width != cand->width || ref->height != cand->height + || ref->frc != cand->frc || ref->asr != cand->asr + || ref->codec != cand->codec) { + tc_log_error(__FILE__, "video parameters mismatch"); + dump_probeinfo(ref, -1, "old"); + dump_probeinfo(cand, -1, "new"); + return 0; + } + + if (i > ref->num_tracks || i > cand->num_tracks) { + tc_log_error(__FILE__, "track parameters mismatch (i=%i|ref=%i|cand=%i)", + i, ref->num_tracks, cand->num_tracks); + return 0; + } + if (ref->track[i].samplerate != cand->track[i].samplerate + || ref->track[i].chan != cand->track[i].chan ) { +// || ref->track[i].bits != cand->track[i].bits ) { +// || ref->track[i].format != cand->track[i].format ) { XXX XXX XXX + tc_log_error(__FILE__, "audio parameters mismatch"); + dump_probeinfo(ref, i, "old"); + dump_probeinfo(cand, i, "new"); + return 0; + } + + return 1; +} + +static void probe_from_vob(ProbeInfo *info, const vob_t *vob) +{ + /* copy only interesting fields */ + if (info != NULL && vob != NULL) { + int i = 0; + + info->width = vob->im_v_width; + info->height = vob->im_v_height; + info->codec = vob->v_codec_flag; + info->asr = vob->im_asr; + info->frc = vob->im_frc; + + for (i = 0; i < TC_MAX_AUD_TRACKS; i++) { + memset(&(info->track[i]), 0, sizeof(ProbeTrackInfo)); + } + i = vob->a_track; + + info->track[i].samplerate = vob->a_rate; + info->track[i].chan = vob->a_chan; + info->track[i].bits = vob->a_bits; + info->track[i].format = vob->a_codec_flag; + } +} + +/* ok, that sucks. I know. I can't do any better now. */ +static const char *current_in_file(vob_t *vob, int kind) +{ + if (kind == TC_VIDEO) + return vob->video_in_file; + if (kind == TC_AUDIO) + return vob->audio_in_file; + return NULL; /* cannot happen */ +} + +#define RETURN_IF_PROBE_FAILED(ret, src) do { \ + if (ret == 0) { \ + tc_log_error(PACKAGE, "probing of source '%s' failed", src); \ + status = TC_IM_THREAD_PROBE_ERROR; \ + } \ +} while (0) + +/* black magic in here? Am I looking for troubles? */ +#define SWAP(type, a, b) do { \ + type tmp = a; \ + a = b; \ + b = tmp; \ +} while (0) + + +/*************************************************************************/ + +typedef struct tcmultiimportdata_ TCMultiImportData; +struct tcmultiimportdata_ { + int kind; + + TCDecoderData *decdata; + vob_t *vob; + + ProbeInfo infos; + + int (*open)(vob_t *vob); + int (*import_loop)(vob_t *vob); + int (*close)(void); + + int (*next)(vob_t *vob); +}; + + +#define MULTIDATA_INIT(MEDIA, VOB, KIND) do { \ + MEDIA ## _multidata.kind = KIND; \ + \ + MEDIA ## _multidata.vob = VOB; \ + MEDIA ## _multidata.decdata = &(MEDIA ## _decdata); \ + \ + MEDIA ## _multidata.open = tc_import_ ## MEDIA ## _open; \ + MEDIA ## _multidata.import_loop = MEDIA ## _import_loop; \ + MEDIA ## _multidata.close = tc_import_ ## MEDIA ## _close; \ + MEDIA ## _multidata.next = tc_next_ ## MEDIA ## _in_file; \ +} while (0) + +#define MULTIDATA_FINI(MEDIA) do { \ + ; /* nothing */ \ +} while (0) + + + +static TCMultiImportData audio_multidata; +static TCMultiImportData video_multidata; + +/*************************************************************************/ + +static void *multi_import_thread(void *_sid) +{ + static int status = TC_IM_THREAD_UNKNOWN; /* can't we be more specific? */ + + TCMultiImportData *sid = _sid; + int ret = TC_OK, track_id = sid->vob->a_track; + ProbeInfo infos; + ProbeInfo *old = &(sid->infos), *new = &infos; + const char *fname = NULL; + long int i = 1; + + while (tc_running() && tc_import_thread_is_active(sid->decdata)) { + ret = sid->open(sid->vob); + if (ret == TC_ERROR) { + status = TC_IM_THREAD_EXT_ERROR; + break; + } + + status = sid->import_loop(sid->vob); + /* source should always be closed */ + + ret = sid->close(); + if (ret == TC_ERROR) { + status = TC_IM_THREAD_EXT_ERROR; + break; + } + + ret = sid->next(sid->vob); + if (ret == TC_ERROR) { + status = TC_IM_THREAD_DONE; + break; + } + + fname = current_in_file(sid->vob, sid->kind); + /* probing coherency check */ + ret = probe_im_stream(fname, new); + RETURN_IF_PROBE_FAILED(ret, fname); + + if (probe_matches(old, new, track_id)) { + if (verbose) { + tc_log_info(__FILE__, "switching to %s source #%li: %s", + sid->decdata->tag, i, fname); + } + } else { + tc_log_error(PACKAGE, "source '%s' in directory" + " not compatible with former", fname); + status = TC_IM_THREAD_PROBE_ERROR; + break; + } + /* now prepare for next probing round by swapping pointers */ + SWAP(ProbeInfo*, old, new); + + i++; + } + status = stop_cause(status); + + tc_framebuffer_interrupt(); + + pthread_exit(&status); +} + +/*************************************************************************/ + +void tc_multi_import_threads_create(vob_t *vob) +{ + int ret; + + probe_from_vob(&(audio_multidata.infos), vob); + MULTIDATA_INIT(audio, vob, TC_AUDIO); + tc_import_thread_start(&audio_decdata); + ret = pthread_create(&audio_decdata.thread_id, NULL, + multi_import_thread, &audio_multidata); + if (ret != 0) { + tc_error("failed to start sequential audio stream import thread"); + } + + probe_from_vob(&(video_multidata.infos), vob); + MULTIDATA_INIT(video, vob, TC_VIDEO); + tc_import_thread_start(&video_decdata); + ret = pthread_create(&video_decdata.thread_id, NULL, + multi_import_thread, &video_multidata); + if (ret != 0) { + tc_error("failed to start sequential video stream import thread"); + } + + tc_info("sequential streams import threads started"); +} + + +void tc_multi_import_threads_cancel(void) +{ + MULTIDATA_FINI(audio); + MULTIDATA_FINI(video); + + tc_import_threads_cancel(); +} + + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ + diff --git a/debian/transcode/transcode-1.1.7/src/decoder.h b/debian/transcode/transcode-1.1.7/src/decoder.h new file mode 100644 index 00000000..a1eb7d09 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/decoder.h @@ -0,0 +1,207 @@ +/* + * decoder.h -- transcode import layer module, declarations. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Enhancements and partial rewrite: + * (C) Francesco Romani - November 2007. + * + * This file is part of transcode, a 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. + * + */ + +#ifndef DECODER_H +#define DECODER_H + +/* + * tc_import_init (NOT thread safe): + * prepare import layer for execution, by loading import modules, + * checking their capabilities against those requested by core, + * intializing them. + * + * After this function terminates succesfully, import threads can be + * created and import layer can be started. + * + * Parameters: + * vob: vob structure. + * a_mod: name of the module to be used for import audio. + * v_mod: name of the module to be used for import video. + * Return Value: + * TC_OK: succesfull. + * TC_ERROR: failure. Reason was already tc_log*()ged out. + * Postconditions: + * Import threads can now be created. + */ +int tc_import_init(vob_t *vob, const char *a_mod, const char *v_mod); + +/* + * tc_import_shutdown (NOT thread safe): + * shutdown import layer after the import threads termination, by + * freeing resources acquired by import modules and unloading them. + * + * Parameters: + * None. + * Return Value: + * None. + * Preconditions: + * Import threads are terminated. + */ +void tc_import_shutdown(void); + + +/* + * tc_import_open (Thread safe): + * open both the audio and video streams. + * + * Parameters: + * vob: vob structure. + * Return Value: + * TC_OK: succesfull. + * TC_ERROR: failure. Reason was already tc_log*()ged out. + * Preconditions: + * import modules are loaded and initialized correctly; + * tc_import_init was executed succesfully. + */ +int tc_import_open(vob_t *vob); + +/* + * tc_import_close (Thread safe): + * close both the audio and video streams. + * + * Parameters: + * None. + * Return Value: + * TC_OK: succesfull. + * TC_ERROR: failure. Reason was already tc_log*()ged out. + * Preconditions: + * Import threads are terminated; + * tc_import_threads_cancel was executed succesfully. + */ +int tc_import_close(void); + +/* + * tc_import_threads_create (Thread safe): + * create both audio and video import threads, and automatically, + * implicitely and immediately starts importing loops and the import + * layer itself. + * + * Parameters: + * vob: vob structure. + * Return Value: + * None. + * Preconditions: + * import modules are loaded and initialized correctly; + * tc_import_init was executed succesfully. + * import streams are been opened correctly; + * tc_import_open was executed succesfully. + */ +void tc_import_threads_create(vob_t *vob); + +/* + * tc_import_threads_cancel (Thread safe): + * destroy both audio and video import threads, and automatically and + * implicitely stop the whole import layer. + * It's important to note that this function assume that import loops + * are already been terminated. + * This is a blocking function. + * + * Parameters: + * None. + * Return Value: + * None. + * Preconditions: + * import threads are terminated for any reason + * (regular stop, end of stream reached, forced interruption). + * tc_import_threads_create was used to startup the threads. + */ +void tc_import_threads_cancel(void); + + +/* + * tc_import_{,video_,audio_}status (Thread safe): + * query the status of import layer. + * + * Import layer has the responsability to provide raw data for further + * layers. Since there always is some buffering, isn't sufficient to + * check if import threads are running or not, we also need to see if + * there is some buffered data in the frame FIFOs. + * + * Parameters: + * None. + * Return Value: + * !0: there is some futher data to process. + * 0: no more data avalaible. + */ +int tc_import_status(void); +int tc_import_audio_status(void); +int tc_import_video_status(void); + +/* + * tc_import_{,video_,audio_}running (Thread safe): + * check if respectively video or audio import thread is running. + * + * Parameters: + * None. + * Return Value: + * !0: thread is running. + * 0: thread is stopped or stopping. + */ +int tc_import_video_running(void); +int tc_import_audio_running(void); + + +/*************************************************************************/ + +/* + * tc_multi_import_threads_create (Thread safe): + * like tc_import_threads_create, but setup internal machinery for + * multi-input handling. + * + * CRITICAL NOTE: + * You MUST use EITHER tc_multi_import_threads_create *OR* + * tc_import_threads_create. + * You CANNOT use BOTH in the same code path, or nasty things will happen + * + * Parameters: + * vob: vob structure. + * Return Value: + * None. + * Preconditions: + * import modules are loaded and initialized correctly; + * tc_import_init was executed succesfully. + * import streams are been opened correctly; + * tc_import_open was executed succesfully. + */ +void tc_multi_import_threads_create(vob_t *vob); + +/* + * tc_multi_import_threads_cancel (Thread safe): + * like tc_import_threads_cancel, but you MUST use this function if you + * called tc_multi_input_threads_create before. + * Otherwise things will turn nasty. + * + * Parameters: + * None. + * Return Value: + * None. + * Preconditions: + * import threads are terminated for any reason + * (regular stop, end of stream reached, forced interruption). + * tc_multi_import_threads_create was used to startup the threads. + */ +void tc_multi_import_threads_cancel(void); + +#endif /* DECODER_H */ diff --git a/debian/transcode/transcode-1.1.7/src/dl_loader.c b/debian/transcode/transcode-1.1.7/src/dl_loader.c new file mode 100644 index 00000000..3b4b9b8e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/dl_loader.c @@ -0,0 +1,223 @@ +/* + * dl_loader.c + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#else +# ifdef OS_DARWIN +# include "libdldarwin/dlfcn.h" +# endif +#endif + +#include "transcode.h" +#include "dl_loader.h" + +const char *mod_path = MOD_PATH; + +char module[TC_BUF_MAX]; + +int (*TCV_export)(int opt, void *para1, void *para2); +int (*TCA_export)(int opt, void *para1, void *para2); +int (*TCV_import)(int opt, void *para1, void *para2); +int (*TCA_import)(int opt, void *para1, void *para2); + +static void watch_export_module(const char *s, int opt, transfer_t *para) +{ + tc_log_msg(__FILE__, "module=%s [option=%02d, flag=%d]", s, opt, ((para==NULL)? -1:para->flag)); +} + +static void watch_import_module(const char *s, int opt, transfer_t *para) +{ + tc_log_msg(__FILE__, "module=%s [option=%02d, flag=%d]", s, opt, ((para==NULL)? -1:para->flag)); + fflush(stdout); +} + +int tcv_export(int opt, void *para1, void *para2) +{ + + int ret; + + if(verbose & TC_WATCH) + watch_export_module("tcv_export", opt, (transfer_t*) para1); + + ret = TCV_export(opt, para1, para2); + + if(ret==TC_EXPORT_ERROR && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "video export module error"); + + if(ret==TC_EXPORT_UNKNOWN && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "option %d unsupported by video export module", opt); + + return(ret); +} + +int tca_export(int opt, void *para1, void *para2) +{ + + int ret; + + if(verbose & TC_WATCH) + watch_export_module("tca_export", opt, (transfer_t*) para1); + + ret = TCA_export(opt, para1, para2); + + if(ret==TC_EXPORT_ERROR && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "audio export module error"); + + if(ret==TC_EXPORT_UNKNOWN && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "option %d unsupported by audio export module", opt); + + return(ret); +} + +int tcv_import(int opt, void *para1, void *para2) +{ + + int ret; + + if(verbose & TC_WATCH) + watch_import_module("tcv_import", opt, (transfer_t*) para1); + + ret = TCV_import(opt, para1, para2); + + if(ret==TC_IMPORT_ERROR && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "video import module error"); + + if(ret==TC_IMPORT_UNKNOWN && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "option %d unsupported by video import module", opt); + + return(ret); +} + +int tca_import(int opt, void *para1, void *para2) +{ + int ret; + + if(verbose & TC_WATCH) + watch_import_module("tca_import", opt, (transfer_t*) para1); + + ret = TCA_import(opt, para1, para2); + + if(ret==TC_IMPORT_ERROR && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "audio import module error"); + + if(ret==TC_IMPORT_UNKNOWN && (verbose & TC_DEBUG)) + tc_log_msg(__FILE__, "option %d unsupported by audio import module", opt); + + return(ret); +} + + +void *load_module(const char *mod_name, int mode) +{ + const char *error; + void *handle; + + if(mode & TC_EXPORT) { + + tc_snprintf(module, sizeof(module), "%s/export_%s.so", ((mod_path==NULL)? TC_DEFAULT_MOD_PATH:mod_path), mod_name); + + if(verbose & TC_DEBUG) + tc_log_msg(__FILE__, "loading %s export module %s", ((mode & TC_VIDEO)? "video": "audio"), module); + + handle = dlopen(module, RTLD_GLOBAL| RTLD_LAZY); + + if (!handle) { + error=dlerror(); + tc_warn("%s", error); + tc_warn("(%s) loading \"%s\" failed", __FILE__, module); + return(NULL); + } + + if(mode & TC_VIDEO) { + TCV_export = dlsym(handle, "tc_export"); + error = dlerror(); + if (error != NULL) { + tc_warn("%s", error); + return(NULL); + } + } + + if(mode & TC_AUDIO) { + TCA_export = dlsym(handle, "tc_export"); + error = dlerror(); + if (error != NULL) { + tc_warn("%s", error); + return(NULL); + } + } + + return(handle); + } + + + if(mode & TC_IMPORT) { + + tc_snprintf(module, sizeof(module), "%s/import_%s.so", ((mod_path==NULL)? TC_DEFAULT_MOD_PATH:mod_path), mod_name); + + if(verbose & TC_DEBUG) + tc_log_msg(__FILE__, "loading %s import module %s", ((mode & TC_VIDEO)? "video": "audio"), module); + + handle = dlopen(module, RTLD_GLOBAL| RTLD_LAZY); + + if (!handle) { + error = dlerror(); + tc_warn("%s", error); + return(NULL); + } + + if(mode & TC_VIDEO) { + TCV_import = dlsym(handle, "tc_import"); + if ((error = dlerror()) != NULL) { + tc_warn("%s", error); + return(NULL); + } + } + + + if(mode & TC_AUDIO) { + TCA_import = dlsym(handle, "tc_import"); + if ((error = dlerror()) != NULL) { + tc_warn("%s", error); + return(NULL); + } + } + + return(handle); + } + + // wrong mode? + return(NULL); +} + +void unload_module(void *handle) +{ + if (dlclose(handle) != 0) { + perror("unloading module"); + } + handle=NULL; +} + diff --git a/debian/transcode/transcode-1.1.7/src/dl_loader.h b/debian/transcode/transcode-1.1.7/src/dl_loader.h new file mode 100644 index 00000000..5ebfc941 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/dl_loader.h @@ -0,0 +1,40 @@ +/* + * dl_loader.h + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a video 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. + * + */ + +#ifndef _DL_LOADER_H +#define _DL_LOADER_H + +void *load_module(const char *mod, int mode); +void unload_module(void *handle); + +// extern int (*TCV_export)(int opt, void *para1, void *para2); +// extern int (*TCA_export)(int opt, void *para1, void *para2); +// extern int (*TCV_import)(int opt, void *para1, void *para2); +// extern int (*TCA_import)(int opt, void *para1, void *para2); + +int tcv_export(int opt, void *para1, void *para2); +int tca_export(int opt, void *para1, void *para2); +int tcv_import(int opt, void *para1, void *para2); +int tca_import(int opt, void *para1, void *para2); + +#endif diff --git a/debian/transcode/transcode-1.1.7/src/encoder-buffer.c b/debian/transcode/transcode-1.1.7/src/encoder-buffer.c new file mode 100644 index 00000000..53af7cba --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/encoder-buffer.c @@ -0,0 +1,408 @@ +/* + * encoder-buffer.c -- encoder interface to transcode frame ringbuffers. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - January 2006 + * + * This file is part of transcode, a 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "transcode.h" +#include "decoder.h" +#include "encoder.h" +#include "filter.h" +#include "framebuffer.h" +#include "counter.h" +#include "video_trans.h" +#include "audio_trans.h" +#include "frame_threads.h" + +#include <stdint.h> +#include <sys/types.h> + +/* + * Quick Summary: + * + * This code provide glue between frames ringbuffer and real encoder + * code, in order to make encoder modular and independent from + * a single frame source, to promote reusability (tcexport). + * This code also take acare of some oddities like handling filtering + * if no frame threads are avalaible. + * + * This code isn't clean as one (i.e.: me) would like since it + * must cope with a lot of legacy constraints and some other nasty + * stuff. Of course situation will be improved in future releases + * when we keep going away from legacy oddities and continue + * sanitize/modernize the codebase. + * + * NOTE about counter/condition/mutex handling inside various + * encoder helpers. + * + * Code are still a little bit confusing since things aren't + * updated or used at the same function level. + * Code works, but isn't still well readable. + * We need stil more cleanup and refactoring for future releases. + */ + + +/*************************************************************************/ +/*************************************************************************/ + +static int have_aud_threads = 0; +static int have_vid_threads = 0; + +/*************************************************************************/ + +/* + * apply_{video,audio}_filters: + * Apply the filter chain to current respectively video + * or audio frame. + * This function is used if no frame threads are avalaible, + * but of course filtering still must be applied. + * This function should be never exported. + * + * Parameters: + * vptr, aptr: respectively video or aufio framebuffer + * pointer to frame to be filtered. + * vob: pointer to vob_t structure holding stream + * parameters. + */ +static void apply_video_filters(vframe_list_t *vptr, vob_t *vob); +static void apply_audio_filters(aframe_list_t *aptr, vob_t *vob); + +/* + * encoder_acquire_{v,a}frame: + * Get respectively a new video or audio framebuffer for encoding. + * This roughly means: + * 1. to wait for a new frame avalaible for encoder + * 2. apply the filters if no frame threads are avalaible + * 3. apply the encoder filters (POST_S_PROCESS) + * 4. verify the status of audio framebuffer after all filtering. + * if acquired framebuffer is skipped, we must acquire a new one + * before continue with encoding, so we must restart from step 1. + * + * Parameters: + * buf: encoder buffer to use (and fullfull with acquired frame). + * vob: pointer to vob_t structure describing stream parameters. + * Used Internally. + * Return Value: + * 0 when a new framebuffer is avalaible for encoding; + * <0 if no more framebuffers are avalaible. + * As for encoder_wait_{v,a}frame, this usually happens + * when video/audio stream ends. + */ +static int encoder_acquire_vframe(TCEncoderBuffer *buf, vob_t *vob); +static int encoder_acquire_aframe(TCEncoderBuffer *buf, vob_t *vob); + +/* + * encoder_dispose_{v,a}frame: + * Mark a framebuffer (respectively video or audio) as completed + * from encoder viewpoint, so release it to source ringbuffer, + * update counters and do all cleanup actions needed internally. + * + * Parameters: + * buf: encoder buffer to use (release currente framebuffers + * and update related counters and internal variables). + * Return Value: + * None. + */ +static void encoder_dispose_vframe(TCEncoderBuffer *buf); +static void encoder_dispose_aframe(TCEncoderBuffer *buf); + + +/*************************************************************************/ + +static void apply_video_filters(vframe_list_t *vptr, vob_t *vob) +{ + if (!have_vid_threads) { + if (TC_FRAME_NEED_PROCESSING(vptr)) { + /* external plugin pre-processing */ + vptr->tag = TC_VIDEO|TC_PRE_M_PROCESS; + tc_filter_process((frame_list_t *)vptr); + + /* internal processing of video */ + vptr->tag = TC_VIDEO; + process_vid_frame(vob, vptr); + + /* external plugin post-processing */ + vptr->tag = TC_VIDEO|TC_POST_M_PROCESS; + tc_filter_process((frame_list_t *)vptr); + } + } + + if (TC_FRAME_NEED_PROCESSING(vptr)) { + /* second stage post-processing - (synchronous) */ + vptr->tag = TC_VIDEO|TC_POST_S_PROCESS; + tc_filter_process((frame_list_t *)vptr); + postprocess_vid_frame(vob, vptr); + /* preview _after_ all post-processing */ + vptr->tag = TC_VIDEO|TC_PREVIEW; + tc_filter_process((frame_list_t *)vptr); + } +} + +static void apply_audio_filters(aframe_list_t *aptr, vob_t *vob) +{ + /* now we try to process the audio frame */ + if (!have_aud_threads) { + if (TC_FRAME_NEED_PROCESSING(aptr)) { + /* external plugin pre-processing */ + aptr->tag = TC_AUDIO|TC_PRE_M_PROCESS; + tc_filter_process((frame_list_t *)aptr); + + /* internal processing of audio */ + aptr->tag = TC_AUDIO; + process_aud_frame(vob, aptr); + + /* external plugin post-processing */ + aptr->tag = TC_AUDIO|TC_POST_M_PROCESS; + tc_filter_process((frame_list_t *)aptr); + } + } + + if (TC_FRAME_NEED_PROCESSING(aptr)) { + /* second stage post-processing - (synchronous) */ + aptr->tag = TC_AUDIO|TC_POST_S_PROCESS; + tc_filter_process((frame_list_t *)aptr); + /* preview _after_ all post-processing */ + aptr->tag = TC_AUDIO|TC_PREVIEW; + tc_filter_process((frame_list_t *)aptr); + } +} + +static int encoder_acquire_vframe(TCEncoderBuffer *buf, vob_t *vob) +{ + int got_frame = TC_TRUE; + + do { + buf->vptr = vframe_retrieve(); + if (!buf->vptr) { + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "frame retrieve interrupted!"); + return -1; /* can't acquire video frame */ + } + got_frame = TC_TRUE; + buf->frame_id = buf->vptr->id + tc_get_frames_skipped_cloned(); + + if (verbose & TC_STATS) { + tc_log_info(__FILE__, "got frame %p (id=%i)", + buf->vptr, buf->frame_id); + } + + /* + * now we do the post processing ... this way, if just a video frame is + * skipped, we'll know. + * + * we have to check to make sure that before we do any processing + * that this frame isn't out of range (if it is, and one is using + * the "-t" split option, we'll see this frame again. + */ + apply_video_filters(buf->vptr, vob); + + if (buf->vptr->attributes & TC_FRAME_IS_SKIPPED) { + if (buf->vptr != NULL + && (buf->vptr->attributes & TC_FRAME_WAS_CLONED) + ) { + /* XXX do we want to track skipped cloned flags? */ + tc_update_frames_cloned(1); + } + + if (buf->vptr != NULL + && (buf->vptr->attributes & TC_FRAME_IS_CLONED) + ) { + /* XXX what to do when a frame is cloned and skipped? */ + /* + * I'd like to say they cancel, but perhaps they will end + * up also skipping the clone? or perhaps they'll keep, + * but modify the clone? Best to do the whole drill :/ + */ + if (verbose & TC_DEBUG) { + tc_log_info (__FILE__, "(%i) V pointer done. " + "Skipped and Cloned: (%i)", + buf->vptr->id, + (buf->vptr->attributes)); + } + + /* update flags */ + buf->vptr->attributes &= ~TC_FRAME_IS_CLONED; + buf->vptr->attributes |= TC_FRAME_WAS_CLONED; + /* + * this has to be done here, + * frame_threads.c won't see the frame again + */ + } + if (buf->vptr != NULL + && !(buf->vptr->attributes & TC_FRAME_IS_CLONED) + ) { + vframe_remove(buf->vptr); + /* reset pointer for next retrieve */ + buf->vptr = NULL; + } + // tc_update_frames_skipped(1); + got_frame = TC_FALSE; + } + } while (!got_frame); + + return 0; +} + +static int encoder_acquire_aframe(TCEncoderBuffer *buf, vob_t *vob) +{ + int got_frame = TC_TRUE; + + do { + buf->aptr = aframe_retrieve(); + if (!buf->aptr) { + if (verbose >= TC_THREADS) + tc_log_msg(__FILE__, "frame retrieve interrupted!"); + return -1; + } + got_frame = TC_TRUE; + + if (verbose & TC_STATS) { + tc_log_info(__FILE__, "got audio frame (id=%i)", buf->aptr->id); + } + + apply_audio_filters(buf->aptr, vob); + + if (buf->aptr->attributes & TC_FRAME_IS_SKIPPED) { + if (buf->aptr != NULL + && !(buf->aptr->attributes & TC_FRAME_IS_CLONED) + ) { + aframe_remove(buf->aptr); + /* reset pointer for next retrieve */ + buf->aptr = NULL; + } + + if (buf->aptr != NULL + && (buf->aptr->attributes & TC_FRAME_IS_CLONED) + ) { + if (verbose & TC_DEBUG) { + tc_log_info(__FILE__, "(%i) A pointer done. Skipped and Cloned: (%i)", + buf->aptr->id, (buf->aptr->attributes)); + } + + /* adjust clone flags */ + buf->aptr->attributes &= ~TC_FRAME_IS_CLONED; + buf->aptr->attributes |= TC_FRAME_WAS_CLONED; + + /* + * this has to be done here, + * frame_threads.c won't see the frame again + */ + } + got_frame = TC_FALSE; + } + } while (!got_frame); + + return 0; +} + + +static void encoder_dispose_vframe(TCEncoderBuffer *buf) +{ + if (buf->vptr != NULL + && (buf->vptr->attributes & TC_FRAME_WAS_CLONED) + ) { + tc_update_frames_cloned(1); + } + + if (buf->vptr != NULL + && !(buf->vptr->attributes & TC_FRAME_IS_CLONED) + ) { + vframe_remove(buf->vptr); + /* reset pointer for next retrieve */ + buf->vptr = NULL; + } + + if (buf->vptr != NULL + && (buf->vptr->attributes & TC_FRAME_IS_CLONED) + ) { + if(verbose & TC_DEBUG) { + tc_log_info(__FILE__, "(%i) V pointer done. Cloned: (%i)", + buf->vptr->id, (buf->vptr->attributes)); + } + buf->vptr->attributes &= ~TC_FRAME_IS_CLONED; + buf->vptr->attributes |= TC_FRAME_WAS_CLONED; + // update counter + //tc_update_frames_cloned(1); + } +} + + +static void encoder_dispose_aframe(TCEncoderBuffer *buf) +{ + if (buf->aptr != NULL + && !(buf->aptr->attributes & TC_FRAME_IS_CLONED) + ) { + aframe_remove(buf->aptr); + /* reset pointer for next retrieve */ + buf->aptr = NULL; + } + + if (buf->aptr != NULL + && (buf->aptr->attributes & TC_FRAME_IS_CLONED) + ) { + if (verbose & TC_DEBUG) { + tc_log_info(__FILE__, "(%i) A pointer done. Cloned: (%i)", + buf->aptr->id, (buf->aptr->attributes)); + } + + buf->aptr->attributes &= ~TC_FRAME_IS_CLONED; + buf->aptr->attributes |= TC_FRAME_WAS_CLONED; + } +} + + +static TCEncoderBuffer tc_builtin_buffer = { + .frame_id = 0, + + .vptr = NULL, + .aptr = NULL, + + .acquire_video_frame = encoder_acquire_vframe, + .acquire_audio_frame = encoder_acquire_aframe, + .dispose_video_frame = encoder_dispose_vframe, + .dispose_audio_frame = encoder_dispose_aframe, +}; + +/* default main transcode buffer */ +TCEncoderBuffer *tc_get_ringbuffer(int aworkers, int vworkers) +{ + have_aud_threads = aworkers; + have_vid_threads = vworkers; + + return &tc_builtin_buffer; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/encoder-common.c b/debian/transcode/transcode-1.1.7/src/encoder-common.c new file mode 100644 index 00000000..a9847315 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/encoder-common.c @@ -0,0 +1,215 @@ +/* + * encoder-common.c -- asynchronous encoder runtime control and statistics. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - January 2006 + * + * This file is part of transcode, a 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. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdint.h> +#include <unistd.h> +#include <pthread.h> +#include "libtc/libtc.h" +#include "encoder-common.h" +#include "tc_defaults.h" + + +/* volatile: for threadness paranoia */ +static int pause_flag = 0; + + +void tc_pause_request(void) +{ + pause_flag = !pause_flag; +} + +void tc_pause(void) +{ + while (pause_flag) { + usleep(TC_DELAY_MIN); + } +} + + +/* counter, for stats and more */ +static uint32_t frames_encoded = 0; +static uint32_t frames_dropped = 0; +static uint32_t frames_skipped = 0; +static uint32_t frames_cloned = 0; +/* counters can be accessed by other (ex: import) threads */ +static pthread_mutex_t frame_counter_lock = PTHREAD_MUTEX_INITIALIZER; + + +uint32_t tc_get_frames_encoded(void) +{ + uint32_t val; + + pthread_mutex_lock(&frame_counter_lock); + val = frames_encoded; + pthread_mutex_unlock(&frame_counter_lock); + + return val; +} + +void tc_update_frames_encoded(uint32_t val) +{ + pthread_mutex_lock(&frame_counter_lock); + frames_encoded += val; + pthread_mutex_unlock(&frame_counter_lock); +} + +uint32_t tc_get_frames_dropped(void) +{ + uint32_t val; + + pthread_mutex_lock(&frame_counter_lock); + val = frames_dropped; + pthread_mutex_unlock(&frame_counter_lock); + + return val; +} + +void tc_update_frames_dropped(uint32_t val) +{ + pthread_mutex_lock(&frame_counter_lock); + frames_dropped += val; + pthread_mutex_unlock(&frame_counter_lock); +} + +uint32_t tc_get_frames_skipped(void) +{ + uint32_t val; + + pthread_mutex_lock(&frame_counter_lock); + val = frames_skipped; + pthread_mutex_unlock(&frame_counter_lock); + + return val; +} + +void tc_update_frames_skipped(uint32_t val) +{ + pthread_mutex_lock(&frame_counter_lock); + frames_skipped += val; + pthread_mutex_unlock(&frame_counter_lock); +} + +uint32_t tc_get_frames_cloned(void) +{ + uint32_t val; + + pthread_mutex_lock(&frame_counter_lock); + val = frames_cloned; + pthread_mutex_unlock(&frame_counter_lock); + + return val; +} + +void tc_update_frames_cloned(uint32_t val) +{ + pthread_mutex_lock(&frame_counter_lock); + frames_cloned += val; + pthread_mutex_unlock(&frame_counter_lock); +} + +uint32_t tc_get_frames_skipped_cloned(void) +{ + uint32_t s, c; + + pthread_mutex_lock(&frame_counter_lock); + s = frames_skipped; + c = frames_cloned; + pthread_mutex_unlock(&frame_counter_lock); + + return (c - s); +} + +/*************************************************************************/ + +pthread_mutex_t run_status_lock = PTHREAD_MUTEX_INITIALIZER; +static volatile int tc_run_status = TC_STATUS_RUNNING; +/* `volatile' is for threading paranoia */ + +static TCRunStatus tc_get_run_status(void) +{ + TCRunStatus rs; + pthread_mutex_lock(&run_status_lock); + rs = tc_run_status; + pthread_mutex_unlock(&run_status_lock); + return rs; +} + +int tc_interrupted(void) +{ + return (TC_STATUS_INTERRUPTED == tc_get_run_status()); +} + +int tc_stopped(void) +{ + return (TC_STATUS_STOPPED == tc_get_run_status()); +} + +int tc_running(void) +{ + return (TC_STATUS_RUNNING == tc_get_run_status()); +} + +void tc_start(void) +{ + pthread_mutex_lock(&run_status_lock); + tc_run_status = TC_STATUS_RUNNING; + pthread_mutex_unlock(&run_status_lock); +} + +void tc_stop(void) +{ + pthread_mutex_lock(&run_status_lock); + /* no preemption, be polite */ + if (tc_run_status == TC_STATUS_RUNNING) { + tc_run_status = TC_STATUS_STOPPED; + } + pthread_mutex_unlock(&run_status_lock); +} + +void tc_interrupt(void) +{ + pthread_mutex_lock(&run_status_lock); + /* preempt and don't care of politeness. */ + if (tc_run_status != TC_STATUS_INTERRUPTED) { + tc_run_status = TC_STATUS_INTERRUPTED; + } + pthread_mutex_unlock(&run_status_lock); +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/encoder-common.h b/debian/transcode/transcode-1.1.7/src/encoder-common.h new file mode 100644 index 00000000..d1f3134b --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/encoder-common.h @@ -0,0 +1,188 @@ +/* + * encoder-common.h -- asynchronous encoder runtime control and statistics. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - January 2006 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef ENCODER_COMMON_H +#define ENCODER_COMMON_H + +/* + * MULTITHREADING NOTE: + * It is *GUARANTEED SAFE* to call those functions from different threads. + */ +/*************************************************************************/ + +/* + * tc_get_frames_{dropped,skipped,encoded,cloned,skipped_cloned}: + * get the current value of a frame counter. + * + * Parameters: + * None + * Return Value: + * the current value of requested counter + */ +uint32_t tc_get_frames_dropped(void); +uint32_t tc_get_frames_skipped(void); +uint32_t tc_get_frames_encoded(void); +uint32_t tc_get_frames_cloned(void); +uint32_t tc_get_frames_skipped_cloned(void); + +/* + * tc_update_frames_{dropped,skipped,encoded,cloned}: + * update the current value of a frame counter of a given value. + * + * Parameters: + * val: value to be added to the current value of requested counter. + * This parameter is usually just '1' (one) + * Return Value: + * None + */ +void tc_update_frames_dropped(uint32_t val); +void tc_update_frames_skipped(uint32_t val); +void tc_update_frames_encoded(uint32_t val); +void tc_update_frames_cloned(uint32_t val); + +/*************************************************************************/ + +/* + * tc_pause_request: + * toggle pausing; if pausing is enabled, further calls to tc_pause() + * will effectively pause application's current thread; otherwise, + * tc_pause() calls will do just nothing. + * + * Parameters: + * None. + * Return value: + * None. + */ +void tc_pause_request(void); + +/* + * tc_pause: + * if pausing enabled, so if tc_pause_request was previously called + * at least once, pause the current application thread for at least + * TC_DELAY_MIN microseconds. + * + * Parameters: + * None. + * Return value: + * None. + * Side effects: + * Incoming socket requests (see socket code), if any, will be handled + * before to return. + */ +void tc_pause(void); + +/*************************************************************************/ +/* encoder (core) run control */ +/*************************************************************************/ + +typedef enum tcrunstatus_ TCRunStatus; +enum tcrunstatus_ { + TC_STATUS_RUNNING = 0, /* default condition */ + TC_STATUS_STOPPED = 1, /* regular stop or end of stream reched */ + TC_STATUS_INTERRUPTED = -1, /* forced interruption (^C) */ +}; + +/* + * tc_interrupt: perform an hard stop of encoder core. + * This means that all transcode parts has to stop as soon and as quickly + * as is possible. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void tc_interrupt(void); + +/* + * tc_stop: perform a soft stop of encoder core. Tipically, this function + * is invoked after end of stream was reached, or after all requested + * stream ranges were encoded succesfully, to notify all the transcode + * parts to shutdown properly. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void tc_stop(void); + +/* + * tc_interrupted (Thread safe): verify if the encoder (core) was halted + * in response of an interruption. + * + * Parameters: + * None. + * Return Value: + * 1: halting cause of encoder (core) was (user) interruption (^C). + * 0: otherwise. + * + * PLEASE NOTE that if this function will return 0 even if encoder (core) + * IS STILL RUNNING! + */ +int tc_interrupted(void); + +/* + * tc_stopped (Thread safe): verify if the encoder (core) was halted + * regulary, most likely because end of stream was reached. + * + * Parameters: + * None. + * Return Value: + * 1: halting cause of encoder (core) was regular (EOS). + * 0: otherwise. + * + * PLEASE NOTE that if this function will return 0 even if encoder (core) + * IS STILL RUNNING! + */ +int tc_stopped(void); + +/* + * tc_running (Thread safe): checks if encoder (core) is still running. + * + * Parameters: + * None. + * Return Value: + * 1: encoder (core) is still running. + * 0: encoder (core) not running. + */ +int tc_running(void); + +/* + * tc_start: start the encoder core. Tipically, this function + * is invoked once at the start of the processing; however, some core modes + * (e.g. PSU mode) may require multiple start. + * Every call to this function should be paired with a tc_stop() call into + * the same code path; however, it is safe to call this function multiple + * times. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void tc_start(void); + +#endif /* ENCODER_COMMON_H */ diff --git a/debian/transcode/transcode-1.1.7/src/encoder.c b/debian/transcode/transcode-1.1.7/src/encoder.c new file mode 100644 index 00000000..d509cd3e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/encoder.c @@ -0,0 +1,1422 @@ +/* + * encoder.c -- transcode export layer module, implementation. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - January 2006 + * New rotation code written by + * Francesco Romani - May 2006 + * + * This file is part of transcode, a 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. + * + */ + +#define SUPPORT_OLD_ENCODER // for now + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "transcode.h" +#include "framebuffer.h" +#include "filter.h" +#include "counter.h" +#include "video_trans.h" +#include "audio_trans.h" +#include "decoder.h" +#include "encoder.h" +#include "frame_threads.h" + +#include "libtc/tcframes.h" + +#include <stdint.h> + +/*************************************************************************/ +/* Our data structure forward declaration */ + +typedef struct tcrotatecontext_ TCRotateContext; +typedef struct tcencoderdata_ TCEncoderData; + +/*************************************************************************/ +/* private function prototypes */ + +/* new-style rotation support */ +static void tc_rotate_init(TCRotateContext *rotor, + const char *video_base_name, + const char *audio_base_name); + +static void tc_rotate_set_frames_limit(TCRotateContext *rotor, + vob_t *vob, uint32_t frames); +static void tc_rotate_set_bytes_limit(TCRotateContext *rotor, + vob_t *vob, uint64_t bytes); + +static void tc_rotate_output_name(TCRotateContext *rotor, vob_t *vob); + +static int tc_rotate_if_needed_null(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes); +static int tc_rotate_if_needed_by_frames(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes); +static int tc_rotate_if_needed_by_bytes(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes); + +/* old-style rotation support is all public, see transcode.h */ + +/* new-style encoder */ + +static int encoder_export(TCEncoderData *data, vob_t *vob); +static void encoder_skip(TCEncoderData *data); +static int encoder_flush(TCEncoderData *data); + +/* rest of API is already public */ + +/* old-style encoder */ +#ifdef SUPPORT_OLD_ENCODER + +static int OLD_tc_export_setup(vob_t *vob, + const char *a_mod, const char *v_mod); +static void OLD_tc_export_shutdown(void); +static int OLD_tc_encoder_init(vob_t *vob); +static int OLD_tc_encoder_open(vob_t *vob); +static int OLD_tc_encoder_close(void); +static int OLD_tc_encoder_stop(void); +static int OLD_encoder_export(TCEncoderData *data, vob_t *vob); + +#endif // SUPPORT_OLD_ENCODER + +/* misc helpers */ +static int need_stop(TCEncoderData *encdata); +static int is_last_frame(TCEncoderData *encdata, int cluster_mode); +static void export_update_formats(vob_t *vob, const TCModuleInfo *vinfo, + const TCModuleInfo *ainfo); +static int alloc_buffers(TCEncoderData *data); +static void free_buffers(TCEncoderData *data); + + +/*************************************************************************/ + +/* + * new encoder module design principles + * 1) keep it simple, stupid + * 2) to have more than one encoder doesn't make sense in transcode, so + * 3) new encoder will be monothread, like the old one + */ + +/*************************************************************************/ +/*************************************************************************/ + +/* + * new-style output rotation support. Always avalaible, but + * only new code is supposed to use it. + * This code is private since only encoder code it's supposed + * to use it. If this change, this code will be put in a + * separate .c/.h pair. + * + * The tricky part of this code it's mainly the + * vob->{video,audio}_out_file mangling. + * This it's still done mainly for legacy reasons. + * After every rotation, such fields will be updated to point + * not to real initialization data, but to private buffers of (a) + * TCRotateContext strucutre. This can hardly seen as good, and + * should be changed/improved in future releases. + * Anyway, original values of mentioned field isn't lost since it + * will be stored in TCRotateContext.{video,audio}_base_name. + * ------------------------------------------------------------ + */ + +/* + * TCExportRotate: + * Generic function called after *every* frame was encoded. + * Rotate output file(s) if condition incapsulate in specific + * functions is satisfied. + * + * Parameters: + * rotor: TCRotateContext to use to check condition. + * vob: pointer to vob_t structure to update with new + * export file(s) name after succesfull rotation. + * bytes: total size of byte encoded (Audio + Video) in last + * rencoding loop. + * Return value: + * TC_OK: successful. + * TC_ERROR: error happened and notified using tc_log*(). + * + * Of course no error can happen if rotating condition isn't met + * (so no rotation it's supposed to happen). + * Please note that caller code CANNOT know when rotation happens: + * This is a feature, not a bug! Having rotation policy incapsulated + * into this code and rotation machinery transparent to caller + * it's EXACTLY the purpose oft this code! :) + */ +typedef int (*TCExportRotate)(TCRotateContext *rotor, vob_t *vob, + uint32_t bytes); + + +struct tcrotatecontext_ { + char video_path_buf[PATH_MAX+1]; + char audio_path_buf[PATH_MAX+1]; + const char *video_base_name; + const char *audio_base_name; + uint32_t chunk_num; + int null_flag; + + uint32_t chunk_frames; + + uint64_t encoded_bytes; + uint64_t chunk_bytes; + + TCExportRotate rotate_if_needed; +}; + +/*************************************************************************/ + + +/* macro goody for output rotation request */ +#define TC_ROTATE_IF_NEEDED(rotor, vob, bytes) \ + ((rotor)->rotate_if_needed((rotor), (vob), bytes)) + +/* + * tc_rotate_init: + * initialize a TCRotateContext with given basenames both for + * audio and video output files. + * Uses null rotation function as default rotation function: + * this means that output rotation just never happen. + * + * Parameters: + * rotor: pointer to a TCRotateContext structure to + * initialize. + * video_base_name: basename for main export file (Audio + Video). + * audio_base_name: basename for auxiliary export file + * (separate audio track). + * Return value: + * None. + */ +static void tc_rotate_init(TCRotateContext *rotor, + const char *video_base_name, + const char *audio_base_name) +{ + if (rotor != NULL) { + memset(rotor, 0, sizeof(TCRotateContext)); + rotor->video_base_name = video_base_name; + rotor->audio_base_name = audio_base_name; + if (video_base_name == NULL || strlen(video_base_name) == 0 + || strcmp(video_base_name, "/dev/null") == 0) { + rotor->null_flag = TC_TRUE; + } else { + rotor->null_flag = TC_FALSE; + strlcpy(rotor->video_path_buf, video_base_name, + sizeof(rotor->video_path_buf)); + /* + * FIXME: Yep, this taste like a duplicate. + * The whole *_out_file thing need a deep review, + * but I want to go a little ahead with the whole + * NMS-powered export layer and write a few more + * NMS export modules before to go with this. -- FR + */ + if (audio_base_name == NULL || strlen(audio_base_name) == 0 + || strcmp(audio_base_name, video_base_name) == 0 + || strcmp(audio_base_name, "/dev/null") == 0) { + /* + * DO NOT separate export audio track, use the same + * export file both for audio and for video + */ + strlcpy(rotor->audio_path_buf, video_base_name, + sizeof(rotor->audio_path_buf)); + } else { + /* separate audio file */ + strlcpy(rotor->audio_path_buf, audio_base_name, + sizeof(rotor->audio_path_buf)); + } + } + rotor->rotate_if_needed = tc_rotate_if_needed_null; + } +} + +/* + * tc_rotate_set {frames,bytes}_limit: + * setup respecitvely frames and bytes limit for each output chunk. + * When calling this function user ask for rotation, so they also + * directly updates vob.{video,audio}_out_file so even first + * tc_encoder_open() later call will uses names of the right format + * (i.e. with the same layout of second and further chunks). + * This is done in order to avoid any later rename() and disomogeneities + * in output file name as experienced in transcode 1.0.x and before. + * + * Calling this functions multiple times will not hurt anything, + * but only the last limit set will be honoured. In other words, + * it's impossible (yet) to limit output BOTH for frames and for size. + * This may change in future releases. + * + * Parameters: + * rotor: TCRotateContext to set limit on. + * vob: pointer to vob structure to update. + * frames: frame limit for each output chunk. + * bytes: size limit for each output chunk. + * Return value: + * None + * Side effects: + * vob parameter will be updated. Modified fields: + * video_out_file, audio_out_file. + */ + +#define PREPARE_OUTPUT_NAME(rotor, vob) \ + if ((rotor)->chunk_num == 0) \ + tc_rotate_output_name((rotor), (vob)) + +static void tc_rotate_set_frames_limit(TCRotateContext *rotor, + vob_t *vob, uint32_t frames) +{ + if (rotor != NULL && !rotor->null_flag) { + rotor->chunk_frames = frames; + rotor->rotate_if_needed = tc_rotate_if_needed_by_frames; + PREPARE_OUTPUT_NAME(rotor, vob); + } +} + +static void tc_rotate_set_bytes_limit(TCRotateContext *rotor, + vob_t *vob, uint64_t bytes) +{ + if (rotor != NULL && !rotor->null_flag) { + rotor->chunk_bytes = bytes; + rotor->rotate_if_needed = tc_rotate_if_needed_by_bytes; + PREPARE_OUTPUT_NAME(rotor, vob); + } +} + +#undef PREPARE_OUTPUT_NAME + +/* helpers ***************************************************************/ + +/* + * all rotation helpers uses at least if()s as possible, so we must + * drop paranoia here + */ + +/* + * tc_rotate_output_name: + * make names of new main/auxiliary output file chunk and updates + * vob fields accordingly. + * + * Parameters: + * rotor: TCRotateContext to use to make new output name(s). + * vob: pointer to vob_t structure to update. + * Return value: + * none. + */ + +/* pretty naif, yet. */ +/* FIXME: OK, we must deeply review the whole *out-file thing ASAP */ +static void tc_rotate_output_name(TCRotateContext *rotor, vob_t *vob) +{ + tc_snprintf(rotor->video_path_buf, sizeof(rotor->video_path_buf), + "%s-%03i", rotor->video_base_name, rotor->chunk_num); + tc_snprintf(rotor->audio_path_buf, sizeof(rotor->audio_path_buf), + "%s-%03i", rotor->audio_base_name, rotor->chunk_num); + vob->video_out_file = rotor->video_path_buf; + vob->audio_out_file = rotor->audio_path_buf; + rotor->chunk_num++; +} + +/*************************************************************************/ +/* + * real rotation policy implementations. Rotate output file(s) + * respectively: + * - never (_null) + * - when encoded frames reach limit (_by_frames) + * - when encoded AND written *bytes* reach limit (_by_bytes). + * + * For details see documentation of TCExportRotate above. + */ + +#define ROTATE_UPDATE_COUNTERS(bytes) do { \ + rotor->encoded_bytes += (bytes); \ +} while (0); + +static int tc_rotate_if_needed_null(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes) +{ + ROTATE_UPDATE_COUNTERS(bytes); + return TC_OK; +} + +#define ROTATE_COMMON_CODE(rotor, vob) do { \ + ret = tc_encoder_close(); \ + if (ret != TC_OK) { \ + tc_log_error(__FILE__, "unable to close output stream"); \ + ret = TC_ERROR; \ + } else { \ + tc_rotate_output_name((rotor), (vob)); \ + tc_log_info(__FILE__, "rotating video output stream to %s", \ + (rotor)->video_path_buf); \ + tc_log_info(__FILE__, "rotating audio output stream to %s", \ + (rotor)->audio_path_buf); \ + ret = tc_encoder_open((vob)); \ + if (ret != TC_OK) { \ + tc_log_error(__FILE__, "unable to reopen output stream"); \ + ret = TC_ERROR; \ + } \ + } \ +} while (0) + + +static int tc_rotate_if_needed_by_frames(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes) +{ + int ret = TC_OK; + ROTATE_UPDATE_COUNTERS(bytes); + + if (tc_get_frames_encoded() >= rotor->chunk_frames) { + ROTATE_COMMON_CODE(rotor, vob); + } + return ret; +} + +static int tc_rotate_if_needed_by_bytes(TCRotateContext *rotor, + vob_t *vob, uint32_t bytes) +{ + int ret = TC_OK; + ROTATE_UPDATE_COUNTERS(bytes); + + if (rotor->encoded_bytes >= rotor->chunk_bytes) { + ROTATE_COMMON_CODE(rotor, vob); + } + return ret; +} + +#undef ROTATE_COMMON_CODE +#undef ROTATE_UPDATE_COUNTERS + +/*************************************************************************/ +/*************************************************************************/ +/* real encoder code */ + + +struct tcencoderdata_ { + /* flags, used internally */ + int error_flag; + int fill_flag; + + /* frame boundaries */ + int frame_first; // XXX + int frame_last; // XXX + /* needed by encoder_skip */ + int saved_frame_last; // XXX + + int this_frame_last; // XXX + int old_frame_last; // XXX + + TCEncoderBuffer *buffer; + + vframe_list_t *venc_ptr; + aframe_list_t *aenc_ptr; + + TCFactory factory; + + TCModule vid_mod; + TCModule aud_mod; + TCModule mplex_mod; + + TCRotateContext rotor_data; + +#ifdef SUPPORT_OLD_ENCODER + transfer_t export_para; + + void *ex_a_handle; + void *ex_v_handle; +#endif +}; + +static TCEncoderData encdata = { + .error_flag = 0, + .fill_flag = 0, + .frame_first = 0, + .frame_last = 0, + .saved_frame_last = 0, + .this_frame_last = 0, + .old_frame_last = 0, + .buffer = NULL, + .venc_ptr = NULL, + .aenc_ptr = NULL, + .factory = NULL, + .vid_mod = NULL, + .aud_mod = NULL, + .mplex_mod = NULL, + /* rotor_data explicitely initialized later */ +#ifdef SUPPORT_OLD_ENCODER + .ex_a_handle = NULL, + .ex_v_handle = NULL, +#endif +}; + + +#define RESET_ATTRIBUTES(ptr) do { \ + (ptr)->attributes = 0; \ +} while (0) + +/* + * is_last_frame: + * check if current frame it's supposed to be the last one in + * encoding frame range. Catch all all known special cases + * + * Parameters: + * encdata: fetch current frame id from this structure reference. + * cluster_mode: boolean flag. When in cluster mode we need to take + * some special care. + * Return value: + * !0: current frame is supposed to be the last one + * 0: otherwise + */ +static int is_last_frame(TCEncoderData *encdata, int cluster_mode) +{ + int fid = encdata->buffer->frame_id; + if (cluster_mode) { + fid -= tc_get_frames_dropped(); + } + + if ((encdata->buffer->vptr->attributes & TC_FRAME_IS_END_OF_STREAM + || encdata->buffer->aptr->attributes & TC_FRAME_IS_END_OF_STREAM)) { + /* `consume' the flag(s) */ + encdata->buffer->vptr->attributes &= ~TC_FRAME_IS_END_OF_STREAM; + encdata->buffer->aptr->attributes &= ~TC_FRAME_IS_END_OF_STREAM; + return 1; + } + return (fid == encdata->frame_last); +} + +/* + * export_update_formats: + * coerce exported formats to the default ones from the loaded + * encoder modules IF AND ONLY IF user doesn't have requested + * specific ones. + * + * That's a temporary workaround until we have a full-NMS + * export layer. + * + * Parameters: + * vob: pointer to vob_t structure to update. + * vinfo: pointer to TCModuleInfo of video encoder module. + * ainfo: pointer to TCModuleInfo of audio encoder module. + * Return value: + * None + */ +static void export_update_formats(vob_t *vob, const TCModuleInfo *vinfo, + const TCModuleInfo *ainfo) +{ + if (vob == NULL || vinfo == NULL || ainfo == NULL) { + /* should never happen */ + tc_log_error(__FILE__, "missing export formats references"); + } + /* + * OK, that's pretty hackish since export_attributes should + * go away in near future. Neverthless, ex_a_codec features + * a pretty unuseful default (CODEC_MP3), so we don't use + * such default value to safely distinguish between -N given + * or not given. + * And so we must use another flag, and export_attributes are + * the simplest things that work, now/ + */ + if (!(vob->export_attributes & TC_EXPORT_ATTRIBUTE_VCODEC)) { + vob->ex_v_codec = vinfo->codecs_out[0]; + } + if (!(vob->export_attributes & TC_EXPORT_ATTRIBUTE_ACODEC)) { + vob->ex_a_codec = ainfo->codecs_out[0]; + } +} + +/* ------------------------------------------------------------ + * + * export init + * + * ------------------------------------------------------------*/ + +int tc_export_init(TCEncoderBuffer *buffer, TCFactory factory) +{ + if (buffer == NULL) { + tc_log_error(__FILE__, "missing encoder buffer reference"); + return TC_ERROR; + } + encdata.buffer = buffer; + +#ifndef SUPPORT_OLD_ENCODER // factory==NULL to signal use of old code + if (factory == NULL) { + tc_log_error(__FILE__, "missing factory reference"); + return TC_ERROR; + } +#endif + encdata.factory = factory; + return TC_OK; +} + +int tc_export_setup(vob_t *vob, + const char *a_mod, const char *v_mod, const char *m_mod) +{ + int match = 0; + const char *mod_name = NULL; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_export_setup(vob, a_mod, v_mod); +#endif + + if (verbose >= TC_DEBUG) { + tc_log_info(__FILE__, "loading export modules"); + } + + mod_name = (a_mod == NULL) ?TC_DEFAULT_EXPORT_AUDIO :a_mod; + encdata.aud_mod = tc_new_module(encdata.factory, "encode", mod_name, TC_AUDIO); + if (!encdata.aud_mod) { + tc_log_error(__FILE__, "can't load audio encoder"); + return TC_ERROR; + } + mod_name = (v_mod == NULL) ?TC_DEFAULT_EXPORT_VIDEO :v_mod; + encdata.vid_mod = tc_new_module(encdata.factory, "encode", mod_name, TC_VIDEO); + if (!encdata.vid_mod) { + tc_log_error(__FILE__, "can't load video encoder"); + return TC_ERROR; + } + mod_name = (m_mod == NULL) ?TC_DEFAULT_EXPORT_MPLEX :m_mod; + encdata.mplex_mod = tc_new_module(encdata.factory, "multiplex", mod_name, + TC_VIDEO|TC_AUDIO); + if (!encdata.mplex_mod) { + tc_log_error(__FILE__, "can't load multiplexor"); + return TC_ERROR; + } + export_update_formats(vob, tc_module_get_info(encdata.vid_mod), + tc_module_get_info(encdata.aud_mod)); + + match = tc_module_match(vob->ex_a_codec, + encdata.aud_mod, encdata.mplex_mod); + if (!match) { + tc_log_error(__FILE__, "audio encoder incompatible " + "with multiplexor"); + return TC_ERROR; + } + match = tc_module_match(vob->ex_v_codec, + encdata.vid_mod, encdata.mplex_mod); + if (!match) { + tc_log_error(__FILE__, "video encoder incompatible " + "with multiplexor"); + return TC_ERROR; + } + tc_rotate_init(&encdata.rotor_data, + vob->video_out_file, vob->audio_out_file); + + return TC_OK; +} + +/* ------------------------------------------------------------ + * + * export close, unload modules + * + * ------------------------------------------------------------*/ + +void tc_export_shutdown(void) +{ +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_export_shutdown(); +#endif + + if (verbose >= TC_DEBUG) { + tc_log_info(__FILE__, "unloading export modules"); + } + + tc_del_module(encdata.factory, encdata.mplex_mod); + tc_del_module(encdata.factory, encdata.vid_mod); + tc_del_module(encdata.factory, encdata.aud_mod); +} + + +/* ------------------------------------------------------------ + * + * encoder init + * + * ------------------------------------------------------------*/ + +int tc_encoder_init(vob_t *vob) +{ + int ret; + const char *options = NULL; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_encoder_init(vob); +#endif + + ret = alloc_buffers(&encdata); + if (ret != TC_OK) { + tc_log_error(__FILE__, "can't allocate encoder buffers"); + return TC_ERROR; + } + + options = (vob->ex_v_string) ?vob->ex_v_string :""; + ret = tc_module_configure(encdata.vid_mod, options, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "video export module error: init failed"); + return TC_ERROR; + } + + options = (vob->ex_a_string) ?vob->ex_a_string :""; + ret = tc_module_configure(encdata.aud_mod, options, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "audio export module error: init failed"); + return TC_ERROR; + } + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder open + * + * ------------------------------------------------------------*/ + +int tc_encoder_open(vob_t *vob) +{ + int ret; + const char *options = NULL; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_encoder_open(vob); +#endif + + options = vob->ex_m_string ? vob->ex_m_string : ""; + ret = tc_module_configure(encdata.mplex_mod, options, vob); + if (ret == TC_ERROR) { + tc_log_warn(__FILE__, "multiplexor module error: init failed"); + return TC_ERROR; + } + + // XXX + tc_module_pass_extradata(encdata.vid_mod, encdata.mplex_mod); + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder close + * + * ------------------------------------------------------------*/ + +int tc_encoder_close(void) +{ + int ret; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_encoder_close(); +#endif + + /* old style code handle flushing in modules, not here */ + ret = encoder_flush(&encdata); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "error while closing encoder: flush failed"); + return TC_ERROR; + } + + ret = tc_module_stop(encdata.mplex_mod); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "multiplexor module error: stop failed"); + return TC_ERROR; + } + + if(verbose >= TC_DEBUG) { + tc_log_info(__FILE__, "encoder closed"); + } + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder stop + * + * ------------------------------------------------------------*/ + +int tc_encoder_stop(void) +{ + int ret; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_tc_encoder_stop(); +#endif + + ret = tc_module_stop(encdata.vid_mod); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "video export module error: stop failed"); + return TC_ERROR; + } + + ret = tc_module_stop(encdata.aud_mod); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "audio export module error: stop failed"); + return TC_ERROR; + } + + free_buffers(&encdata); + + if(verbose >= TC_DEBUG) { + tc_log_info(__FILE__, "encoder stopped"); + } + return TC_OK; +} + +/* ------------------------------------------------------------ + * + * encoder main loop helpers + * + * ------------------------------------------------------------*/ + +static int alloc_buffers(TCEncoderData *data) +{ + data->venc_ptr = vframe_alloc_single(); + if (data->venc_ptr == NULL) { + goto no_vframe; + } + data->aenc_ptr = aframe_alloc_single(); + if (data->aenc_ptr == NULL) { + goto no_aframe; + } + return TC_OK; + +no_aframe: + tc_del_video_frame(data->venc_ptr); +no_vframe: + return TC_ERROR; +} + +static void free_buffers(TCEncoderData *data) +{ + tc_del_video_frame(data->venc_ptr); + tc_del_audio_frame(data->aenc_ptr); +} + +/* + * NOTE about counter/condition/mutex handling inside various + * encoder helpers. + * + * Code are still a little bit confusing since things aren't + * updated or used at the same function level. + * Code works, but isn't still well readable. + * We need stil more cleanup and refactoring for future releases. + */ + + +/* + * dispatch the acquired frames to encoder modules, and adjust frame counters + */ +static int encoder_export(TCEncoderData *data, vob_t *vob) +{ + int video_delayed = 0; + int ret; + +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return OLD_encoder_export(data, vob); +#endif + /* remove spurious attributes */ + RESET_ATTRIBUTES(data->venc_ptr); + RESET_ATTRIBUTES(data->aenc_ptr); + + /* step 1: encode video */ + ret = tc_module_encode_video(data->vid_mod, + data->buffer->vptr, data->venc_ptr); + if (ret != TC_OK) { + tc_log_error(__FILE__, "error encoding video frame"); + data->error_flag = 1; + } + if (data->venc_ptr->attributes & TC_FRAME_IS_DELAYED) { + data->venc_ptr->attributes &= ~TC_FRAME_IS_DELAYED; + video_delayed = 1; + } + + /* step 2: encode audio */ + if (video_delayed) { + data->buffer->aptr->attributes |= TC_FRAME_IS_CLONED; + tc_log_info(__FILE__, "Delaying audio"); + } else { + ret = tc_module_encode_audio(data->aud_mod, + data->buffer->aptr, data->aenc_ptr); + if (ret != TC_OK) { + tc_log_error(__FILE__, "error encoding audio frame"); + data->error_flag = 1; + } + } + + /* step 3: multiplex and rotate */ + // FIXME: Do we really need bytes-written returned from this, or can + // we just return TC_OK/TC_ERROR like other functions? --AC + ret = tc_module_multiplex(data->mplex_mod, + data->venc_ptr, data->aenc_ptr); + if (ret < 0) { + tc_log_error(__FILE__, "error multiplexing encoded frames"); + data->error_flag = 1; + } + data->error_flag = TC_ROTATE_IF_NEEDED(&encdata.rotor_data, vob, ret); + + /* step 4: show and update stats */ + if (tc_progress_meter) { + int last = (data->frame_last == TC_FRAME_LAST) + ?(-1) :data->frame_last; + if (!data->fill_flag) { + data->fill_flag = 1; + } + counter_print(1, data->buffer->frame_id, data->frame_first, last); + } + + tc_update_frames_encoded(1); + return (data->error_flag) ?TC_ERROR :TC_OK; +} + + +#define RETURN_IF_NOT_OK(RET, KIND) do { \ + if ((RET) != TC_OK) { \ + tc_log_error(__FILE__, "error encoding final %s frame", (KIND)); \ + return TC_ERROR; \ + } \ +} while (0) + +#define RETURN_IF_MUX_ERROR(BYTES) do { \ + if ((BYTES) < 0) { \ + tc_log_error(__FILE__, "error multiplexing final audio frame"); \ + return TC_ERROR; \ + } \ +} while (0) + + +/* DO NOT rotate here, this data belongs to current chunk */ +static int encoder_flush(TCEncoderData *data) +{ + int ret = TC_ERROR, bytes = 0; + + do { + RESET_ATTRIBUTES(data->aenc_ptr); + ret = tc_module_encode_audio(data->aud_mod, NULL, data->aenc_ptr); + RETURN_IF_NOT_OK(ret, "audio"); + + bytes = tc_module_multiplex(data->mplex_mod, NULL, data->aenc_ptr); + } while (bytes != 0); + RETURN_IF_MUX_ERROR(bytes); + + do { + RESET_ATTRIBUTES(data->venc_ptr); + ret = tc_module_encode_video(data->vid_mod, NULL, data->venc_ptr); + RETURN_IF_NOT_OK(ret, "video"); + + bytes = tc_module_multiplex(data->mplex_mod, data->venc_ptr, NULL); + } while (bytes != 0); + RETURN_IF_MUX_ERROR(bytes); + + return TC_OK; +} + + +void tc_export_rotation_limit_frames(vob_t *vob, uint32_t frames) +{ +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return; +#endif + + tc_rotate_set_frames_limit(&encdata.rotor_data, vob, frames); +} + +void tc_export_rotation_limit_megabytes(vob_t *vob, uint32_t megabytes) +{ +#ifdef SUPPORT_OLD_ENCODER + if (!encdata.factory) + return; +#endif + + tc_rotate_set_bytes_limit(&encdata.rotor_data, + vob, megabytes * 1024 * 1024); +} + +/*************************************************************************/ + +#ifdef SUPPORT_OLD_ENCODER + +#include "dl_loader.h" + +/* ------------------------------------------------------------ + * + * export init + * + * ------------------------------------------------------------*/ + +static int OLD_tc_export_setup(vob_t *vob, + const char *a_mod, const char *v_mod) +{ + const char *mod_name = NULL; + + // load export modules + mod_name = (a_mod == NULL) ?TC_DEFAULT_EXPORT_AUDIO :a_mod; + encdata.ex_a_handle = load_module((char*)mod_name, TC_EXPORT + TC_AUDIO); + if (encdata.ex_a_handle == NULL) { + tc_log_warn(__FILE__, "loading audio export module failed"); + return TC_ERROR; + } + + mod_name = (v_mod==NULL) ?TC_DEFAULT_EXPORT_VIDEO :v_mod; + encdata.ex_v_handle = load_module((char*)mod_name, TC_EXPORT + TC_VIDEO); + if (encdata.ex_v_handle == NULL) { + tc_log_warn(__FILE__, "loading video export module failed"); + return TC_ERROR; + } + + encdata.export_para.flag = verbose; + tca_export(TC_EXPORT_NAME, &encdata.export_para, NULL); + + if(encdata.export_para.flag != verbose) { + // module returned capability flag + int cc=0; + + if (verbose & TC_DEBUG) { + tc_log_info(__FILE__, "audio capability flag 0x%x | 0x%x", + encdata.export_para.flag, vob->im_a_codec); + } + + switch (vob->im_a_codec) { + case CODEC_PCM: + cc = (encdata.export_para.flag & TC_CAP_PCM); + break; + case CODEC_AC3: + cc = (encdata.export_para.flag & TC_CAP_AC3); + break; + case CODEC_RAW: + cc = (encdata.export_para.flag & TC_CAP_AUD); + break; + default: + cc = 0; + } + + if (cc == 0) { + tc_log_warn(__FILE__, "audio codec not supported by export module"); + return TC_ERROR; + } + } else { /* encdata.export_para.flag == verbose */ + if (vob->im_a_codec != CODEC_PCM) { + tc_log_warn(__FILE__, "audio codec not supported by export module"); + return TC_ERROR; + } + } + + encdata.export_para.flag = verbose; + tcv_export(TC_EXPORT_NAME, &encdata.export_para, NULL); + + if (encdata.export_para.flag != verbose) { + // module returned capability flag + int cc = 0; + + if (verbose & TC_DEBUG) { + tc_log_info(__FILE__, "video capability flag 0x%x | 0x%x", + encdata.export_para.flag, vob->im_v_codec); + } + + switch (vob->im_v_codec) { + case CODEC_RGB: + cc = (encdata.export_para.flag & TC_CAP_RGB); + break; + case CODEC_YUV: + cc = (encdata.export_para.flag & TC_CAP_YUV); + break; + case CODEC_YUV422: + cc = (encdata.export_para.flag & TC_CAP_YUV422); + break; + case CODEC_RAW: + case CODEC_RAW_YUV: /* fallthrough */ + cc = (encdata.export_para.flag & TC_CAP_VID); + break; + default: + cc = 0; + } + + if (cc == 0) { + tc_log_warn(__FILE__, "video codec not supported by export module"); + return TC_ERROR; + } + } else { /* encdata.export_para.flag == verbose */ + if (vob->im_v_codec != CODEC_RGB) { + tc_log_warn(__FILE__, "video codec not supported by export module"); + return TC_ERROR; + } + } + + return TC_OK; +} + +/* ------------------------------------------------------------ + * + * export close, unload modules + * + * ------------------------------------------------------------*/ + +static void OLD_tc_export_shutdown(void) +{ + if (verbose & TC_DEBUG) { + tc_log_info(__FILE__, "unloading export modules"); + } + + unload_module(encdata.ex_a_handle); + unload_module(encdata.ex_v_handle); +} + + +/* ------------------------------------------------------------ + * + * encoder init + * + * ------------------------------------------------------------*/ + +static int OLD_tc_encoder_init(vob_t *vob) +{ + int ret; + + encdata.export_para.flag = TC_VIDEO; + ret = tcv_export(TC_EXPORT_INIT, &encdata.export_para, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "video export module error: init failed"); + return TC_ERROR; + } + + encdata.export_para.flag = TC_AUDIO; + ret = tca_export(TC_EXPORT_INIT, &encdata.export_para, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "audio export module error: init failed"); + return TC_ERROR; + } + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder open + * + * ------------------------------------------------------------*/ + +static int OLD_tc_encoder_open(vob_t *vob) +{ + int ret; + + encdata.export_para.flag = TC_VIDEO; + ret = tcv_export(TC_EXPORT_OPEN, &encdata.export_para, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "video export module error: open failed"); + return TC_ERROR; + } + + encdata.export_para.flag = TC_AUDIO; + ret = tca_export(TC_EXPORT_OPEN, &encdata.export_para, vob); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "audio export module error: open failed"); + return TC_ERROR; + } + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder close + * + * ------------------------------------------------------------*/ + +static int OLD_tc_encoder_close(void) +{ + /* + * close, errors not fatal. + * flushing handled internally by export modules. + */ + + encdata.export_para.flag = TC_AUDIO; + tca_export(TC_EXPORT_CLOSE, &encdata.export_para, NULL); + + encdata.export_para.flag = TC_VIDEO; + tcv_export(TC_EXPORT_CLOSE, &encdata.export_para, NULL); + + if(verbose & TC_DEBUG) { + tc_log_info(__FILE__, "encoder closed"); + } + return TC_OK; +} + + +/* ------------------------------------------------------------ + * + * encoder stop + * + * ------------------------------------------------------------*/ + +static int OLD_tc_encoder_stop(void) +{ + int ret; + + encdata.export_para.flag = TC_VIDEO; + ret = tcv_export(TC_EXPORT_STOP, &encdata.export_para, NULL); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "video export module error: stop failed"); + return TC_ERROR; + } + + encdata.export_para.flag = TC_AUDIO; + ret = tca_export(TC_EXPORT_STOP, &encdata.export_para, NULL); + if (ret != TC_OK) { + tc_log_warn(__FILE__, "audio export module error: stop failed"); + return TC_ERROR; + } + + return TC_OK; +} + +/* + * dispatch the acquired frames to encoder modules + */ +static int OLD_encoder_export(TCEncoderData *data, vob_t *vob) +{ + int video_delayed = 0; + + /* encode and export video frame */ + data->export_para.buffer = data->buffer->vptr->video_buf; + data->export_para.size = data->buffer->vptr->video_size; + data->export_para.attributes = data->buffer->vptr->attributes; + if (data->buffer->vptr->attributes & TC_FRAME_IS_KEYFRAME) { + data->export_para.attributes |= TC_FRAME_IS_KEYFRAME; + } + data->export_para.flag = TC_VIDEO; + + if(tcv_export(TC_EXPORT_ENCODE, &data->export_para, vob) < 0) { + tc_log_warn(__FILE__, "error encoding video frame"); + data->error_flag = 1; + } + /* maybe clone? */ + data->buffer->vptr->attributes = data->export_para.attributes; + if (data->buffer->vptr->attributes & TC_FRAME_IS_DELAYED) { + data->buffer->vptr->attributes &= ~TC_FRAME_IS_DELAYED; + video_delayed = 1; + } + + /* encode and export audio frame */ + data->export_para.buffer = data->buffer->aptr->audio_buf; + data->export_para.size = data->buffer->aptr->audio_size; + data->export_para.attributes = data->buffer->aptr->attributes; + data->export_para.flag = TC_AUDIO; + + if(video_delayed > 0) { + data->buffer->aptr->attributes |= TC_FRAME_IS_CLONED; + tc_log_info(__FILE__, "Delaying audio"); + } else { + if (tca_export(TC_EXPORT_ENCODE, &data->export_para, vob) < 0) { + tc_log_warn(__FILE__, "error encoding audio frame"); + data->error_flag = 1; + } + + /* maybe clone? */ + data->buffer->aptr->attributes = data->export_para.attributes; + } + + if (tc_progress_meter) { + int last = (data->frame_last == TC_FRAME_LAST) ?(-1) :data->frame_last; + if (!data->fill_flag) { + data->fill_flag = 1; + } + counter_print(1, data->buffer->frame_id, data->frame_first, last); + } + + // XXX: _always_ update? + tc_update_frames_encoded(1); + return data->error_flag ? TC_ERROR : TC_OK; +} + + +/************************************************************************* + * old style rotation support code. + * TEMPORARY merged here until it will be deleted together with + * old encoder code when NMS-powered export layer is ready to switch + * (read: when we have enough encode/multiplexor module to go). + *************************************************************************/ + +#include "libtc/xio.h" + +//----------------------------------------------------------------- +// +// r,R - switch output file +// +//----------------------------------------------------------------- + +static int rotate_ctr = 0; +static int rotate_flag = 0; +static char *base = NULL; + +void tc_outstream_rotate_request(void) +{ + //set flag + rotate_flag = 1; +} + +void tc_outstream_rotate(void) +{ + char buf[TC_BUF_MAX]; + vob_t *vob=tc_get_vob(); + + if (!rotate_flag) + return; + + //reset flag to avoid re-entry + rotate_flag=0; + + // do not try to rename /dev/null + if(strcmp(vob->video_out_file, "/dev/null") == 0) + return; + + // get current outfile basename + base = tc_strdup(vob->video_out_file); + + //check + if (base == NULL) + return; + + // close output + if (tc_encoder_close()<0) + tc_error("failed to close output"); + + // create new filename + tc_snprintf(buf, sizeof(buf), "%s-%03d", base, rotate_ctr++); + //rename old outputfile + if (xio_rename(base, buf) < 0) + tc_error("failed to rename output file"); + + // reopen output + if (tc_encoder_open(vob) < 0) + tc_error("failed to open output"); + + tc_log_info(__FILE__, "outfile %s saved to %s", base, buf); + free(base); +} + +/*************************************************************************/ + +#endif // SUPPORT_OLD_ENCODER + +/* + * fake encoding, simply adjust frame counters. + */ +static void encoder_skip(TCEncoderData *data) +{ + if (tc_progress_meter) { + if (!data->fill_flag) { + data->fill_flag = 1; + } + counter_print(0, data->buffer->frame_id, data->saved_frame_last, + data->frame_first-1); + } + data->buffer->vptr->attributes |= TC_FRAME_IS_OUT_OF_RANGE; + data->buffer->aptr->attributes |= TC_FRAME_IS_OUT_OF_RANGE; +} + +static int need_stop(TCEncoderData *encdata) +{ + return !tc_running() || !tc_import_status() || encdata->error_flag; +} + +/* ------------------------------------------------------------ + * + * encoder main loop + * + * ------------------------------------------------------------*/ + +void tc_encoder_loop(vob_t *vob, int frame_first, int frame_last) +{ + int err = 0, eos = 0; /* End Of Stream flag */ + + if (encdata.this_frame_last != frame_last) { + encdata.old_frame_last = encdata.this_frame_last; + encdata.this_frame_last = frame_last; + } + + encdata.error_flag = 0; /* reset */ + encdata.frame_first = frame_first; + encdata.frame_last = frame_last; + encdata.saved_frame_last = encdata.old_frame_last; + + while (!need_stop(&encdata)) { + /* stop here if pause requested */ + tc_pause(); + + err = encdata.buffer->acquire_video_frame(encdata.buffer, vob); + if (err) { + if (verbose >= TC_DEBUG) { + tc_log_warn(__FILE__, "failed to acquire next raw video frame"); + } + break; /* can't acquire video frame */ + } + + err = encdata.buffer->acquire_audio_frame(encdata.buffer, vob); + if (err) { + if (verbose >= TC_DEBUG) { + tc_log_warn(__FILE__, "failed to acquire next raw audio frame"); + } + break; /* can't acquire frame */ + } + + eos = is_last_frame(&encdata, tc_cluster_mode); + if (eos) { + break; + } + + /* check frame id */ + if (frame_first <= encdata.buffer->frame_id + && encdata.buffer->frame_id < frame_last) { + encoder_export(&encdata, vob); + } else { /* frame not in range */ + encoder_skip(&encdata); + } /* frame processing loop */ + + /* release frame buffer memory */ + encdata.buffer->dispose_video_frame(encdata.buffer); + encdata.buffer->dispose_audio_frame(encdata.buffer); + } + /* main frame decoding loop */ + + if (verbose >= TC_CLEANUP) { + if (eos) { + tc_log_info(__FILE__, "encoder last frame finished (%i/%i)", + encdata.buffer->frame_id, encdata.frame_last); + } + tc_log_info(__FILE__, "export terminated - buffer(s) empty"); + } + return; +} + + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/encoder.h b/debian/transcode/transcode-1.1.7/src/encoder.h new file mode 100644 index 00000000..4ed26992 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/encoder.h @@ -0,0 +1,324 @@ +/* + * encoder.h - interface for the main encoder loop in transcode + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updated and partially rewritten by + * Francesco Romani - January 2006 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef ENCODER_H +#define ENCODER_H + +#include "transcode.h" +#include "framebuffer.h" + +#include "libtc/tcmodule-core.h" +#include "encoder-common.h" + +/* + * MULTITHREADING WARNING: + * It is in general *NOT SAFE* to call functions declared on this header from + * different threads. See comments below. + */ + +/************************************************************************* + * A TCEncoderBuffer structure, alog with it's operations, incapsulate + * the actions needed by encoder to acquire and dispose a single A/V frame + * to encode. + * + * Main purpose of this structure is to help to modularize and cleanup + * encoder core code. Unfortunately, a propoer cleanup and refactoring isn't + * fully possible without heavily reviewing transcode's inner frame buffering + * and frame handling, but this task is really critical and should planned + * really carefully. + * + * The need of TCEncoderBuffer also emerges given the actual frame buffer + * handling. TCEncoderBuffer operations take care of hide the most part + * of nasty stuff needed by current structure (see comments in + * encoder-buffer.c). + * + * A proper reorganization of frame handling core code will GREATLY shrink, + * or even make completely unuseful, the whole TCEncoderBuffer machinery. + */ +typedef struct tcencoderbuffer_ TCEncoderBuffer; +struct tcencoderbuffer_ { + int frame_id; /* current frame identifier (both for A and V, yet) */ + + vframe_list_t *vptr; /* current video frame */ + aframe_list_t *aptr; /* current audio frame */ + + int (*acquire_video_frame)(TCEncoderBuffer *buf, vob_t *vob); + int (*acquire_audio_frame)(TCEncoderBuffer *buf, vob_t *vob); + void (*dispose_video_frame)(TCEncoderBuffer *buf); + void (*dispose_audio_frame)(TCEncoderBuffer *buf); +}; + +/* default main transcode buffer */ +TCEncoderBuffer *tc_get_ringbuffer(int aworkers, int vworkers); + + +/** + * tc_export_{audio,video}_notify: + * notify encoder that a new {audio,video} frame is ready + * to be encoded. + * You NEED to call those functions to properly syncronize encoder + * and avoid deadlocks. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void tc_export_audio_notify(void); +void tc_export_video_notify(void); + +/*************************************************************************/ + + +/************************************************************************* + * helper routines. Client code needs to call those routines before + * (tc_export_init/tc_export_setup) or after (tc_export_shutdown) + * all the others. + */ + +/* + * tc_export_init: + * select a TCEncoderBuffer and a TCFactory to use for further + * operations. Both buffer and factory will be used until a new + * call to tc_export_init occurs. + * PLEASE NOTE: there NOT are sensible defaults, so client + * cose NEEDS to call this function as first one in code using + * encoder. + * + * Parameters: + * buffer: TCEncoderBuffer to use in future encoding loops. + * factory: TCFactory to use for future module (un)loading. + * Return Value: + * 0: succesfull + * !0: given one or more bad (NULL) value(s). + */ +int tc_export_init(TCEncoderBuffer *buffer, TCFactory factory); + +/* + * tc_export_setup: + * load export modules (encoders and multiplexor) using Module Factory + * selected via tc_export_init, checking if loaded modules are + * compatible with requested audio/video codec, and prepare for + * real encoding. + * + * Parameters: + * vob: pointer to vob_t. + * tc_export_setup need to fetch from a vob structure some informations + * needed by proper loading (es: module path). + * a_mod: name of audio encoder module to load. + * v_mod: name of video encoder module to load. + * m_mod: name of multiplexor module to load. + * Return Value: + * 0: succesfull + * <0: failure: failed to load one or more requested modules, + * *OR* there is at least one incompatibility between requested + * modules and requested codecs. + * (i.e. audio encoder module VS requested audio codec) + * (i.e. video encoder module VS multiplexor module) + * Preconditions: + * Module Factory avalaible and selected using tc_export_init. + */ +int tc_export_setup(vob_t *vob, + const char *a_mod, const char *v_mod, const char *m_mod); + + +/* + * tc_export_shutdown: + * revert operations done by tc_export_setup, unloading encoder and + * multiplexor modules. + * + * Parameters: + * None. + * Return Value: + * None. + * Preconditions: + * tc_export_setup() was previously called. To call this function if + * tc_export_setup() wasn't called will cause undefined behaviour. + */ +void tc_export_shutdown(void); + + +/************************************************************************* + * new-style output rotation support. + * This couple of functions + * tc_export_rotation_limit_frames + * tc_export_rotation_limit_megabytes + * + * Allow the client code to automatically split output into chunks by + * specifying a maxmimum size, resp. in frames OR in megabytes, for each + * output chunk. + * + * Those functions MUST BE used BEFORE to call first tc_encoder_open(), + * otherwise will fall into unspecifed behaviour. + * It's important to note that client code CAN call multiple times + * (even if isn't usually useful to do so ;) ) tc_export_rotation_limit*, + * but only one limit can be used, so the last limit set will be used. + * In other words, is NOT (yet) possible to limit output chunk size + * BOTH by frames and by size. + */ + +/* + * tc_export_rotation_limit_frames: + * rotate output file(s) every given amount of encoded frames. + * + * Parameters: + * vob: pointer to main vob_t structure. + * frames: maximum of frames that every output chunk should contain. + * Return value: + * None. + */ +void tc_export_rotation_limit_frames(vob_t *vob, uint32_t frames); + +/* + * tc_export_rotation_limit_megabytes: + * rotate output file(s) after a given amount of data was encoded. + * + * Parameters: + * vob: pointer to main vob_t structure. + * megabytes: maximum size that every output chunk should have. + * Return value: + * None. + */ +void tc_export_rotation_limit_megabytes(vob_t *vob, uint32_t megabytes); + + +/*************************************************************************/ + +/************************************************************************* + * main encoder API. + * + * There isn't explicit reference to encoder data structure, + * so there always be one and only one global hidden encoder instance. + * In current (and in the prevedible future) doesn't make sense to + * have more than one encoder, so it's instance is global, hidden, implicit. + * + * PLEASE NOTE: + * current encoder does not _explicitely_ use more than one thread. + * This means that audio and video encoding, as well as multiplexing, happens + * sequentially on the same (and unique) encoder thread. + * It's definitively possible (and already happens) that real encoder code loaded + * by modules uses internally more than one thread, but this is completely opaque + * to encoder. + */ + +/* + * tc_encoder_init: + * initialize the A/V encoders, by (re)configuring encoder modules. + * + * Parameters: + * vob: pointer to vob_t. + * tc_encoder_init need to fetch from a vob structure some informations + * needed by it's inizalitation. + * Return Value: + * -1: error configuring modules. Reason of error will be notified + * via tc_log*(). + * 0: succesfull. + */ +int tc_encoder_init(vob_t *vob); + +/* + * tc_encoder_open: + * open output file(s), by (re)configuring multiplexor module. + * + * Parameters: + * vob: pointer to vob_t. + * tc_encoder_open need to fetch from a vob structure some informations + * needed by it's inizalitation. + * Return Value: + * -1: error configuring module(s) or opening file(s). Reason of error will be + * notified via tc_log*(). + * 0: succesfull. + */ +int tc_encoder_open(vob_t *vob); + +/* + * tc_encoder_loop: + * encodes a range of frames from stream(s) using given settings. + * This is the main and inner encoding loop. + * Encoding usually halts with last frame in range is encountered, but + * it can also stop if some error happens when acquiring new frames, + * or, of course, if there is an asynchronous stop request + * Please note that FIRST frame in given range will be encoded, but + * LAST frame in given range will NOT. + * + * Parameters: + * vob: pointer to a vob_t structure holding both informations about + * input streams and settings for output streams + * (i.e.: bitrate, GOP size...). + * frame_first: sequence number of first frame in range to encode. + * All frames before this one will be acquired via + * TCEncoderBuffer routines, but will also be discarded. + * frame_last: sequence number of last frame in range to encode. + * *encoding halts when this frame is acquired*, so this + * frame will NOT encoded. + * Return Value: + * None. + * Preconditions: + * encoder properly initialized. This means: + * tc_export_init() called succesfully; + * tc_export_setup() called succesfully; + * tc_encoder_init() called succesfully; + * tc_encoder_open() called succesfully; + */ +void tc_encoder_loop(vob_t *vob, int frame_first, int frame_last); + +/* + * tc_encoder_stop: + * stop both the audio and the video encoders. + * + * Parameters: + * None. + * Return Value: + * 0: succesfull. + * <0: failure, reason will be notified via tc_log*(). + */ +int tc_encoder_stop(void); + +/* + * tc_encoder_close: + * stop multiplexor and close output file. + * + * Parameters: + * None. + * Return Value: + * 0: succesfull. + * <0: failure, reason will be notified via tc_log*(). + */ +int tc_encoder_close(void); + + +/*************************************************************************/ + +#endif /* ENCODER_H */ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/export_profile.c b/debian/transcode/transcode-1.1.7/src/export_profile.c new file mode 100644 index 00000000..08703fff --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/export_profile.c @@ -0,0 +1,511 @@ +/* + * export_profile.c -- transcode export profile support code - implementation + * (C) 2006-2010 - Francesco Romani <fromani at gmail dot com> + * + * This file is part of transcode, a 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 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <unistd.h> + +#include "export_profile.h" +#include "libtc/libtc.h" +#include "libtc/cfgfile.h" +#include "libtc/tccodecs.h" + +/* OK, that's quite ugly but I found nothing better, yet.*/ +#ifdef TCEXPORT_PROFILE +/* same value for both macros */ +# define TC_EXPORT_PROFILE_OPT "-P" +#else +# define TC_EXPORT_PROFILE_OPT "--export_prof" +#endif + +#define USER_PROF_PATH ".transcode/profiles" + +/* all needed support variables/data packed in a nice structure */ +typedef struct tcexportprofile_ TCExportProfile; + +struct tcexportprofile_ { + size_t profile_count; + char **profiles; + + TCExportInfo info; + + /* auxiliary variables */ + const char *video_codec; + const char *audio_codec; + + const char *pre_clip_area; + const char *post_clip_area; +}; + +#define CLIP_AREA_INIT { 0, 0, 0, 0 } + +/* used in tc_log_*() calls */ +const char *package = __FILE__; + +static TCExportProfile prof_data = { + .profile_count = 0, + .profiles = NULL, + + .video_codec = NULL, + .audio_codec = NULL, + .pre_clip_area = NULL, + .post_clip_area = NULL, + + /* + * we need to take care of strings deallocating + * them between module_read_config() calls, to + * avoid memleaks, so we use NULL marking here. + */ + .info.video.string = NULL, + .info.video.module = NULL, + .info.video.module_opts = NULL, + .info.video.log_file = NULL, + + .info.audio.string = NULL, + .info.audio.module = NULL, + .info.audio.module_opts = NULL, + + .info.mplex.string = NULL, + .info.mplex.module = NULL, + .info.mplex.module_opts = NULL, + .info.mplex.out_file = NULL, + .info.mplex.out_file_aux = NULL, + + /* standard initialization */ + .info.video.width = PAL_W, + .info.video.height = PAL_H, + .info.video.keep_asr_flag = TC_FALSE, + .info.video.fast_resize_flag = TC_FALSE, + .info.video.zoom_interlaced_flag = TC_FALSE, + .info.video.pre_clip = CLIP_AREA_INIT, + .info.video.post_clip = CLIP_AREA_INIT, + .info.video.frc = 3, // XXX (magic number) + .info.video.asr = -1, // XXX + .info.video.par = 0, + .info.video.encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE, + .info.video.gop_size = VKEYFRAMES, + .info.video.quantizer_min = VMINQUANTIZER, + .info.video.quantizer_max = VMAXQUANTIZER, + .info.video.format = CODEC_NULL, + .info.video.quality = -1, + .info.video.bitrate = VBITRATE, + .info.video.bitrate_max = VBITRATE, + .info.video.pass_number = VMULTIPASS, + + .info.audio.format = CODEC_NULL, + .info.audio.quality = -1, + .info.audio.bitrate = ABITRATE, + .info.audio.sample_rate = RATE, + .info.audio.sample_bits = BITS, + .info.audio.channels = CHANNELS, + .info.audio.mode = AMODE, + .info.audio.vbr_flag = TC_FALSE, + .info.audio.flush_flag = TC_FALSE, + .info.audio.bit_reservoir = TC_TRUE, +}; + +/* private helpers: declaration *******************************************/ + +/* + * tc_load_single_export_profile: + * tc_load_export_profile backend. + * Find and load, by looking into user profile path then into system + * profiles path, the i-th selected profile. + * If profile can't be loaded, it will just skipped. + * If verbose >= TC_DEBUG and the profile wasn't loaded, notify + * the user using tc_log*. + * If verbose >= TC_INFO and profile was loaded, notify the user + * using tc_log*. + * + * Parameters: + * i: load the i-th already parsed (see below) export profile. + * config: use this TCConfigEntry array, provided by frontend, to + * parse profile data file + * sys_path: system path to look for profile data + * user_path: user path to look for profile data + * Return value: + * 1: profile data succesfully loaded + * 0: profile data not loaded for some reasons (profile file + * not found or not readable). + * Side effects: + * Isn't a proper side effect, anyway it's worth to note that + * this function _will_ alter some data not explicitely provided, + * via config parameter. Note that this function WILL NOT alter + * config data, so caller providing config data will have full + * control of those unproper side effects by careful craft of + * TCConfigEntry data. + * Also note that this function mangle global private prof_data + * variable, most notably by invoking cleanup_strings on it + * to avoid memleaks in subsequent calls of module_read_config. + * Preconditions: + * this function should be always used _after_ a succesfull + * call to tc_setup_export_profiles, which parse the profiles + * selected by user. Otherwise it's still safe to call this function, + * but it always fail. + */ +static int tc_load_single_export_profile(int i, TCConfigEntry *config, + const char *sys_path, + const char *user_path); + +/* utilities used internally (yet) */ + +/* + * cleanup_strings: + * free()s and reset to NULL every not-NULL string in a given + * TCExportInfo structure + * + * Parameters: + * info: pointero to a TCExportInfo structure to cleanup. + * Return value: + * None. + */ +static void cleanup_strings(TCExportInfo *info); + +/* + * setup_clip_area: + * helper to parse a clipping area string into a TCArea structure. + * Automagically expand the clipping information using the same + * logic of transcode core (actually this code is a very + * little more than a rip-off form src/transcode.c). + * + * Parameters: + * str: clipping area string to parse. + * area: pointero to a TCArea where clipping parameters will be stored. + * Return value: + * 1: succesfull + * -1: error: malformed clipping string or bad parameters. + */ +static int setup_clip_area(const char *str, TCArea *area); + + +/*************************************************************************/ + +int tc_setup_export_profile(int *argc, char ***argv) +{ + const char *optval = NULL; + int ret; + + if (argc == NULL || argv == NULL) { + tc_log_warn(package, "tc_setup_export_profile: bad data reference"); + return -2; + } + + /* guess package name from command line */ + package = (*argv)[0]; + ret = tc_mangle_cmdline(argc, argv, + TC_EXPORT_PROFILE_OPT, &optval); + if (ret == 0) { /* success */ + prof_data.profiles = tc_strsplit(optval, ',', + &prof_data.profile_count); + ret = (int)prof_data.profile_count; + if (verbose >= TC_INFO) { + tc_log_info(package, "E: %-16s | %i", "profiles parsed", ret); + } + } + return ret; +} + +void tc_cleanup_export_profile(void) +{ + tc_strfreev(prof_data.profiles); + prof_data.profile_count = 0; + + cleanup_strings(&prof_data.info); +} + +const TCExportInfo *tc_load_export_profile(void) +{ + /* not all settings will be accessible from here */ + /* note static here */ + static TCConfigEntry profile_conf[] = { + /* video stuff */ + { "video_codec", &(prof_data.video_codec), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_module", &(prof_data.info.video.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_module_options", &(prof_data.info.video.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_fourcc", &(prof_data.info.video.string), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_bitrate", &(prof_data.info.video.bitrate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 12000000 }, + { "video_bitrate_max", &(prof_data.info.video.bitrate_max), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 12000000 }, + { "video_gop_size", &(prof_data.info.video.gop_size), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 2000 }, + { "video_encode_fields", &(prof_data.info.video.encode_fields), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 3 }, + // FIXME: switch to char/string? + { "video_frc", &(prof_data.info.video.frc), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 5 }, + { "video_asr", &(prof_data.info.video.asr), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 9 }, + { "video_par", &(prof_data.info.video.par), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 9 }, + // FIXME: expand achronym? + { "video_pre_clip", &(prof_data.pre_clip_area), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_post_clip", &(prof_data.post_clip_area), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_width", &(prof_data.info.video.width), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, + 1, TC_MAX_V_FRAME_WIDTH }, + { "video_height", &(prof_data.info.video.height), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, + 1, TC_MAX_V_FRAME_HEIGHT }, + { "video_keep_asr", &(prof_data.info.video.keep_asr_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + { "video_fast_resize", &(prof_data.info.video.fast_resize_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + { "video_zoom_interlaced", &(prof_data.info.video.zoom_interlaced_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + /* audio stuff */ + { "audio_codec", &(prof_data.audio_codec), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_module", &(prof_data.info.audio.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_module_options", &(prof_data.info.audio.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_bitrate", &(prof_data.info.audio.bitrate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000000 }, + { "audio_frequency", &(prof_data.info.audio.sample_rate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 48000 }, + // XXX: review min + { "audio_bits", &(prof_data.info.audio.sample_bits), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 8, 16 }, // XXX + { "audio_channels", &(prof_data.info.audio.channels), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 2 }, + /* multiplexing */ + { "mplex_module", &(prof_data.info.mplex.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "mplex_module_options", &(prof_data.info.mplex.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { NULL, NULL, 0, 0, 0, 0 } + }; + char home_path[PATH_MAX + 1]; + const char *home = getenv("HOME"); + int i = 0; + + if (home != NULL) { + tc_snprintf(home_path, sizeof(home_path), "%s/%s", + home, USER_PROF_PATH); + } else { + tc_log_warn(package, "can't determinate home directory!"); + return NULL; + } + + for (i = 0; i < prof_data.profile_count; i++) { + tc_load_single_export_profile(i, profile_conf, + PROF_PATH, home_path); + } + return &prof_data.info; +} + +/* it's pretty naif yet. FR */ +void tc_export_profile_to_vob(const TCExportInfo *info, vob_t *vob) +{ + if (info == NULL || vob == NULL) { + return; + } + vob->ex_v_string = info->video.module_opts; + vob->ex_a_string = info->audio.module_opts; + vob->ex_m_string = info->mplex.module_opts; + vob->ex_v_codec = info->video.format; + vob->ex_a_codec = info->audio.format; + vob->ex_v_fcc = info->video.string; + vob->ex_frc = info->video.frc; + vob->ex_asr = info->video.asr; + vob->ex_par = info->video.par; + vob->encode_fields = info->video.encode_fields; + vob->divxbitrate = info->video.bitrate; + vob->mp3bitrate = info->audio.bitrate; + vob->video_max_bitrate = info->video.bitrate_max; + vob->divxkeyframes = info->video.gop_size; + vob->mp3frequency = info->audio.sample_rate; + vob->dm_bits = info->audio.sample_bits; + vob->dm_chan = info->audio.channels; + vob->mp3mode = info->audio.mode; + vob->bitreservoir = info->audio.bit_reservoir; + vob->zoom_interlaced = info->video.zoom_interlaced_flag; + if (info->video.fast_resize_flag) { + tc_compute_fast_resize_values(vob, TC_FALSE); + } else { + vob->zoom_width = info->video.width; + vob->zoom_height = info->video.height; + } +} + +/************************************************************************* + * private helpers: implementation + **************************************************************************/ + +#define SETUP_CODEC(TYPE) do { \ + int codec = 0; /* shortcut */\ + if (prof_data.TYPE ## _codec != NULL) { \ + codec = tc_codec_from_string(prof_data.TYPE ## _codec); \ + prof_data.info.TYPE.format = codec; \ + tc_free((char*)prof_data.TYPE ## _codec); /* avoid const warning */ \ + prof_data.TYPE ## _codec = NULL; \ + } \ +} while (0) + +#define SETUP_CLIPPING(TYPE) do { \ + if (prof_data.TYPE ## _clip_area != NULL) { \ + memset(&(prof_data.info.video.TYPE ## _clip), 0, sizeof(TCArea)); \ + setup_clip_area(prof_data.TYPE ## _clip_area, \ + &(prof_data.info.video.TYPE ## _clip)); \ + tc_free((char*)prof_data.TYPE ## _clip_area); /* avoid const warning */ \ + prof_data.TYPE ## _clip_area = NULL; \ + } \ +} while (0) + +static int tc_load_single_export_profile(int i, TCConfigEntry *config, + const char *sys_path, + const char *user_path) +{ + int found = 0, ret = 0; + char path_buf[PATH_MAX+1]; + const char *basedir = NULL; + + if (sys_path == NULL || user_path == NULL || config == NULL + || ((i < 0) || i >= prof_data.profile_count)) { + /* paranoia */ + tc_log_warn(package, "tc_load_single_export_profile:" + " bad data reference"); + return -1; + } + + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s.cfg", + user_path, prof_data.profiles[i]); + ret = access(path_buf, R_OK); + if (ret == 0) { + found = 1; + basedir = user_path; + } else { + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s.cfg", + sys_path, prof_data.profiles[i]); + ret = access(path_buf, R_OK); + if (ret == 0) { + found = 1; + basedir = PROF_PATH; + } + } + + if (found) { + char prof_name[TC_BUF_MIN]; + cleanup_strings(&prof_data.info); + tc_snprintf(prof_name, sizeof(prof_name), "%s.cfg", + prof_data.profiles[i]); + + tc_set_config_dir(basedir); + ret = module_read_config(prof_name, NULL, config, package); + if (ret == 0) { + found = 0; /* module_read_config() failed */ + } else { + if (verbose >= TC_INFO) { + tc_log_info(package, "E: %-16s | %s", "loaded profile", + path_buf); + } + SETUP_CODEC(video); + SETUP_CODEC(audio); + SETUP_CLIPPING(pre); + SETUP_CLIPPING(post); + } + } else { + if (verbose >= TC_DEBUG) { + tc_log_warn(package, "E: %-16s | %s (skipped)", "unable to load", + path_buf); + } + } + return found; +} + +#undef SETUP_CODEC +#undef SETUP_CLIPPING + +/* + * module_read_config (used internally, see later) + * allocates new strings for option values, so + * we need to take care of them using this couple + * of functions + */ + +#define CLEANUP_STRING(FIELD) do { \ + if (info->FIELD != NULL) {\ + tc_free(info->FIELD); \ + info->FIELD = NULL; \ + } \ +} while (0) + +static void cleanup_strings(TCExportInfo *info) +{ + if (info != NULL) { + /* paranoia */ + + CLEANUP_STRING(video.string); + CLEANUP_STRING(video.module); + CLEANUP_STRING(video.module_opts); + CLEANUP_STRING(video.log_file); + + CLEANUP_STRING(audio.string); + CLEANUP_STRING(audio.module); + CLEANUP_STRING(audio.module_opts); + + CLEANUP_STRING(mplex.string); + CLEANUP_STRING(mplex.module); + CLEANUP_STRING(mplex.module_opts); + CLEANUP_STRING(mplex.out_file); + CLEANUP_STRING(mplex.out_file_aux); + } +} + +#undef CLEANUP_STRING + +/*************************************************************************/ + +static int setup_clip_area(const char *str, TCArea *area) +{ + int n = sscanf(str, "%i,%i,%i,%i", + &area->top, &area->left, &area->bottom, &area->right); + if (n < 0) { + return -1; + } + + /* symmetrical clipping for only 1-3 arguments */ + if (n == 1 || n == 2) { + area->bottom = area->top; + } + if (n == 2 || n == 3) { + area->right = area->left; + } + + return 1; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/export_profile.h b/debian/transcode/transcode-1.1.7/src/export_profile.h new file mode 100644 index 00000000..98ee9071 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/export_profile.h @@ -0,0 +1,116 @@ +/* + * export_profile.h -- transcode export profile support code - interface + * (C) 2006-2010 - Francesco Romani <fromani at gmail dot com> + * + * This file is part of transcode, a 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 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef EXPORT_PROFILE_H +#define EXPORT_PROFILE_H + +#include "transcode.h" +#include "tcinfo.h" + +/* + * GENERAL WARNING: none of those functions + * are intended to be thread-safe + */ + +/* + * tc_setup_export_profile: + * determine the export profile(s) to load later, by extracting + * informations by command line options (argc\argv). + * In more detail, this function handles '--export_prof PROFILE' + * option. + * Also removes used option by options array, so later processing + * of used option is easier. + * + * Parameters: + * argc: POINTER to integer representing the number of items in argv + * array. + * argv: POINTER to array of C-string representing option keys + * and values. + * Return value: + * -2: internal error + * -1: bad parameters (== NULL) + * 0: bad option value + * >0: succesfull, and return value is number of profile parsed. + * Side effects: + * if operation is succesfull AND if user provided (valid) --export_prof + * option, both option and it's argument are removed from argv vector, + * so *TWO* items of argv vector will be NULL-ified, and argc is + * decreased by two. + * This function also trasparently set some internal variables. + * Preconditions: + * argc != NULL + * argv != NULL + */ +int tc_setup_export_profile(int *argc, char ***argv); + +/* + * tc_setup_export_profile: + * release all resources acquired by tc_setup_export_profile. + * + * Parameters: + * None. + * Return vaule: + * None + */ +void tc_cleanup_export_profile(void); + +/* + * tc_load_export_profile: + * sequentially load all profiles recognized using + * tc_setup_export_profile, so if two or more profile specifies + * a value for an option, the later will prevail. + * + * Parameters: + * None + * Return value: + * if succesfull, return a pointer to a TCEXportInfo structure + * intialized with sensible defaults and containing the values + * set by loaded profile(s). There is no need to free() returned + * structure, it's handled internally. + * If an error happens, return NULL, and tc_log*() reason + * (see side effects below). + * Side effects: + * if verbose value is >= TC_DEBUG *AND* a profile can + * be loaded, tc_log'd out the unavalaible profile. + * if verbose value is >= TC_INFO, tc_log out every loaded + * profile. + */ +const TCExportInfo *tc_load_export_profile(void); + +/* + * tc_export_profile_to_vob: + * translate values stored in a TCExportInfo structure into + * a vob_t structure, doing the needed adaptations. + * This function ignore bad (or unreproducible, even if it's + * very unlikely) values/combination sotre in TCExportInfo + * structures reporting errors using tc_log*. + * + * Parameters: + * info: pointer to TCExportInfo to translate + * vob: pointer to vob_t storing translated values. + * Return value: + * None + * Side effects: + * tc_log*() is used internally. + */ +void tc_export_profile_to_vob(const TCExportInfo *info, vob_t *vob); + +#endif /* EXPORT_PROFILE_H */ diff --git a/debian/transcode/transcode-1.1.7/src/filter.c b/debian/transcode/transcode-1.1.7/src/filter.c new file mode 100644 index 00000000..8d4fe4e3 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/filter.c @@ -0,0 +1,603 @@ +/* + * filter.c -- audio/video filter handling + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "filter.h" + +// temp defines during module system switchover +//#define SUPPORT_NMS // support NMS modules? +#define SUPPORT_CLASSIC // support classic modules? + +#ifdef SUPPORT_CLASSIC +/* For dlopening old-style modules */ +# ifdef HAVE_DLFCN_H +# include <dlfcn.h> +# else +# ifdef OS_DARWIN +# include "libdldarwin/dlfcn.h" +# endif +# endif +#endif // SUPPORT_CLASSIC + +/*************************************************************************/ + +/* Data for a single filter instance. An ID value of 0 indicates that no + * filter is present. */ + +typedef struct FilterInstance_ { + char name[MAX_FILTER_NAME_LEN+1]; // Filter name + int id; // Unique ID value for this filter instance + int enabled; // Nonzero if filter is inabled +#ifdef SUPPORT_CLASSIC + void *handle; // DLL handle for old-style modules + TCFilterOldEntryFunc entry; // Module entry point for old-style modules +#endif +#ifdef SUPPORT_NMS +#error please add field(s) needed for NMS +#endif +} FilterInstance; + + +/* Flag: are we initialized? */ +static int initialized = 0; + +/* Filter instance table. */ +static FilterInstance filters[MAX_FILTERS]; + + +/* Macro to check that tc_filter_init() has been called, and abort the + * function otherwise. Pass the appropriate return value (nothing for a + * void function) as the macro parameter. */ +#define CHECK_INITIALIZED(...) do { \ + if (!initialized) { \ + tc_log_warn(__FILE__, "%s() called before initialization!", \ + __FUNCTION__); \ + return __VA_ARGS__; \ + } \ +} while (0) + +/*************************************************************************/ + +/** + * id_to_index: Local helper function to convert a filter ID value to a + * filters[] index. Outputs an error message with tc_log_warn() on error. + * + * Parameters: + * id: Filter ID. + * func: Calling function's name (__FUNCTION__). + * Return value: + * filters[] index corresponding to given ID, or -1 on error. + */ + +static int id_to_index(int id, const char *func) +{ + int i; + + if (id <= 0) { + tc_log_warn(__FILE__, "Bad filter ID %d passed to %s()", id, func); + return -1; + } + for (i = 0; i < MAX_FILTERS; i++) { + if (filters[i].id == id) + break; + } + if (i >= MAX_FILTERS) { + tc_log_warn(__FILE__, "Filter ID %d does not exist in %s()", id, func); + return -1; + } + return i; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* tc_filter_init: Initialize the filter subsystem. Must be called before + * any other filter functions. + * + * Parameters: + * None. + * Return value: + * Nonzero on success, zero on failure. + */ + +int tc_filter_init(void) +{ + int i; + + if (initialized) { + tc_log_warn(__FILE__, "tc_filter_init() called twice!"); + return 1; + } + for (i = 0; i < MAX_FILTERS; i++) + filters[i].id = 0; + initialized = 1; + return 1; +} + +/*************************************************************************/ + +/* tc_filter_fini: Close down the filter subsystem. If the filter system + * has not yet been initialized, do nothing. After calling this function, + * no filter functions (other than tc_filter_init()) may be called. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void tc_filter_fini(void) +{ + int i; + + if (!initialized) + return; + + for (i = 0; i < MAX_FILTERS; i++) { + if (filters[i].id != 0) + tc_filter_remove(filters[i].id); + } + + initialized = 0; +} + +/*************************************************************************/ + +/** + * tc_filter_process: Sends the given frame to all enabled filters for + * processing. + * + * Parameters: + * frame: Frame to process. + * Return value: + * None. + * Prerequisites: + * frame->tag is set to an appropriate value + */ + +void tc_filter_process(frame_list_t *frame) +{ + int last_id; + + CHECK_INITIALIZED(); + if (!frame) { + tc_log_warn(__FILE__, "tc_filter_process: frame is NULL!"); + return; + } + + /* The order of the filters is given by their ID values--however, this + * does not necessarily match the order in the filters[] array. We + * keep track of the last ID processed (starting at 0, lower than any + * valid ID), then each time through the loop, search for the lowest ID + * greater than that value, which will be the next filter to process. + * The loop ends when no enabled filter has an ID greater than the last + * ID processed. */ + + last_id = 0; + for (;;) { + int next_filter = -1, i; + + for (i = 0; i < MAX_FILTERS; i++) { + if (filters[i].id <= last_id || !filters[i].enabled) + continue; + if (next_filter < 0 || filters[i].id < filters[next_filter].id) + next_filter = i; + } + if (next_filter < 0) + break; + last_id = filters[next_filter].id; + +#ifdef SUPPORT_NMS +# error please write NMS support code +#endif + +#ifdef SUPPORT_CLASSIC + if (!filters[next_filter].entry) { + tc_log_warn(__FILE__, "Filter %s (%d) missing entry function" + " (bug?), disabling", filters[i].name, last_id); + filters[next_filter].enabled = 0; + continue; + } + frame->filter_id = last_id; + filters[next_filter].entry(frame, NULL); +#endif + } // for (;;) +} + +/*************************************************************************/ + +/** + * tc_filter_add: Adds the given filter at the end of the filter chain, + * and initializes it using the given option string. + * + * Parameters: + * name: Name of filter to add. + * options: Options to pass to filter (if NULL, no options are passed). + * Return value: + * Filter ID (nonzero) on success, zero on failure. + */ + +int tc_filter_add(const char *name, const char *options) +{ + int i, id; + + CHECK_INITIALIZED(0); + if (!name) { + tc_log_warn(__FILE__, "tc_filter_add: name is NULL!"); + return 0; + } else if (!*name) { + tc_log_warn(__FILE__, "tc_filter_add: name is empty!"); + return 0; + } else if (strlen(name) > MAX_FILTER_NAME_LEN) { + tc_log_warn(__FILE__, "tc_filter_add: name \"%s\" is too long!" + " (max %d chars)", name, MAX_FILTER_NAME_LEN); + return 0; + } + + /* Find the largest ID value currently in use, and use the next value */ + id = 0; + for (i = 0; i < MAX_FILTERS; i++) { + if (filters[i].id > id) + id = filters[i].id; + } + id++; + if (id <= 0) { // wraparound check + tc_log_warn(__FILE__, "tc_filter_add: out of filter IDs, restart %s", + PACKAGE); + return 0; + } + + /* Find the first available filter table entry, returning an error if + * none is found, and initialize the entry */ + for (i = 0; i < MAX_FILTERS; i++) { + if (!filters[i].id) + break; + } + if (i >= MAX_FILTERS) { + tc_log_warn(__FILE__, "tc_filter_add: no free filter slots! (max %d)", + MAX_FILTERS); + return 0; + } + strlcpy(filters[i].name, name, sizeof(filters[i].name)); + filters[i].enabled = 0; + +#ifdef SUPPORT_NMS +# error please write NMS support code +#endif + +#ifdef SUPPORT_CLASSIC + { + char path[1000]; + frame_list_t dummy_frame; + + /* Load the module and look up the tc_filter() address */ + if (tc_snprintf(path, sizeof(path), "%s/filter_%s.so", + tc_get_vob()->mod_path, name) < 0) { + tc_log_error(__FILE__, "tc_filter_add: path buffer overflow"); + return 0; + } + filters[i].handle = dlopen(path, RTLD_NOW); + if (!filters[i].handle) { + const char *error = dlerror(); + if (!error) + error = "Unknown error"; + tc_log_warn(PACKAGE, "Unable to load filter %s: %s", name, error); + return 0; + } + filters[i].entry = dlsym(filters[i].handle, "tc_filter"); + if (!filters[i].entry) { + const char *error = dlerror(); + if (!error) + error = "Unknown error (corrupt module?)"; + tc_log_warn(PACKAGE, "Unable to initialize filter %s: %s", + name, error); + dlclose(filters[i].handle); + return 0; + } + filters[i].id = id; /* loaded, at least */ + if (verbose & TC_DEBUG) + tc_log_msg(__FILE__, "tc_filter_add: module %s loaded", path); + + /* Call tc_filter() to initialize the module */ + dummy_frame.filter_id = id; + dummy_frame.tag = TC_FILTER_INIT; + /* Maximum size of a single frame, video or audio */ + dummy_frame.size = 0; + /* XXX: it seems never used, so 0 should be safe -- FR */ + if (filters[i].entry(&dummy_frame, (char *)options) < 0) { + tc_warn("Initialization of filter %s failed, skipping.", name); + tc_filter_remove(id); + } + if (verbose & TC_DEBUG) + tc_log_msg(__FILE__, "tc_filter_add: filter %s successfully" + " initialized", name); + } +#endif // SUPPORT_CLASSIC + + /* Module was successfully loaded and initialized, so enable it */ + filters[i].enabled = 1; + return 1; +} + +/*************************************************************************/ + +/** + * tc_filter_find: Return the ID for the named filter. + * + * Parameters: + * name: Name of filter to find. + * Return value: + * Filter ID (nonzero) on success, zero on error or if given filter is + * not loaded. + */ + +int tc_filter_find(const char *name) +{ + int i; + + CHECK_INITIALIZED(0); + for (i = 0; i < MAX_FILTERS; i++) { + if (strcmp(filters[i].name, name) == 0) + return filters[i].id; + } + return 0; +} + +/*************************************************************************/ + +/** + * tc_filter_remove: Remove the given filter. + * + * Parameters: + * id: ID of filter to remove. + * Return value: + * None. + */ + +void tc_filter_remove(int id) +{ + int i; + + CHECK_INITIALIZED(); + if ((i = id_to_index(id, __FUNCTION__)) < 0) + return; + +#ifdef SUPPORT_NMS +# error please write NMS support code +#endif + +#ifdef SUPPORT_CLASSIC + if (filters[i].handle) { + if (filters[i].entry) { + frame_list_t ptr; + ptr.tag = TC_FILTER_CLOSE; + ptr.filter_id = filters[i].id; + filters[i].entry(&ptr, NULL); + } else { + tc_log_warn(__FILE__, "Filter %s (%d) missing entry function" + " (bug?)", filters[i].name, id); + } + dlclose(filters[i].handle); + filters[i].handle = NULL; + filters[i].entry = NULL; + } +#endif + + memset(filters[i].name, 0, sizeof(filters[i].name)); + filters[i].id = 0; + filters[i].enabled = 0; +} + +/*************************************************************************/ + +/** + * tc_filter_enable: Enable the given filter. + * + * Parameters: + * id: ID of filter to enable. + * Return value: + * Nonzero on success, zero on error. + */ + +int tc_filter_enable(int id) +{ + int i; + + CHECK_INITIALIZED(0); + i = id_to_index(id, __FUNCTION__); + if (i < 0) + return 0; + filters[i].enabled = 1; + return 1; +} + +/*************************************************************************/ + +/** + * tc_filter_disable: Disable the given filter. + * + * Parameters: + * id: ID of filter to enable. + * Return value: + * Nonzero on success, zero on error. + */ + +int tc_filter_disable(int id) +{ + int i; + + CHECK_INITIALIZED(0); + i = id_to_index(id, __FUNCTION__); + if (i < 0) + return 0; + filters[i].enabled = 0; + return 1; +} + +/*************************************************************************/ + +/** + * tc_filter_configure: Configure the given filter. + * + * Parameters: + * id: ID of filter to configure. + * options: Option string for filter. + * Return value: + * Nonzero on success, zero on error. + */ + +int tc_filter_configure(int id, const char *options) +{ + int i; + + CHECK_INITIALIZED(0); + i = id_to_index(id, __FUNCTION__); + if (i < 0) + return 0; + +#ifdef SUPPORT_NMS +# error please write NMS support code +#endif + +#ifdef SUPPORT_CLASSIC + { + frame_list_t dummy_frame; + + if (!filters[i].entry) { + tc_log_warn(__FILE__, "Filter %s (%d) missing entry function" + " (bug?), disabling", filters[i].name, id); + filters[i].enabled = 0; + return 0; + } + /* Old filter API does a close before reconfiguring */ + dummy_frame.filter_id = id; + dummy_frame.tag = TC_FILTER_CLOSE; + filters[i].entry(&dummy_frame, NULL); + dummy_frame.filter_id = id; + dummy_frame.tag = TC_FILTER_INIT; + dummy_frame.size = 0; + /* XXX: it seems never used, so 0 should be safe -- FR */ + if (filters[i].entry(&dummy_frame, (char *)options) < 0) { + tc_log_warn(PACKAGE, "Reconfiguration of filter %s failed," + " disabling.", filters[i].name); + filters[i].enabled = 0; + return 0; + } + return 1; + } +#endif +} + +/*************************************************************************/ + +/** + * tc_filter_get_conf: Return current configuration information for the + * given option on the given filter. If `option' is NULL, returns a + * summary of configuration information in an undefined format. + * + * Parameters: + * id: ID of filter to retrieve configuration information for. + * option: Name of option to retrieve information for, or NULL. + * Return value: + * A pointer to an unmodifiable string containing the result, or NULL + * on error. + */ + +const char *tc_filter_get_conf(int id, const char *option) +{ + int i; + + CHECK_INITIALIZED(NULL); + i = id_to_index(id, __FUNCTION__); + if (i < 0) + return 0; + +#ifdef SUPPORT_NMS +# error please write NMS support code +#endif + +#ifdef SUPPORT_CLASSIC + { + frame_list_t dummy_frame; + static char buf[PATH_MAX]; + + memset(buf, 0, sizeof(buf)); + dummy_frame.filter_id = id; + dummy_frame.tag = TC_FILTER_GET_CONFIG; + if (filters[i].entry) { + if (filters[i].entry(&dummy_frame, buf) == 0) + return buf; + } else { + tc_log_warn(__FILE__, "Filter %s (%d) missing entry function" + " (bug?), disabling", filters[i].name, id); + filters[i].enabled = 0; + } + return NULL; + } +#endif +} + +/*************************************************************************/ + +/** + * tc_filter_list: Return a list of filters according to the given + * parameter. The list is comma-space separated, with each name enclosed + * in double quotes; filters are listed in the same order they are applied. + * + * Parameters: + * what: Selects what kind of modules to list (TC_FILTER_LIST_*). + * Return value: + * A pointer to an unmodifiable string containing the result. + */ + +const char *tc_filter_list(enum tc_filter_list_enum what) +{ + static char buf[MAX_FILTERS * (MAX_FILTER_NAME_LEN+4)]; + int last_id; + + *buf = 0; + CHECK_INITIALIZED(buf); + + /* Use the same logic as in tc_filter_process() to retrieve the filters + * in ID order. */ + last_id = 0; + for (;;) { + int next_filter = -1, i; + + for (i = 0; i < MAX_FILTERS; i++) { + if (filters[i].id <= last_id) + continue; + if (what == TC_FILTER_LIST_ENABLED && !filters[i].enabled) + continue; + if (what == TC_FILTER_LIST_DISABLED && filters[i].enabled) + continue; + if (next_filter < 0 || filters[i].id < filters[next_filter].id) + next_filter = i; + } + if (next_filter < 0) + break; + last_id = filters[next_filter].id; + tc_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s\"%s\"", + last_id==0 ? "" : ", ", filters[next_filter].name); + } + return buf; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/filter.h b/debian/transcode/transcode-1.1.7/src/filter.h new file mode 100644 index 00000000..dd2ca91e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/filter.h @@ -0,0 +1,62 @@ +/* + * filter.h -- audio/video filter include file + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef FILTER_H +#define FILTER_H + +#include "framebuffer.h" + +/*************************************************************************/ + +/* Maximum number of filter instances that can be loaded. */ +#define MAX_FILTERS 16 + +/* Maximum length of a filter name, in bytes. */ +#define MAX_FILTER_NAME_LEN 32 + +/* Parameters to tc_filter_list(). */ +enum tc_filter_list_enum { + TC_FILTER_LIST_LOADED, + TC_FILTER_LIST_ENABLED, + TC_FILTER_LIST_DISABLED, +}; + + +/* Filter interface functions. */ +extern int tc_filter_init(void); +extern void tc_filter_fini(void); +extern void tc_filter_process(frame_list_t *frame); +extern int tc_filter_add(const char *name, const char *options); +extern int tc_filter_find(const char *name); +extern void tc_filter_remove(int id); +extern int tc_filter_enable(int id); +extern int tc_filter_disable(int id); +extern int tc_filter_configure(int id, const char *options); +extern const char *tc_filter_get_conf(int id, const char *option); +extern const char *tc_filter_list(enum tc_filter_list_enum what); + +/* Type of the exported module entry point for the old module system, and a + * prototype for tc_filter() for those modules. */ +typedef int (*TCFilterOldEntryFunc)(void *ptr, char *options); +extern int tc_filter(frame_list_t *ptr, char *options); + +/*************************************************************************/ + +#endif /* FILTER_H */ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/frame_threads.c b/debian/transcode/transcode-1.1.7/src/frame_threads.c new file mode 100644 index 00000000..95a7ef7e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/frame_threads.c @@ -0,0 +1,376 @@ +/* + * frame_threads.c -- implementation of transcode multithreaded filter + * processing code. + * + * Copyright (C) Thomas Oestreich - June 2001 + * updates and partial rewrite: + * Copyright (C) Francesco Romani - October 2007 + * + * This file is part of transcode, a 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 <pthread.h> + +#include "transcode.h" +#include "encoder-common.h" +#include "framebuffer.h" +#include "video_trans.h" +#include "audio_trans.h" +#include "decoder.h" +#include "filter.h" + +#include "frame_threads.h" + +/*************************************************************************/ + +typedef struct tcframethreaddata_ TCFrameThreadData; +struct tcframethreaddata_ { + pthread_t threads[TC_FRAME_THREADS_MAX]; /* thread pool */ + int count; /* how many workers? */ + + pthread_mutex_t lock; + volatile int running; /* _pool_ running flag */ +}; + +TCFrameThreadData audio_threads = { + .count = 0, + .lock = PTHREAD_MUTEX_INITIALIZER, + .running = TC_FALSE, +}; + +TCFrameThreadData video_threads = { + .count = 0, + .lock = PTHREAD_MUTEX_INITIALIZER, + .running = TC_FALSE, +}; + +/*************************************************************************/ + +/* + * tc_frame_threads_stop (Thread safe): + * set the stop flag for a thread pool. + * + * Parameters: + * data: thread pool descriptor. + * Return Value: + * None. + */ +static void tc_frame_threads_stop(TCFrameThreadData *data) +{ + pthread_mutex_lock(&data->lock); + data->running = TC_FALSE; + pthread_mutex_unlock(&data->lock); +} + +/* + * tc_frame_threads_are_active (Thread safe): + * verify if there is a pending stop request for given thread pool. + * + * Parameters: + * data: thread pool descriptor. + * Return Value: + * !0: there is pending pool stop request. + * 0: otherwise. + */ +static int tc_frame_threads_are_active(TCFrameThreadData *data) +{ + int ret; + pthread_mutex_lock(&data->lock); + ret = data->running; + pthread_mutex_unlock(&data->lock); + return ret; +} + +/* + * stop_requested: verify if the pool thread has to stop. + * First thread in the pool notifying the core has to stop must + * set the flag to notify the others. + * + * Parameters: + * data: thread pool descriptor. + * Return Value: + * !0: thread pool has to halt as soon as is possible. + * 0: thread pool can continue to run. + */ +static int stop_requested(TCFrameThreadData *data) +{ + return (!tc_running() || !tc_frame_threads_are_active(data)); +} + + +/*************************************************************************/ +/* frame processing core threads */ +/*************************************************************************/ + + +#define DUP_vptr_if_cloned(vptr) do { \ + if(vptr->attributes & TC_FRAME_IS_CLONED) { \ + vframe_list_t *tmptr = vframe_dup(vptr); \ + \ + /* ptr was successfully cloned */ \ + /* delete clone flag */ \ + tmptr->attributes &= ~TC_FRAME_IS_CLONED; \ + vptr->attributes &= ~TC_FRAME_IS_CLONED; \ + \ + /* set info for filters */ \ + tmptr->attributes |= TC_FRAME_WAS_CLONED; \ + \ + /* this frame is to be processed _after_ the current one */ \ + /* so put it back into the queue */ \ + vframe_push_next(tmptr, TC_FRAME_WAIT); \ + \ + } \ +} while (0) + + + +#define DUP_aptr_if_cloned(aptr) do { \ + if(aptr->attributes & TC_FRAME_IS_CLONED) { \ + aframe_list_t *tmptr = aframe_dup(aptr); \ + \ + /* ptr was successfully cloned */ \ + \ + /* delete clone flag */ \ + tmptr->attributes &= ~TC_FRAME_IS_CLONED; \ + aptr->attributes &= ~TC_FRAME_IS_CLONED; \ + \ + /* set info for filters */ \ + tmptr->attributes |= TC_FRAME_WAS_CLONED; \ + \ + /* this frame is to be processed _after_ the current one */ \ + /* so put it back into the queue */ \ + aframe_push_next(tmptr, TC_FRAME_WAIT); \ + \ + } \ +} while (0) + + +#define SET_STOP_FLAG(DATAP, MSG) do { \ + if (verbose >= TC_CLEANUP) \ + tc_log_msg(__FILE__, "%s", (MSG)); \ + tc_frame_threads_stop((DATAP)); \ +} while (0) + +static void *process_video_frame(void *_vob) +{ + static int res = 0; // XXX + vframe_list_t *ptr = NULL; + vob_t *vob = _vob; + + while (!stop_requested(&video_threads)) { + ptr = vframe_reserve(); + if (ptr == NULL) { + SET_STOP_FLAG(&video_threads, "video interrupted: exiting!"); + res = 1; + break; + } + if (ptr->attributes & TC_FRAME_IS_END_OF_STREAM) { + SET_STOP_FLAG(&video_threads, "video stream end: marking!"); + } + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + vframe_remove(ptr); /* release frame buffer memory */ + continue; + } + + if (TC_FRAME_NEED_PROCESSING(ptr)) { + // external plugin pre-processing + ptr->tag = TC_VIDEO|TC_PRE_M_PROCESS; + tc_filter_process((frame_list_t *)ptr); + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + vframe_remove(ptr); /* release frame buffer memory */ + continue; + } + + // clone if the filter told us to do so. + DUP_vptr_if_cloned(ptr); + + // internal processing of video + ptr->tag = TC_VIDEO; + process_vid_frame(vob, ptr); + + // external plugin post-processing + ptr->tag = TC_VIDEO|TC_POST_M_PROCESS; + tc_filter_process((frame_list_t *)ptr); + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + vframe_remove(ptr); /* release frame buffer memory */ + continue; + } + } + + vframe_push_next(ptr, TC_FRAME_READY); + } + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "video stream end: got, so exiting!"); + + pthread_exit(&res); + return NULL; +} + + +static void *process_audio_frame(void *_vob) +{ + static int res = 0; // XXX + aframe_list_t *ptr = NULL; + vob_t *vob = _vob; + + while (!stop_requested(&audio_threads)) { + ptr = aframe_reserve(); + if (ptr == NULL) { + SET_STOP_FLAG(&audio_threads, "audio interrupted: exiting!"); + break; + res = 1; + } + if (ptr->attributes & TC_FRAME_IS_END_OF_STREAM) { + SET_STOP_FLAG(&audio_threads, "audio stream end: marking!"); + } + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + aframe_remove(ptr); /* release frame buffer memory */ + continue; + } + + if (TC_FRAME_NEED_PROCESSING(ptr)) { + // external plugin pre-processing + ptr->tag = TC_AUDIO|TC_PRE_M_PROCESS; + tc_filter_process((frame_list_t *)ptr); + + DUP_aptr_if_cloned(ptr); + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + aframe_remove(ptr); /* release frame buffer memory */ + continue; + } + + // internal processing of audio + ptr->tag = TC_AUDIO; + process_aud_frame(vob, ptr); + + // external plugin post-processing + ptr->tag = TC_AUDIO|TC_POST_M_PROCESS; + tc_filter_process((frame_list_t *)ptr); + + if (ptr->attributes & TC_FRAME_IS_SKIPPED) { + aframe_remove(ptr); /* release frame buffer memory */ + continue; + } + } + + aframe_push_next(ptr, TC_FRAME_READY); + } + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "audio stream end: got, so exiting!"); + + pthread_exit(&res); + return NULL; +} + +/*************************************************************************/ + + +int tc_frame_threads_have_video_workers(void) +{ + return (video_threads.count > 0); +} + +int tc_frame_threads_have_audio_workers(void) +{ + return (audio_threads.count > 0); +} + + +void tc_frame_threads_init(vob_t *vob, int vworkers, int aworkers) +{ + int n = 0; + + if (vworkers > 0 && !video_threads.running) { + video_threads.count = vworkers; + video_threads.running = TC_TRUE; /* enforce, needed when restarting */ + + if (verbose >= TC_DEBUG) + tc_log_info(__FILE__, "starting %i video frame" + " processing thread(s)", vworkers); + + // start the thread pool + for (n = 0; n < vworkers; n++) { + if (pthread_create(&video_threads.threads[n], NULL, + process_video_frame, vob) != 0) + tc_error("failed to start video frame processing thread"); + } + } + + if (aworkers > 0 && !audio_threads.running) { + audio_threads.count = aworkers; + audio_threads.running = TC_TRUE; /* enforce, needed when restarting */ + + if (verbose >= TC_DEBUG) + tc_log_info(__FILE__, "starting %i audio frame" + " processing thread(s)", aworkers); + + // start the thread pool + for (n = 0; n < aworkers; n++) { + if (pthread_create(&audio_threads.threads[n], NULL, + process_audio_frame, vob) != 0) + tc_error("failed to start audio frame processing thread"); + } + } + return; +} + +void tc_frame_threads_close(void) +{ + void *status = NULL; + int n = 0; + + if (audio_threads.count > 0) { + tc_frame_threads_stop(&audio_threads); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "wait for %i audio frame processing threads", + audio_threads.count); + for (n = 0; n < audio_threads.count; n++) + pthread_join(audio_threads.threads[n], &status); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "audio frame processing threads canceled"); + } + + if (video_threads.count > 0) { + tc_frame_threads_stop(&video_threads); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "wait for %i video frame processing threads", + video_threads.count); + for (n = 0; n < video_threads.count; n++) + pthread_join(video_threads.threads[n], &status); + if (verbose >= TC_CLEANUP) + tc_log_msg(__FILE__, "video frame processing threads canceled"); + } +} + + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/frame_threads.h b/debian/transcode/transcode-1.1.7/src/frame_threads.h new file mode 100644 index 00000000..05df5760 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/frame_threads.h @@ -0,0 +1,84 @@ +/* + * frame_threads.h -- declaration of transcode multithreaded filter + * processing code. + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef FRAME_THREADS_H +#define FRAME_THREADS_H + +#include "transcode.h" + +/* + * SUMMARY: + * + * Those are the frame processing threads, implementing the threaded + * filter layer. There isn't direct control to those threads. They + * start to run after init(), and they are stopped by fini(). + * It is important to note that each thread is equivalent to each + * other, and each one will take care of one frame and applies to + * it the whole filter chain. + */ + +/* + * tc_frame_threads_init: start the frame threads pool and implicitely + * and automatically starts the frame filter layer. + * + * Parameters: + * vob: vob structure. + * vworkers: number of threads in the video filter pool. + * aworkers: number of threads in the audio filter pool. + * Return Value: + * None. + */ +void tc_frame_threads_init(vob_t *vob, int vworkers, int aworkers); + +/* + * tc_frame_threads_close: destroy both audio and video filter pool threads, + * and automatically and implicitely stop the whole filter layer. + * It's important to note that this function assume that all processing loops + * are already been terminated. + * This is a blocking function. + * + * Parameters: + * None. + * Return Value: + * None. + * Preconditions: + * processing threads are terminated for any reason + * (regular stop, end of stream reached, forced interruption). + */ +void tc_frame_threads_close(void); + +/* + * tc_frame_threads_audio_{video,audio}_workers: + * query the number of avalaible (not active) audio,video frame + * worker threads. + * + * Parameters: + * None. + * Return Value: + * The number of avalaible audio,video frame worker threads. + */ +int tc_frame_threads_have_video_workers(void); +int tc_frame_threads_have_audio_workers(void); + +#endif /* FRAME_THREADS_H */ diff --git a/debian/transcode/transcode-1.1.7/src/framebuffer.c b/debian/transcode/transcode-1.1.7/src/framebuffer.c new file mode 100644 index 00000000..4a4d8cdd --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/framebuffer.c @@ -0,0 +1,1275 @@ +/* + * framebuffer.c -- audio/video frame ringbuffers, reloaded. + * (C) 2005-2010 - Francesco Romani <fromani -at- gmail -dot- com> + * Based on code + * (C) 2001-2006 - Thomas Oestreich. + * + * This file is part of transcode, a 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 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <pthread.h> + +#include "transcode.h" +#include "tc_defaults.h" +#include "framebuffer.h" +#include "decoder.h" +#include "encoder-common.h" + +#include "libtc/tcframes.h" +#include "libtc/ratiocodes.h" + +/* + * Summary: + * This code acts as generic ringbuffer implementation, with + * specializations for main (audio and video) ringbufffers + * in order to cope legacy constraints from 1.0.x series. + * It replaces former src/{audio,video}_buffer.c in (hopefully!) + * a more generic, clean, maintanable, compact way. + * + * Please note that there is *still* some other ringbuffer + * scatthered through codebase (subtitle buffer,d emux buffers, + * possibly more). They will be merged lately or will be dropped + * or reworked. + * + * This code can, of course, be further improved (and GREATLY improved, + * especially for multithreading safeness), but doing so + * hasn't high priority on my TODO list, I've covered with this + * piece of code most urgent todos for 1.1.0. -- FR + */ + +static pthread_mutex_t aframe_list_lock = PTHREAD_MUTEX_INITIALIZER; +static aframe_list_t *aframe_list_head = NULL; +static aframe_list_t *aframe_list_tail = NULL; +static pthread_cond_t audio_import_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t audio_filter_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t audio_export_cond = PTHREAD_COND_INITIALIZER; + +static pthread_mutex_t vframe_list_lock = PTHREAD_MUTEX_INITIALIZER; +static vframe_list_t *vframe_list_head = NULL; +static vframe_list_t *vframe_list_tail = NULL; +static pthread_cond_t video_import_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t video_filter_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t video_export_cond = PTHREAD_COND_INITIALIZER; + +void tc_framebuffer_interrupt_import(void) +{ + pthread_mutex_lock(&aframe_list_lock); + pthread_cond_signal(&audio_import_cond); + pthread_mutex_unlock(&aframe_list_lock); + + pthread_mutex_lock(&vframe_list_lock); + pthread_cond_signal(&video_import_cond); + pthread_mutex_unlock(&vframe_list_lock); +} + +void tc_framebuffer_interrupt(void) +{ + pthread_mutex_lock(&aframe_list_lock); + pthread_cond_signal(&audio_import_cond); + pthread_cond_broadcast(&audio_filter_cond); + /* filter layer deserves special care */ + pthread_cond_signal(&audio_export_cond); + pthread_mutex_unlock(&aframe_list_lock); + + pthread_mutex_lock(&vframe_list_lock); + pthread_cond_signal(&video_import_cond); + pthread_cond_broadcast(&video_filter_cond); + /* filter layer deserves special care */ + pthread_cond_signal(&video_export_cond); + pthread_mutex_unlock(&vframe_list_lock); +} + +/* ------------------------------------------------------------------ */ + +/* + * Layered, custom allocator/disposer for ringbuffer structures. + * The idea is to simplify (from ringbuffer viewpoint!) frame + * allocation/disposal and to make it as much generic as is possible + * (avoif if()s and so on). + */ + +typedef TCFramePtr (*TCFrameAllocFn)(const TCFrameSpecs *); + +typedef void (*TCFrameFreeFn)(TCFramePtr); + +/* ------------------------------------------------------------------ */ + +typedef struct tcringframebuffer_ TCRingFrameBuffer; +struct tcringframebuffer_ { + /* real ringbuffer */ + TCFramePtr *frames; + + /* indexes of ringbuffer */ + int next; + int last; + + /* counters. How many frames in various TCFrameStatus-es? */ + int null; + int empty; + int wait; + int locked; + int ready; + + /* what we need here? */ + const TCFrameSpecs *specs; + /* (de)allocation helpers */ + TCFrameAllocFn alloc; + TCFrameFreeFn free; +}; + +static TCRingFrameBuffer tc_audio_ringbuffer; +static TCRingFrameBuffer tc_video_ringbuffer; + +/* + * Specs used internally. I don't export this structure directly + * because I want to be free to change it if needed + */ +static TCFrameSpecs tc_specs = { + /* Largest supported values, to ensure the buffer is always big enough + * (see FIXME in tc_framebuffer_set_specs()) */ + .frc = 3, // PAL, why not + .width = TC_MAX_V_FRAME_WIDTH, + .height = TC_MAX_V_FRAME_HEIGHT, + .format = TC_CODEC_RGB, + .rate = RATE, + .channels = CHANNELS, + .bits = BITS, + .samples = 48000.0, +}; + +/* + * Frame allocation/disposal helpers, needed by code below + * thin wrappers around libtc facilities + * I don't care about layering and performance loss, *here*, because + * frame are supposed to be allocated/disposed ahead of time, and + * always outside inner (performance-sensitive) loops. + */ + +/* ------------------------------------------------------------------ */ + +#define TCFRAMEPTR_IS_NULL(tcf) (tcf.generic == NULL) + +static TCFramePtr tc_video_alloc(const TCFrameSpecs *specs) +{ + TCFramePtr frame; + frame.video = tc_new_video_frame(specs->width, specs->height, + specs->format, TC_FALSE); + return frame; +} + +static TCFramePtr tc_audio_alloc(const TCFrameSpecs *specs) +{ + TCFramePtr frame; + frame.audio = tc_new_audio_frame(specs->samples, specs->channels, + specs->bits); + return frame; +} + + +static void tc_video_free(TCFramePtr frame) +{ + tc_del_video_frame(frame.video); +} + +static void tc_audio_free(TCFramePtr frame) +{ + tc_del_audio_frame(frame.audio); +} + +/* ------------------------------------------------------------------ */ + +/* exported commodities :) */ + +vframe_list_t *vframe_alloc_single(void) +{ + return tc_new_video_frame(tc_specs.width, tc_specs.height, + tc_specs.format, TC_TRUE); +} + +aframe_list_t *aframe_alloc_single(void) +{ + return tc_new_audio_frame(tc_specs.samples, tc_specs.channels, + tc_specs.bits); +} + +/* ------------------------------------------------------------------ */ + +static void tc_ring_framebuffer_dump_status(const TCRingFrameBuffer *rfb, + const char *id) +{ + tc_log_msg(__FILE__, "%s: null=%i empty=%i wait=%i" + " locked=%i ready=%i", + id, rfb->null, rfb->empty, rfb->wait, + rfb->locked, rfb->ready); +} + + +const TCFrameSpecs *tc_framebuffer_get_specs(void) +{ + return &tc_specs; +} + +/* + * using an <OOP-ism>accessor</OOP-ism> is also justified here + * by the fact that we compute (ahead of time) samples value for + * later usage. + */ +void tc_framebuffer_set_specs(const TCFrameSpecs *specs) +{ + /* silently ignore NULL specs */ + if (specs != NULL) { + double fps; + + /* raw copy first */ + ac_memcpy(&tc_specs, specs, sizeof(TCFrameSpecs)); + + /* restore width/height/bpp + * (FIXME: temp until we have a way to know the max size that will + * be used through the decode/process/encode chain; without + * this, -V yuv420p -y raw -F rgb (e.g.) crashes with a + * buffer overrun) + */ + tc_specs.width = TC_MAX_V_FRAME_WIDTH; + tc_specs.height = TC_MAX_V_FRAME_HEIGHT; + tc_specs.format = TC_CODEC_RGB; + + /* then deduct missing parameters */ + if (tc_frc_code_to_value(tc_specs.frc, &fps) == TC_NULL_MATCH) { + fps = 1.0; /* sane, very worst case value */ + } +/* tc_specs.samples = (double)tc_specs.rate/fps; */ + tc_specs.samples = (double)tc_specs.rate; + /* + * FIXME + * ok, so we use a MUCH larger buffer (big enough to store 1 *second* + * of raw audio, not 1 *frame*) than needed for reasons similar as + * seen for above video. + * Most notably, this helps in keeping buffers large enough to be + * suitable for encoder flush (see encode_lame.c first). + */ + } +} + +/* ------------------------------------------------------------------ */ +/* NEW API, yet private */ +/* ------------------------------------------------------------------ */ + +/* + * Threading notice: + * + * Generic code doesn't use any locking at all (yet). + * That's was a design choice. For clarity, locking is + * provided by back-compatibility wrapper functions, + * or by any other higher-lever caller. + * + * Client code (= outside this code) MUST NEVER used not-thread + * safe code. + */ + + +/* + * tc_init_ring_framebuffer: (NOT thread safe) + * initialize a framebuffer ringbuffer by allocating needed + * amount of frames using given parameters. + * + * Parameters: + * rfb: ring framebuffer structure to initialize. + * specs: frame specifications to use for allocation. + * alloc: frame allocation function to use. + * free: frame disposal function to use. + * size: size of ringbuffer (number of frame to allocate) + * Return Value: + * > 0: wrong (NULL) parameters + * 0: succesfull + * < 0: allocation failed for one or more framesbuffers/internal error + */ +static int tc_init_ring_framebuffer(TCRingFrameBuffer *rfb, + const TCFrameSpecs *specs, + TCFrameAllocFn alloc, + TCFrameFreeFn free, + int size) +{ + if (rfb == NULL || specs == NULL || size < 0 + || alloc == NULL || free == NULL) { + return 1; + } + size = (size > 0) ?size :1; /* allocate at least one frame */ + + rfb->frames = tc_malloc(size * sizeof(TCFramePtr)); + if (rfb->frames == NULL) { + return -1; + } + + rfb->specs = specs; + rfb->alloc = alloc; + rfb->free = free; + + for (rfb->last = 0; rfb->last < size; rfb->last++) { + rfb->frames[rfb->last] = rfb->alloc(rfb->specs); + if (TCFRAMEPTR_IS_NULL(rfb->frames[rfb->last])) { + if (verbose >= TC_DEBUG) { + tc_log_error(__FILE__, "failed frame allocation"); + } + return -1; + } + + rfb->frames[rfb->last].generic->status = TC_FRAME_NULL; + rfb->frames[rfb->last].generic->bufid = rfb->last; + } + + rfb->next = 0; + + rfb->null = size; + rfb->empty = 0; + rfb->wait = 0; + rfb->locked = 0; + rfb->ready = 0; + + if (verbose >= TC_STATS) { + tc_log_info(__FILE__, "allocated %i frames in ringbuffer", size); + } + return 0; +} + +/* + * tc_fini_ring_framebuffer: (NOT thread safe) + * finalize a framebuffer ringbuffer by freeing all acquired + * resources (framebuffer memory). + * + * Parameters: + * rfb: ring framebuffer structure to finalize. + * Return Value: + * None. + */ +static void tc_fini_ring_framebuffer(TCRingFrameBuffer *rfb) +{ + if (rfb != NULL && rfb->free != NULL) { + int i = 0, n = rfb->last; + + for (i = 0; i < rfb->last; i++) { + rfb->free(rfb->frames[i]); + } + tc_free(rfb->frames); + rfb->last = 0; + + if (verbose >= TC_STATS) { + tc_log_info(__FILE__, "freed %i frames in ringbuffer", n); + } + } +} + +/* + * tc_ring_framebuffer_retrieve_frame: (NOT thread safe) + * retrieve next unclaimed (TC_FRAME_NULL) framebuffer from + * ringbuffer and return a pointer to it for later usage + * by client code. + * + * Parameters: + * rfb: ring framebuffer to use + * Return Value: + * Always a framebuffer generic pointer. That can be pointing to + * NULL if there aren't no more unclaimed (TC_FRAME_NULL) framebuffer + * avalaible; otherwise it contains + * a pointer to retrieved framebuffer. + * DO NOT *free() such pointer directly! use + * tc_ring_framebuffer_release_frame() instead! + */ +static TCFramePtr tc_ring_framebuffer_retrieve_frame(TCRingFrameBuffer *rfb) +{ + TCFramePtr ptr; + ptr.generic = NULL; + + if (rfb != NULL) { + int i = 0; + + ptr = rfb->frames[rfb->next]; + for (i = 0; i < rfb->last; i++) { + if (ptr.generic->status == TC_FRAME_NULL) { + break; + } + rfb->next++; + rfb->next %= rfb->last; + ptr = rfb->frames[rfb->next]; + } + + if (ptr.generic->status != TC_FRAME_NULL) { + if (verbose >= TC_FLIST) { + tc_log_warn(__FILE__, "retrieved buffer=%i, but not empty", + ptr.generic->status); + } + ptr.generic = NULL; /* enforce NULL-ness */ + } else { + if (verbose >= TC_FLIST) { + tc_log_msg(__FILE__, "retrieved buffer = %i [%i]", + rfb->next, ptr.generic->bufid); + } + /* adjust internal pointer */ + rfb->null--; + rfb->next++; + rfb->next %= rfb->last; + } + } + return ptr; +} + +/* + * tc_ring_framebuffer_release_frame: (NOT thread safe) + * release a previously retrieved frame back to ringbuffer, + * removing claim from it and making again avalaible (TC_FRAME_NULL). + * + * Parameters: + * rfb: ring framebuffer to use. + * frame: generic pointer to frame to release. + * Return Value: + * > 0: wrong (NULL) parameters. + * 0: succesfull + * < 0: internal error (frame to be released isn't empty). + */ +static int tc_ring_framebuffer_release_frame(TCRingFrameBuffer *rfb, + TCFramePtr frame) +{ + if (rfb == NULL || TCFRAMEPTR_IS_NULL(frame)) { + return 1; + } + if (verbose >= TC_FLIST) { + tc_log_msg(__FILE__, "releasing frame #%i [%i]", + frame.generic->bufid, rfb->next); + } + frame.generic->status = TC_FRAME_NULL; + rfb->null++; + return 0; +} + +/* + * tc_ring_framebuffer_register_frame: (NOT thread safe) + * retrieve and register a framebuffer from a ringbuffer by + * attaching an ID to it, setup properly status and updating + * internal ringbuffer counters. + * + * That's the function that client code is supposed to use + * (maybe wrapped by some thin macros to save status setting troubles). + * In general, dont' use retrieve_frame directly, use register_frame + * instead. + * + * Parameters: + * rfb: ring framebuffer to use + * id: id to attach to registered framebuffer + * status: status of framebuffer to register. This was needed to + * make registering process multi-purpose. + * Return Value: + * Always a generic framebuffer pointer. That can be pointing to NULL + * if there isn't no more framebuffer avalaible on given ringbuffer; + * otherwise, it will point to a valid framebuffer. + */ +static TCFramePtr tc_ring_framebuffer_register_frame(TCRingFrameBuffer *rfb, + int id, int status) +{ + TCFramePtr ptr; + + /* retrive a valid pointer from the pool */ + if (verbose >= TC_FLIST) { + tc_log_msg(__FILE__, "register frame id = %i", id); + } +#ifdef STATBUFFER + ptr = tc_ring_framebuffer_retrieve_frame(rfb); +#else + ptr = rfb->alloc(rfb->specs); +#endif + + if (!TCFRAMEPTR_IS_NULL(ptr)) { + if (status == TC_FRAME_EMPTY) { + rfb->empty++; + /* blank common attributes */ + memset(ptr.generic, 0, sizeof(frame_list_t)); + ptr.generic->id = id; + } else if (status == TC_FRAME_WAIT) { + rfb->wait++; + } + ptr.generic->status = status; + + /* enforce */ + ptr.generic->next = NULL; + ptr.generic->prev = NULL; + + if (verbose >= TC_FLIST) { + tc_ring_framebuffer_dump_status(rfb, "register_frame"); + } + } + return ptr; +} + +/* + * tc_ring_framebuffer_remove_frame: (NOT thread safe) + * De-register and release a given framebuffer; + * also updates internal ringbuffer counters. + * + * That's the function that client code is supposed to use. + * In general, don't use release_frame directly, use remove_frame + * instead. + * + * Parameters: + * rfb: ring framebuffer to use. + * frame: generic pointer to frambuffer to remove. + * Return Value: + * None. + */ +static void tc_ring_framebuffer_remove_frame(TCRingFrameBuffer *rfb, + TCFramePtr frame) +{ + if (rfb != NULL || !TCFRAMEPTR_IS_NULL(frame)) { + if (frame.generic->status == TC_FRAME_READY) { + rfb->ready--; + } + if (frame.generic->status == TC_FRAME_LOCKED) { + rfb->locked--; + } + /* release valid pointer to pool */ +#ifdef STATBUFFER + tc_ring_framebuffer_release_frame(rfb, frame); +#else + rfb->free(frame); +#endif + + if (verbose >= TC_FLIST) { + tc_ring_framebuffer_dump_status(rfb, "remove_frame"); + } + } +} + +/* ------------------------------------------------------------------ */ +/* Backwared-compatible API */ +/* ------------------------------------------------------------------ */ + +int aframe_alloc(int num) +{ + return tc_init_ring_framebuffer(&tc_audio_ringbuffer, &tc_specs, + tc_audio_alloc, tc_audio_free, num); +} + +int vframe_alloc(int num) +{ + return tc_init_ring_framebuffer(&tc_video_ringbuffer, &tc_specs, + tc_video_alloc, tc_video_free, num); +} + + +void aframe_free(void) +{ + tc_fini_ring_framebuffer(&tc_audio_ringbuffer); +} + +void vframe_free(void) +{ + tc_fini_ring_framebuffer(&tc_video_ringbuffer); +} + + +/* ------------------------------------------------------------------ */ + +/* + * Macro VS generic functions like above: + * + * I've used generic code and TCFramePtr in every place I was + * capable to introduce them in a *clean* way without using any + * casting. Of course there is still a lot of room for improvements, + * but back compatibility is an issue too. I'd like to get rid + * of all those macro and swtich to pure generic code of course, + * so this will be improved in future revisions. In the + * meantime, patches and suggestions welcome ;) -- FR + */ + +#define LIST_FRAME_APPEND(ptr, tail) do { \ + if ((tail) != NULL) { \ + (tail)->next = (ptr); \ + (ptr)->prev = (tail); \ + } \ + (tail) = (ptr); \ +} while (0) + +#define LIST_FRAME_INSERT(ptr, head) do { \ + if ((head) == NULL) { \ + (head) = ptr; \ + } \ +} while (0) + + +aframe_list_t *aframe_register(int id) +{ + int interrupted = TC_FALSE; + TCFramePtr frame; + + pthread_mutex_lock(&aframe_list_lock); + + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|register) requesting a new audio frame"); + + while ((!interrupted && tc_import_audio_running()) + && tc_audio_ringbuffer.null == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|register) audio frame not ready, waiting"); + pthread_cond_wait(&audio_import_cond, &aframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|register) audio frame wait ended"); + interrupted = !tc_running(); + } + + if (interrupted) { + frame.audio = NULL; + } else { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "new audio frame ready"); + + frame = tc_ring_framebuffer_register_frame(&tc_audio_ringbuffer, + id, TC_FRAME_EMPTY); + if (!TCFRAMEPTR_IS_NULL(frame)) { + /* + * complete initialization: + * backward-compatible stuff + */ + LIST_FRAME_APPEND(frame.audio, aframe_list_tail); + /* first frame registered must set aframe_list_head */ + LIST_FRAME_INSERT(frame.audio, aframe_list_head); + } + } + pthread_mutex_unlock(&aframe_list_lock); + return frame.audio; +} + +vframe_list_t *vframe_register(int id) +{ + int interrupted = TC_FALSE; + TCFramePtr frame; + + pthread_mutex_lock(&vframe_list_lock); + + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|register) requesting a new video frame"); + + while ((!interrupted && tc_import_video_running()) + && tc_video_ringbuffer.null == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|register) video frame not ready, waiting"); + pthread_cond_wait(&video_import_cond, &vframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|register) video frame wait ended"); + interrupted = !tc_running(); + } + + if (interrupted) { + frame.video = NULL; + } else { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "new video frame ready"); + + frame = tc_ring_framebuffer_register_frame(&tc_video_ringbuffer, + id, TC_FRAME_EMPTY); + if (!TCFRAMEPTR_IS_NULL(frame)) { + /* + * complete initialization: + * backward-compatible stuff + */ + LIST_FRAME_APPEND(frame.video, vframe_list_tail); + /* first frame registered must set vframe_list_head */ + LIST_FRAME_INSERT(frame.video, vframe_list_head); + } + } + pthread_mutex_unlock(&vframe_list_lock); + return frame.video; +} + + +/* ------------------------------------------------------------------ */ + + +#define LIST_FRAME_LINK(ptr, f, tail) do { \ + /* insert after ptr */ \ + (ptr)->next = (f)->next; \ + (f)->next = (ptr); \ + (ptr)->prev = (f); \ + \ + if ((ptr)->next == NULL) { \ + /* must be last ptr in the list */ \ + (tail) = (ptr); \ + } \ +} while (0) + + +aframe_list_t *aframe_dup(aframe_list_t *f) +{ + int interrupted = TC_FALSE; + TCFramePtr frame; + + if (f == NULL) { + tc_log_warn(__FILE__, "aframe_dup: empty frame"); + return NULL; + } + + pthread_mutex_lock(&aframe_list_lock); + + while (!interrupted && tc_audio_ringbuffer.null == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|dup) audio frame not ready, waiting"); + pthread_cond_wait(&audio_import_cond, &aframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|dup) audio frame wait ended"); + interrupted = !tc_running(); + } + + frame = tc_ring_framebuffer_register_frame(&tc_audio_ringbuffer, + 0, TC_FRAME_WAIT); + if (!TCFRAMEPTR_IS_NULL(frame)) { + aframe_copy(frame.audio, f, 1); + + LIST_FRAME_LINK(frame.audio, f, aframe_list_tail); + } + pthread_mutex_unlock(&aframe_list_lock); + return frame.audio; +} + +vframe_list_t *vframe_dup(vframe_list_t *f) +{ + int interrupted = TC_FALSE; + TCFramePtr frame; + + if (f == NULL) { + tc_log_warn(__FILE__, "vframe_dup: empty frame"); + return NULL; + } + + pthread_mutex_lock(&vframe_list_lock); + + while (!interrupted && tc_video_ringbuffer.null == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|dup) video frame not ready, waiting"); + pthread_cond_wait(&video_import_cond, &vframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|dup) video frame wait ended"); + interrupted = !tc_running(); + } + + frame = tc_ring_framebuffer_register_frame(&tc_video_ringbuffer, + 0, TC_FRAME_WAIT); + if (!TCFRAMEPTR_IS_NULL(frame)) { + vframe_copy(frame.video, f, 1); + + LIST_FRAME_LINK(frame.video, f, vframe_list_tail); + } + pthread_mutex_unlock(&vframe_list_lock); + return frame.video; +} + + +/* ------------------------------------------------------------------ */ + +#define LIST_FRAME_REMOVE(ptr, head, tail) do { \ + if ((ptr)->prev != NULL) { \ + ((ptr)->prev)->next = (ptr)->next; \ + } \ + if ((ptr)->next != NULL) { \ + ((ptr)->next)->prev = (ptr)->prev; \ + } \ + \ + if ((ptr) == (tail)) { \ + (tail) = (ptr)->prev; \ + } \ + if ((ptr) == (head)) { \ + (head) = (ptr)->next; \ + } \ +} while (0) + +void aframe_remove(aframe_list_t *ptr) +{ + if (ptr == NULL) { + tc_log_warn(__FILE__, "aframe_remove: given NULL frame pointer"); + } else { + TCFramePtr frame; + frame.audio = ptr; + + pthread_mutex_lock(&aframe_list_lock); + + LIST_FRAME_REMOVE(ptr, aframe_list_head, aframe_list_tail); + + tc_ring_framebuffer_remove_frame(&tc_audio_ringbuffer, frame); + pthread_cond_signal(&audio_import_cond); + + pthread_mutex_unlock(&aframe_list_lock); + } +} + +void vframe_remove(vframe_list_t *ptr) +{ + if (ptr == NULL) { + tc_log_warn(__FILE__, "vframe_remove: given NULL frame pointer"); + } else { + TCFramePtr frame; + frame.video = ptr; + + pthread_mutex_lock(&vframe_list_lock); + + LIST_FRAME_REMOVE(ptr, vframe_list_head, vframe_list_tail); + + tc_ring_framebuffer_remove_frame(&tc_video_ringbuffer, frame); + pthread_cond_signal(&video_import_cond); + + pthread_mutex_unlock(&vframe_list_lock); + } +} + +/* ------------------------------------------------------------------ */ +/* ------------------------------------------------------------------ */ + +static aframe_list_t *aframe_retrieve_nowait(void) +{ + aframe_list_t *ptr = NULL; + pthread_mutex_lock(&aframe_list_lock); + + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "(A|retrieve_nowait) requesting a new audio frame"); + } + if (aframe_list_head != NULL && aframe_list_head->status == TC_FRAME_READY) { + ptr = aframe_list_head; + } + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "got a new audio frame reference: %p", ptr); + } + pthread_mutex_unlock(&aframe_list_lock); + return ptr; +} + + +static vframe_list_t *vframe_retrieve_nowait(void) +{ + vframe_list_t *ptr = NULL; + pthread_mutex_lock(&vframe_list_lock); + + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "(V|retrieve_nowait) requesting a new video frame"); + } + if (vframe_list_head != NULL && vframe_list_head->status == TC_FRAME_READY) { + ptr = vframe_list_head; + } + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "got a new video frame reference: %p", ptr); + } + pthread_mutex_unlock(&vframe_list_lock); + return ptr; +} + + +/* ------------------------------------------------------------------ */ + +aframe_list_t *aframe_retrieve(void) +{ + int interrupted = TC_FALSE; + aframe_list_t *ptr = NULL; + pthread_mutex_lock(&aframe_list_lock); + + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|retrieve) requesting a new audio frame"); + while (!interrupted + && (aframe_list_head == NULL + || aframe_list_head->status != TC_FRAME_READY)) { + if (verbose >= TC_FLIST) { + tc_log_msg(__FILE__, "(A|retrieve) audio frame not ready, waiting"); + tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer, "retrieve"); + } + pthread_cond_wait(&audio_export_cond, &aframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|retrieve) audio wait just ended"); + interrupted = !tc_running(); + } + + if (!interrupted) { + ptr = aframe_list_head; + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "got a new audio frame reference: %p", ptr); + } + pthread_mutex_unlock(&aframe_list_lock); + return ptr; +} + + +vframe_list_t *vframe_retrieve(void) +{ + int interrupted = TC_FALSE; + vframe_list_t *ptr = NULL; + pthread_mutex_lock(&vframe_list_lock); + + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|retrieve) requesting a new video frame"); + while (!interrupted + && (vframe_list_head == NULL + || vframe_list_head->status != TC_FRAME_READY)) { + if (verbose >= TC_FLIST) { + tc_log_msg(__FILE__, "(V|retrieve) video frame not ready, waiting"); + tc_ring_framebuffer_dump_status(&tc_video_ringbuffer, "retrieve"); + } + pthread_cond_wait(&video_export_cond, &vframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|retrieve) video wait just ended"); + interrupted = !tc_running(); + } + + if (!interrupted) { + ptr = vframe_list_head; + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "got a new video frame reference: %p", ptr); + } + pthread_mutex_unlock(&vframe_list_lock); + return ptr; +} + +#undef LIST_FRAME_RETRIEVE + +/* ------------------------------------------------------------------ */ + +void aframe_flush(void) +{ + int i = 0, done = TC_FALSE; + + do { + aframe_list_t *ptr = aframe_retrieve_nowait(); + if (!ptr) { + done = TC_TRUE; + } else { + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, + "flushing audio buffer id=[%i] bufid=[%i]", + ptr->id, ptr->bufid); + } + aframe_remove(ptr); + i++; + } + } while (!done); + + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "flushed %i audio frames", i); + } +} + +void vframe_flush(void) +{ + int i = 0, done = TC_FALSE; + + do { + vframe_list_t *ptr = vframe_retrieve_nowait(); + if (!ptr) { + done = TC_TRUE; + } else { + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, + "flushing video buffer id=[%i] bufid=[%i]", + ptr->id, ptr->bufid); + } + vframe_remove(ptr); + i++; + } + } while (!done); + + if (verbose >= TC_CLEANUP) { + tc_log_msg(__FILE__, "flushed %i video frames", i); + } +} + +void tc_framebuffer_flush(void) +{ + aframe_flush(); + vframe_flush(); +} + + +/* ------------------------------------------------------------------ */ + +#define DEC_COUNTERS(RFB, STATUS) do { \ + if ((STATUS) == TC_FRAME_READY) { \ + (RFB)->ready--; \ + } \ + if ((STATUS) == TC_FRAME_LOCKED) { \ + (RFB)->locked--; \ + } \ + if ((STATUS) == TC_FRAME_WAIT) { \ + (RFB)->wait--; \ + } \ +} while(0) + +#define INC_COUNTERS(RFB, STATUS) do { \ + if ((STATUS) == TC_FRAME_READY) { \ + (RFB)->ready++; \ + } \ + if ((STATUS) == TC_FRAME_LOCKED) { \ + (RFB)->locked++; \ + } \ + if ((STATUS) == TC_FRAME_WAIT) { \ + (RFB)->wait++; \ + } \ +} while(0) + +#define FRAME_SET_STATUS(RFB, PTR, NEW_STATUS) do { \ + DEC_COUNTERS((RFB), (PTR)->status); \ + (PTR)->status = (NEW_STATUS); \ + INC_COUNTERS((RFB), (PTR)->status); \ +} while (0) + +#define FRAME_LOOKUP(RFB, PTR, OLD_STATUS, NEW_STATUS) do { \ + /* move along the chain and check for status */ \ + for (; (PTR) != NULL; (PTR) = (PTR)->next) { \ + if ((PTR)->status == (OLD_STATUS)) { \ + /* found matching frame */ \ + FRAME_SET_STATUS(RFB, PTR, NEW_STATUS); \ + break; \ + } \ + } \ +} while (0) + + +aframe_list_t *aframe_reserve(void) +{ + int interrupted = TC_FALSE; + aframe_list_t *ptr = NULL; + + pthread_mutex_lock(&aframe_list_lock); + + while (!interrupted && tc_audio_ringbuffer.wait == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|reserve) audio frame not ready, waiting"); + pthread_cond_wait(&audio_filter_cond, &aframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(A|reserve) audio wait just ended"); + interrupted = !tc_running(); + } + + if (!interrupted) { + ptr = aframe_list_head; + FRAME_LOOKUP(&tc_audio_ringbuffer, ptr, TC_FRAME_WAIT, TC_FRAME_LOCKED); + } + + pthread_mutex_unlock(&aframe_list_lock); + return ptr; +} + +vframe_list_t *vframe_reserve(void) +{ + int interrupted = TC_FALSE; + vframe_list_t *ptr = NULL; + + pthread_mutex_lock(&vframe_list_lock); + + while (!interrupted && tc_video_ringbuffer.wait == 0) { + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|reserve) video frame not ready, waiting"); + pthread_cond_wait(&video_filter_cond, &vframe_list_lock); + if (verbose >= TC_FLIST) + tc_log_msg(__FILE__, "(V|reserve) video wait just ended"); + interrupted = !tc_running(); + } + + if (!interrupted) { + ptr = vframe_list_head; + FRAME_LOOKUP(&tc_video_ringbuffer, ptr, TC_FRAME_WAIT, TC_FRAME_LOCKED); + } + + pthread_mutex_unlock(&vframe_list_lock); + return ptr; +} + +#undef FRAME_LOOKUP + +/* ------------------------------------------------------------------ */ + + +#define FRAME_SET_EXT_STATUS(RFB, PTR, NEW_STATUS) do { \ + if ((PTR)->status == TC_FRAME_EMPTY) { \ + (RFB)->empty--; \ + } \ + FRAME_SET_STATUS((RFB), (PTR), (NEW_STATUS)); \ + if ((PTR)->status == TC_FRAME_EMPTY) { \ + (RFB)->empty++; \ + } \ +} while (0) + + +void aframe_push_next(aframe_list_t *ptr, int status) +{ + if (ptr == NULL) { + /* a bit more of paranoia */ + tc_log_warn(__FILE__, "aframe_push_next: given NULL frame pointer"); + } else { + pthread_mutex_lock(&aframe_list_lock); + FRAME_SET_EXT_STATUS(&tc_audio_ringbuffer, ptr, status); + + if (status == TC_FRAME_WAIT) { + pthread_cond_signal(&audio_filter_cond); + } else if (status == TC_FRAME_READY && ptr == aframe_list_head) { // XXX + pthread_cond_signal(&audio_export_cond); + } + if (verbose >= TC_FLIST) { + tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer, "push_next"); + } + pthread_mutex_unlock(&aframe_list_lock); + } +} + + +void vframe_push_next(vframe_list_t *ptr, int status) +{ + if (ptr == NULL) { + /* a bit more of paranoia */ + tc_log_warn(__FILE__, "vframe_push_next: given NULL frame pointer"); + } else { + pthread_mutex_lock(&vframe_list_lock); + FRAME_SET_EXT_STATUS(&tc_video_ringbuffer, ptr, status); + + if (status == TC_FRAME_WAIT) { + pthread_cond_signal(&video_filter_cond); + } else if (status == TC_FRAME_READY && ptr == vframe_list_head) { // XXX + pthread_cond_signal(&video_export_cond); + } + if (verbose >= TC_FLIST) { + tc_ring_framebuffer_dump_status(&tc_video_ringbuffer, "push_next"); + } + pthread_mutex_unlock(&vframe_list_lock); + } +} + +#undef FRAME_SET_STATUS +#undef FRAME_SET_EXT_STATUS + +/* ------------------------------------------------------------------ */ + + +void aframe_dump_status(void) +{ + tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer, + "audio buffer status"); +} + +void vframe_dump_status(void) +{ + tc_ring_framebuffer_dump_status(&tc_video_ringbuffer, + "video buffer status"); +} + +int vframe_have_more(void) +{ + int ret; + pthread_mutex_lock(&vframe_list_lock); + ret = (vframe_list_tail == NULL) ?0 :1; + pthread_mutex_unlock(&vframe_list_lock); + return ret; +} + +int aframe_have_more(void) +{ + int ret; + pthread_mutex_lock(&aframe_list_lock); + ret = (aframe_list_tail == NULL) ?0 :1; + pthread_mutex_unlock(&aframe_list_lock); + return ret; +} + +/* ------------------------------------------------------------------ */ +/* Frame copying routines */ +/* ------------------------------------------------------------------ */ + +void aframe_copy(aframe_list_t *dst, const aframe_list_t *src, + int copy_data) +{ + if (!dst || !src) { + tc_log_warn(__FILE__, "aframe_copy: given NULL frame pointer"); + return; + } + + /* copy all common fields with just one move */ + ac_memcpy(dst, src, sizeof(frame_list_t)); + + if (copy_data == 1) { + /* really copy video data */ + ac_memcpy(dst->audio_buf, src->audio_buf, dst->audio_size); + } else { + /* soft copy, new frame points to old audio data */ + dst->audio_buf = src->audio_buf; + } +} + +void vframe_copy(vframe_list_t *dst, const vframe_list_t *src, + int copy_data) +{ + if (!dst || !src) { + tc_log_warn(__FILE__, "vframe_copy: given NULL frame pointer"); + return; + } + + /* copy all common fields with just one move */ + ac_memcpy(dst, src, sizeof(frame_list_t)); + + dst->deinter_flag = src->deinter_flag; + dst->free = src->free; + /* + * we assert that plane pointers *are already properly set* + * we're focused on copy _content_ here. + */ + + if (copy_data == 1) { + /* really copy video data */ + ac_memcpy(dst->video_buf, src->video_buf, dst->video_size); + } else { + /* soft copy, new frame points to old video data */ + dst->video_buf = src->video_buf; + } +} + +/*************************************************************************/ + +void vframe_get_counters(int *im, int *fl, int *ex) +{ + pthread_mutex_lock(&vframe_list_lock); + *im = tc_video_ringbuffer.null + tc_video_ringbuffer.empty; + *fl = tc_video_ringbuffer.wait + tc_video_ringbuffer.locked; + *ex = tc_video_ringbuffer.ready; + pthread_mutex_unlock(&vframe_list_lock); +} + +void aframe_get_counters(int *im, int *fl, int *ex) +{ + pthread_mutex_lock(&aframe_list_lock); + *im = tc_audio_ringbuffer.null + tc_audio_ringbuffer.empty; + *fl = tc_audio_ringbuffer.wait + tc_audio_ringbuffer.locked; + *ex = tc_audio_ringbuffer.ready; + pthread_mutex_unlock(&aframe_list_lock); +} + +void tc_framebuffer_get_counters(int *im, int *fl, int *ex) +{ + int v_im, v_fl, v_ex, a_im, a_fl, a_ex; + + vframe_get_counters(&v_im, &v_fl, &v_ex); + aframe_get_counters(&a_im, &a_fl, &a_ex); + + *im = v_im + a_im; + *fl = v_fl + a_fl; + *ex = v_ex + a_ex; +} + +/*************************************************************************/ +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/framebuffer.h b/debian/transcode/transcode-1.1.7/src/framebuffer.h new file mode 100644 index 00000000..30528907 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/framebuffer.h @@ -0,0 +1,499 @@ +/* + * framebuffer.h -- declarations of audio/video frame ringbuffers. + * + * Copyright (C) Thomas Oestreich - June 2001 + * Updates and Enhancements + * (C) 2007-2010 - Francesco Romani <fromani -at- gmail -dot- com> + * + * This file is part of transcode, a 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. + * + */ + + +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include <stdint.h> + +#include "libtc/tcframes.h" +#include "tc_defaults.h" + + +/************************************************************************* + * Transcode Framebuffer in a Nutshell (aka: how this code works) + * -------------------------------------------------------------- + * + * Introduction: + * ------------- + * This is a quick, terse overview of design principles beyond the + * framebuffer and about the design of this code. Full-blown + * documentation is avalaible under doc/. + * + * When reading framebuffer documentation/code, always take in mind + * the thread layout of transcode: + * + * - import layer is supposed to run 2 threads concurrently + * - filter layer is supposed to run 0..N threads concurrently + * - export layer is supposed to run 1 thread + * + * So, in any transcode execution, framebuffer code is supposed to + * serve from 3 to N+3 concurrent threads. + * + * Framebuffer entities: + * --------------------- + * XXX + * + * frame status transitions scheme (API reminder): + * ----------------------------------------------- + * + * .---------<---------------<------+-------<------. + * V | 7 | 6 + * .------------. .--------. .--------. .--------. + * | frame pool | --> | import | | filter | | export | + * `------------' 1 `--------' `--------' `--------' + * | A | A + * | 3 | | 4 | + * 2 | | V 5 | + * V .-------------. | + * `---->| frame chain |--->' + * `-------------' + * + * In frame lifetime order: + * 1. {a,v}frame_register (import) + * 2. {a,v}frame_push_next (import) + * 3. {a,v}frame_reserve (filter) + * 4. {a,v}frame_push_next (filter) + * 5. {a,v}frame_retrieve (export) + * 6. {a,v}frame_remove (export) + * [ 7. {a,v}frame_remove (filter) ] + * + * Operating conditions: + * + * 1. single source, full range, no interruptions + * 2. single source, full range, interruption + * 3. single source, sub range, no interruptions + * 4. single source, sub range, interruption + * 5. single source, multi sub ranges, no interruptions + * 5. single source, multi sub ranges, interruption + */ + + +/* + * frame*buffer* specifications, needed to properly allocate + * and initialize single frame buffers + */ +typedef struct tcframespecs_ TCFrameSpecs; +struct tcframespecs_ { + int frc; /* frame ratio code is more precise than value */ + + /* video fields */ + int width; + int height; + int format; /* TC_CODEC_reserve preferred, + * CODEC_reserve still supported for compatibility + */ + /* audio fields */ + int rate; + int channels; + int bits; + + /* private field, used internally */ + double samples; +}; + +/* + * tc_framebuffer_get_specs: (NOT thread safe) + * Get a pointer to a TCFrameSpecs structure representing current + * framebuffer structure. Frame handling code will use those parameters + * to allocate framebuffers. + * + * Parameters: + * None. + * Return Value: + * Constant pointer to a TCFrameSpecs structure. There is no need + * to *free() this structure. + */ +const TCFrameSpecs *tc_framebuffer_get_specs(void); + +/* + * tc_framebuffer_set_specs: (NOT thread safe) + * Setup new framebuffer parameters, to be used by internal framebuffer + * code to properly handle frame allocation. + * PLEASE NOTE that only allocation performed AFTER calling this function + * will use those parameters. + * PLEASE ALSO NOTE that is HIGHLY unsafe to mix allocation by changing + * TCFrameSpecs in between without freeing ringbuffers. Just DO NOT. + * + * Parameters: + * Constant pointer to a TCFrameSpecs holding new framebuffer parameters. + * Return Value: + * None. + */ +void tc_framebuffer_set_specs(const TCFrameSpecs *specs); + +/* + * tc_framebuffer_interrupt: (thread safe) + * Interrupt the framebuffer immediately (see below for specific meaning + * of this act in various functions). + * When framebuffer is interrupted, frames belonging to any processing + * stage are no longer avalaible; frame unavalaibility is notified as + * soon as is possible. + * When a framebuffer is interrupted, it becomes ready to be finalized; + * Effectively, the only operations that make sense to be performed on + * an interrupted framebuffer, is to finalize it. + * From statements above easily descend that interruption is irreversible. + * + * Parameters: + * None. + * Return Value: + * None. + * Side effects: + * Any frame-claiming function will fail after the invocation of this + * function (see description above). + */ +void tc_framebuffer_interrupt(void); +void tc_framebuffer_interrupt_import(void); + +/* + * vframe_alloc, aframe_alloc: (NOT thread safe) + * Allocate respectively a video or audio frame ringbuffer capable to hold + * given amount of frames, with a minimum of one. + * Each framebuffer is allocated using TCFrameSpecs parameters. + * Use vframe_free/aframe_free to release acquired ringbuffers. + * + * Parameters: + * num: size of ringbuffer to allocate (number of framebuffers holded + * in ringbuffer). + * Return Value: + * 0: succesfull + * !0: error, tipically this means that one (or more) frame + * can't be allocated. + */ +int vframe_alloc(int num); +int aframe_alloc(int num); + +/* + * vframe_alloc_single, aframe_alloc_single: (NOT thread safe) + * allocate a single framebuffer (respectively, video or audio) + * in compliacy with last TCFrameSpecs set. + * Those functione are mainly intended to provide a convenient + * way to encoder/decoder/whatelse to allocate private framebuffers + * without doing any size computation or waste memory. + * Returned value can be SAFELY deallocated using + * tc_del_video_frame or tc_del_audio_frame. + * + * Parameters: + * None. + * Return Value: + * respectively a pointer to a vframe_list_t or aframe_list_t, + * like, tc_new_video_frame() or tc_new_audio_frame() called + * with right parameters. + * NULL if allocation fails. + */ +vframe_list_t *vframe_alloc_single(void); +aframe_list_t *aframe_alloc_single(void); + +/* + * vframe_free, aframe_free: (NOT thread safe) + * release all framebuffer memory acquired respect. for video and + * audio frame ringbuffers. + * Please remember thet ffter those functions called, almost + * all other ringbuffer functions will fail. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void vframe_free(void); +void aframe_free(void); + +/* + * vframe_flush, aframe_flush: (NOT thread safe) + * flush all framebuffers still in ringbuffer, by marking those as unused. + * This will reset ringbuffer to an empty state, ready to be (re)used again. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void vframe_flush(void); +void aframe_flush(void); + +/* + * tc_framebuffer_flush: (NOT thread safe) + * flush all active ringbuffers, and mark all frames as unused. + * This will reset ringbuffers to an empty state, ready to be (re)used again. + * + * Parameters: + * None. + * Return Value: + * None. + */ +void tc_framebuffer_flush(void); + +/* + * vframe_register, aframe_register: (thread safe) + * Frame claiming functions. + * Respectively wait for an empty audio and video frame, + * then register it in frame chain, attach the given `id' + * and finally return the pointer to caller. + * + * Those function are (and should be) used at the beginning + * of the frame chain. Those should are the first function + * that a framebuffer should see in it's lifecycle. + * + * In transcode, those functions are (and should be) used + * only in the decoder. + * + * Note: + * DO NOT *free() returned pointer! The memory needed for frames is + * handled by transcode internally. + * + * Parameters: + * id: set framebuffer id to this value. + * The meaning of `id' is enterely client-depended. + * Return Value: + * A valid pointer to respectively an empty video or audio frame. + * If framebuffer is interrupted, both returns NULL. + * Side effects: + * Being frame claiming functions, those functions will block + * calling thread until a new frame will be avalaible, OR + * until an interruption happens. + */ +vframe_list_t *vframe_register(int id); +aframe_list_t *aframe_register(int id); + +/* + * vframe_reserve, aframe_reserve: (thread safe) + * Frame claiming functions. + * Respectively wait for a processing-needing + * (`waiting' in transcode slang) audio and video frame, + * then reserve it, preventing other calls to those functions + * to claim it twice, and finally return the pointer to caller. + * + * Those function are (and should be) used in the middle + * of the frame chain. + * + * In transcode, those functions are (and should be) used + * only in the filter layer. + * + * Note: + * DO NOT *free() returned pointer! The memory needed for frames is + * handled by transcode internally. + * + * Parameters: + * None. + * Return Value: + * A valid pointer to respectively an empty video or audio frame. + * If framebuffer is interrupted, both returns NULL. + * Side effects: + * Being frame claiming functions, those functions will block + * calling thread until a new frame will be avalaible, OR + * until an interruption happens. + */ +vframe_list_t *vframe_reserve(void); +aframe_list_t *aframe_reserve(void); + +/* + * vframe_retrieve, aframe_retrieve: (thread safe) + * Frame claiming functions. + * Respectively wait for a audio and video frame ready to be + * encoded, then retrieve it, preventing other calls to those + * functions to claim it twice, and finally return the pointer + * to caller. + * + * Those function are (and should be) used at the end + * of the frame chain. + * + * In transcode, those functions are (and should be) used + * only in the encoder. + * + * Note: + * DO NOT *free() returned pointer! The memory needed for frames is + * handled by transcode internally. + * + * Parameters: + * None. + * Return Value: + * A valid pointer to respectively an empty video or audio frame. + * If framebuffer is interrupted, both returns NULL. + * Side effects: + * Being frame claiming functions, those functions will block + * calling thread until a new frame will be avalaible, OR + * until an interruption happens. + */ +vframe_list_t *vframe_retrieve(void); +aframe_list_t *aframe_retrieve(void); + +/* + * vframe_remove, aframe_remove: (thread safe) + * Respectively release an audio or video frame, + * by marking it as unused and putting it back on the frame pool. + * + * Those function are (and should be) used at the end + * of the frame chain. Those should are the last function + * that a framebuffer should see in it's lifecycle. + * + * In transcode, those functions are (and should be) used + * only in the encoder. + * + * Parameters: + * ptr: framebuffer to release. + * Return Value: + * None. + */ +void vframe_remove(vframe_list_t *ptr); +void aframe_remove(aframe_list_t *ptr); + +/* + * vframe_push_next, aframe_push_next: (thread safe) + * Push a frame into next processing stage, by changing + * its status. + * Those functions are used when a processing stage terminate + * its operations on a given frame and so it want to pass such + * frame to next stage. + * + * In transcode, those functions are (and should be) used + * in the decoder and in the filter stage. + * + * Parameters: + * ptr: framebuffer pointer to be updated. + * status: new framebuffer status (= stage). + * Return Value: + * None. + * Side effects: + * A blocked thread can (and it will likely) be awaken + * by this operation. + */ +void vframe_push_next(vframe_list_t *ptr, TCFrameStatus status); +void aframe_push_next(aframe_list_t *ptr, TCFrameStatus status); + +/* + * vframe_dup, aframe_dup: (thread safe) + * Frame claiming functions. + * Duplicate given respectively video or audio framebuffer. + * New framebuffer will be a full (deep) copy of old one + * (see aframe_copy/vframe_copy documentation to learn about + * deep copy). + * + * Parameters: + * f: framebuffer to be copied. + * Return Value: + * A valid pointer to respectively duplicate video or audio frame. + * If framebuffer is interrupted, both returns NULL. + * Side Effects: + * Being frame claiming functions, those functions will block + * calling thread until a new frame will be avalaible, OR + * until an interruption happens. + */ +vframe_list_t *vframe_dup(vframe_list_t *f); +aframe_list_t *aframe_dup(aframe_list_t *f); + +/* + * vframe_copy, aframe_copy (thread safe) + * perform a soft or optionally deep copy respectively of a + * video or audio framebuffer. A soft copy just copies metadata; + * #ifdef STATBUFFER + * soft copy also let the buffer pointers point to original frame + * buffers, so data isn't really copied around. + * #endif + * A deep copy just ac_memcpy()s buffer data from a frame to other + * one, so new frame will be an independent copy of old one. + * + * Parameters: + * dst: framebuffer which will hold te copied (meta)data. + * src: framebuffer to be copied. + * Mind the fact that when using softcopy real buffers will + * remain the ones of this frame + * copy_data: boolean flag. If 0, do softcopy; do deepcopy otherwise. + * + * Return Value: + * None. + */ +void vframe_copy(vframe_list_t *dst, const vframe_list_t *src, int copy_data); +void aframe_copy(aframe_list_t *dst, const aframe_list_t *src, int copy_data); + +/* + * vframe_dump_status, aframe_dump_status: (NOT thread safe) + * tc_log* out current framebuffer ringbuffer internal status, e.g. + * counters for null/ready/empty/loacked frames) respectively for + * video and audio ringbuffers. + * + * THREAD SAFENESS WARNING: + * WRITEME + * + * Parameters: + * None. + * Return Value: + * None. + * Side effects: + * See THREAD SAFENESS WARNING above. + */ +void vframe_dump_status(void); +void aframe_dump_status(void); + +/* + * vframe_have_more, aframe_have_more (thread safe): + * check if video/audio frame list is empty or not. + * + * Parameters: + * None. + * Return Value: + * !0 if frame list has at least one frame + * 0 otherwise + */ +int vframe_have_more(void); +int aframe_have_more(void); + +/* + * {v,a}frame_get_counters (thead safe): + * get the number of frames currently hold in the processing layers, + * respectively for video and audio pipelines. + * + * Parameters: + * im: if not NULL, store here the number of frames + * hold by import layer. + * fl: if not NULL, store here the number of frames + * hold by filter layer. + * ex: if not NULL, store here the number of frames + * hold by export layer. + * Return Value: + * None. + */ +void vframe_get_counters(int *im, int *fl, int *ex); +void aframe_get_counters(int *im, int *fl, int *ex); + +/* + * tc_framebuffer_get_counters (thread safe): + * get the total number of frames currently hold in the processing + * layers, by considering both video and audio pipelines. + * + * Parameters: + * im: if not NULL, store here the number of frames + * hold by import layer. + * fl: if not NULL, store here the number of frames + * hold by filter layer. + * ex: if not NULL, store here the number of frames + * hold by export layer. + * Return Value: + * None. + */ +void tc_framebuffer_get_counters(int *im, int *fl, int *ex); + +#endif /* FRAMEBUFFER_H */ diff --git a/debian/transcode/transcode-1.1.7/src/probe.c b/debian/transcode/transcode-1.1.7/src/probe.c new file mode 100644 index 00000000..2cdbdfc5 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/probe.c @@ -0,0 +1,859 @@ +/* + * probe.c - probe input file for parameters + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "probe.h" +#include "libtc/libtc.h" +#include "libtc/tccodecs.h" +#include "libtc/ratiocodes.h" +#include "import/magic.h" + +#include <sys/wait.h> // for waitpid() + +/*************************************************************************/ + +/* Handy macro to check whether the probe flags allow setting of a + * particular field (pass the flag name without TC_PROBE_NO_): */ +#define MAY_SET(flagname) (!(flags & TC_PROBE_NO_##flagname)) + +/* Internal routine declarations: */ + +static int do_probe(const char *file, const char *nav_seek_file, int title, + int range, int mplayer_flag, int verbose_flag, + ProbeInfo *info_ret); +static void select_modules(int flags, vob_t *vob); + +/*************************************************************************/ +/*************************************************************************/ + +/* External interfaces */ + +/*************************************************************************/ + +/** + * probe_stream_data: Probe a single source file and store the stream + * informations in data structure. + * + * Parameters: + * file: File name to probe. + * range: Amount of input file to probe, in MB. + * info: Structure to be filled in with probed data. + * Return value: + * Nonzero on success, zero on error. + * Preconditions: + * info != NULL, range > 0 + */ +int probe_stream_data(const char *file, int range, ProbeInfo *info) +{ + if (!info || range <= 0) { + tc_log_error(PACKAGE, "wrong probing parameters"); + return 0; + } + + if (!file) { + tc_log_warn(PACKAGE, "missing source to probe"); + memset(info, 0, sizeof(ProbeInfo)); + } else { + if (!do_probe(file, NULL, 0, range, 0, + (verbose >= TC_DEBUG) ? verbose : 0, info) + ) { + if (verbose & TC_DEBUG) { + tc_log_warn(PACKAGE, "(%s) failed to probe stream '%s'", + __FILE__, file); + } + return 0; + } + } + return 1; +} + + +/** + * probe_source: Probe the given input file(s) and store the results in + * the global data structure. + * + * Parameters: + * vid_file: Video file name, or NULL if none. + * aud_file: Audio file name, or NULL if none. + * range: Amount of input files to probe, in MB. + * flags: Flags indicating which global parameters should be left + * alone (TC_PROBE_NO_xxx flags). + * vob: Pointer to global data structure. + * Return value: + * Nonzero on success, zero on error. + * Preconditions: + * vob != NULL + */ + +int probe_source(const char *vid_file, const char *aud_file, int range, + int flags, vob_t *vob) +{ + ProbeInfo vinfo, ainfo; // video and audio info structures + + /* Probe the video file, if present */ + if (vid_file) { + if (!do_probe(vid_file, vob->nav_seek_file, vob->dvd_title, range, + (flags & TC_PROBE_NO_BUILTIN), + (verbose >= TC_DEBUG) ? verbose : 0, &vinfo) + ) { + if (verbose & TC_DEBUG) { + tc_log_warn(PACKAGE, "(%s) failed to probe video source", + __FILE__); + } + return 0; + } + } else { + vob->has_video = 0; + } + + /* Probe the audio file, if present */ + if (aud_file) { + if (!do_probe(aud_file, vob->nav_seek_file, vob->dvd_title, range, + (flags & TC_PROBE_NO_BUILTIN), + (verbose >= TC_DEBUG) ? verbose : 0, &ainfo) + ) { + if (verbose & TC_DEBUG) { + tc_log_warn(PACKAGE, "(%s) failed to probe audio source", + __FILE__); + } + return 0; + } + } /* else it might be contained in the video file */ + + /* Set global parameters based on probed data */ + probe_to_vob(vid_file ? &vinfo : NULL, aud_file ? &ainfo : NULL, + flags, vob); + if (verbose & TC_DEBUG) { + tc_log_info(PACKAGE, "(%s) V magic=0x%lx, A magic=0x%lx," + " V codec=0x%lx, A codec=0x%lx", __FILE__, + vob->v_format_flag, vob->a_format_flag, + vob->v_codec_flag, vob->a_codec_flag); + tc_log_info(PACKAGE, "(%s) V magic=%s, A magic=%s, V codec=%s," + " A codec=%s", __FILE__, + mformat2str(vob->v_format_flag), + mformat2str(vob->a_format_flag), + tc_codec_to_comment(vob->v_codec_flag), + tc_codec_to_comment(vob->a_codec_flag)); + } + + /* All done, return success */ + return 1; +} + +/*************************************************************************/ + +/** + * probe_source_xml: Probe video or audio parameters from an XML file as + * specified by the vob_t data structure. + * + * Parameters: + * vob: Global vob_t data structure. + * which: PROBE_XML_VIDEO or PROBE_XML_AUDIO. + * Return value: + * Nonzero on success, zero on error. + * Side effects: + * Prints an error message on error. + */ + +/* FIXME: is this the right place for these? */ + +int probe_source_xml(vob_t *vob, int which) +{ + int retval = 1; +#ifdef HAVE_LIBXML2 + int tochild[2], fromchild[2]; /* pipes */ + pid_t pid; + int resize; + + if (pipe(tochild) == -1) { + tc_log_perror(PACKAGE, "probe_source_xml(): pipe(tochild) failed"); + return 0; + } + if (pipe(fromchild) == -1) { + tc_log_perror(PACKAGE, "probe_source_xml(): pipe(fromchild) failed"); + close(tochild[0]); + close(tochild[1]); + return 0; + } + pid = fork(); + if (pid == -1) { + tc_log_perror(PACKAGE, "probe_source_xml(): fork failed"); + return 0; + } else if (pid > 0) { + /* Child process */ + const char *new_argv[6]; + close(tochild[1]); + close(fromchild[0]); + if (tochild[0] != 0) { + if (dup2(tochild[0], 0) == -1) { + tc_log_perror(PACKAGE, "probe_source_xml(): dup2(0) failed"); + exit(-1); + } + close(tochild[0]); + } + if (fromchild[1] != 1) { // theoretically always true, but JIC + if (dup2(fromchild[1], 1) == -1) { + tc_log_perror(PACKAGE, "probe_source_xml(): dup2(1) failed"); + exit(-1); + } + close(fromchild[1]); + } + new_argv[0] = "tcxmlcheck"; + new_argv[1] = "-i"; + new_argv[2] = vob->video_in_file; + new_argv[3] = "-B"; + new_argv[4] = (which==PROBE_XML_VIDEO ? "-V" : "-A"); + new_argv[5] = NULL; + execvp("tcxmlcheck", (char **)new_argv); + tc_log_perror(PACKAGE, "probe_source_xml(): exec(tcxmlcheck) failed"); + exit(-1); + } + /* Parent process */ + retval = 0; + close(tochild[0]); + close(fromchild[1]); + if (write(tochild[1], vob, sizeof(vob_t)) != sizeof(vob_t)) { + tc_log_error(PACKAGE, "Error writing data to tcxmlcheck: %s", + strerror(errno)); + close(tochild[1]); + close(fromchild[0]); + /* Can't just return--need to reap the child */ + goto reapchild; + } + close(tochild[1]); + if (read(fromchild[0], vob, sizeof(vob_t)) != sizeof(vob_t)) { + tc_log_error(PACKAGE, "Error reading data from tcxmlcheck"); + close(fromchild[0]); + goto reapchild; + } + if (read(fromchild[0], &resize, sizeof(int)) != sizeof(int)) { + tc_log_error(PACKAGE, "Error reading data from tcxmlcheck 2"); + close(fromchild[0]); + goto reapchild; + } + close(fromchild[0]); + if (which == PROBE_XML_VIDEO && resize == 2) { + // XML forced resize, clear command line parameters + resize1 = TC_FALSE; + resize2 = TC_FALSE; + zoom = TC_FALSE; + vob->resize1_mult = 32; + vob->vert_resize1 = 0; + vob->hori_resize1 = 0; + vob->resize2_mult = 32; + vob->vert_resize2 = 0; + vob->hori_resize2 = 0; + vob->zoom_width = 0; + vob->zoom_height = 0; + vob->zoom_filter = TCV_ZOOM_LANCZOS3; + } + retval = 1; + + reapchild: // clean up after the child process + waitpid(pid, NULL, 0); +#endif // HAVE_LIBXML2 + + return retval; +} + +/*************************************************************************/ + +/** + * mformat2str: Return a descriptive + * string for the given video format flag. + * + * Parameters: + * flag: Flag to return string for. + * Return value: + * String describing `flag'. + */ + + +const char *mformat2str(int flag) +{ + switch (flag) { + case TC_MAGIC_PAL: return "PAL"; + case TC_MAGIC_NTSC: return "NTSC"; + case TC_MAGIC_TS: return "MPEG transport stream"; + case TC_MAGIC_YUV4MPEG: return "YUV4MPEG"; + case TC_MAGIC_NUV: return "NuppelVideo"; + case TC_MAGIC_DVD_PAL: return "DVD PAL"; + case TC_MAGIC_DVD_NTSC: return "DVD NTSC"; + case TC_MAGIC_AVI: return "RIFF data, AVI"; + case TC_MAGIC_MOV: return "QuickTime"; + case TC_MAGIC_XML: return "XML file"; + case TC_MAGIC_TIFF1: return "TIFF image"; + case TC_MAGIC_TIFF2: return "TIFF image"; + case TC_MAGIC_JPEG: return "JPEG image"; + case TC_MAGIC_BMP: return "BMP image"; + case TC_MAGIC_PNG: return "PNG image"; + case TC_MAGIC_GIF: return "GIF image"; + case TC_MAGIC_PPM: return "PPM image"; + case TC_MAGIC_PGM: return "PGM image"; + case TC_MAGIC_CDXA: return "RIFF data, CDXA"; + case TC_MAGIC_AC3: return "AC3"; + case TC_MAGIC_MP3: return "MP3"; + case TC_MAGIC_MP2: return "MP2"; + case TC_MAGIC_OGG: return "OGG stream"; + case TC_MAGIC_WAV: return "RIFF data, WAVE"; + case TC_MAGIC_V4L_VIDEO: return "V4L,video"; + case TC_MAGIC_V4L_AUDIO: return "V4L,audio"; + case TC_MAGIC_PVN: return "PVN video"; + } + return ""; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* Internal routines */ + +/*************************************************************************/ + +/** + * do_probe: Perform the actual probing of the source file. + * + * Parameters: + * file: Filename to probe. + * nav_seek_file: Navigation file for `file', or NULL if none. + * title: Title to probe for DVD probing. + * range: Amount of file to probe, in MB. + * mplayer_flag: If nonzero, use mplayer to probe file. + * verbose_flag: Verbosity flag to pass to tcprobe. + * info_ret: Structure to be filled in with probed data. + * Return value: + * Nonzero on success, zero on failure. + * Preconditions: + * file != NULL + */ + +static int do_probe(const char *file, const char *nav_seek_file, int title, + int range, int mplayer_flag, int verbose_flag, + ProbeInfo *info_ret) +{ + char cmdbuf[PATH_MAX+1000]; + FILE *pipe; + + if (mplayer_flag) { + if (tc_snprintf(cmdbuf, sizeof(cmdbuf), + "tcprobe -B -M -i \"%s\" -d %d", + file, verbose_flag) < 0) + return 0; + } else { + if (tc_snprintf(cmdbuf, sizeof(cmdbuf), + "tcprobe -B -i \"%s\" -T %d -H %d -d %d", + file, title, range, verbose_flag) < 0) + return 0; + if (nav_seek_file + && tc_snprintf(cmdbuf+strlen(cmdbuf), sizeof(cmdbuf)-strlen(cmdbuf), + " -f \"%s\"", nav_seek_file) < 0) + return 0; + } + pipe = popen(cmdbuf, "r"); + if (!pipe) + return 0; + if (fread(&tc_probe_pid, sizeof(pid_t), 1, pipe) != 1) { + pclose(pipe); + return 0; + } + if (fread(info_ret, sizeof(*info_ret), 1, pipe) != 1) { + pclose(pipe); + return 0; + } + pclose(pipe); + return 1; +} + +/*************************************************************************/ + +/** + * probe_to_vob: Use the results of probing the input files to set global + * parameters. + * + * Parameters: + * vinfo: Pointer to probe results for video file, or NULL if no video + * file. + * ainfo: Pointer to probe results for audio file, or NULL if no audio + * file. + * flags: TC_PROBE_NO_xxx flags. + * vob: Pointer to global data structure. + * Return value: + * None. + * Preconditions: + * vob != NULL + */ + +void probe_to_vob(ProbeInfo *vinfo, ProbeInfo *ainfo, int flags, vob_t *vob) +{ + int track; // user-selected audio track, sanity-checked + + track = vob->a_track; + if (track < 0 || track >= TC_MAX_AUD_TRACKS) + track = 0; + + if (vinfo) { + int D_arg, D_arg_ms; // for setting A/V sync + + /* Set frame size */ + if (MAY_SET(FRAMESIZE)) { + if (vinfo->width > 0) + vob->im_v_width = vinfo->width; + if (vinfo->height > 0) + vob->im_v_height = vinfo->height; + } + + /* Set frame rate */ + if (MAY_SET(FPS)) { + if (vinfo->frc > 0) { + vob->im_frc = vinfo->frc; + tc_frc_code_to_value(vob->im_frc, &vob->fps); + } else if (vinfo->fps > 0) + vob->fps = vinfo->fps; + } + + /* Set aspect ratio */ + if (MAY_SET(IMASR)) { + if (vinfo->asr > 0) + vob->im_asr = vinfo->asr; + } + + /* Set additional attributes */ + if (vinfo->attributes) + vob->attributes = vinfo->attributes; + + /* Clear demux sync flag if appropriate */ + if (MAY_SET(DEMUX) && (vob->attributes & TC_INFO_NO_DEMUX)) { + vob->demuxer = 0; + } + + /* Calculate A/V sync correction */ + if (vinfo->pts_start > 0 && vinfo->track[track].pts_start > 0) { + double pts_diff = vinfo->pts_start - vinfo->track[track].pts_start; + D_arg = (int)(vob->fps * pts_diff); + D_arg_ms = (int)((pts_diff - D_arg/vob->fps) * 1000); + } else { + D_arg = 0; + D_arg_ms = 0; + } + /* This voodoo to determine whether to set the A/V sync parameters + * is from the original probe.c, with the following comments: + * - case 1: demuxer disabled needs PTS sync mode + * - case 2: check if PTS of requested audio track requires + * video frame dropping + * vob->demuxer>0 and audio_pts > video_pts + * - case 3: fully PTS based sync modes requested + */ + if ((MAY_SET(DEMUX) && (vob->attributes & TC_INFO_NO_DEMUX)) + || (MAY_SET(DEMUX) && (vinfo->pts_start < vinfo->track[track].pts_start)) + || (vob->demuxer == 3 || vob->demuxer == 4) + ) { + if (MAY_SET(AVSHIFT)) + vob->sync = D_arg; + if (MAY_SET(AV_FINE)) + vob->sync_ms = D_arg_ms; + } + + /* Set starting presentation unit */ + if (MAY_SET(SEEK)) { + if (vinfo->unit_cnt > 0) + vob->ps_unit = vinfo->unit_cnt; + } + + /* Set format/codec flags and miscellaneous fields */ + if (vinfo->magic) + vob->v_format_flag = vinfo->magic; + if (vinfo->codec) + vob->v_codec_flag = vinfo->codec; + vob->pts_start = vinfo->pts_start; + + /* If the width or height are 0, assume no video was detected + * (FIXME: what about -g?) */ + if (vinfo->width == 0 || vinfo->height == 0) + vob->has_video = 0; + + /* If no separate audio file was found, use the video file for + * audio processing */ + if (!ainfo) + ainfo = vinfo; + + } // if (vinfo) + + if (ainfo) { + + /* Set audio format parameters */ + if (MAY_SET(RATE)) { + if (ainfo->track[track].samplerate > 0) + vob->a_rate = ainfo->track[track].samplerate; + } + if (MAY_SET(BITS)) { + if (ainfo->track[track].bits > 0) + vob->a_bits = ainfo->track[track].bits; + } + if (MAY_SET(CHAN)) { + if (ainfo->track[track].chan > 0) + vob->a_chan = ainfo->track[track].chan; + } + + /* Set audio codec, if not set by user */ + if (MAY_SET(ACODEC)) { + if (ainfo->track[track].format > 0) + vob->a_codec_flag = ainfo->track[track].format; + } + + /* Set format flag and miscellaneous fields */ + if (ainfo->magic) + vob->a_format_flag = ainfo->magic; + if (ainfo->track[track].bitrate > 0) + vob->a_stream_bitrate = ainfo->track[track].bitrate; + if (ainfo->track[track].padrate > 0) + vob->a_padrate = ainfo->track[track].padrate; + if (ainfo->track[track].lang > 0) + vob->lang_code = ainfo->track[track].lang; + + /* See if audio was detected */ + if (ainfo->num_tracks == 0) + vob->has_audio = 0; + if (ainfo->track[track].format == CODEC_NULL) + vob->has_audio_track = 0; + + /* Set video format/codec fields as well if no video present */ + if (!vinfo) { + if (ainfo->magic) + vob->v_format_flag = ainfo->magic; + if (ainfo->codec) + vob->v_codec_flag = ainfo->codec; + } + + } // if (ainfo) + + /* Make note of whether the input is an XML file */ + if (vinfo && vinfo->magic != vinfo->magic_xml) + vob->vmod_probed_xml = "xml"; + else + vob->vmod_probed_xml = NULL; + if (ainfo && ainfo->magic != ainfo->magic_xml) + vob->amod_probed_xml = "xml"; + else + vob->amod_probed_xml = NULL; + + if (MAY_SET(MODULES)) { + /* Select appropriate import modules */ + select_modules(flags, vob); + } +} + +/*************************************************************************/ + +/** + * select_modules: Use the results of probing the input files to set + * global parameters. + * + * Parameters: + * flags: TC_PROBE_NO_xxx flags. + * vob: Pointer to global data structure. + * Return value: + * None. + * Preconditions: + * vob != NULL + */ + +static void select_modules(int flags, vob_t *vob) +{ + char *default_amod; + + + vob->vmod_probed = NULL; + vob->amod_probed = NULL; + + /* If no video or audio, use null module */ + if (!vob->has_video) { + vob->vmod_probed = "null"; + vob->im_v_width = 0; + vob->im_v_height = 0; + } + if (!vob->has_audio) { + vob->amod_probed = "null"; + vob->a_rate = 0; + vob->a_chan = 0; + } + + /* Choose a default audio module based on the audio codec */ + switch (vob->a_codec_flag) { + case CODEC_MP2: default_amod = "mp3"; break; + case CODEC_MP3: default_amod = "mp3"; break; + case CODEC_AC3: default_amod = "ac3"; break; + case CODEC_PCM: default_amod = "raw"; break; + case CODEC_VORBIS: default_amod = "ogg"; break; + case CODEC_VAG: default_amod = "vag"; break; + default: default_amod = "null"; break; + } + + /* Choose modules based on file format */ + + switch (vob->v_format_flag) { + + case TC_MAGIC_MPLAYER: + vob->vmod_probed = "mplayer"; + vob->amod_probed = "mplayer"; + break; + + case TC_MAGIC_V4L_VIDEO: + vob->vmod_probed = "v4l"; + if (MAY_SET(FRAMESIZE)) { + vob->im_v_width = PAL_W/2; + vob->im_v_height = PAL_H/2; + if (vob->im_v_codec != CODEC_RGB) + vob->im_v_width &= -16; + } + break; + + case TC_MAGIC_V4L2_VIDEO: + vob->vmod_probed = "v4l2"; + vob->amod_probed = "v4l2"; + if (MAY_SET(FRAMESIZE)) { + vob->im_v_width = PAL_W/2; + vob->im_v_height = PAL_H/2; + if (vob->im_v_codec != CODEC_RGB) + vob->im_v_width &= -16; + } + break; + + case TC_MAGIC_BKTR_VIDEO: + vob->vmod_probed = "bktr"; + if (MAY_SET(FRAMESIZE) && !(vob->im_v_width>0 && vob->im_v_height>0)) { + vob->im_v_width = PAL_W/2; + vob->im_v_height = PAL_H/2; + if (vob->im_v_codec != CODEC_RGB) + vob->im_v_width &= -16; + } + break; + + case TC_MAGIC_YUV4MPEG: + vob->vmod_probed = "yuv4mpeg"; + break; + + case TC_MAGIC_BSDAV: + vob->vmod_probed = "bsdav"; + break; + + case TC_MAGIC_NUV: + vob->vmod_probed = "nuv"; + vob->amod_probed = "nuv"; + break; + + case TC_MAGIC_OGG: + vob->vmod_probed = "ogg"; + vob->amod_probed = "ogg"; + + case TC_MAGIC_DVD_NTSC: + if (MAY_SET(DEMUX)) { + if (vob->demuxer < 0) + vob->demuxer = 1; + /* Activate special handling for 24fps video */ + if (vob->fps < PAL_FPS && (vob->demuxer==1 || vob->demuxer==3)) + vob->demuxer++; + } + /* Fall through to common DVD handling */ + case TC_MAGIC_DVD_PAL: + vob->vmod_probed = "dvd"; + vob->amod_probed = "dvd"; + break; + + case TC_MAGIC_AVI: + if (vob->pass_flag & TC_VIDEO) + vob->vmod_probed = "avi"; + break; + + case TC_MAGIC_MOV: + vob->vmod_probed = "mov"; + break; + + case TC_MAGIC_TS: + vob->vmod_probed = "ts"; + + case TC_MAGIC_TIFF1: + case TC_MAGIC_TIFF2: + case TC_MAGIC_JPEG: + case TC_MAGIC_PPM: + case TC_MAGIC_PGM: + case TC_MAGIC_BMP: + case TC_MAGIC_PNG: + case TC_MAGIC_GIF: + case TC_MAGIC_SGI: + vob->vmod_probed = "im"; + break; + + case TC_MAGIC_DV_NTSC: + case TC_MAGIC_DV_PAL: + if (vob->pass_flag & TC_VIDEO) + vob->vmod_probed = "dv"; + break; + + case TC_MAGIC_CDXA: + vob->vmod_probed = "vob"; + vob->amod_probed = "vob"; + break; + + case TC_MAGIC_MP3: + vob->amod_probed = "mp3"; + break; + + case TC_MAGIC_AC3: + vob->amod_probed = "ac3"; + break; + + case TC_MAGIC_PV3: + vob->vmod_probed = "pv3"; + vob->amod_probed = "pv3"; // really just BE raw after demuxing + break; + + case TC_MAGIC_PVN: + vob->vmod_probed = "pvn"; + break; + + case TC_MAGIC_X11: + vob->vmod_probed = "x11"; + break; + + } // switch (vob->v_format_flag) + + switch (vob->a_format_flag) { + case TC_MAGIC_V4L_AUDIO: vob->amod_probed = "v4l"; break; + case TC_MAGIC_V4L2_AUDIO: vob->amod_probed = "v4l2"; break; + case TC_MAGIC_SUNAU_AUDIO: vob->amod_probed = "sunau"; break; + case TC_MAGIC_BSDAV: vob->amod_probed = "bsdav"; break; + case TC_MAGIC_WAV: vob->amod_probed = "raw"; break; + case TC_MAGIC_MOV: vob->amod_probed = "mov"; break; + case TC_MAGIC_TS: vob->amod_probed = "ts"; break; + case TC_MAGIC_MP3: vob->amod_probed = "mp3"; break; + case TC_MAGIC_AC3: vob->amod_probed = "ac3"; break; + case TC_MAGIC_OSS_AUDIO: + if (!vob->amod_probed) + vob->amod_probed = "oss"; + break; + case TC_MAGIC_AVI: + if (vob->pass_flag & TC_AUDIO) + vob->amod_probed = "avi"; + break; + } // switch (vob->a_format_flag) + + /* Choose modules based on codec */ + switch (vob->v_codec_flag) { + + case TC_CODEC_DV: + if (!vob->vmod_probed) + vob->vmod_probed = "dv"; + if (!vob->amod_probed) { + if (vob->v_format_flag == TC_MAGIC_AVI) + vob->amod_probed = default_amod; + else + vob->amod_probed = "dv"; + } + break; + + case TC_CODEC_MPEG: + case TC_CODEC_M2V: + case TC_CODEC_MPEG1: + if (!vob->vmod_probed) + vob->vmod_probed = "mpeg2"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + case TC_CODEC_MPEG2: + if (!vob->vmod_probed) { + vob->vmod_probed = "vob"; + if (MAY_SET(DEMUX)) { + if (vob->demuxer < 0) + vob->demuxer = 1; + /* Activate special handling for 24fps video */ + if (vob->fps < PAL_FPS && (vob->demuxer==1 || vob->demuxer==3)) + vob->demuxer++; + } + } + if (!vob->amod_probed) + vob->amod_probed = vob->has_audio ? "vob" : "null"; + break; + + case TC_CODEC_MJPEG: + case TC_CODEC_MPG1: + case TC_CODEC_MP42: + case TC_CODEC_MP43: + case TC_CODEC_RV10: + case TC_CODEC_ASV1: + case TC_CODEC_ASV2: + case TC_CODEC_FFV1: + case TC_CODEC_H264: + if (!vob->vmod_probed) + vob->vmod_probed = "ffmpeg"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + case TC_CODEC_LZO1: + case TC_CODEC_LZO2: + /* Overwrite video import module selected from format */ + vob->vmod_probed = "lzo"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + case TC_CODEC_THEORA: + if (vob->v_format_flag != TC_MAGIC_OGG && !vob->vmod_probed) + vob->vmod_probed = "mplayer"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + case TC_CODEC_DIVX3: + case TC_CODEC_DIVX4: + case TC_CODEC_DIVX5: + case TC_CODEC_XVID: + if (vob->v_format_flag != TC_MAGIC_OGG && !vob->vmod_probed) + vob->vmod_probed = "ffmpeg"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + case TC_CODEC_YUV420P: + case TC_CODEC_YUV422P: + case TC_CODEC_RGB: + if (!vob->vmod_probed) + vob->vmod_probed = "raw"; + if (!vob->amod_probed) + vob->amod_probed = default_amod; + break; + + } // switch (vob->v_codec_flag) + + /* If still not known, default to the null module */ + if (!vob->vmod_probed) + vob->vmod_probed = "null"; + if (!vob->amod_probed) + vob->amod_probed = "null"; + + /* Set XML import modules */ + if (!vob->vmod_probed_xml) + vob->vmod_probed_xml = vob->vmod_probed; + if (!vob->amod_probed_xml) + vob->amod_probed_xml = vob->amod_probed; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/probe.h b/debian/transcode/transcode-1.1.7/src/probe.h new file mode 100644 index 00000000..9589aed3 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/probe.h @@ -0,0 +1,124 @@ +/* + * probe.h - declarations for input file probing + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef PROBE_H +#define PROBE_H + +/*************************************************************************/ + +/* Structures to hold probed data */ + +typedef struct { + int samplerate; + int chan; + int bits; + int bitrate; + int padrate; // Padding byterate + int format; + int lang; + int attribute; // 0=subtitle, 1=AC3, 2=PCM + int tid; // Logical track id, in case of gaps + double pts_start; +} ProbeTrackInfo; + + +typedef struct { + + int width; // Frame width + int height; // Frame height + + double fps; // Encoder fps + + long codec; // Video codec + long magic; // File type/magic + long magic_xml; // Type/magic of content in XML file + + int asr; // Aspect ratio code + int frc; // Frame cate code + + int par_width; // Pixel aspect (== sample aspect ratio) + int par_height; + + int attributes; // Video attributes + + int num_tracks; // Number of audio tracks + + ProbeTrackInfo track[TC_MAX_AUD_TRACKS]; + + long frames; // Total frames + long time; // Total time in secs + + int unit_cnt; // Detected presentation units + double pts_start; // Video PTS start + + long bitrate; // Video stream bitrate + + int ext_attributes[4]; // Reserved for MPEG + + int is_video; // NTSC flag + +} ProbeInfo; + +/*************************************************************************/ + +/* External interface */ +int probe_source(const char *vid_file, const char *aud_file, int range, + int flags, vob_t *vob); +int probe_source_xml(vob_t *vob, int which); + +int probe_stream_data(const char *file, int range, ProbeInfo *info); +void probe_to_vob(ProbeInfo *vinfo, ProbeInfo *ainfo, int flags, vob_t *vob); + +/* Flags for probe_source(), indicating which parameters were specified by + * the user and shouldn't be overwritten */ +enum { + TC_PROBE_NO_FRAMESIZE = 1, + TC_PROBE_NO_FPS = 2, + TC_PROBE_NO_DEMUX = 4, + TC_PROBE_NO_RATE = 8, + TC_PROBE_NO_CHAN = 16, + TC_PROBE_NO_BITS = 32, + TC_PROBE_NO_SEEK = 64, + TC_PROBE_NO_TRACK = 128, + TC_PROBE_NO_BUFFER = 256, +// TC_PROBE_NO_FRC = 512, // unused + TC_PROBE_NO_ACODEC = 1024, + TC_PROBE_NO_AVSHIFT = 2048, + TC_PROBE_NO_AV_FINE = 4096, + TC_PROBE_NO_IMASR = 8192, + TC_PROBE_NO_BUILTIN = 16384, // external probe (mplayer) + TC_PROBE_NO_MODULES = 32768, +}; + +/* `which' value for probe_xml() */ +enum { + PROBE_XML_VIDEO = 0, + PROBE_XML_AUDIO, +}; + +/* Auxiliary info routines */ +const char *mformat2str(int flag); + +/* info_server.c */ +void server_thread(vob_t *vob); + +/*************************************************************************/ + +#endif // PROBE_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/socket.c b/debian/transcode/transcode-1.1.7/src/socket.c new file mode 100644 index 00000000..5cf1d7e8 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/socket.c @@ -0,0 +1,881 @@ +/* socket.c -- routines for controlling transcode over a socket + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "encoder.h" +#include "filter.h" +#include "socket.h" +#include "libtc/libtc.h" + +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +/*************************************************************************/ + +/* Pathname for listener socket */ +static char socket_path[PATH_MAX+1] = ""; +/* Socket descriptors for listener socket and client socket */ +static int server_sock = -1; +static int client_sock = -1; + +/* For communicating with "pv" module (FIXME: should go away) */ +pthread_mutex_t tc_socket_msg_lock = PTHREAD_MUTEX_INITIALIZER; +enum tc_socket_msg_cmd_enum tc_socket_msg_cmd; +int tc_socket_msg_arg = 0; + +/*************************************************************************/ +/*************************************************************************/ + +/** + * sendall: Send data to a client_sock, handling partial writes transparently. + * + * Parameters: + * sock: Socket descriptor. + * buf: Data to write. + * count: Number of bytes to send. + * Return value: + * Total number of bytes sent; -1 indicates that the send failed. + */ + +static int sendall(int sock, const void *buf, size_t count) +{ + int total_sent = 0; + + if (sock < 0 || !buf || count < 0) { + tc_log_warn(__FILE__, "sendall(): invalid parameters!"); + errno = EINVAL; + return -1; + } + while (count > 0) { + int retval = send(sock, buf, count, 0); + if (retval <= 0) { + tc_log_warn(__FILE__, "sendall(): socket write failed (%s)", + retval<0 ? strerror(errno) : "Connection closed"); + if (total_sent == 0) + return -1; /* Nothing sent yet, abort with error return */ + break; + } + total_sent += retval; + buf = (int8_t *)buf + retval; + count -= retval; + } + return total_sent; +} + +/*************************************************************************/ + +/** + * sendstr: Send a string to a client_sock, handling partial writes transparently. + * + * Parameters: + * sock: Socket descriptor. + * str: String to write. + * Return value: + * Total number of bytes sent; -1 indicates that the send failed. + */ + +static int sendstr(int sock, const char *str) +{ + return sendall(sock, str, strlen(str)); +} + +/*************************************************************************/ + +/** + * dump_vob: Send the contents of the global `vob' structure over the + * socket in a "parameter=value" format, one field per line. + * + * Parameters: + * sock: Socket descriptor to send data to. + * Return value: + * None. + */ + +static void dump_vob(int sock) +{ + vob_t *vob = tc_get_vob(); + char buf[1000]; + int n; + +#define SEND(field,fmt) \ + n = tc_snprintf(buf, sizeof(buf), "%s=" fmt "\n", #field, vob->field); \ + if (n > 0) \ + sendall(sock, buf, n); + + /* Generated via find-and-replace from vob_t definition in transcode.h */ + SEND(vmod_probed, "%s"); + SEND(amod_probed, "%s"); + SEND(vmod_probed_xml, "%s"); + SEND(amod_probed_xml, "%s"); + SEND(verbose, "%d"); + SEND(video_in_file, "%s"); + SEND(audio_in_file, "%s"); + SEND(nav_seek_file, "%s"); + SEND(has_audio, "%d"); + SEND(has_audio_track, "%d"); + SEND(has_video, "%d"); + SEND(lang_code, "%d"); + SEND(a_track, "%d"); + SEND(v_track, "%d"); + SEND(s_track, "%d"); + SEND(sync, "%d"); + SEND(sync_ms, "%d"); + SEND(dvd_title, "%d"); + SEND(dvd_chapter1, "%d"); + SEND(dvd_chapter2, "%d"); + SEND(dvd_max_chapters, "%d"); + SEND(dvd_angle, "%d"); + SEND(ps_unit, "%d"); + SEND(ps_seq1, "%d"); + SEND(ps_seq2, "%d"); + SEND(ts_pid1, "%d"); + SEND(ts_pid2, "%d"); + SEND(vob_offset, "%d"); + SEND(vob_chunk, "%d"); + SEND(vob_chunk_num1, "%d"); + SEND(vob_chunk_num2, "%d"); + SEND(vob_chunk_max, "%d"); + SEND(vob_percentage, "%d"); + SEND(vob_psu_num1, "%d"); + SEND(vob_psu_num2, "%d"); + SEND(vob_info_file, "%s"); + SEND(pts_start, "%f"); + SEND(psu_offset, "%f"); + SEND(demuxer, "%d"); + SEND(v_format_flag, "%ld"); + SEND(v_codec_flag, "%ld"); + SEND(a_format_flag, "%ld"); + SEND(a_codec_flag, "%ld"); + SEND(quality, "%d"); + SEND(a_stream_bitrate, "%d"); + SEND(a_chan, "%d"); + SEND(a_bits, "%d"); + SEND(a_rate, "%d"); + SEND(a_padrate, "%d"); + SEND(im_a_size, "%d"); + SEND(ex_a_size, "%d"); + SEND(im_a_codec, "%d"); + SEND(a_leap_frame, "%d"); + SEND(a_leap_bytes, "%d"); + SEND(a_vbr, "%d"); + SEND(a52_mode, "%d"); + SEND(dm_bits, "%d"); + SEND(dm_chan, "%d"); + SEND(v_stream_bitrate, "%d"); + SEND(fps, "%f"); + SEND(im_frc, "%d"); + SEND(ex_fps, "%f"); + SEND(ex_frc, "%d"); + SEND(hard_fps_flag, "%d"); + SEND(pulldown, "%d"); + SEND(im_v_height, "%d"); + SEND(im_v_width, "%d"); + SEND(im_v_size, "%d"); + SEND(im_asr, "%d"); + SEND(im_par, "%d"); + SEND(im_par_width, "%d"); + SEND(im_par_height, "%d"); + SEND(ex_asr, "%d"); + SEND(ex_par, "%d"); + SEND(ex_par_width, "%d"); + SEND(ex_par_height, "%d"); + SEND(attributes, "%d"); + SEND(im_v_codec, "%d"); + SEND(encode_fields, "%d"); + SEND(dv_yuy2_mode, "%d"); + SEND(volume, "%f"); + SEND(ac3_gain[0], "%f"); + SEND(ac3_gain[1], "%f"); + SEND(ac3_gain[2], "%f"); + SEND(clip_count, "%d"); + SEND(ex_v_width, "%d"); + SEND(ex_v_height, "%d"); + SEND(ex_v_size, "%d"); + SEND(reduce_h, "%d"); + SEND(reduce_w, "%d"); + SEND(resize1_mult, "%d"); + SEND(vert_resize1, "%d"); + SEND(hori_resize1, "%d"); + SEND(resize2_mult, "%d"); + SEND(vert_resize2, "%d"); + SEND(hori_resize2, "%d"); + SEND(zoom_width, "%d"); + SEND(zoom_height, "%d"); + SEND(zoom_interlaced, "%d"); + SEND(zoom_filter, "%d"); + SEND(antialias, "%d"); + SEND(deinterlace, "%d"); + SEND(decolor, "%d"); + SEND(aa_weight, "%f"); + SEND(aa_bias, "%f"); + SEND(gamma, "%f"); + SEND(ex_clip_top, "%d"); + SEND(ex_clip_bottom, "%d"); + SEND(ex_clip_left, "%d"); + SEND(ex_clip_right, "%d"); + SEND(im_clip_top, "%d"); + SEND(im_clip_bottom, "%d"); + SEND(im_clip_left, "%d"); + SEND(im_clip_right, "%d"); + SEND(post_ex_clip_top, "%d"); + SEND(post_ex_clip_bottom, "%d"); + SEND(post_ex_clip_left, "%d"); + SEND(post_ex_clip_right, "%d"); + SEND(pre_im_clip_top, "%d"); + SEND(pre_im_clip_bottom, "%d"); + SEND(pre_im_clip_left, "%d"); + SEND(pre_im_clip_right, "%d"); + SEND(video_out_file, "%s"); + SEND(audio_out_file, "%s"); + SEND(avifile_in, "%p"); // not sure if there's any point sending these... + SEND(avifile_out, "%p"); + SEND(avi_comment_fd, "%d"); + SEND(audio_file_flag, "%d"); + SEND(divxbitrate, "%d"); + SEND(divxkeyframes, "%d"); + SEND(divxquality, "%d"); + SEND(divxcrispness, "%d"); + SEND(divxmultipass, "%d"); + SEND(video_max_bitrate, "%d"); + SEND(divxlogfile, "%s"); + SEND(min_quantizer, "%d"); + SEND(max_quantizer, "%d"); + SEND(rc_period, "%d"); + SEND(rc_reaction_period, "%d"); + SEND(rc_reaction_ratio, "%d"); + SEND(divx5_vbv_prof, "%d"); + SEND(divx5_vbv_bitrate, "%d"); + SEND(divx5_vbv_size, "%d"); + SEND(divx5_vbv_occupancy, "%d"); + SEND(mp3bitrate, "%d"); + SEND(mp3frequency, "%d"); + SEND(mp3quality, "%f"); + SEND(mp3mode, "%d"); + SEND(bitreservoir, "%d"); + SEND(lame_preset, "%s"); + SEND(audiologfile, "%s"); + SEND(ex_a_codec, "%d"); + SEND(ex_v_codec, "%d"); + SEND(ex_v_fcc, "%s"); + SEND(ex_a_fcc, "%s"); + SEND(ex_profile_name, "%s"); + SEND(pass_flag, "%d"); + SEND(encoder_flush, "%d"); + SEND(mod_path, "%s"); + SEND(ttime, "%p"); // format this as a -c style string? + SEND(frame_interval, "%u"); + SEND(im_v_string, "%s"); + SEND(im_a_string, "%s"); + SEND(ex_v_string, "%s"); + SEND(ex_a_string, "%s"); + SEND(ex_m_string, "%s"); + SEND(m2v_requant, "%f"); + SEND(mpeg_profile, "%d"); + SEND(export_attributes, "%u"); + +#undef SEND +} + +/*************************************************************************/ +/*************************************************************************/ + +/* Socket actions. */ + +/*************************************************************************/ + +/** + * handle_config(): Process a "config" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_config(char *params) +{ + char *filter_name, *filter_params; + int filter_id; + + filter_name = params; + filter_params = filter_name + strcspn(filter_name, " \t"); + *filter_params++ = 0; + filter_params += strspn(filter_params, " \t"); + if (!*filter_name || !*filter_params) + return 0; + + filter_id = tc_filter_find(filter_name); + if (!filter_id) + return 0; + else + return tc_filter_configure(filter_id, filter_params) == 0; +} + +/*************************************************************************/ + +/** + * handle_disable(): Process a "disable" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_disable(char *params) +{ + int filter_id; + + filter_id = tc_filter_find(params); + if (!filter_id) + return 0; + else + return tc_filter_disable(filter_id); +} + +/*************************************************************************/ + +/** + * handle_enable(): Process an "enable" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_enable(char *params) +{ + int filter_id; + + filter_id = tc_filter_find(params); + if (!filter_id) + return 0; + else + return tc_filter_enable(filter_id); +} + +/*************************************************************************/ + +/** + * handle_help(): Process a "help" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_help(char *params) +{ + sendstr(client_sock, + "load <filter> <initial string>\n" + "unload <filter>\n" + "enable <filter>\n" + "disable <filter>\n" + "config <filter> <string>\n" + "parameters <filter>\n" + "list [ load | enable | disable ]\n" + "dump\n" + "progress\n" + "pause\n" + "preview <command>\n" + " [ draw | undo | pause | fastfw |\n" + " slowfw | slowbw | rotate |\n" + " rotate | display | slower |\n" + " faster | toggle | grab ]\n" + "help\n" + "version\n" + "quit\n" + ); + return 1; +} + +/*************************************************************************/ + +/** + * handle_list(): Process a "list" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_list(char *params) +{ + const char *list = NULL; + + if (strncasecmp(params, "load", 2) == 0) + list = tc_filter_list(TC_FILTER_LIST_LOADED); + else if (strncasecmp(params, "enable", 2) == 0) + list = tc_filter_list(TC_FILTER_LIST_ENABLED); + else if (strncasecmp(params, "disable", 2) == 0) + list = tc_filter_list(TC_FILTER_LIST_DISABLED); + + if (list) { + sendstr(client_sock, list); + return 1; + } else { + return 0; + } +} + +/*************************************************************************/ + +/** + * handle_load(): Process a "load" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_load(char *params) +{ + char *name, *options; + + name = params; + options = strchr(params, ' '); + if (options) { + *options++ = 0; + options += strspn(options, " \t"); + } + return tc_filter_add(name, options); +} + +/*************************************************************************/ + +/** + * handle_parameter(): Process a "parameter" command received on the + * socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_parameter(char *params) +{ + int filter_id; + const char *s; + + filter_id = tc_filter_find(params); + if (!filter_id) + return 0; + s = tc_filter_get_conf(filter_id, NULL); + if (!s) + return 0; + sendstr(client_sock, s); + return 1; +} + +/*************************************************************************/ + +/** + * handle_preview(): Process a "preview" command received on the socket. + * + * Parameters: + * params: Command parameters. + * Return value: + * Nonzero on success, zero on failure. + */ + +static int handle_preview(char *params) +{ + int filter_id, cmd, arg; + char *cmdstr, *argstr; + + /* Check that the preview filter is loaded, and load it if not */ + filter_id = tc_filter_find("pv"); + if (!filter_id) { + filter_id = tc_filter_add("pv", "cache=20"); + if (!filter_id) + return 0; + } + + /* Parse out preview command name and (optional) argument */ + cmdstr = strtok(params, " \t"); + if (cmdstr) // there are strtok() implementations that crash without this! + argstr = strtok(NULL, " \t"); + else + argstr = NULL; + if (argstr) + arg = atoi(argstr); + else + arg = 0; + + if (!strncasecmp(cmdstr, "draw", 2)) { + cmd = TC_SOCK_PV_DRAW; + } else if (!strncasecmp(cmdstr, "pause", 2)) { + cmd = TC_SOCK_PV_PAUSE; + } else if (!strncasecmp(cmdstr, "undo", 2)) { + cmd = TC_SOCK_PV_UNDO; + } else if (!strncasecmp(cmdstr, "fastfw", 6)) { + cmd = TC_SOCK_PV_FAST_FW; + } else if (!strncasecmp(cmdstr, "fastbw", 6)) { + cmd = TC_SOCK_PV_FAST_BW; + } else if (!strncasecmp(cmdstr, "slowfw", 6)) { + cmd = TC_SOCK_PV_SLOW_FW; + } else if (!strncasecmp(cmdstr, "slowbw", 6)) { + cmd = TC_SOCK_PV_SLOW_BW; + } else if (!strncasecmp(cmdstr, "toggle", 6)) { + cmd = TC_SOCK_PV_TOGGLE; + } else if (!strncasecmp(cmdstr, "slower", 6)) { + cmd = TC_SOCK_PV_SLOWER; + } else if (!strncasecmp(cmdstr, "faster", 6)) { + cmd = TC_SOCK_PV_FASTER; + } else if (!strncasecmp(cmdstr, "rotate", 6)) { + cmd = TC_SOCK_PV_ROTATE; + } else if (!strncasecmp(cmdstr, "display", 6)) { + cmd = TC_SOCK_PV_DISPLAY; + } else if (!strncasecmp(cmdstr, "grab", 4)) { + cmd = TC_SOCK_PV_SAVE_JPG; + } else { + return 0; + } + + pthread_mutex_lock(&tc_socket_msg_lock); + tc_socket_msg_cmd = cmd; + tc_socket_msg_arg = arg; + pthread_mutex_unlock(&tc_socket_msg_lock); + + return 1; +} + +/*************************************************************************/ + +/** + * handle: Handle a single message from a socket. + * + * Parameters: + * buf: Line read from socket. + * Return value: + * Zero if the socket is to be closed, nonzero otherwise. + */ + +static int handle(char *buf) +{ + char *cmd, *params; + int len, retval; + + len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') + buf[--len] = 0; + if (len > 0 && buf[len-1] == '\r') + buf[--len] = 0; + //tc_log_msg(__FILE__, "read from socket: |%s|", buf); + + cmd = buf + strspn(buf, " \t"); + params = cmd + strcspn(cmd, " \t"); + *params++ = 0; + params += strspn(params, " \t"); + + if (!*cmd) { // not strictly necessary, but lines up else if's nicely + retval = 0; + } else if (strncasecmp(cmd, "config", 2) == 0) { + retval = handle_config(params); + } else if (strncasecmp(cmd, "disable", 2) == 0) { + retval = handle_disable(params); + } else if (strncasecmp(cmd, "dump", 2) == 0) { + dump_vob(client_sock); + retval = 1; + } else if (strncasecmp(cmd, "enable", 2) == 0) { + retval = handle_enable(params); + } else if (strncasecmp(cmd, "help", 2) == 0) { + retval = handle_help(params); + } else if (strncasecmp(cmd, "list", 2) == 0) { + retval = handle_list(params); + } else if (strncasecmp(cmd, "load", 2) == 0) { + retval = handle_load(params); + } else if (strncasecmp(cmd, "parameters", 3) == 0) { + retval = handle_parameter(params); + } else if (strncasecmp(cmd, "pause", 3) == 0) { + tc_pause_request(); + retval = 1; + } else if (strncasecmp(cmd, "preview", 3) == 0) { + retval = handle_preview(params); + } else if (strncasecmp(cmd, "progress", 3) == 0) { + tc_progress_meter = !tc_progress_meter; + retval = 1; + } else if (strncasecmp(cmd, "quit", 2) == 0 + || strncasecmp(cmd, "exit", 2) == 0) { + return 0; // tell caller to close socket + } else if (strncasecmp(cmd, "unload", 2) == 0) { + retval = 0; // FIXME: not implemented + } else if (strncasecmp(cmd, "version", 2) == 0) { + sendstr(client_sock, PACKAGE_VERSION "\n"); + retval = 1; + } else { + retval = 0; + } + + sendstr(client_sock, retval ? "OK\n" : "FAILED\n"); + return 1; // socket remains open +} + +/*************************************************************************/ +/*************************************************************************/ + +/* External interfaces. */ + +/*************************************************************************/ + +/** + * tc_socket_init: Initialize the socket code, and open a listener socket + * with the given pathname. + * + * Parameters: + * socket_path_: Pathname to use for communication socket. + * Return value: + * Nonzero on success, zero on failure. + * Side effects: + * If `socket_path' points to an existing file, the file is removed. + */ + +int tc_socket_init(const char *socket_path_) +{ + struct sockaddr_un server_addr; + + client_sock = -1; + server_sock = -1; + + if (tc_snprintf(socket_path, sizeof(socket_path), "%s", socket_path_) < 0){ + tc_log_error(__FILE__, "Socket pathname too long (1)"); + *socket_path = 0; + return 0; + } + + errno = 0; + if (unlink(socket_path) != 0 && errno != ENOENT) { + tc_log_error(__FILE__, "Unable to remove \"%s\": %s", + socket_path, strerror(errno)); + return 0; + } + + server_addr.sun_family = AF_UNIX; + if (tc_snprintf(server_addr.sun_path, sizeof(server_addr.sun_path), + "%s", socket_path) < 0 + ) { + tc_log_error(__FILE__, "Socket pathname too long"); + return 0; + } + server_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_sock < 0) { + tc_log_perror(__FILE__, "Unable to create server socket"); + return 0; + } + if (bind(server_sock, (struct sockaddr *)&server_addr, + sizeof(server_addr)) + ) { + tc_log_perror(__FILE__, "Unable to bind server socket"); + close(server_sock); + server_sock = -1; + unlink(socket_path); // just in case + return 0; + } + if (listen(server_sock, 5) < 0) { + tc_log_perror(__FILE__, "Unable to activate server socket"); + close(server_sock); + unlink(socket_path); + return 0; + } + + return 1; +} + +/*************************************************************************/ + +/** + * tc_socket_fini: Close the listener and client sockets, if open, and + * perform any other necessary cleanup. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void tc_socket_fini(void) +{ + if (client_sock >= 0) { + close(client_sock); + client_sock = -1; + } + if (server_sock >= 0) { + close(server_sock); + server_sock = -1; + unlink(socket_path); + } +} + +/*************************************************************************/ + +/** + * tc_socket_poll_internal: Check server and (if connected) client sockets for + * pending events, and process them, with tunable timeout. + * + * Parameters: + * blocking: if !0, blocks forever caller until next event. + * if 0, just check once if there is an event to process + * and execute (or just return) immediately. + * Return value: + * None. + * Preconditions: + * valid (!= -1) server socket avalaible. + */ + +static void tc_socket_poll_internal(int blocking) +{ + fd_set rfds, wfds; + char msgbuf[TC_BUF_MAX]; + int maxfd = -1, retval = -1; + struct timeval tv = {0, 0}, *ptv = (blocking) ?NULL :&tv; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + FD_SET(server_sock, &rfds); + maxfd = server_sock; + + if (client_sock >= 0) { + FD_SET(client_sock, &rfds); + //FD_SET(client_sock, &wfds); + if (client_sock > maxfd) + maxfd = client_sock; + } + retval = select(maxfd+1, &rfds, &wfds, NULL, ptv); + + if (retval == 0) { + /* nothing interesting happened. It happens :) */ + return; + } + if (retval < 0 && errno != EINTR) { + /* EINTR is an expected "exceptional" condition */ + tc_log_warn(__FILE__, "select(): %s", strerror(errno)); + return; + } + + if (FD_ISSET(server_sock, &rfds)) { + int newsock = accept(server_sock, NULL, 0); + if (newsock < 0) { + tc_log_warn(__FILE__, "Unable to accept new connection: %s", + strerror(errno)); + return; + } + if (client_sock >= 0) { + /* We already have a connection, so drop this one */ + close(newsock); + } else { + client_sock = newsock; + } + } + + if (client_sock >= 0 && FD_ISSET(client_sock, &rfds)) { + retval = recv(client_sock, msgbuf, sizeof(msgbuf)-1, 0); + if (retval <= 0) { + if (retval < 0) + tc_log_perror(__FILE__, "Unable to read message from socket"); + close(client_sock); + client_sock = -1; + } else { + if (retval > sizeof(msgbuf)-1) // paranoia + retval = sizeof(msgbuf)-1; + msgbuf[retval] = 0; + if (!handle(msgbuf)) { + close(client_sock); + client_sock = -1; + } + } + } +} + +/** + * tc_socket_poll: Check server and (if connected) client sockets for + * pending events, and process them. + * This call should never block caller, returnin immediately if there + * isn't any event pending. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void tc_socket_poll(void) +{ + if (server_sock < 0 && client_sock < 0) { + /* Nothing to poll! */ + return; + } + tc_socket_poll_internal(0); +} + +/** + * tc_socket_wait: Wait forever server and (if connected) client sockets + * for next event, and process the next first one and exit. + * This call will always block until next event happens. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void tc_socket_wait(void) +{ + if (server_sock < 0 && client_sock < 0) { + pause(); + /* if no server socket is avalaible (= no tc_socket_init + * called before), nothing will ever happen, so + * we must block forever to satisfy function requirements. + */ + return; + } + tc_socket_poll_internal(1); +} + + +/*************************************************************************/ + +/** + * tc_socket_submit: Send a string to the socket. Does nothing if no + * socket is open. + * + * Parameters: + * str: String to send. + * Return value: + * None. + */ + +void tc_socket_submit(const char *str) +{ + if (socket >= 0) + sendstr(client_sock, str); +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/socket.h b/debian/transcode/transcode-1.1.7/src/socket.h new file mode 100644 index 00000000..e96e3b02 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/socket.h @@ -0,0 +1,59 @@ +/* socket.h -- include file for control socket routines + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include <pthread.h> + +/*************************************************************************/ + +/* External interface. */ +int tc_socket_init(const char *socket_path); +void tc_socket_fini(void); +void tc_socket_poll(void); +void tc_socket_wait(void); +void tc_socket_submit(const char *str); + +/* Variables and constants for communicating with the "pv" module + * (FIXME: these should go away) */ +extern pthread_mutex_t tc_socket_msg_lock; +extern int tc_socket_msg_arg; +enum tc_socket_msg_cmd_enum { + TC_SOCK_PV_NONE = 0, + TC_SOCK_PV_PAUSE, + TC_SOCK_PV_DRAW, + TC_SOCK_PV_UNDO, + TC_SOCK_PV_SLOW_FW, + TC_SOCK_PV_SLOW_BW, + TC_SOCK_PV_FAST_FW, + TC_SOCK_PV_FAST_BW, + TC_SOCK_PV_SLOWER, + TC_SOCK_PV_FASTER, + TC_SOCK_PV_TOGGLE, + TC_SOCK_PV_ROTATE, + TC_SOCK_PV_DISPLAY, + TC_SOCK_PV_SAVE_JPG, +}; +extern enum tc_socket_msg_cmd_enum tc_socket_msg_cmd; + + +/*************************************************************************/ + +#endif /* SOCKET_H */ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/split.c b/debian/transcode/transcode-1.1.7/src/split.c new file mode 100644 index 00000000..9d5b1a8b --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/split.c @@ -0,0 +1,319 @@ +/* + * split.c + * + * Copyright (C) Thomas Oestreich - January 2002 + * + * This file is part of transcode, a 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 "transcode.h" +#include "split.h" + +#define PMAX_BUF 1024 +char split_cmd_buf[PMAX_BUF]; + +static int entries; + +typedef struct seq_s { + + int unit; + long frame; + int seq; + int pseq; + long offset; + int foffset; +} seq_t; + +static seq_t **seq=NULL; + +#define MAX_UNITS 128 + +static long uframe[MAX_UNITS]; +static long unit_offset[MAX_UNITS]; + +#define debug_return {return(-1);} + +static int split_stream_core(const char *file, const char *source) +{ + + FILE *fd; + FILE *tmp; + char buffer[256]; + + int n=-1; + + if(file == NULL) { + + if(tc_snprintf(split_cmd_buf, PMAX_BUF, "tccat -i %s | tcdemux -W 2>/dev/null", source)<0) debug_return; + // popen + if((fd = popen(split_cmd_buf, "r"))== NULL) debug_return; + + tc_log_info(__FILE__, "generating auto-split information from file \"%s\"", source); + + // open temp. file + tmp = tmpfile(); + + while ( fgets (buffer, 256, fd) ) { + ++n; + if ( fputs (buffer, tmp) < 0 ) debug_return; + } + + pclose(fd); + fd = tmp; + + } else { + + if((fd = fopen(file, "r"))==NULL) debug_return; + + tc_log_info(__FILE__, "reading auto-split information from file \"%s\"", file); + + // determine number of lines + + while (fgets (buffer, 256, fd)) { ++n; } + } + + // need n+2 pointers???? + seq = (seq_t **) malloc( (n+2) * sizeof(seq_t*)); + if ( seq == NULL ) debug_return; + + n=-1; + fseek (fd, 0, SEEK_SET); + + do { + ++n; + + if((seq[n] = (seq_t *) malloc(sizeof(seq_t))) == NULL) debug_return; + + } while((fscanf(fd, "%d %ld %d %d %ld %d", &seq[n]->unit, &seq[n]->frame, &seq[n]->seq, &seq[n]->pseq, &seq[n]->offset, &seq[n]->foffset)!=EOF)); + + entries=n; + + if(seq[n]!=NULL) seq[n] = (seq_t *) malloc(sizeof(seq_t)); + + //add fake closing entry + seq[n]->unit=seq[n-1]->unit+1; + seq[n]->frame=seq[n-1]->frame=0; + seq[n]->seq=seq[n-1]->seq+1; + + fclose(fd); + + return(0); +} + + +static long get_frame_index(int unit, long frame_inc) +{ + //first entry is unique + + long n; + int m; + + if(frame_inc==0) return(unit_offset[unit]); + + n=unit_offset[unit] + frame_inc; + m=seq[n]->seq; + + while(seq[n]->unit == unit && n < entries && seq[n]->seq == m ) ++n; + + return(n); +} + +//---------------------------------------------- +// +// main routine +// +//---------------------------------------------- + + +int split_stream(vob_t *vob, const char *file, int this_unit, int *fa, int *fb, int opt_flag) +{ + + int n, unit_ctr=-1, last_unit=-1; + + int s1, s2, video=1; + + long max_frames=-1; + int unit=0, foff=0; + + long _fa, _fb; + + long _n, frame_inc=0, poff=0; + + int startc, chunks; + + if(split_stream_core(file, ((vob->vob_chunk == vob->vob_chunk_max) ? vob->audio_in_file:vob->video_in_file))<0) { + tc_log_error(__FILE__, "failed to read VOB navigation file %s", file); + return(-1); + } + + tc_log_info(__FILE__, "done reading %d entries", entries); + + //analyze data: + + for(n=0; n<MAX_UNITS; ++n) uframe[n]=0; + + // (I) determine presentation units and number of frames + + for(n=0; n<entries; ++n) { + + if(last_unit != seq[n]->unit) { + last_unit=seq[n]->unit; + ++unit_ctr; + unit_offset[unit_ctr]=n; + } + + ++uframe[unit_ctr]; + } + + for(n=0; n<=unit_ctr; ++n) { + if(max_frames<=uframe[n]) { + unit = n; + max_frames = uframe[n]; + } + + if(this_unit > unit_ctr) { + if(verbose &TC_DEBUG) tc_log_msg(__FILE__, "invalid PSU %s", file); + return(-1); + } + + if(-1 < this_unit) unit = this_unit; + + if(verbose &TC_DEBUG) tc_log_msg(__FILE__, "unit=%d, frames=%ld, offset=%ld (%d)", n, uframe[n], unit_offset[n], vob->ps_unit); + } + + // (II) determine largest (main) presentation unit + + if(verbose &TC_DEBUG) tc_log_msg(__FILE__, "selecting unit %d, frames=%ld, offset=%ld", unit, uframe[unit], unit_offset[unit]); + + + // video or audio mode ? + + if((vob->vob_percentage) ? (vob->vob_chunk==vob->vob_chunk_max && vob->vob_chunk_max==100 ) : (vob->vob_chunk == vob->vob_chunk_max)) video=0; + + //check user error + if(vob->vob_chunk_num2>vob->vob_chunk_max) vob->vob_chunk_num2=vob->vob_chunk_max; + if(vob->vob_chunk_num1>vob->vob_chunk_max) vob->vob_chunk_num1=0; + + // determine number of chunks to process: (0.6.0pre5) + + if(video) { + chunks = (vob->vob_percentage || vob->vob_chunk_num2==0) ? 1:vob->vob_chunk_num2-vob->vob_chunk_num1; + startc = (vob->vob_percentage || vob->vob_chunk_num2==0) ? vob->vob_chunk:vob->vob_chunk_num1; + } else { + chunks = (vob->vob_percentage || vob->vob_chunk_num2==0) ? vob->vob_chunk_max:vob->vob_chunk_num2-vob->vob_chunk_num1; + startc = (vob->vob_percentage || vob->vob_chunk_num2==0) ? 0:vob->vob_chunk_num1; + } + + //--------------------------------------------------------------------- + + // (III) determine pack offset for given number of VOB chunks + // Modified by CARON Dominique <domi@lpm.univ-monp2.fr> to optimize the + // cluster mode + // vob->vob_chunk = percentage of frames to skip before frames to encode + // vob->vob_chunk_max = percentage of frames to encode + + + frame_inc = (vob->vob_percentage) ? (long) ((startc * uframe[unit])/100) : (long) ((startc * uframe[unit])/vob->vob_chunk_max); + + if(verbose &TC_DEBUG) tc_log_msg(__FILE__, "estimated chunk offset = %ld", frame_inc); + + _n = get_frame_index(unit, frame_inc); + + poff = seq[_n]->offset; + foff = seq[_n]->foffset; + + _fa = seq[_n]->frame; + + // parameter for option "-c" + *fa = foff; + *fb = foff - seq[_n]->frame; + + s1 = seq[_n]->seq; + + if(verbose &TC_DEBUG) tc_log_msg(__FILE__, "chunk %d starts at frame %ld, pack offset %ld, finc=%d", startc, _n, poff, foff); + + + // (IV) determine end of chunk(s) + // Modified by CARON Dominique <domi@lpm.univ-monp2.fr> to optimize the + // cluster mode + + frame_inc = (vob->vob_percentage) ? (long) (((vob->vob_chunk+vob->vob_chunk_max) * uframe[unit])/100) : (long) (((startc+chunks) * uframe[unit])/vob->vob_chunk_max); + + _n = get_frame_index(unit, frame_inc); + + _fb = seq[_n]->frame; + + s2 = seq[_n]->seq; + + if(_fb==0) { + _fb = uframe[unit]; + *fb += uframe[unit]; + } else { + *fb += seq[_n]->frame; + } + + // (V) set vob parameter + + vob->vob_offset = poff; + vob->ps_unit = 0; + + vob->ps_seq1 = 0; + vob->ps_seq2 = (s2==0 && _n) ? seq[_n-1]->seq-s1+3 : s2-s1+2; + + tc_log_msg(__FILE__, "chunk %d/%d PU=%d (-L 0 -c %ld-%ld) mapped onto (-L %ld -c %d-%d)", vob->vob_chunk, vob->vob_chunk_max-1, unit, _fa, _fb, poff, *fa, *fb); + + + //--------------------------------------------------------------------- + + if(opt_flag==1) { //cluster mode + + if(video) { + + // no sound + vob->amod_probed="null"; + vob->has_audio=0; + + tc_log_info(__FILE__, "video mode"); + + } else { + + // no video + vob->vmod_probed="null"; + vob->has_video=0; + + vob->vob_offset = poff; + vob->ps_unit = 0; + + tc_log_info(__FILE__, "audio mode"); + } + } + + //--------------------------------------------------------------------- + + if(seq != NULL) { + + // seq array not needed anymore + for (n=0; n < entries+1; ++n) { + free(seq[n]); + } + + free(seq); + } + + return(0); +} diff --git a/debian/transcode/transcode-1.1.7/src/split.h b/debian/transcode/transcode-1.1.7/src/split.h new file mode 100644 index 00000000..f0eae0ce --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/split.h @@ -0,0 +1,29 @@ +/* + * split.h + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef _SPLIT_H +#define _SPLIT_H + +int split_stream(vob_t *vob, const char *file, int unit, int *fa, int *fb, int opt_flag); + +#endif diff --git a/debian/transcode/transcode-1.1.7/src/tc_defaults.h b/debian/transcode/transcode-1.1.7/src/tc_defaults.h new file mode 100644 index 00000000..801aeae5 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/tc_defaults.h @@ -0,0 +1,226 @@ +/* + * tc_defaults.h + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <limits.h> + +#ifndef TC_DEFAULTS_H +#define TC_DEFAULTS_H + +#define TC_DEFAULT_MOD_PATH "/usr/local/lib/transcode" + +#define TC_DEFAULT_IN_FILE "/dev/zero" +#define TC_DEFAULT_OUT_FILE "/dev/null" + +// default PAL video size +#define PAL_W 720 +#define PAL_H 576 +#define BPP 24 +#define PAL_FPS 25.0 +#define MIN_FPS 1.0 +#define NTSC_FILM ((double)(24000)/1001.0) +#define NTSC_VIDEO ((double)(30000)/1001.0) + +//NTSC +#define NTSC_W 720 +#define NTSC_H 480 + +//new max frame size: +#define TC_MAX_V_FRAME_WIDTH 2500 +#define TC_MAX_V_FRAME_HEIGHT 2000 + +// max bytes per pixel +#define TC_MAX_V_BYTESPP 4 + +// audio defaults +#define RATE 48000 +#define BITS 16 +#define CHANNELS 2 + +#define SIZE_RGB_FRAME ((int) TC_MAX_V_FRAME_WIDTH*TC_MAX_V_FRAME_HEIGHT*(BPP/8)) +#define SIZE_PCM_FRAME ((int) (RATE/MIN_FPS) * BITS/8 * CHANNELS * 3) + +#define TC_PAD_AUD_FRAMES 10 +#define TC_MAX_SEEK_BYTES (1<<20) + +// DivX/MPEG-4 encoder defaults +#define VBITRATE 1800 +#define VKEYFRAMES 250 +#define VCRISPNESS 100 + +#define VMULTIPASS 0 +#define VQUALITY 5 + +#define VMINQUANTIZER 2 +#define VMAXQUANTIZER 31 + +#define VQUANTIZER 10 + +#define RC_PERIOD 2000 +#define RC_REACTION_PERIOD 10 +#define RC_REACTION_RATIO 20 + +// Divx5 VBV (Video Bitrate Verifier) +// Home theatre profile and settings +#define DIVX5_VBV_PROFILE 3 +#define DIVX5_VBV_BITRATE (4000000/400) +#define DIVX5_VBV_SIZE (3145728/16384) +#define DIVX5_VBV_OCCUPANCY (2359296/64) + +//---------------------------------- + +#define ABITRATE 128 +#define AQUALITY 5 +#define AVBR 0 +#define AMODE 0 + +typedef enum tcdebugmode_ TCDebugMode; +enum tcdebugmode_ { + TC_QUIET = 0, + TC_INFO = 1, + TC_DEBUG = 2, + TC_STATS = 4, + TC_CLEANUP = 8, + TC_FLIST = 16, + TC_SYNC = 32, + TC_COUNTER = 64, + TC_PRIVATE = 128, + TC_THREADS = 256, + TC_WATCH = 512, +}; + +//import/export/filter frame buffer status flag +#define TC_NONE 0 +#define TC_VIDEO 1 +#define TC_AUDIO 2 +#define TC_SUBEX 4 +#define TC_RESERVED 8 +#define TC_EXTRA 16 + +#define TC_FILTER_INIT 16 +#define TC_PRE_S_PROCESS 32 +#define TC_PRE_M_PROCESS 64 +#define TC_INT_M_PROCESS 128 +#define TC_POST_M_PROCESS 256 +#define TC_POST_S_PROCESS 512 +#define TC_PREVIEW 1024 +#define TC_FILTER_CLOSE 2048 +#define TC_FILTER_GET_CONFIG 4096 + +#define TC_IMPORT 8192 +#define TC_EXPORT 16384 + +#define TC_DELAY_MAX 40000 +#define TC_DELAY_MIN 10000 + +#define TC_DEFAULT_IMPORT_AUDIO "null" +#define TC_DEFAULT_IMPORT_VIDEO "null" +#define TC_DEFAULT_EXPORT_AUDIO "null" +#define TC_DEFAULT_EXPORT_VIDEO "null" +#define TC_DEFAULT_EXPORT_MPLEX "null" + +#define TC_FRAME_BUFFER 10 +#define TC_FRAME_THREADS 1 +#define TC_FRAME_THREADS_MAX 32 + +#define TC_FRAME_FIRST 0 +#define TC_FRAME_LAST INT_MAX + +#define TC_LEAP_FRAME 1000 +#define TC_MAX_AUD_TRACKS 32 + +//-------------------------------------------------- + +#define CODEC_NULL 0x0 + +#define CODEC_RGB 1 +#define CODEC_YUV 2 +#define CODEC_MP4 4 +#define CODEC_YUY2 8 +#define CODEC_DV 16 +#define CODEC_RAW 32 +#define CODEC_RAW_RGB 64 +#define CODEC_RAW_YUV 128 +#define CODEC_YUV422 256 + +#define CODEC_PCM 0x1 +#define CODEC_AC3 0x2000 +#define CODEC_MP2 0x50 +#define CODEC_MP3 0x55 +#define CODEC_DIVX 0x161 +#define CODEC_IMA4 0x11 +#define CODEC_LPCM 0x10001 +#define CODEC_DTS 0x1000F //?? +#define CODEC_VORBIS 0xfffe +#define CODEC_VAG 0xfeed //me pizza + +#define TC_INFO_NO_DEMUX 1 +#define TC_INFO_MPEG_PS 2 +#define TC_INFO_MPEG_ES 4 +#define TC_INFO_MPEG_PES 8 + +#define TC_FRAME_DV_PAL 144000 +#define TC_FRAME_DV_NTSC 120000 + +#define TC_SUBTITLE_HDRMAGIC 0x00030001 + +#define TC_DEFAULT_AAWEIGHT (1.0f/3.0f) +#define TC_DEFAULT_AABIAS (0.5f) + +#define TC_A52_DRC_OFF 1 +#define TC_A52_DEMUX 2 +#define TC_A52_DOLBY_OFF 4 + +#define AVI_FILE_LIMIT 2048 + +#define M2V_REQUANT_FACTOR 1.00f + +/* + * flags used in modules for supporting export profiles (--export_prof) + * if one of those flag is set, then use the value provided by the user. + * otherwise use the ones the export modules suggests. + */ +typedef enum tcexportattribute_ TCExportAttribute; +enum tcexportattribute_ { + TC_EXPORT_ATTRIBUTE_NONE = ( 0), + TC_EXPORT_ATTRIBUTE_VBITRATE = (1<< 1), /* -w */ + TC_EXPORT_ATTRIBUTE_ABITRATE = (1<< 2), /* -b */ + TC_EXPORT_ATTRIBUTE_FIELDS = (1<< 3), /* --encode_fields */ + TC_EXPORT_ATTRIBUTE_VMODULE = (1<< 4), /* -y X,* */ + TC_EXPORT_ATTRIBUTE_AMODULE = (1<< 5), /* -y *,X */ + TC_EXPORT_ATTRIBUTE_FRC = (1<< 6), /* --export_fps *,X */ + TC_EXPORT_ATTRIBUTE_FPS = (1<< 7), /* --export_fps X,* */ + TC_EXPORT_ATTRIBUTE_VCODEC = (1<< 8), /* -F */ + TC_EXPORT_ATTRIBUTE_ACODEC = (1<< 9), /* -N */ + TC_EXPORT_ATTRIBUTE_ARATE = (1<<10), /* -E X,*,* */ + TC_EXPORT_ATTRIBUTE_ABITS = (1<<11), /* -E *,X,* */ + TC_EXPORT_ATTRIBUTE_ACHANS = (1<<12), /* -E *,*,X */ + TC_EXPORT_ATTRIBUTE_ASR = (1<<13), /* --export_asr */ + TC_EXPORT_ATTRIBUTE_PAR = (1<<14), /* --export_par */ + TC_EXPORT_ATTRIBUTE_GOP = (1<<15), /* key frames */ +}; + +#endif /* TC_DEFAULTS_H */ diff --git a/debian/transcode/transcode-1.1.7/src/tcinfo.h b/debian/transcode/transcode-1.1.7/src/tcinfo.h new file mode 100644 index 00000000..bb81ebd5 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/tcinfo.h @@ -0,0 +1,224 @@ +/* + * tcinfo.h - definitions of info_t and decode_t + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef TCINFO_H +#define TCINFO_H + +#ifndef PROBE_H +# include "probe.h" // for ProbeInfo +#endif + +/*************************************************************************/ + +typedef struct _info_t { + + int fd_in; // Input stream file descriptor + int fd_out; // Output stream file descriptor + + long magic; // Specifies file magic for extract thread + int track; // Track to extract + long stype; // Specifies stream type for extract thread + long codec; // Specifies codec for extract thread + int verbose; // Verbosity + + int dvd_title; + int dvd_chapter; + int dvd_angle; + + int vob_offset; + + int ps_unit; + int ps_seq1; + int ps_seq2; + + int ts_pid; + + int seek_allowed; + + int demux; + int select; // Selected packet payload type + int subid; // Selected packet substream ID + int keep_seq; // Do not drop first sequence (cluster mode) + + double fps; + + int fd_log; + + const char *name; // Source name as supplied with -i option + const char *nav_seek_file; // Seek/index file + + int probe; // Flag for probe only mode + int factor; // Amount of file to probe, in MB + + ProbeInfo *probe_info; + + int quality; + int error; + + long frame_limit[3]; + int hard_fps_flag; // If this is set, disable demuxer smooth drop + +} info_t; + +typedef struct { + int fd_in; // Input stream file descriptor + int fd_out; // Output stream file descriptor + double ac3_gain[3]; + long frame_limit[3]; + int dv_yuy2_mode; + int padrate; // Zero padding rate + long magic; // Specifies file magic + long stype; // Specifies stream type + long codec; // Specifies codec + int verbose; // Verbosity + int quality; + const char *name; // Source name as supplied with -i option + int width; + int height; + int a52_mode; + long format; // Specifies raw stream format for output + int select; // Selected packet payload type +} decode_t; + +/*************************************************************************/ + +#include "libtc/tccodecs.h" + +typedef struct tcarea_ TCArea; +struct tcarea_ { + int top; + int left; + int bottom; + int right; +}; + +typedef struct tcexportinfo_ TCExportInfo; +struct tcexportinfo_ { + uint32_t attributes; + + /* + * common fields for video/audio/mplex sections: + * string : generic opaque string identifier + * (for video, mainly used for fourcc). + * module : (NMS) encode module to use. + * module_opts : (opaque) option string to module. + * + * common fields for video/audio sections: + * format : identifier of video format to use in encoding phase. + * Isn't a proper codec identifer since it can be a + * 'special' format like TC_CODEC_COPY + * FIXME: can sometimes be CODEC_XXX not TC_CODEC_XXX. + * quality : encoding quality condensed in a single param. + * Rarely used but still needed. + * bitrate : MEAN video bitrate to use (kbps) in encoding. + */ + struct { + char *string; + char *module; + char *module_opts; + + TCCodecID format; + int quality; + int bitrate; + + char *log_file; + /* path to log file to use, if needed, for multipass encoding. */ + + int width; + int height; + /* + * FINAL requested video frame width/height: encoded video stream will + * have this width/height. + */ + int keep_asr_flag; + /* final asr should be forced equal to import asr? */ + int fast_resize_flag; /* self explanatory :) */ + int zoom_interlaced_flag; /* self explanatory :) */ + int asr; + /* + * frame aspect ratio; often (but NOT always) + * computed from width/height pair + */ + + int frc; /* frame rate code */ + int par; + /* pixel aspect ratio; 1:1 by default, overridden by user in case */ + int encode_fields; + /* field based encoding selection */ + + TCArea pre_clip; + /* clip specified area BEFORE any other operation */ + TCArea post_clip; + /* clip specified area AFTER any other operation */ + + int gop_size; + /* video GOP size also known as keyframe interval */ + int quantizer_min; + int quantizer_max; + /* quantizer range to use in encoding */ + + int bitrate_max; + /* + * Maximum video bitrate to use (kbps) in encoding. + * Rarely used but still needed. + */ + + int pass_number; /* set for usage in multipass encoding */ + } video; + + struct { + char *string; + char *module; + char *module_opts; + + TCCodecID format; + int quality; + int bitrate; + + int sample_rate; /* audio sample rate (Hz) */ + int sample_bits; /* bits to use for each audio sample */ + int channels; /* number of channels in audio stream */ + int mode; /* audio mode: mode, stereo, joint stereo... */ + /* + * those last three fields are mainly used by lame, but they + * should be generalized + */ + int vbr_flag; + int flush_flag; + int bit_reservoir; + } audio; + + struct { + char *string; + char *module; + char *module_opts; + + char *out_file; /* self explanatory :) */ + char *out_file_aux; + /* + * path of extra output file (separate audio track. + * Provided for back compatibilty, can go away in future revisions. + */ + } mplex; +}; + +/*************************************************************************/ + +#endif // TCINFO_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/transcode.c b/debian/transcode/transcode-1.1.7/src/transcode.c new file mode 100644 index 00000000..0c7c797e --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/transcode.c @@ -0,0 +1,2863 @@ +/* + * transcode.c + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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 "transcode.h" +#include "decoder.h" +#include "encoder.h" +#include "dl_loader.h" +#include "framebuffer.h" +#include "counter.h" +#include "frame_threads.h" +#include "filter.h" +#include "probe.h" +#include "socket.h" +#include "split.h" + +#include "libtc/xio.h" +#include "libtc/libtc.h" +#include "libtc/cfgfile.h" +#include "libtc/tccodecs.h" +#include "libtc/ratiocodes.h" + +#include <ctype.h> +#include <math.h> + +#include "cmdline.h" + + +/* ------------------------------------------------------------ + * + * default options + * + * ------------------------------------------------------------*/ + + +int pcmswap = TC_FALSE; +int rgbswap = TC_FALSE; +int rescale = TC_FALSE; +int im_clip = TC_FALSE; +int ex_clip = TC_FALSE; +int pre_im_clip = TC_FALSE; +int post_ex_clip= TC_FALSE; +int flip = TC_FALSE; +int mirror = TC_FALSE; +int resize1 = TC_FALSE; +int resize2 = TC_FALSE; +int decolor = TC_FALSE; +int zoom = TC_FALSE; +int dgamma = TC_FALSE; +int keepasr = TC_FALSE; +int fast_resize = TC_FALSE; + +// global information structure +static vob_t *vob = NULL; +int verbose = TC_INFO; + +//------------------------------------------------------------- +// core parameter + +int tc_buffer_delay_dec = -1; +int tc_buffer_delay_enc = -1; +int tc_cluster_mode = 0; +int tc_decoder_delay = 0; +int tc_progress_meter = -1; // so we know whether it's set by the user +int tc_progress_rate = 1; +int tc_accel = AC_ALL; //acceleration code +unsigned int tc_avi_limit = (unsigned int)-1; +pid_t tc_probe_pid = 0; +int tc_niceness = 0; + +int max_frame_buffer = TC_FRAME_BUFFER; +int max_frame_threads = TC_FRAME_THREADS; + +//------------------------------------------------------------- + + +/*************************************************************************/ +/*********************** Exported utility routines ***********************/ +/*************************************************************************/ + +/** + * version: Print a version message. The message is only printed for the + * first call. + * + * Parameters: + * None. + * Return value: + * None. + */ + +void version(void) +{ + static int tcversion = 0; + if (tcversion++) + return; + fprintf(stderr, "%s v%s (C) 2001-2003 Thomas Oestreich," + " 2003-2010 Transcode Team\n", + PACKAGE, VERSION); +} + +/*************************************************************************/ + +/** + * tc_get_vob: Return a pointer to the global vob_t data structure. + * + * Parameters: + * None. + * Return value: + * A pointer to the global vob_t data structure. + */ + +vob_t *tc_get_vob() +{ + return vob; +} + +/*************************************************************************/ + +/** + * validate_source_path: Check whether the given string represents a valid + * source pathname. + * + * Parameters: + * path: String to check. + * Return value: + * Nonzero if the string is a valid source pathname, else zero. + * Side effects: + * Prints an error message using tc_error() if the string is not a + * valid pathname. + */ + +static int validate_source_path(const char *path) +{ + struct stat st; + + if (!path || !*path) { + tc_error("No filename given"); + return 0; + } + if (strcmp(path, "-") == 0) // allow stdin (maybe also /dev/stdin? FIXME) + return 1; + if (*path == '!' || *path == ':') /* from transcode.c -- why? */ + return 1; + if (xio_stat(path, &st) == 0) + return 1; + tc_error("Invalid filename \"%s\": %s", path, strerror(errno)); + return 0; +} + +/*************************************************************************/ + +int tc_next_video_in_file(vob_t *vob) +{ + vob->video_in_file = tc_glob_next(vob->video_in_files); + if (vob->video_in_file != NULL) { + return TC_OK; + } + return TC_ERROR; +} + +int tc_next_audio_in_file(vob_t *vob) +{ + vob->audio_in_file = tc_glob_next(vob->audio_in_files); + if (vob->audio_in_file != NULL) { + return TC_OK; + } + return TC_ERROR; +} + +int tc_has_more_video_in_file(vob_t *vob) +{ + int ret = TC_FALSE; + + if (core_mode == TC_MODE_DIRECTORY) { + ret = tc_glob_has_more(vob->video_in_files); + } + return ret; +} + +int tc_has_more_audio_in_file(vob_t *vob) +{ + int ret = TC_FALSE; + + if (core_mode == TC_MODE_DIRECTORY) { + ret = tc_glob_has_more(vob->audio_in_files); + } + return ret; +} + +/*************************************************************************/ +/*********************** Internal utility routines ***********************/ +/*************************************************************************/ + +/*************************************************************************/ +/*********************** Event thread support ****************************/ +/*************************************************************************/ + +static pthread_t event_thread_id = (pthread_t)0; +static const char *signame = "unknown signal"; + +static void tc_stop_all(void) +{ + tc_stop(); + tc_framebuffer_interrupt(); +} + +static void event_handler(int sig) +{ + switch (sig) { + case SIGINT: + signame = "SIGINT"; + break; + case SIGTERM: + signame = "SIGTERM"; + break; + case SIGPIPE: + signame = "SIGPIPE"; + break; + } + + tc_interrupt(); + tc_framebuffer_interrupt(); +} + +/** + * event_thread: Thread that watches for certain termination signals, + * terminating the transcode process cleanly. + * + * Parameters: + * None. + * Return value: + * None. + */ + +static void *event_thread(void* blocked_) +{ + struct sigaction handler; + sigset_t *blocked = blocked_; + + /* catch everything */ + pthread_sigmask(SIG_UNBLOCK, blocked, NULL); /* XXX */ + + /* install handlers, mutually exclusive */ + memset(&handler, 0, sizeof(struct sigaction)); + handler.sa_flags = 0; + handler.sa_mask = *blocked; + handler.sa_handler = event_handler; + + sigaction(SIGINT, &handler, NULL); // XXX + sigaction(SIGTERM, &handler, NULL); // XXX +/* sigaction(SIGPIPE, &handler, NULL); */// XXX + + /* Loop waiting for external events */ + for (;;) { + pthread_testcancel(); + + tc_socket_wait(); + + if (tc_interrupted()) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "(sighandler) %s received", signame); + + /* Kill the tcprobe process if it's running */ + if (tc_probe_pid > 0) + kill(tc_probe_pid, SIGTERM); + } + pthread_testcancel(); + } + return NULL; +} + +/*************************************************************************/ + +/** + * stop_event_thread: Ensure that the event handling thread is destroyed. + * + * Parameters: + * None. + * Return value: + * None. + */ + +static void stop_event_thread(void) +{ + if (event_thread_id) { + void *thread_status = NULL; + +// pthread_kill(event_thread_id, SIGINT); + pthread_cancel(event_thread_id); + pthread_join(event_thread_id, &thread_status); + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * load_all_filters: Loads all filters specified by the -J option. + * + * Parameters: + * filter_list: String containing filter list from -J option. + * Return value: + * None. + * Side effects: + * Destroys `filter_list'. + */ + +static void load_all_filters(char *filter_list) +{ + if (!filter_list) + return; + + while (*filter_list) { + char *s = filter_list + strcspn(filter_list, ","); + char *options; + + while (*s && s > filter_list && s[-1] == '\\') + s += 1 + strcspn(s+1, ","); + if (*s) + *s++ = 0; + options = strchr(filter_list, '='); + if (options) + *options++ = 0; + tc_filter_add(filter_list, options); + filter_list = s; + } +} + +/*************************************************************************/ + +/** + * transcode_init: initialize the transcoding engine. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * Return value: + * 0 on success, -1 on error. + */ + +static int transcode_init(vob_t *vob, TCEncoderBuffer *tc_ringbuffer) +{ + /* load import modules and check capabilities */ + if (tc_import_init(vob, im_aud_mod, im_vid_mod) < 0) { + tc_log_error(PACKAGE, "failed to init import modules"); + return -1; + } + + /* load and initialize filters */ + tc_filter_init(); + load_all_filters(plugins_string); + + /* load export modules and check capabilities + * (only create a TCModule factory if a multiplex module was given) */ + if (tc_export_init(tc_ringbuffer, + ex_mplex_mod + ? tc_new_module_factory(vob->mod_path,verbose) + : NULL) != TC_OK + ) { + tc_log_error(PACKAGE, "failed to init export layer"); + return -1; + } + if (tc_export_setup(vob, ex_aud_mod, ex_vid_mod, ex_mplex_mod) != TC_OK) { + tc_log_error(PACKAGE, "failed to init export modules"); + return -1; + } + return 0; +} + +/** + * transcode_fini: finalize (shutdown) the transcoding engine. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * Return value: + * 0 on success, -1 on error. + */ + +static int transcode_fini(vob_t *vob) +{ + /* unload import modules */ + tc_import_shutdown(); + /* unload filters */ + tc_filter_fini(); + /* unload export modules */ + tc_export_shutdown(); + + return 0; +} + +/* ------------------------------------------------------------- + * single file continuous or interval mode + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b */ +static int transcode_mode_default(vob_t *vob) +{ + struct fc_time *tstart = NULL; + + tc_start(); + + // init decoder and open the source + if (0 != vob->ttime->vob_offset) { + vob->vob_offset = vob->ttime->vob_offset; + } + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + // this must be called after tc_import_open + tc_import_threads_create(vob); + + // init encoder + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output files + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + // tell counter about all encoding ranges + counter_reset_ranges(); + if (!tc_cluster_mode) { + int last_etf = 0; + for (tstart = vob->ttime; tstart; tstart = tstart->next) { + if (tstart->etf == TC_FRAME_LAST) { + // variable length range, oh well + counter_reset_ranges(); + break; + } + if (tstart->stf > last_etf) + counter_add_range(last_etf, tstart->stf-1, 0); + counter_add_range(tstart->stf, tstart->etf-1, 1); + last_etf = tstart->etf; + } + } + + // get start interval + tstart = vob->ttime; + + while (tstart) { + // set frame range (in cluster mode these will already be set) + if (!tc_cluster_mode) { + frame_a = tstart->stf; + frame_b = tstart->etf; + } + // main encoding loop, returns when done with all frames + tc_encoder_loop(vob, frame_a, frame_b); + + // check for user cancelation request + if (tc_interrupted()) { + break; + } + + // next range + tstart = tstart->next; + // see if we're using vob_offset + if ((tstart != NULL) && (tstart->vob_offset != 0)) { + tc_decoder_delay = 3; + tc_import_threads_cancel(); + tc_import_close(); + tc_framebuffer_flush(); + vob->vob_offset = tstart->vob_offset; + vob->sync = sync_seconds; + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + tc_import_threads_create(vob); + } + } + tc_stop_all(); + + // close output files + tc_encoder_close(); + // stop encoder + tc_encoder_stop(); + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + return TC_OK; +} + + +/* ------------------------------------------------------------ + * split output AVI file + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b, splitavi_frames, base */ +static int transcode_mode_avi_split(vob_t *vob) +{ + char buf[TC_BUF_MAX]; + int fa, fb, ch1 = 0; + + tc_start(); + + // init decoder and open the source + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + // encoder init + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // need to loop for ch1 + ch1 = 0; + + do { + if (!strlen(base)) + strlcpy(base, vob->video_out_file, TC_BUF_MIN); + + // create new filename + tc_snprintf(buf, sizeof(buf), "%s%03d.avi", base, ch1++); + vob->video_out_file = buf; + vob->audio_out_file = buf; + + // open output + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + fa = frame_a; + fb = frame_a + splitavi_frames; + + tc_encoder_loop(vob, fa, ((fb > frame_b) ? frame_b : fb)); + + // close output + tc_encoder_close(); + + // restart + frame_a += splitavi_frames; + if (frame_a >= frame_b) + break; + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "import status=%d", tc_import_status()); + + // check for user cancelation request + if (tc_interrupted()) + break; + + } while (tc_import_status()); + + tc_stop_all(); + + tc_encoder_stop(); + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + return TC_OK; +} + + +/* globals: frame_a, frame_b */ +static int transcode_mode_directory(vob_t *vob) +{ + struct fc_time *tstart = NULL; + + tc_start(); + + if (strcmp(vob->audio_in_file, vob->video_in_file) != 0) + tc_error("directory mode DOES NOT support separate audio files (A=%s|V=%s)", + vob->audio_in_file, vob->video_in_file); + + tc_multi_import_threads_create(vob); + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + + // tell counter about all encoding ranges + counter_reset_ranges(); + if (!tc_cluster_mode) { + int last_etf = 0; + for (tstart = vob->ttime; tstart; tstart = tstart->next) { + if (tstart->etf == TC_FRAME_LAST) { + // variable length range, oh well + counter_reset_ranges(); + break; + } + if (tstart->stf > last_etf) + counter_add_range(last_etf, tstart->stf-1, 0); + counter_add_range(tstart->stf, tstart->etf-1, 1); + last_etf = tstart->etf; + } + } + + // get start interval + for (tstart = vob->ttime; + tstart != NULL && !tc_interrupted(); + tstart = tstart->next) { + // set frame range (in cluster mode these will already be set) + if (!tc_cluster_mode) { + frame_a = tstart->stf; + frame_b = tstart->etf; + } + // main encoding loop, returns when done with all frames + tc_encoder_loop(vob, frame_a, frame_b); + } + + tc_stop_all(); + + tc_encoder_close(); + tc_encoder_stop(); + tc_multi_import_threads_cancel(); + + return TC_OK; +} + +/* --------------------------------------------------------------- + * VOB PSU mode: transcode and split based on program stream units + * --------------------------------------------------------------*/ + +/* globals: frame_a, frame_b */ +static int transcode_mode_psu(vob_t *vob, const char *psubase) +{ + char buf[TC_BUF_MAX]; + int fa, fb, psu_cur = vob->vob_psu_num1; + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output + if (no_split) { + vob->video_out_file = psubase; + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + tc_decoder_delay = 3; + + counter_on(); + + for (;;) { + int ret; + + memset(buf, 0, sizeof buf); + if (!no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), psubase, psu_cur); + // update vob structure + vob->video_out_file = buf; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "using output filename %s", + vob->video_out_file); + } + + // get seek/frame information for next PSU + // need to process whole PSU + vob->vob_chunk = 0; + vob->vob_chunk_max = 1; + + ret = split_stream(vob, nav_seek_file, psu_cur, &fa, &fb, 0); + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE,"processing PSU %d, -L %d -c %d-%d %s (ret=%d)", + psu_cur, vob->vob_offset, fa, fb, buf, ret); + + // exit condition + if (ret < 0 || psu_cur == vob->vob_psu_num2) + break; + + // do not process units with a small frame number, assume it is junk + if ((fb-fa) > psu_frame_threshold) { + tc_start(); + + // start new decoding session with updated vob structure + // this starts the full decoder setup, including the threads + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + // frame threads may need a reboot too. + tc_frame_threads_init(vob, max_frame_threads, max_frame_threads); + + // open new output file + if (!no_split) { + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + // core + // we try to encode more frames and let the decoder safely + // drain the queue to avoid threads not stopping + + tc_encoder_loop(vob, fa, TC_FRAME_LAST); + + // close output file + if (!no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + if (verbose >= TC_CLEANUP) { + // for debug + vframe_dump_status(); + aframe_dump_status(); + } + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + // flush all buffers before we proceed to next PSU + tc_framebuffer_flush(); + + vob->psu_offset += (double) (fb-fa); + } else { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "skipping PSU %d with %d frame(s)", + psu_cur, fb-fa); + + } + + psu_cur++; + if (tc_interrupted()) + break; + } //next PSU + + // close output + if (no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + tc_stop_all(); + + tc_encoder_stop(); + + return TC_OK; +} + +/* ------------------------------------------------------------ + * DVD chapter mode + * ------------------------------------------------------------*/ + +/* globals: frame_a, frame_b, chbase */ +static int transcode_mode_dvd(vob_t *vob) +{ +#ifdef HAVE_LIBDVDREAD + char buf[TC_BUF_MAX]; + int ch1, ch2; + + tc_start(); + + if (tc_encoder_init(vob) != TC_OK) + tc_error("failed to init encoder"); + + // open output + if (no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), "%s.avi", chbase); + // update vob structure + vob->video_out_file = buf; + vob->audio_out_file = buf; + + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to open output"); + } + + // 1 sec delay after decoder closing + tc_decoder_delay = 1; + + // loop each chapter + ch1 = vob->dvd_chapter1; + ch2 = vob->dvd_chapter2; + + //ch = -1 is allowed but makes no sense + if (ch1 < 0) + ch1 = 1; + + for (;;) { + vob->dvd_chapter1 = ch1; + vob->dvd_chapter2 = -1; + + if (!no_split) { + // create new filename + tc_snprintf(buf, sizeof(buf), "%s-ch%02d.avi", chbase, ch1); + // update vob structure + vob->video_out_file = buf; + vob->audio_out_file = buf; + } + + // start decoding with updated vob structure + if (tc_import_open(vob) < 0) + tc_error("failed to open input source"); + + // start the AV import threads that load the frames into transcode + tc_import_threads_create(vob); + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "%d chapters for title %d detected", + vob->dvd_max_chapters, vob->dvd_title); + + // encode + if (!no_split) { + if (tc_encoder_open(vob) != TC_OK) + tc_error("failed to init encoder"); + } + + // main encoding loop, selecting an interval won't work + tc_encoder_loop(vob, frame_a, frame_b); + + if (!no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + // cancel import threads + tc_import_threads_cancel(); + // stop decoder and close the source + tc_import_close(); + + // flush all buffers before we proceed + tc_framebuffer_flush(); + + // exit, i) if import module could not determine max_chapters + // ii) all chapters are done + // iii) someone hit ^C + + if (vob->dvd_max_chapters ==- 1 + || ch1 == vob->dvd_max_chapters || ch1 == ch2 + || tc_interrupted()) + break; + ch1++; + } + + if (no_split) { + if (tc_encoder_close() != TC_OK) + tc_warn("failed to close encoder - non fatal"); + } + + tc_stop_all(); + tc_encoder_stop(); +#endif + + return TC_OK; +} + +/*************************************************************************/ + +/** + * new_vob: Create a new vob_t structure and fill it with appropriate + * values. + * + * Parameters: + * None. + * Return value: + * A pointer to the newly-created vob_t structure, or NULL on error. + * Notes: + * On error, errno is valid. + */ + +static vob_t *new_vob(void) +{ + vob_t *vob = tc_malloc(sizeof(vob_t)); + if (!vob) + return NULL; + + vob->divxbitrate = VBITRATE; + vob->video_max_bitrate = 0; /* 0 = set by encoder */ + vob->divxkeyframes = VKEYFRAMES; + vob->divxquality = VQUALITY; + vob->divxmultipass = VMULTIPASS; + vob->divxcrispness = VCRISPNESS; + vob->m2v_requant = M2V_REQUANT_FACTOR; + + vob->min_quantizer = VMINQUANTIZER; + vob->max_quantizer = VMAXQUANTIZER; + + vob->rc_period = RC_PERIOD; + vob->rc_reaction_period = RC_REACTION_PERIOD; + vob->rc_reaction_ratio = RC_REACTION_RATIO; + + vob->divx5_vbv_prof = DIVX5_VBV_PROFILE; + vob->divx5_vbv_bitrate = DIVX5_VBV_BITRATE; + vob->divx5_vbv_size = DIVX5_VBV_SIZE; + vob->divx5_vbv_occupancy = DIVX5_VBV_OCCUPANCY; + + vob->mp3bitrate = ABITRATE; + vob->mp3frequency = 0; + vob->mp3quality = AQUALITY; + vob->mp3mode = AMODE; + vob->a_rate = RATE; + vob->a_stream_bitrate = 0; + vob->a_bits = BITS; + vob->a_chan = CHANNELS; + vob->a_padrate = 0; + + vob->dm_bits = 0; + vob->dm_chan = 0; + + vob->im_a_size = SIZE_PCM_FRAME; + vob->im_v_width = PAL_W; + vob->im_v_height = PAL_H; + vob->im_v_size = SIZE_RGB_FRAME; + vob->ex_a_size = SIZE_PCM_FRAME; + vob->ex_v_width = PAL_W; + vob->ex_v_height = PAL_H; + vob->ex_v_size = SIZE_RGB_FRAME; + vob->a_track = 0; + vob->v_track = 0; + vob->volume = 0; + vob->ac3_gain[0] = 1.0; + vob->ac3_gain[1] = 1.0; + vob->ac3_gain[2] = 1.0; + vob->audio_out_file = NULL; + vob->video_out_file = NULL; + vob->avifile_in = NULL; + vob->avifile_out = NULL; + vob->avi_comment_fd = -1; + vob->nav_seek_file = NULL; + vob->audio_file_flag = 0; + vob->audio_in_file = NULL; + vob->video_in_file = NULL; + vob->clip_count = 0; + vob->ex_a_codec = CODEC_MP3; //or fall back to module default + vob->ex_v_codec = CODEC_NULL; //determined by export module type + vob->ex_v_fcc = NULL; + vob->ex_a_fcc = NULL; + vob->ex_profile_name = NULL; + vob->fps = PAL_FPS; + vob->ex_fps = 0; + vob->im_frc = 0; + vob->ex_frc = 0; + vob->pulldown = 0; + vob->im_clip_top = 0; + vob->im_clip_bottom = 0; + vob->im_clip_left = 0; + vob->im_clip_right = 0; + vob->ex_clip_top = 0; + vob->ex_clip_bottom = 0; + vob->ex_clip_left = 0; + vob->ex_clip_right = 0; + vob->resize1_mult = 32; + vob->vert_resize1 = 0; + vob->hori_resize1 = 0; + vob->resize2_mult = 32; + vob->vert_resize2 = 0; + vob->hori_resize2 = 0; + vob->sync = 0; + vob->sync_ms = 0; + vob->sync_samples = 0; + vob->dvd_title = 1; + vob->dvd_chapter1 = 1; + vob->dvd_chapter2 = -1; + vob->dvd_max_chapters = -1; + vob->dvd_angle = 1; + vob->pass_flag = 0; + vob->verbose = TC_QUIET; + vob->antialias = 0; + vob->deinterlace = 0; + vob->decolor = 0; + vob->im_a_codec = CODEC_PCM; + vob->im_v_codec = CODEC_YUV; + vob->mod_path = MOD_PATH; + vob->audiologfile = NULL; + vob->divxlogfile = NULL; + vob->ps_unit = 0; + vob->ps_seq1 = 0; + vob->ps_seq2 = TC_FRAME_LAST; + vob->a_leap_frame = TC_LEAP_FRAME; + vob->a_leap_bytes = 0; + vob->demuxer = -1; + vob->a_codec_flag = CODEC_AC3; + vob->gamma = 0.0; + vob->encoder_flush = TC_TRUE; + vob->has_video = 1; + vob->has_audio = 1; + vob->has_audio_track = 1; + vob->lang_code = 0; + vob->v_format_flag = 0; + vob->v_codec_flag = 0; + vob->a_format_flag = 0; + vob->im_asr = 0; + vob->im_par = 0; + vob->im_par_width = 0; + vob->im_par_height = 0; + vob->ex_asr = -1; + vob->ex_par = 0; + vob->ex_par_width = 0; + vob->ex_par_height = 0; + vob->quality = VQUALITY; + vob->amod_probed = "null"; + vob->vmod_probed = "null"; + vob->amod_probed_xml = NULL; + vob->vmod_probed_xml = NULL; + vob->a_vbr = 0; + vob->pts_start = 0.0f; + vob->vob_offset = 0; + vob->vob_chunk = 0; + vob->vob_chunk_max = 0; + vob->vob_chunk_num1 = 0; + vob->vob_chunk_num2 = 0; + vob->vob_psu_num1 = 0; + vob->vob_psu_num2 = INT_MAX; + vob->vob_info_file = NULL; + vob->vob_percentage = 0; + vob->im_a_string = NULL; + vob->im_v_string = NULL; + vob->ex_a_string = NULL; + vob->ex_v_string = NULL; + vob->ex_m_string = NULL; + + vob->reduce_h = 1; + vob->reduce_w = 1; + + //-Z + vob->zoom_width = 0; + vob->zoom_height = 0; + vob->zoom_filter = TCV_ZOOM_LANCZOS3; + vob->zoom_interlaced = 0; + + vob->frame_interval = 1; // write every frame + + //anti-alias + vob->aa_weight = TC_DEFAULT_AAWEIGHT; + vob->aa_bias = TC_DEFAULT_AABIAS; + + vob->a52_mode = 0; + vob->encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE; + + vob->ttime = NULL; + + vob->psu_offset = 0.0f; + vob->bitreservoir = TC_TRUE; + vob->lame_preset = NULL; + + vob->ts_pid1 = 0x0; + vob->ts_pid2 = 0x0; + + vob->dv_yuy2_mode = 0; + vob->hard_fps_flag = 0; + vob->mpeg_profile = PROF_NONE; + + vob->attributes = 0; + vob->export_attributes = TC_EXPORT_ATTRIBUTE_NONE; + + return vob; +} + +/*************************************************************************/ + +/* + * parse_navigation_file: + * parse navigation data file and setup vob data fields accordingly. + * This function handle both aviindex and tcdemux -W generated files. + * + * Parameters: + * vob: Pointer to the global vob_t data structure. + * nav_seek_file: Path of navigation file. + * Return Value: + * None + */ +static void parse_navigation_file(vob_t *vob, const char *nav_seek_file) +{ + if (nav_seek_file) { + FILE *fp = NULL; + struct fc_time *tmptime = NULL; + char buf[TC_BUF_MIN]; + int line_count = 0; + int flag = 0; + int is_aviindex = 0; + + if (vob->vob_offset) { + tc_warn("-L and --nav_seek are incompatible."); + } + + fp = fopen(nav_seek_file, "r"); + if (NULL == fp) { + tc_error("unable to open: %s", nav_seek_file); + } + + tmptime = vob->ttime; + line_count = 0; + + // check if this is an AVIIDX1 file + if (fgets(buf, sizeof(buf), fp)) { + if (strncasecmp(buf, "AVIIDX1", 7) == 0) + is_aviindex=1; + fseek(fp, 0, SEEK_SET); + } else { + tc_error("An error happend while reading the nav_seek file"); + } + + if (!is_aviindex) { + while (tmptime){ + flag = 0; + for (; fgets(buf, sizeof(buf), fp); line_count++) { + int L, new_frame_a; + + if (2 == sscanf(buf, "%*d %*d %*d %*d %d %d ", &L, &new_frame_a)) { + if (line_count == tmptime->stf) { + int len = tmptime->etf - tmptime->stf; + tmptime->stf = frame_a = new_frame_a; + tmptime->etf = frame_b = new_frame_a + len; + tmptime->vob_offset = L; + flag = 1; + line_count++; + break; + } + } + } + tmptime = tmptime->next; + } + } else { // is_aviindex==1 + fgets(buf, sizeof(buf), fp); // magic + fgets(buf, sizeof(buf), fp); // comment + + while (tmptime) { + int new_frame_a, type, key; + long chunk, chunkptype, last_keyframe = 0; + long long pos, len; + char tag[4]; + double ms = 0.0; + flag = 0; + + for (; fgets(buf, sizeof(buf), fp); line_count++) { + // TAG TYPE CHUNK CHUNK/TYPE POS LEN KEY MS + if (sscanf(buf, "%s %d %ld %ld %lld %lld %d %lf", + tag, &type, &chunk, &chunkptype, &pos, &len, &key, &ms)) { + if (type != 1) + continue; + if (key) + last_keyframe = chunkptype; + if (chunkptype == tmptime->stf) { + int lenf = tmptime->etf - tmptime->stf; + new_frame_a = chunkptype - last_keyframe; + + // If we are doing pass-through, we cannot skip frames, but only start + // passthrough on a keyframe boundary. At least, we respect the + // last frame the user whishes. + if (vob->pass_flag & TC_VIDEO) { + new_frame_a = 0; + lenf += (chunkptype - last_keyframe); + } + + tmptime->stf = frame_a = new_frame_a; + tmptime->etf = frame_b = new_frame_a + lenf; + tmptime->vob_offset = last_keyframe; + flag = 1; + line_count++; + break; + } + } + } + tmptime = tmptime->next; + } + } + fclose(fp); + + if (!flag) { + //frame not found + tc_warn("%s: frame %d out of range (%d frames found)", + nav_seek_file, frame_a, line_count); + tc_error("invalid option parameter for -c / --nav_seek"); + } + } +} + +/*************************************************************************/ + +static void setup_input_sources(vob_t *vob) +{ + if (vob->video_in_file == NULL && vob->audio_in_file == NULL) + tc_error("no input sources avalaible"); + if (vob->audio_in_file == NULL) + vob->audio_in_file = vob->video_in_file; + + /* + * let's try happily both sources independently. + * At least one will succeed, if we're here. + */ + vob->video_in_files = tc_glob_open(vob->video_in_file, 0); + if (vob->video_in_files) { + /* we always have at least one source */ + tc_next_video_in_file(vob); + } + if (!validate_source_path(vob->video_in_file)) { + tc_error("invalid input video file: %s", vob->video_in_file); + } + + vob->audio_in_files = tc_glob_open(vob->audio_in_file, 0); + if (vob->audio_in_files) { + /* we always have at least one source */ + tc_next_audio_in_file(vob); + } + if (!validate_source_path(vob->audio_in_file)) { + tc_error("invalid input audio file: %s", vob->audio_in_file); + } +} + +static void teardown_input_sources(vob_t *vob) +{ + if (vob->video_in_files) { + tc_glob_close(vob->video_in_files); + vob->video_in_files = NULL; + } + if (vob->audio_in_files) { + tc_glob_close(vob->audio_in_files); + vob->audio_in_files = NULL; + } +} + +/*************************************************************************/ + +/* support macros */ + +#define CLIP_CHECK(MODE, NAME, OPTION) do { \ + /* force to even for YUV mode */ \ + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { \ + if (vob->MODE ## _left % 2 != 0) { \ + tc_warn("left/right %s must be even in YUV/YUV422 mode", NAME); \ + vob->MODE ## _left--; \ + } \ + if (vob->MODE ## _right % 2 != 0) { \ + tc_warn("left/right %s must be even in YUV/YUV422 mode", NAME); \ + vob->MODE ## _right--; \ + } \ + if (vob->im_v_codec == CODEC_YUV && vob->MODE ## _top % 2 != 0) { \ + tc_warn("top/bottom %s must be even in YUV mode", NAME); \ + vob->MODE ## _top--; \ + } \ + if (vob->im_v_codec == CODEC_YUV && vob->MODE ## _bottom % 2 != 0) { \ + tc_warn("top/bottom %s must be even in YUV mode", NAME); \ + vob->MODE ## _bottom--; \ + } \ + } \ + /* check against import parameter, this is pre processing! */ \ + if (vob->ex_v_height - vob->MODE ## _top - vob->MODE ## _bottom <= 0 \ + || vob->ex_v_height - vob->MODE ## _top - vob->MODE ## _bottom > TC_MAX_V_FRAME_HEIGHT) \ + tc_error("invalid top/bottom clip parameter for option %s", OPTION); \ + \ + if (vob->ex_v_width - vob->MODE ## _left - vob->MODE ## _right <= 0 \ + || vob->ex_v_width - vob->MODE ## _left - vob->MODE ## _right > TC_MAX_V_FRAME_WIDTH) \ + tc_error("invalid left/right clip parameter for option %s", OPTION); \ + \ + vob->ex_v_height -= (vob->MODE ## _top + vob->MODE ## _bottom); \ + vob->ex_v_width -= (vob->MODE ## _left + vob->MODE ## _right); \ +} while (0) + + + +#define SHUTDOWN_MARK(STAGE) do { \ + if (verbose & TC_DEBUG) { \ + fprintf(stderr, " %s |", (STAGE)); \ + fflush(stderr); \ + } \ +} while (0) + + +/*************************************************************************/ + +/* common support data */ + +typedef struct ratio_t { + int t, b; +} ratio_t; + +static const ratio_t asrs[] = { + { 1, 1 }, { 1, 1 }, { 4, 3 }, { 16, 9 }, + { 221, 100 }, { 250, 100 }, { 125, 100 } +}; + +static const char *demuxer_desc[] = { + "sync AV at PTS start - demuxer disabled", + "sync AV at initial MPEG sequence", + "initial MPEG sequence / enforce frame rate", + "sync AV at initial PTS", + "initial PTS / enforce frame rate", +}; + +static const char *deinterlace_desc[] = { + "disabled", /* never used */ + "interpolate scanlines (fast)", + "handled by encoder (if available)", + "zoom to full frame (slow)", + "drop field / half height (fast)", + "interpolate scanlines / blend frames", + +}; + + static const char *antialias_desc[] = { + "disabled", /* never used */ + "de-interlace effects only", + "resize effects only", + "process full frame (slow)" +}; + + +/** + * main: transcode main routine. Performs initialization, parses command + * line options, and calls the transcoding routines. + * + * Parameters: + * argc: Command line argument count. + * argv: Command line argument vector. + * Return value: + * Zero on success, nonzero on error (exit code). + */ + +int main(int argc, char *argv[]) +{ + sigset_t sigs_to_block; + + const char *psubase = NULL; + + double fch, asr; + int leap_bytes1, leap_bytes2; + int max_frame_buffer = TC_FRAME_BUFFER; + + struct fc_time *tstart = NULL; + + TCFrameSpecs specs; + + //main thread id + writepid = getpid(); + + /* ------------------------------------------------------------ + * + * (I) set transcode defaults: + * + * ------------------------------------------------------------ */ + + // create global vob structure + vob = new_vob(); + if (!vob) { + tc_error("data initialization failed"); + } + + // prepare for signal catching + sigemptyset(&sigs_to_block); + sigaddset(&sigs_to_block, SIGINT); + sigaddset(&sigs_to_block, SIGTERM); + // enabling this breaks the import_vob module. + //sigaddset(&sigs_to_block, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL); + + // start of the signal handler thread is delayed later + + // close all threads at exit + atexit(stop_event_thread); + + /* ------------------------------------------------------------ + * + * (II) parse command line + * + * ------------------------------------------------------------ */ + + /* + * A *FEW* special options that deserve separate treatment. + * PLEASE keep VERY LOW the number of this special cases. + */ + libtc_init(&argc, &argv); + + if (!parse_cmdline(argc, argv, vob)) + exit(EXIT_FAILURE); + + setup_input_sources(vob); + + if (tc_progress_meter < 0) { + // if we have verbosity disabled, default to no progress meter. + if (verbose) { + tc_progress_meter = 1; + } else { + tc_progress_meter = 0; + } + } + + if (psu_mode) { + if (vob->video_out_file == NULL) + tc_error("please specify output file name for psu mode"); + if (!strchr(vob->video_out_file, '%') && !no_split) { + char *pc = tc_malloc(PATH_MAX); + char *suffix = strrchr(vob->video_out_file, '.'); + if (suffix) { + *suffix = '\0'; + } else { + suffix = ""; + } + tc_snprintf(pc, PATH_MAX, "%s-psu%%02d%s", + vob->video_out_file, suffix); + psubase = pc; + } else { + psubase = vob->video_out_file; + } + } + + // user doesn't want to start at all;-( + if (tc_interrupted()) + goto summary; + + // display program version + if (verbose) + version(); + + if (tc_niceness) { + if (nice(tc_niceness) < 0) { + tc_warn("setting nice to %d failed", tc_niceness); + } + } + + /* ------------------------------------------------------------ + * + * (III) auto probe properties of input stream + * + * ------------------------------------------------------------ */ + + if (auto_probe) { + // interface to "tcprobe" + int result = probe_source(vob->video_in_file, vob->audio_in_file, seek_range, + preset_flag, vob); + if (verbose) { + tc_log_info(PACKAGE, "V: %-16s | %s (%s)", "auto-probing", + (vob->video_in_file != NULL) ?vob->video_in_file :"N/A", + result ? "OK" : "FAILED"); + tc_log_info(PACKAGE, "V: %-16s | %s in %s (module=%s)", + "import format", + tc_codec_to_comment(vob->v_codec_flag), + mformat2str(vob->v_format_flag), + no_vin_codec == 0 ? im_vid_mod : vob->vmod_probed); + tc_log_info(PACKAGE, "A: %-16s | %s (%s)", "auto-probing", + (vob->audio_in_file != NULL) ?vob->audio_in_file :"N/A", + result ? "OK" : "FAILED"); + tc_log_info(PACKAGE, "A: %-16s | %s in %s (module=%s)", + "import format", + tc_codec_to_comment(vob->a_codec_flag), + mformat2str(vob->a_format_flag), + no_ain_codec==0 ? im_aud_mod : vob->amod_probed); + } + } + + if (vob->vmod_probed_xml && strstr(vob->vmod_probed_xml, "xml") != NULL + && vob->video_in_file) { + if (!probe_source_xml(vob, PROBE_XML_VIDEO)) + tc_error("failed to probe video XML source"); + } + if (vob->amod_probed_xml && strstr(vob->amod_probed_xml, "xml") != NULL + && vob->audio_in_file) { + if (!probe_source_xml(vob, PROBE_XML_AUDIO)) + tc_error("failed to probe audio XML source"); + } + + /* ------------------------------------------------------------ + * + * (IV) autosplit stream for cluster processing + * + * currently, only VOB streams are supported + * + * ------------------------------------------------------------*/ + + // set up ttime from -c or default + if (fc_ttime_string) { + // FIXME: should be in -c handler, but we need to know vob->fps first + free_fc_time(vob->ttime); + if (parse_fc_time_string(fc_ttime_string, vob->fps, ",", + (verbose>1 ? 1 : 0), &vob->ttime) == -1) + tc_error("error parsing time specifications"); + } else { + vob->ttime = new_fc_time(); + vob->ttime->fps = vob->fps; + vob->ttime->stf = TC_FRAME_FIRST; + vob->ttime->etf = TC_FRAME_LAST; + vob->ttime->next = NULL; + } + frame_a = vob->ttime->stf; + frame_b = vob->ttime->etf; + vob->ttime->vob_offset = 0; + tstart = vob->ttime; + counter_on(); //activate + + // determine -S,-c,-L option parameter for distributed processing + parse_navigation_file(vob, nav_seek_file); + + if (vob->vob_chunk_max) { + int this_unit = -1; + + // overwrite tcprobe's unit preset: + if (preset_flag & TC_PROBE_NO_SEEK) + this_unit = vob->ps_unit; + + if (split_stream(vob, vob->vob_info_file, this_unit, &frame_a, &frame_b, 1) < 0) + tc_error("cluster mode option -W error"); + } + + /* ------------------------------------------------------------ + * + * some sanity checks for command line parameters + * + * ------------------------------------------------------------*/ + + // -M + if (vob->demuxer == -1) { + vob->demuxer = 1; + } + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | (%i) %s", "AV demux/sync", + vob->demuxer, demuxer_desc[vob->demuxer]); + } + + // -P + if (vob->pass_flag & TC_VIDEO) { + vob->im_v_codec = (vob->im_v_codec == CODEC_YUV) ?CODEC_RAW_YUV :CODEC_RAW; + vob->ex_v_codec = CODEC_RAW; + + // suggestion: + if (no_v_out_codec) + ex_vid_mod = "raw"; + no_v_out_codec = 0; + + if (no_a_out_codec) + ex_aud_mod = "raw"; + no_a_out_codec = 0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "pass-through"); + } + + // -x + if (no_vin_codec && vob->video_in_file != NULL && vob->vmod_probed == NULL) + tc_error("module autoprobe failed, no option -x found"); + + + //overwrite results of autoprobing if modules are provided + if (no_vin_codec && vob->vmod_probed!=NULL) { + im_vid_mod = (char *)vob->vmod_probed_xml; + //need to load the correct module if the input file type is xml + } + + if (no_ain_codec && vob->amod_probed!=NULL) { + im_aud_mod = (char *)vob->amod_probed_xml; + //need to load the correct module if the input file type is xml + } + + // make zero frame size default for no video + if (im_vid_mod != NULL && strcmp(im_vid_mod, "null") == 0) { + vob->im_v_width = 0; + vob->im_v_height = 0; + } + + //initial aspect ratio + asr = (double) vob->im_v_width/vob->im_v_height; + + // -g + + // import size + // force to even for YUV mode + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { + if (vob->im_v_width % 2 != 0) { + tc_warn("frame width must be even in YUV/YUV422 mode"); + vob->im_v_width--; + } + if (vob->im_v_codec == CODEC_YUV && vob->im_v_height % 2 != 0) { + tc_warn("frame height must be even in YUV mode"); + vob->im_v_height--; + } + } + if (verbose & TC_INFO) { + if (vob->im_v_width && vob->im_v_height) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d %4.2f:1 %s", + "import frame", vob->im_v_width, vob->im_v_height, + asr, tc_asr_code_describe(vob->im_asr)); + } else { + tc_log_info(PACKAGE, "V: %-16s | disabled", "import frame"); + } + } + + // init frame size with cmd line frame size + vob->ex_v_height = vob->im_v_height; + vob->ex_v_width = vob->im_v_width; + + // import bytes per frame (RGB 24bits) + vob->im_v_size = vob->im_v_height * vob->im_v_width * BPP/8; + // export bytes per frame (RGB 24bits) + vob->ex_v_size = vob->im_v_size; + + // calc clip settings for encoding to mpeg (vcd,svcd,xvcd,dvd) + // --export_prof {vcd,vcd-pal,vcd-ntsc,svcd,svcd-pal,svcd-ntsc,dvd,dvd-pal,dvd-ntsc} + + if (vob->mpeg_profile != PROF_NONE) { + ratio_t imasr = asrs[0]; + ratio_t exasr = asrs[0]; + + int impal = 0; + int pre_clip; + + // Make an educated guess if this is pal or ntsc + switch (vob->mpeg_profile) { + case VCD: + case SVCD: + case XVCD: + case DVD: + if (vob->im_v_height == 288 || vob->im_v_height == 576) + impal = 1; + if ((int)vob->fps == 25 || vob->im_frc == 3) + impal = 1; + break; + case VCD_PAL: + case SVCD_PAL: + case XVCD_PAL: + case DVD_PAL: + impal = 1; + break; + default: + break; + } + + // choose height dependent on pal or NTSC. + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + if (!vob->zoom_height) + vob->zoom_height = impal ?288 :240; + break; + + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + case DVD: + case DVD_PAL: + case DVD_NTSC: + if (!vob->zoom_height) + vob->zoom_height = impal ?576 :480; + break; + + default: + break; + } + + // choose width if not set by user. + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 352; + vob->ex_asr = 2; + break; + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 480; + vob->ex_asr = 2; + break; + case DVD: + case DVD_PAL: + case DVD_NTSC: + if (!vob->zoom_width) + vob->zoom_width = 720; + if (vob->ex_asr <= 0) + vob->ex_asr = 2; // assume 4:3 + break; + default: + break; + } + + // an input file without any aspect ratio setting (an AVI maybe?) + // so make a guess. + + if (vob->im_asr == 0) { + int i, mini=0; + const ratio_t *r = &asrs[1]; + double diffs[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + double mindiff = 2.0; + + for (i = 0; i < 6; i++) { + diffs[i] = (double)(r->b*vob->im_v_width) / (double)(r->t*vob->im_v_height); + r++; + } + + // look for the diff which is closest to 1.0 + + for (i = 0; i < 6; i++) { + double a = fabs(1.0 - diffs[i]); + if (a < mindiff) { + mindiff = a; + mini = i+1; + } + } + vob->im_asr = mini; + } + + imasr = asrs[vob->im_asr]; + exasr = asrs[vob->ex_asr]; + + pre_clip = vob->im_v_height - (vob->im_v_height * imasr.t * exasr.b ) / (imasr.b * exasr.t ); + + if (pre_im_clip == TC_FALSE) { + if (pre_clip % 2 != 0) { + vob->pre_im_clip_top = pre_clip/2+1; + vob->pre_im_clip_bottom = pre_clip/2-1; + } else { + vob->pre_im_clip_bottom = vob->pre_im_clip_top = pre_clip/2; + } + if (vob->pre_im_clip_top % 2 != 0 || vob->pre_im_clip_bottom % 2 != 0) { + vob->pre_im_clip_top--; + vob->pre_im_clip_bottom++; + } + } + + //FIXME hack, kludge, etc. EMS + if ((vob->im_v_height != vob->zoom_height) + || ((vob->im_v_width != vob->zoom_width) && (vob->ex_v_width != 704))) + zoom = TC_TRUE; + else + zoom = TC_FALSE; + + if (pre_clip) pre_im_clip = TC_TRUE; + + // shall we really go this far? + // If yes, there can be much more settings adjusted. + if (ex_vid_mod == NULL || !strcmp(ex_vid_mod, "mpeg2enc")) { +#ifdef HAVE_MJPEGTOOLS + if (!ex_aud_mod) + ex_aud_mod = "mp2enc"; + no_v_out_codec = 0; + ex_vid_mod = "mpeg2enc"; + //FIXME this should be in export_mpeg2enc.c + if (!vob->ex_v_fcc) { + switch (vob->mpeg_profile) { + case VCD: + case VCD_PAL: + case VCD_NTSC: + vob->ex_v_fcc = "1"; + break; + case SVCD: + case SVCD_PAL: + case SVCD_NTSC: + case XVCD: + case XVCD_PAL: + case XVCD_NTSC: + vob->ex_v_fcc = "4"; + break; + case DVD: + case DVD_PAL: + case DVD_NTSC: + vob->ex_v_fcc = "8"; + break; + default: + break; + } + } +#endif + } else if(!strcmp(ex_vid_mod, "ffmpeg")) { + if (!ex_aud_mod) + ex_aud_mod = "ffmpeg"; + switch (vob->mpeg_profile) { + case VCD: + vob->ex_v_fcc = "vcd"; + break; + case VCD_PAL: + vob->ex_v_fcc = "vcd-pal"; + break; + case VCD_NTSC: + vob->ex_v_fcc = "vcd-ntsc"; + break; + case SVCD: + vob->ex_v_fcc = "svcd"; + break; + case SVCD_PAL: + vob->ex_v_fcc = "svcd-pal"; + break; + case SVCD_NTSC: + vob->ex_v_fcc = "svcd-ntsc"; + break; + case XVCD: + vob->ex_v_fcc = "xvcd"; + break; + case XVCD_PAL: + vob->ex_v_fcc = "xvcd-pal"; + break; + case XVCD_NTSC: + vob->ex_v_fcc = "xvcd-ntsc"; + break; + case DVD: + vob->ex_v_fcc = "dvd"; + break; + case DVD_PAL: + vob->ex_v_fcc = "dvd-pal"; + break; + case DVD_NTSC: + vob->ex_v_fcc = "dvd-ntsc"; + break; + case PROF_NONE: + break; + } + } // ffmpeg + + if (ex_aud_mod == NULL) { +#ifdef HAVE_MJPEGTOOLS + no_a_out_codec=0; + ex_aud_mod = "mp2enc"; +#endif + } + } // mpeg_profile != PROF_NONE + + + // --PRE_CLIP + if (pre_im_clip) { + CLIP_CHECK(pre_im_clip, "pre_clip", "--pre_clip"); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d (%d,%d,%d,%d)", + "pre clip frame", vob->ex_v_width, vob->ex_v_height, + vob->pre_im_clip_top, vob->pre_im_clip_left, + vob->pre_im_clip_bottom, vob->pre_im_clip_right); + } + } + + // -j + if (im_clip) { + CLIP_CHECK(im_clip, "clip", "-j"); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d", "clip frame (<-)", + vob->ex_v_width, vob->ex_v_height); + } + } + + // -I + /* can this really happen? */ + if (vob->deinterlace < 0 || vob->deinterlace > 5) { + tc_error("invalid parameter for option -I"); + } + + if ((verbose & TC_INFO) && vob->deinterlace) { + tc_log_info(PACKAGE, + "V: %-16s | (mode=%i) %s", + "de-interlace", vob->deinterlace, + deinterlace_desc[vob->deinterlace]); + } + + if (vob->deinterlace == 4) + vob->ex_v_height /= 2; + + // Calculate the missing w or h based on the ASR + if (zoom && (vob->zoom_width == 0 || vob->zoom_height == 0)) { + enum missing_t { NONE, CALC_W, CALC_H, ALL } missing = ALL; + ratio_t asr = asrs[0]; + float oldr; + + // check if we have at least on width or height + if (vob->zoom_width == 0 && vob->zoom_height == 0) + missing = ALL; + else if (vob->zoom_width == 0 && vob->zoom_height > 0) + missing = CALC_W; + else if (vob->zoom_width > 0 && vob->zoom_height == 0) + missing = CALC_H; + else if (vob->zoom_width > 0 && vob->zoom_height > 0) + missing = NONE; + + // try import + if (vob->im_asr > 0 && vob->im_asr < 5) + asr = asrs[vob->im_asr]; + // try the export aspectratio + else if (vob->ex_asr > 0 && vob->ex_asr < 5) + asr = asrs[vob->ex_asr]; + + switch (missing) { + case ALL: + tc_error("Neither zoom width nor height set, can't guess anything"); + case CALC_W: + vob->zoom_width = vob->zoom_height * asr.t; vob->zoom_width /= asr.b; + break; + case CALC_H: + vob->zoom_height = vob->zoom_width * asr.b; vob->zoom_height /= asr.t; + break; + case NONE: + default: + /* can't happen */ + break; + } + + // for error printout + oldr = (float)vob->zoom_width/(float)vob->zoom_height; + + // align + if (vob->zoom_height % 8 != 0) + vob->zoom_height += 8-(vob->zoom_height%8); + if (vob->zoom_width % 8 != 0) + vob->zoom_width += 8-(vob->zoom_width%8); + oldr = ((float)vob->zoom_width/(float)vob->zoom_height-oldr)*100.0; + oldr = oldr<0?-oldr:oldr; + + tc_log_info(PACKAGE, "V: %-16s | %03dx%03d %4.2f:1 error %.2f%%", + "auto resize", vob->zoom_width, vob->zoom_height, + (float)vob->zoom_width/(float)vob->zoom_height, oldr); + } + + // -Z ...,fast + if (fast_resize) { + int ret = tc_compute_fast_resize_values(vob, TC_FALSE); + if (ret == 0) { + if (vob->hori_resize1 == 0 && vob->vert_resize1 == 0) + resize1 = TC_FALSE; + else + resize1 = TC_TRUE; + if (vob->hori_resize2 == 0 && vob->vert_resize2 == 0) + resize2 = TC_FALSE; + else + resize2 = TC_TRUE; + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: %-16s | Using -B %d,%d,8 -X %d,%d,8", + "fast resize", + vob->vert_resize1, vob->hori_resize1, + vob->vert_resize2, vob->hori_resize2); + } + zoom = TC_FALSE; + } else { + if(verbose & TC_INFO) { + tc_log_info(PACKAGE, + "V: %-16s | requested but can't be used (W or H mod 8 != 0)", + "fast resize"); + } + } + } + + // -X + if (resize2) { + if (vob->resize2_mult % 8 != 0) + tc_error("resize multiplier for option -X is not a multiple of 8"); + + // works only for frame dimension beeing an integral multiple of vob->resize2_mult: + if (vob->vert_resize2 + && (vob->vert_resize2 * vob->resize2_mult + vob->ex_v_height) % vob->resize2_mult != 0) + tc_error("invalid frame height for option -X, check also option -j"); + + if (vob->hori_resize2 + && (vob->hori_resize2 * vob->resize2_mult + vob->ex_v_width) % vob->resize2_mult != 0) + tc_error("invalid frame width for option -X, check also option -j"); + + vob->ex_v_height += (vob->vert_resize2 * vob->resize2_mult); + vob->ex_v_width += (vob->hori_resize2 * vob->resize2_mult); + + //check2: + + if (vob->ex_v_height > TC_MAX_V_FRAME_HEIGHT + || vob->ex_v_width >TC_MAX_V_FRAME_WIDTH) + tc_error("invalid resize parameter for option -X"); + + if (vob->vert_resize2 <0 || vob->hori_resize2 < 0) + tc_error("invalid resize parameter for option -X"); + + // new aspect ratio: + asr *= (double) vob->ex_v_width * (vob->ex_v_height - vob->vert_resize2*vob->resize2_mult)/ + ((vob->ex_v_width - vob->hori_resize2*vob->resize2_mult) * vob->ex_v_height); + + vob->vert_resize2 *= (vob->resize2_mult/8); + vob->hori_resize2 *= (vob->resize2_mult/8); + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-X)", + "new aspect ratio", + vob->ex_v_width, vob->ex_v_height, asr); + } + + // -B + if (resize1) { + if (vob->resize1_mult % 8 != 0) + tc_error("resize multiplier for option -B is not a multiple of 8"); + + // works only for frame dimension beeing an integral multiple of vob->resize1_mult: + if (vob->vert_resize1 + && (vob->ex_v_height - vob->vert_resize1*vob->resize1_mult) % vob->resize1_mult != 0) + tc_error("invalid frame height for option -B, check also option -j"); + + if (vob->hori_resize1 + && (vob->ex_v_width - vob->hori_resize1*vob->resize1_mult) % vob->resize1_mult != 0) + tc_error("invalid frame width for option -B, check also option -j"); + + vob->ex_v_height -= (vob->vert_resize1 * vob->resize1_mult); + vob->ex_v_width -= (vob->hori_resize1 * vob->resize1_mult); + + //check: + if (vob->vert_resize1 < 0 || vob->hori_resize1 < 0) + tc_error("invalid resize parameter for option -B"); + + //new aspect ratio: + asr *= (double) vob->ex_v_width * (vob->ex_v_height + vob->vert_resize1*vob->resize1_mult)/ + ((vob->ex_v_width + vob->hori_resize1*vob->resize1_mult) * vob->ex_v_height); + + vob->vert_resize1 *= (vob->resize1_mult/8); + vob->hori_resize1 *= (vob->resize1_mult/8); + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-B)", + "new aspect ratio", + vob->ex_v_width, vob->ex_v_height, asr); + } + + // -Z + if (zoom) { + // new aspect ratio: + asr *= (double) vob->zoom_width*vob->ex_v_height/(vob->ex_v_width * vob->zoom_height); + + vob->ex_v_width = vob->zoom_width; + vob->ex_v_height = vob->zoom_height; + + if (verbose & TC_INFO && vob->ex_v_height > 0) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (%s)", + "zoom", + vob->ex_v_width, vob->ex_v_height, asr, + tcv_zoom_filter_to_string(vob->zoom_filter)); + } + + // -Y + if (ex_clip) { + CLIP_CHECK(ex_clip, "clip", "-Y"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d", "clip frame (->)", + vob->ex_v_width, vob->ex_v_height); + } + + // -r + if (rescale) { + vob->ex_v_height /= vob->reduce_h; + vob->ex_v_width /= vob->reduce_w; + + //new aspect ratio: + asr *= (double)vob->ex_v_width/vob->ex_v_height*(vob->reduce_h*vob->ex_v_height)/ + (vob->reduce_w*vob->ex_v_width); + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d %4.2f:1 (-r)", + "rescale frame", + vob->ex_v_width, vob->ex_v_height,asr); + + // sanity check for YUV + if (vob->im_v_codec == CODEC_YUV || vob->im_v_codec == CODEC_YUV422) { + if (vob->ex_v_width%2 != 0 || (vob->im_v_codec == CODEC_YUV && vob->ex_v_height % 2 != 0)) { + tc_error("rescaled width/height must be even for YUV mode, try -V rgb24"); + } + } + } + + // --keep_asr + if (keepasr) { + int clip, zoomto; + double asr_out = (double)vob->ex_v_width/(double)vob->ex_v_height; + double asr_in = (double)vob->im_v_width/(double)vob->im_v_height; + double delta = 0.01; + double asr_cor = 1.0; + + + if (vob->im_asr) { + switch (vob->im_asr) { + case 1: + asr_cor = (1.0); + break; + case 2: + asr_cor = (4.0/3.0); + break; + case 3: + asr_cor = (16.0/9.0); + break; + case 4: + asr_cor = (2.21); + break; + } + } + + if (!zoom) + tc_error ("keep_asr only works with -Z"); + + if (asr_in-delta < asr_out && asr_out < asr_in+delta) + tc_error ("Aspect ratios are too similar, don't use --keep_asr "); + + if (asr_in > asr_out) { + /* adjust height */ + int clipV = (vob->im_clip_top +vob->im_clip_bottom); + int clipH = (vob->im_clip_left+vob->im_clip_right); + int clip1 = 0; + int clip2 = 0; + + zoomto = (int)((double)(vob->ex_v_width) / + ( ((double)(vob->im_v_width -clipH) / (vob->im_v_width/asr_cor/vob->im_v_height) )/ + (double)(vob->im_v_height-clipV))+.5); + clip = vob->ex_v_height - zoomto; + if (zoomto % 2 != 0) + (clip>0?zoomto--:zoomto++); // XXX + clip = vob->ex_v_height - zoomto; + clip /= 2; + clip1 = clip2 = clip; + + if (clip & 1) { + clip1--; + clip2++; + } + ex_clip = TC_TRUE; + vob->ex_clip_top = -clip1; + vob->ex_clip_bottom = -clip2; + + vob->zoom_height = zoomto; + } else { + /* adjust width */ + int clipV = (vob->im_clip_top +vob->im_clip_bottom); + int clipH = (vob->im_clip_left+vob->im_clip_right); + int clip1 = 0; + int clip2 = 0; + zoomto = (int)((double)vob->ex_v_height * ( + ( ((double)(vob->im_v_width-clipH)) / (vob->im_v_width/asr_cor/vob->im_v_height) ) / + (double)(vob->im_v_height-clipV)) +.5); + + clip = vob->ex_v_width - zoomto; + + if (zoomto % 2 != 0) + (clip>0?zoomto--:zoomto++); // XXX + clip = vob->ex_v_width - zoomto; + clip /= 2; + clip1 = clip2 = clip; + + if (clip & 1) { + clip1--; + clip2++; + } + ex_clip = TC_TRUE; + vob->ex_clip_left = -clip1; + vob->ex_clip_right = -clip2; + + vob->zoom_width = zoomto; + } + + if (vob->ex_v_height - vob->ex_clip_top - vob->ex_clip_bottom <= 0) + tc_error("invalid top/bottom clip parameter calculated from --keep_asr"); + + if (vob->ex_v_width - vob->ex_clip_left - vob->ex_clip_right <= 0) + tc_error("invalid left/right clip parameter calculated from --keep_asr"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes (%d,%d,%d,%d)", "keep aspect", + vob->ex_clip_top, vob->ex_clip_left, + vob->ex_clip_bottom, vob->ex_clip_right); + } + + // -z + + if (flip && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "flip frame"); + + // -l + if (mirror && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "mirror frame"); + + // -k + if (rgbswap && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "rgb2bgr"); + + // -K + if (decolor && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | yes", "b/w reduction"); + + // -G + if (dgamma && verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | %.3f", "gamma correction", vob->gamma); + + // number of bits/pixel + // + // Christoph Lampert writes in transcode-users/2002-July/003670.html + // B*1000 B*1000*asr + // bpp = --------; W^2 = ------------ + // W*H*F bpp * F + // If this number is less than 0.15, you will + // most likely see visual artefacts (e.g. in high motion scenes). If you + // reach 0.2 or more, the visual quality normally is rather good. + // For my tests, this corresponded roughly to a fixed quantizer of 4, + // which is not brilliant, but okay. + + if (vob->divxbitrate > 0 && vob->divxmultipass != 3 + && verbose & TC_INFO) { + double div = vob->ex_v_width * vob->ex_v_height * vob->fps; + double bpp = vob->divxbitrate * 1000; + const char *judge = ""; + + if (div < 1.0) + bpp = 0.0; + else + bpp /= div; + + if (bpp <= 0.0) + judge = " (unknown)"; + else if (bpp > 0.0 && bpp <= 0.15) + judge = " (low)"; + + tc_log_info(PACKAGE, "V: %-16s | %.3f%s", "bits/pixel", bpp, judge); + } + + // -C + if (vob->antialias < 0 || vob->antialias > 3) { + tc_error("invalid parameter for option -C"); + } else { + if ((verbose & TC_INFO) && vob->antialias) { + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d|%.2f|%.2f) %s", + "anti-alias", + vob->antialias, vob->aa_weight, vob->aa_bias, + antialias_desc[vob->antialias]); + } + } + + // --POST_CLIP + + if (post_ex_clip) { + CLIP_CHECK(post_ex_clip, "post_clip", "--post_clip"); + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %03dx%03d", + "post clip frame", + vob->ex_v_width, vob->ex_v_height); + } + + + // -W + if (vob->vob_percentage) { + if (vob->vob_chunk < 0 || vob->vob_chunk < 0) + tc_error("invalid parameter for option -W"); + } else { + if (vob->vob_chunk < 0 || vob->vob_chunk > vob->vob_chunk_max + 1) + tc_error("invalid parameter for option -W"); + } + + // -f + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: %-16s | %.3f,%d", + "decoding fps,frc", + vob->fps, vob->im_frc); + + // -R + if (vob->divxmultipass && verbose & TC_INFO) { + switch (vob->divxmultipass) { + case 1: + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s %s", + "multi-pass", + vob->divxmultipass, + "writing data (pass 1) to", + vob->divxlogfile); + break; + case 2: + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s %s", + "multi-pass", + vob->divxmultipass, + "reading data (pass2) from", + vob->divxlogfile); + break; + case 3: + if (vob->divxbitrate > VMAXQUANTIZER) + vob->divxbitrate = VQUANTIZER; + tc_log_info(PACKAGE, + "V: %-16s | (mode=%d) %s (quant=%d)", + "single-pass", + vob->divxmultipass, + "constant quantizer/quality", + vob->divxbitrate); + break; + } + } + + // export frame size final check + if (vob->ex_v_height < 0 || vob->ex_v_width < 0) { + tc_warn("invalid export frame combination %dx%d", vob->ex_v_width, vob->ex_v_height); + tc_error("invalid frame processing requested"); + } + + // -V + if (vob->im_v_codec == CODEC_YUV) { + vob->ex_v_size = (3*vob->ex_v_height * vob->ex_v_width)>>1; + vob->im_v_size = (3*vob->im_v_height * vob->im_v_width)>>1; + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | YUV420 (4:2:0) aka I420", + "video format"); + } else if (vob->im_v_codec == CODEC_YUV422) { + vob->ex_v_size = (2*vob->ex_v_height * vob->ex_v_width); + vob->im_v_size = (2*vob->im_v_height * vob->im_v_width); + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | YUV422 (4:2:2)", + "video format"); + } else { + vob->ex_v_size = vob->ex_v_height * vob->ex_v_width * BPP/8; + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | RGB24", + "video format"); + } + + // -m + // different audio/video output files not yet supported + if (vob->audio_out_file == NULL) + vob->audio_out_file = vob->video_out_file; + + // -n + if (no_ain_codec == 1 && vob->has_audio == 0 + && vob->a_codec_flag == CODEC_AC3) { + if (vob->amod_probed == NULL || strcmp(vob->amod_probed,"null") == 0) { + if (verbose & TC_DEBUG) + tc_log_warn(PACKAGE, + "problems detecting audio format - using 'null' module"); + vob->a_codec_flag = 0; + } + } + + if (preset_flag & TC_PROBE_NO_TRACK) { + //tracks specified by user + } else { + if (!vob->has_audio_track && vob->has_audio) { + tc_warn("requested audio track %d not found - using 'null' module", vob->a_track); + vob->a_codec_flag = 0; + } + } + + //audio import disabled + if (vob->a_codec_flag == 0) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "import"); + im_aud_mod = "null"; + } else { + //audio format, if probed sucessfully + if (verbose & TC_INFO) { + if (vob->a_stream_bitrate) + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5lx %-12s [%4d,%2d,%1d] %4d kbps", + "import format", + vob->a_codec_flag, + tc_codec_to_comment(vob->a_codec_flag), + vob->a_rate, vob->a_bits, vob->a_chan, + vob->a_stream_bitrate); + else + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5lx %-12s [%4d,%2d,%1d]", + "import format", + vob->a_codec_flag, + tc_codec_to_comment(vob->a_codec_flag), + vob->a_rate, vob->a_bits, vob->a_chan); + } + } + + if (vob->im_a_codec == CODEC_PCM && vob->a_chan > 2 && !(vob->pass_flag & TC_AUDIO)) { + // Input is more than 2 channels (i.e. 5.1 AC3) but PCM internal + // representation can't handle that, adjust the channel count to reflect + // what modules will actually have presented to them. + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d channels -> %d channels", + "downmix", vob->a_chan, 2); + vob->a_chan = 2; + } + + if (vob->ex_a_codec == 0 || vob->a_codec_flag == 0 + || ex_aud_mod == NULL || strcmp(ex_aud_mod, "null") == 0) { + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "export"); + ex_aud_mod = "null"; + } else { + // audio format + if (ex_aud_mod && strlen(ex_aud_mod) != 0) { + if (strcmp(ex_aud_mod, "mpeg") == 0) + vob->ex_a_codec = CODEC_MP2; + if (strcmp(ex_aud_mod, "mp2enc") == 0) + vob->ex_a_codec = CODEC_MP2; + if (strcmp(ex_aud_mod, "mp1e") == 0) + vob->ex_a_codec=CODEC_MP2; + } + + // calc export bitrate + switch (vob->ex_a_codec) { + case 0x1: // PCM + vob->mp3bitrate = ((vob->mp3frequency > 0) ?vob->mp3frequency :vob->a_rate) * + ((vob->dm_bits > 0) ?vob->dm_bits :vob->a_bits) * + ((vob->dm_chan > 0) ?vob->dm_chan :vob->a_chan) / 1000; + break; + case 0x2000: // PCM + if (vob->im_a_codec == CODEC_AC3) { + vob->mp3bitrate = vob->a_stream_bitrate; + } + break; + } + + if (verbose & TC_INFO) { + if (vob->pass_flag & TC_AUDIO) + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5x %-12s [%4d,%2d,%1d] %4d kbps", + "export format", + vob->im_a_codec, + tc_codec_to_comment(vob->im_a_codec), + vob->a_rate, vob->a_bits, vob->a_chan, + vob->a_stream_bitrate); + else + tc_log_info(PACKAGE, + "A: %-16s | 0x%-5x %-12s [%4d,%2d,%1d] %4d kbps", + "export format", + vob->ex_a_codec, + tc_codec_to_comment(vob->ex_a_codec), + ((vob->mp3frequency > 0) ?vob->mp3frequency :vob->a_rate), + ((vob->dm_bits > 0) ?vob->dm_bits :vob->a_bits), + ((vob->dm_chan > 0) ?vob->dm_chan :vob->a_chan), + vob->mp3bitrate); + tc_log_info(PACKAGE, "V: %-16s | %s%s", "export format", + tc_codec_to_string(vob->ex_v_codec), + (vob->ex_v_codec == 0) ?" (module dependant)" :""); + } + } + + // Do not run out of audio-data + // import_ac3 now correctly probes the channels of the ac3 stream + // (previous versions always returned "2"). This breakes transcode + // when doing -A --tibit + if (vob->im_a_codec == CODEC_AC3) + vob->a_chan = vob->a_chan > 2 ?2 :vob->a_chan; + + // -f and --export_fps/export_frc + // + // set import/export frc/fps + if (vob->im_frc == 0) + tc_frc_code_from_value(&vob->im_frc, vob->fps); + + // ex_fps given, but not ex_frc + if (vob->ex_frc == 0 && (vob->ex_fps != 0.0)) + tc_frc_code_from_value(&vob->ex_frc, vob->ex_fps); + + if (vob->ex_frc == 0 && vob->im_frc != 0) + vob->ex_frc = vob->im_frc; + + // ex_frc always overwrites ex_fps + if (vob->ex_frc > 0) + tc_frc_code_to_value(vob->ex_frc, &vob->ex_fps); + + if (vob->im_frc <= 0 && vob->ex_frc <= 0 && vob->ex_fps == 0) + vob->ex_fps = vob->fps; + + if (vob->im_frc == -1) + vob->im_frc = 0; + if (vob->ex_frc == -1) + vob->ex_frc = 0; + + // --export_fps + + if(verbose & TC_INFO) + tc_log_info(PACKAGE, + "V: %-16s | %.3f,%d", + "encoding fps,frc", + vob->ex_fps, vob->ex_frc); + + + // --a52_demux + + if ((vob->a52_mode & TC_A52_DEMUX) && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %s", "A52 demuxing", + "(yes) 3 front, 2 rear, 1 LFE (5.1)"); + + //audio language, if probed sucessfully + if(vob->lang_code > 0 && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %c%c", + "language", + vob->lang_code >> 8, vob->lang_code & 0xff); + + // recalculate audio bytes per frame since video frames per second + // may have changed + + // samples per audio frame + fch = vob->a_rate/vob->ex_fps; + + // bytes per audio frame + vob->im_a_size = (int)(fch * (vob->a_bits/8) * vob->a_chan); + vob->im_a_size = (vob->im_a_size >> 2) << 2; + + // rest: + fch *= (vob->a_bits/8) * vob->a_chan; + + leap_bytes1 = TC_LEAP_FRAME * (fch - vob->im_a_size); + leap_bytes2 = - leap_bytes1 + TC_LEAP_FRAME * (vob->a_bits/8) * vob->a_chan; + leap_bytes1 = (leap_bytes1 >> 2) << 2; + leap_bytes2 = (leap_bytes2 >> 2) << 2; + + if(leap_bytes1<leap_bytes2) { + vob->a_leap_bytes = leap_bytes1; + } else { + vob->a_leap_bytes = -leap_bytes2; + vob->im_a_size += (vob->a_bits/8) * vob->a_chan; + } + + // final size in bytes + vob->ex_a_size = vob->im_a_size; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d (%.6f)", + "bytes per frame", vob->im_a_size, fch); + + if(no_audio_adjust) { + vob->a_leap_bytes=0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | disabled", "adjustment"); + + } else + if (verbose & TC_INFO) + tc_log_info(PACKAGE, + "A: %-16s | %d@%d", "adjustment", + vob->a_leap_bytes, vob->a_leap_frame); + + // -s + + if (vob->volume > 0 && vob->a_chan != 2) { + //tc_error("option -s not yet implemented for mono streams"); + } + + if (vob->volume > 0 && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %5.3f", + "rescale stream", vob->volume); + + // -D + if (vob->sync_ms >= (int) (1000.0/vob->ex_fps) + || vob->sync_ms <= - (int) (1000.0/vob->ex_fps)) { + vob->sync = (int) (vob->sync_ms/1000.0*vob->ex_fps); + vob->sync_ms -= vob->sync * (int) (1000.0/vob->ex_fps); + } + + if ((vob->sync || vob->sync_ms) && (verbose & TC_INFO)) + tc_log_info(PACKAGE, + "A: %-16s | %d ms [ %d (A) | %d ms ]", + "AV shift", + vob->sync * (int) (1000.0/vob->ex_fps) + vob->sync_ms, + vob->sync, vob->sync_ms); + + // -d + if (pcmswap) + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | yes", "swap bytes"); + + // -E + + //set export parameter to input parameter, if no re-sampling is requested + if (vob->dm_chan == 0) + vob->dm_chan = vob->a_chan; + if (vob->dm_bits == 0) + vob->dm_bits = vob->a_bits; + + // -P + if (vob->pass_flag & TC_AUDIO) { + vob->im_a_codec = CODEC_RAW; + vob->ex_a_codec = CODEC_RAW; + //suggestion: + if (no_a_out_codec) + ex_aud_mod = "raw"; + no_a_out_codec = 0; + + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "A: %-16s | yes", "pass-through"); + } + + // -m + // different audio/video output files need two export modules + if (no_a_out_codec == 0 && vob->audio_out_file == NULL + && strcmp(ex_vid_mod, ex_aud_mod) != 0) + tc_error("different audio/export modules require use of option -m"); + + + // --accel +#if defined(ARCH_X86) || defined(ARCH_X86_64) + if (verbose & TC_INFO) + tc_log_info(PACKAGE, "V: IA32/AMD64 accel | %s ", + ac_flagstotext(tc_accel & ac_cpuinfo())); +#endif + + ac_init(tc_accel); + + // more checks with warnings + + if (verbose & TC_INFO) { + // -o + if (vob->video_out_file == NULL && vob->audio_out_file == NULL + && core_mode == TC_MODE_DEFAULT) { + vob->video_out_file = TC_DEFAULT_OUT_FILE; + vob->audio_out_file = TC_DEFAULT_OUT_FILE; + tc_warn("no option -o found, encoded frames send to \"%s\"", + vob->video_out_file); + } + + // -y + if (core_mode == TC_MODE_DEFAULT + && vob->video_out_file != NULL && no_v_out_codec) + tc_warn("no option -y found, option -o ignored, writing to \"/dev/null\""); + + if (core_mode == TC_MODE_AVI_SPLIT && no_v_out_codec) + tc_warn("no option -y found, option -t ignored, writing to \"/dev/null\""); + + if (vob->im_v_codec == CODEC_YUV + && (vob->im_clip_left % 2 != 0 || vob->im_clip_right % 2 + || vob->im_clip_top % 2 != 0 || vob->im_clip_bottom % 2 != 0)) + tc_warn ("Odd import clipping paramter(s) detected, may cause distortion"); + + if (vob->im_v_codec == CODEC_YUV + && (vob->ex_clip_left % 2 != 0 || vob->ex_clip_right % 2 + || vob->ex_clip_top % 2 != 0 || vob->ex_clip_bottom % 2 != 0)) + tc_warn ("Odd export clipping paramter(s) detected, may cause distortion"); + } + + // -u + if (tc_buffer_delay_dec == -1) //adjust core parameter + tc_buffer_delay_dec = (vob->pass_flag & TC_VIDEO || ex_vid_mod==NULL || strcmp(ex_vid_mod, "null") == 0) + ?TC_DELAY_MIN :TC_DELAY_MAX; + + if (tc_buffer_delay_enc == -1) //adjust core parameter + tc_buffer_delay_enc = (vob->pass_flag & TC_VIDEO || ex_vid_mod==NULL || strcmp(ex_vid_mod, "null") == 0) + ?TC_DELAY_MIN :TC_DELAY_MAX; + + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "encoder delay = decode=%d encode=%d usec", + tc_buffer_delay_dec, tc_buffer_delay_enc); + + if (core_mode == TC_MODE_AVI_SPLIT && !strlen(base) && !vob->video_out_file) + tc_error("no option -o found, no base for -t given, so what?"); + + /* ------------------------------------------------------------- + * + * OK, so far, now start the support threads, setup buffers, ... + * + * ------------------------------------------------------------- */ + + //this will speed up in pass-through mode + if(vob->pass_flag && !(preset_flag & TC_PROBE_NO_BUFFER)) + max_frame_buffer = 50; + + if (vob->fps >= vob->ex_fps) { + /* worst case -> lesser fps (more audio samples for second) */ + specs.frc = vob->im_frc; + } else { + specs.frc = vob->ex_frc; + } + specs.width = TC_MAX(vob->im_v_width, vob->ex_v_width); + specs.height = TC_MAX(vob->im_v_height, vob->ex_v_height); + specs.format = vob->im_v_codec; + + /* XXX: explain me up */ + specs.rate = TC_MAX(vob->a_rate, vob->mp3frequency); + specs.channels = TC_MAX(vob->a_chan, vob->dm_chan); + specs.bits = TC_MAX(vob->a_bits, vob->dm_bits); + + tc_framebuffer_set_specs(&specs); + + if (verbose & TC_INFO) { + tc_log_info(PACKAGE, "V: video buffer | %i @ %ix%i [0x%x]", + max_frame_buffer, specs.width, specs.height, specs.format); + tc_log_info(PACKAGE, "A: audio buffer | %i @ %ix%ix%i", + max_frame_buffer, specs.rate, specs.channels, specs.bits); + } + +#ifdef STATBUFFER + // allocate buffer + if (verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "allocating %d framebuffers (static)", + max_frame_buffer); + + if (vframe_alloc(max_frame_buffer) < 0) + tc_error("static framebuffer allocation failed"); + if (aframe_alloc(max_frame_buffer) < 0) + tc_error("static framebuffer allocation failed"); + +#else + if(verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "%d framebuffers (dynamic) requested", + max_frame_buffer); +#endif + + // load import/export modules and filters plugins + if (transcode_init(vob, tc_get_ringbuffer(max_frame_threads, max_frame_threads)) < 0) + tc_error("plug-in initialization failed"); + + // start socket stuff + if (socket_file) + if (!tc_socket_init(socket_file)) + tc_error("failed to initialize socket handler"); + + // now we start the signal handler thread + if (pthread_create(&event_thread_id, NULL, event_thread, &sigs_to_block) != 0) + tc_error("failed to start signal handler thread"); + + + // start frame processing threads + tc_frame_threads_init(vob, max_frame_threads, max_frame_threads); + + + /* ------------------------------------------------------------ + * + * transcoder core modes + * + * ------------------------------------------------------------*/ + + switch (core_mode) { + case TC_MODE_DEFAULT: + transcode_mode_default(vob); + break; + + case TC_MODE_AVI_SPLIT: + transcode_mode_avi_split(vob); + break; + + case TC_MODE_PSU: + transcode_mode_psu(vob, psubase); + break; + + case TC_MODE_DIRECTORY: + transcode_mode_directory(vob); + break; + + case TC_MODE_DVD_CHAPTER: + transcode_mode_dvd(vob); + break; + + case TC_MODE_DEBUG: + /* FIXME: get rid of this? */ + tc_log_msg(PACKAGE, "debug \"core\" mode"); + break; + + default: + //should not get here: + tc_error("internal error"); + } + + /* ------------------------------------------------------------ + * shutdown transcode, all cores modes end up here, core modes + * must take care of proper import/export API shutdown. + * + * 1) stop and cancel frame processing threads + * 2) unload all external modules + * 3) cancel internal signal/server thread + * ------------------------------------------------------------*/ + + // turn counter off + counter_off(); + + SHUTDOWN_MARK("clean up"); + + // stop and cancel frame processing threads + tc_frame_threads_close(); + SHUTDOWN_MARK("frame threads"); + + // unload all external modules + transcode_fini(NULL); + SHUTDOWN_MARK("unload modules"); + + // cancel no longer used internal signal handler threads + if (event_thread_id) { + SHUTDOWN_MARK("cancel signal"); + stop_event_thread(); + event_thread_id = (pthread_t)0; + } + + SHUTDOWN_MARK("internal threads"); + + // shut down control socket, if active + tc_socket_fini(); + SHUTDOWN_MARK("control socket"); + + // all done + if (verbose & TC_DEBUG) + fprintf(stderr, " done\n"); + + summary: + // print a summary + if ((verbose & TC_INFO) && vob->clip_count) + tc_log_info(PACKAGE, "clipped %d audio samples", + vob->clip_count/2); + + if (verbose & TC_INFO) { + long drop = - tc_get_frames_dropped(); + + tc_log_info(PACKAGE, "encoded %ld frames (%ld dropped, %ld cloned)," + " clip length %6.2f s", + (long)tc_get_frames_encoded(), drop, + (long)tc_get_frames_cloned(), + tc_get_frames_encoded()/vob->ex_fps); + } + +#ifdef STATBUFFER + // free buffers + vframe_free(); + aframe_free(); + if(verbose & TC_DEBUG) + tc_log_msg(PACKAGE, "buffer released"); +#endif + + teardown_input_sources(vob); + + if (vob) + tc_free(vob); + + //exit at last + if (tc_interrupted()) + return 127; + return 0; +} + +// this Code below here _never_ gets called. +// it is just there to trick the linker to not remove +// unneeded object files from a .a file. + +#include "libtc/static_tclist.h" +#include "libtc/static_optstr.h" +#include "libtc/static_tctimer.h" +#include "avilib/static_avilib.h" +#include "avilib/static_wavlib.h" + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/transcode.h b/debian/transcode/transcode-1.1.7/src/transcode.h new file mode 100644 index 00000000..90c2a22d --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/transcode.h @@ -0,0 +1,500 @@ +/* + * transcode.h + * + * Copyright (C) Thomas Oestreich - June 2001 + * + * This file is part of transcode, a 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. + * + */ + +#ifndef _TRANSCODE_H +#define _TRANSCODE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "avilib/avilib.h" +#include "aclib/ac.h" +#include "libtc/tcglob.h" +#include "libtc/framecode.h" +#include "libtcvideo/tcvideo.h" + + +#ifdef __bsdi__ +typedef unsigned int uint32_t; +#endif + +#include "tc_defaults.h" +#include "framebuffer.h" +#include "libtc/libtc.h" + +/*************************************************************************/ + +/* ---------------------------- + * + * MPEG profiles for setting + * sensible defaults + * + * ----------------------------*/ + +typedef enum { + PROF_NONE = 0, + VCD, + VCD_PAL, + VCD_NTSC, + SVCD, + SVCD_PAL, + SVCD_NTSC, + XVCD, + XVCD_PAL, + XVCD_NTSC, + DVD, + DVD_PAL, + DVD_NTSC +} mpeg_profile_t; + + +/* ---------------------------- + * + * Global information structure + * + * ----------------------------*/ + +typedef struct _transfer_t { + int flag; + FILE *fd; + int size; + uint8_t *buffer; + uint8_t *buffer2; + int attributes; +} transfer_t; + +typedef struct _vob_t { + + // import info + + const char *vmod_probed; + const char *amod_probed; + const char *vmod_probed_xml; // Modules for reading XML data + const char *amod_probed_xml; + + int verbose; + + TCGlob *video_in_files; + TCGlob *audio_in_files; + const char *video_in_file; // Video source file + const char *audio_in_file; // Audio source file + + const char *nav_seek_file; // Seek/index information + + int has_audio; // Does the stream have audio? + int has_audio_track; // Does the requested audio track exist? + int has_video; // Does the stream have video? + + int lang_code; // Language of audio track + + int a_track; // Audio track ID + int v_track; // Video track ID + int s_track; // Subtitle track ID + + int sync; // Frame offset for audio/video synchronization + int sync_ms; // Fine-tuning for audio/video synchronization + int sync_samples; // sync_ms converted to samples + + int dvd_title; + int dvd_chapter1; + int dvd_chapter2; + int dvd_max_chapters; + int dvd_angle; + + int ps_unit; + int ps_seq1; + int ps_seq2; + + int ts_pid1; + int ts_pid2; + + int vob_offset; + int vob_chunk; + int vob_chunk_num1; + int vob_chunk_num2; + int vob_chunk_max; + int vob_percentage; + + int vob_psu_num1; + int vob_psu_num2; + + const char *vob_info_file; + + double pts_start; + + double psu_offset; // PSU offset to pass to extsub + + int demuxer; + + long v_format_flag; // Video stream format + long v_codec_flag; // Video codec + long a_format_flag; // Audio stream format + long a_codec_flag; // Audio codec + + int quality; + + // Audio stream parameters + + int a_stream_bitrate; // Source stream bitrate + + int a_chan; + int a_bits; + int a_rate; + + int a_padrate; // Zero padding rate + + int im_a_size; // Import total bytes per audio frame + int ex_a_size; // Export total bytes per audio frame + + int im_a_codec; // True frame buffer audio codec + + int a_leap_frame; + int a_leap_bytes; + + int a_vbr; // LAME VBR switch + + int a52_mode; + + int dm_bits; + int dm_chan; + + // Video stream parameters + + int v_stream_bitrate; // Source stream bitrate + + double fps; // Import frame rate (default 25 fps) + int im_frc; // Import frame rate code + double ex_fps; // Export frame rate (default 25 fps) + int ex_frc; // Export frame rate code + int hard_fps_flag; // If this is set, disable demuxer smooth drop + + int pulldown; // Set 3:2 pulldown flags on MPEG export + + int im_v_height; // Import picture height + int im_v_width; // Import picture width + int im_v_size; // Total number of bytes per frame + + int im_asr; // Import aspect ratio code + int im_par; // Import pixel aspect (code) + int im_par_width; // Import pixel aspect width + int im_par_height; // Import pixel aspect height + int ex_asr; // Export aspect ratio code + int ex_par; // Export pixel aspect (code) + int ex_par_width; // Export pixel aspect width + int ex_par_height; // Export pixel aspect height + + int attributes; // More video frame attributes + + int im_v_codec; // True frame buffer video codec + + int encode_fields; // Interlaced field handling flag + + int dv_yuy2_mode; // Decode DV video in YUY2 mode? + + // Audio frame manipulation info + + double volume; // Audio amplitude rescale parameter + double ac3_gain[3]; // Audio amplitude rescale parameter for ac3 + int clip_count; // # of bytes clipped after volume adjustment + + // Video frame manipulation info + + int ex_v_width; // Export picture width + int ex_v_height; // Export picture height + int ex_v_size; // Total number of bytes per frame + + int reduce_h; // Reduction factor for frame height + int reduce_w; // Reduction factor for frame width + + int resize1_mult; // Multiplier for {vert,hori}_resize1 + int vert_resize1; // Height resize amount (shrink) + int hori_resize1; // Width resize amount (shrink) + + int resize2_mult; // Multiplier for {vert,hori}_resize2 + int vert_resize2; // Height resize amount (expand) + int hori_resize2; // Width resize amount (expand) + + int zoom_width; // Zoom width + int zoom_height; // Zoom height + int zoom_interlaced; // Zoom in interlaced mode? + + TCVZoomFilter zoom_filter; + + int antialias; + int deinterlace; + int decolor; + + double aa_weight; // Antialiasing center pixel weight + double aa_bias; // Antialiasing horizontal/vertical bias + + double gamma; + + int ex_clip_top; + int ex_clip_bottom; + int ex_clip_left; + int ex_clip_right; + + int im_clip_top; + int im_clip_bottom; + int im_clip_left; + int im_clip_right; + + int post_ex_clip_top; + int post_ex_clip_bottom; + int post_ex_clip_left; + int post_ex_clip_right; + + int pre_im_clip_top; + int pre_im_clip_bottom; + int pre_im_clip_left; + int pre_im_clip_right; + + // Export info + + const char *video_out_file; + const char *audio_out_file; + + avi_t *avifile_in; + avi_t *avifile_out; + int avi_comment_fd; // Text file to read AVI header comments from + + int audio_file_flag; // Nonzero if audio goes to its own file + + // Encoding parameters + + int divxbitrate; + int divxkeyframes; + int divxquality; + int divxcrispness; + int divxmultipass; + int video_max_bitrate; + const char *divxlogfile; + + int min_quantizer; + int max_quantizer; + + int rc_period; + int rc_reaction_period; + int rc_reaction_ratio; + + int divx5_vbv_prof; // Profile number + int divx5_vbv_bitrate; // Video Bitrate Verifier constraint overrides + int divx5_vbv_size; + int divx5_vbv_occupancy; + + int mp3bitrate; + int mp3frequency; + float mp3quality; // 0=best (very slow), 9=worst (default=5) + int mp3mode; // 0=joint-stereo, 1=full-stereo, 2=mono + + int bitreservoir; + const char *lame_preset; + + const char *audiologfile; + + int ex_a_codec; // Audio codec for export module + int ex_v_codec; // Video codec for export module + + const char *ex_v_fcc; // Video fourcc string + const char *ex_a_fcc; // Audio fourcc string/identifier + const char *ex_profile_name; // User profile name + + int pass_flag; + int encoder_flush; // flush encoders on close (yes) + + const char *mod_path; + + struct fc_time *ttime; // For framecode parsing (list of structs) + + unsigned int frame_interval; // Select every `frame_interval' frames only + + char *im_v_string; // Extra options for import video module + char *im_a_string; // Extra options for import audio module + char *ex_v_string; // Extra options for export video module + char *ex_a_string; // Extra options for export audio module + char *ex_m_string; // Extra options for multiplexor module + + float m2v_requant; // Requantize factor for mpeg2 video streams + + mpeg_profile_t mpeg_profile; + + unsigned int export_attributes; +} vob_t; + + +typedef struct subtitle_header_s { + + unsigned int header_length; + unsigned int header_version; + unsigned int payload_length; + + unsigned int lpts; + double rpts; + + unsigned int discont_ctr; + +} subtitle_header_t; + +/*************************************************************************/ + +// Module functions + +int tc_import(int opt, void *para1, void *para2); +int tc_export(int opt, void *para1, void *para2); + +// Some functions exported by transcode + +vob_t *tc_get_vob(void); + +int tc_next_video_in_file(vob_t *vob); +int tc_next_audio_in_file(vob_t *vob); + +int tc_has_more_video_in_file(vob_t *vob); +int tc_has_more_audio_in_file(vob_t *vob); + +void tc_outstream_rotate(void); +void tc_outstream_rotate_request(void); + +void version(void); + +extern int verbose; +extern int pcmswap; +extern int rescale; +extern int im_clip; +extern int ex_clip; +extern int pre_im_clip; +extern int post_ex_clip; +extern int flip; +extern int mirror; +extern int rgbswap; +extern int resize1; +extern int resize2; +extern int decolor; +extern int zoom; +extern int dgamma; +extern int keepasr; +extern int fast_resize; + +// Core parameters + +extern int tc_buffer_delay_dec; +extern int tc_buffer_delay_enc; +extern int tc_cluster_mode; +extern int tc_decoder_delay; +extern int tc_progress_meter; +extern int tc_progress_rate; +extern int tc_accel; +extern unsigned int tc_avi_limit; +extern pid_t tc_probe_pid; +extern int tc_niceness; + +extern int max_frame_buffer; +extern int max_frame_threads; + +// Various constants + +enum { + TC_EXPORT_NAME = 10, + TC_EXPORT_OPEN, + TC_EXPORT_INIT, + TC_EXPORT_ENCODE, + TC_EXPORT_CLOSE, + TC_EXPORT_STOP, +}; + +enum { + TC_EXPORT_ERROR = -1, + TC_EXPORT_OK = 0, + TC_EXPORT_UNKNOWN = 1, +}; + +enum { + TC_IMPORT_NAME = 20, + TC_IMPORT_OPEN, + TC_IMPORT_DECODE, + TC_IMPORT_CLOSE, +}; + +enum { + TC_IMPORT_ERROR = -1, + TC_IMPORT_OK = 0, + TC_IMPORT_UNKNOWN = 1, +}; + +enum { + TC_CAP_NONE = 0, + TC_CAP_PCM = 1, + TC_CAP_RGB = 2, + TC_CAP_AC3 = 4, + TC_CAP_YUV = 8, + TC_CAP_AUD = 16, + TC_CAP_VID = 32, + TC_CAP_MP3 = 64, + TC_CAP_YUY2 = 128, + TC_CAP_DV = 256, + TC_CAP_YUV422 = 512, +}; + +enum { + TC_MODE_DEFAULT = 0, + TC_MODE_AVI_SPLIT = 1, + TC_MODE_DVD_CHAPTER = 2, + TC_MODE_PSU = 4, + TC_MODE_DIRECTORY = 16, + TC_MODE_DEBUG = 32, +}; + +enum { + TC_ENCODE_FIELDS_PROGRESSIVE = 0, + TC_ENCODE_FIELDS_TOP_FIRST, + TC_ENCODE_FIELDS_BOTTOM_FIRST, + TC_ENCODE_FIELDS_UNKNOWN, +}; + +/*************************************************************************/ + +#endif // _TRANSCODE_H + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/video_trans.c b/debian/transcode/transcode-1.1.7/src/video_trans.c new file mode 100644 index 00000000..bee77ef1 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/video_trans.c @@ -0,0 +1,607 @@ +/* + * video_trans.c - video frame transformation routines + * Written by Andrew Church <achurch@achurch.org> + * Based on code written by Thomas Oestreich. + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "transcode.h" +#include "framebuffer.h" +#include "video_trans.h" +#include "libtcvideo/tcvideo.h" + +/*************************************************************************/ + +/* Structure that holds video frame information for passing around to + * processing routines. Since this is used only locally, we don't add + * the fields to vframe_list_t itself. */ + +typedef struct { + vframe_list_t *ptr; + int preadj_w, preadj_h; // width and height used for secondary buffer + int Bpp; // BYTES (not bits) per pixel + int nplanes; // number of planes + uint8_t *planes[3]; // pointer to start of each plane + uint8_t *tmpplanes[3]; // same, for secondary buffer + int width_div[3]; // width divisors for each plane + int height_div[3]; // height divisors for each plane + uint8_t black_pixel[3]; // "black" value for each plane (e.g. 128 for U/V) +} video_trans_data_t; + +/* Macro to perform a transformation on a frame. `vtd' is a pointer to a + * video_trans_data_t; the given function `func' will be called for each + * plane `i' as: + * func(handle, vtd->planes[i], vtd->tmpplanes[i], vtd->ptr->v_width, + * vtd->ptr->v_height, vtd->Bpp, args) + * where `args' are all arguments to this macro (if any) following `vtd'. + * swap_buffers(vtd) is called after the processing is complete. + */ +#define PROCESS_FRAME(func,vtd,args...) do { \ + int i; \ + for (i = 0; i < (vtd)->nplanes; i++) { \ + func(handle, (vtd)->planes[i], (vtd)->tmpplanes[i], \ + (vtd)->ptr->v_width / (vtd)->width_div[i], \ + (vtd)->ptr->v_height / (vtd)->height_div[i], \ + (vtd)->Bpp , ## args); \ + } \ + swap_buffers(vtd); \ +} while (0) + +/* Handle for calling tcvideo functions. */ +static TCVHandle handle = 0; + +/*************************************************************************/ +/*************************** Internal routines ***************************/ +/*************************************************************************/ + +/** + * set_vtd: Initialize the given vtd structure from the given + * vframe_list_t, and update ptr->video_size. + * + * Parameters: + * vtd: Pointer to video frame data to be initialized. + * ptr: Pointer to video frame buffer. + * Return value: + * None. + */ + +static void set_vtd(video_trans_data_t *vtd, vframe_list_t *ptr) +{ + int i; + + vtd->ptr = ptr; + vtd->preadj_w = 0; + vtd->preadj_h = 0; + /* Set some defaults */ + vtd->Bpp = 1; + vtd->nplanes = 1; + vtd->planes[0] = ptr->video_buf; + vtd->tmpplanes[0] = ptr->video_buf_Y[ptr->free]; + vtd->width_div[0] = 1; + vtd->height_div[0] = 1; + vtd->black_pixel[0] = 0; + /* Now set parameters based on image format */ + if (ptr->v_codec == CODEC_YUV) { + vtd->nplanes = 3; + vtd->Bpp = 1; + vtd->width_div[1] = 2; + vtd->width_div[2] = 2; + vtd->height_div[1] = 2; + vtd->height_div[2] = 2; + vtd->black_pixel[1] = 128; + vtd->black_pixel[2] = 128; + } else if (vtd->ptr->v_codec == CODEC_YUV422) { + vtd->nplanes = 3; + vtd->Bpp = 1; + vtd->width_div[1] = 2; + vtd->width_div[2] = 2; + vtd->height_div[1] = 1; + vtd->height_div[2] = 1; + vtd->black_pixel[1] = 128; + vtd->black_pixel[2] = 128; + } else if (vtd->ptr->v_codec == CODEC_RGB) { + vtd->Bpp = 3; + } + ptr->video_size = 0; + for (i = 0; i < vtd->nplanes; i++) { + int planesize = (ptr->v_width/vtd->width_div[i]) + * (ptr->v_height/vtd->height_div[i]) + * vtd->Bpp; + ptr->video_size += planesize; + if (i < vtd->nplanes-1) { + vtd->planes[i+1] = vtd->planes[i] + planesize; + vtd->tmpplanes[i+1] = vtd->tmpplanes[i] + planesize; + } + } +} + +/*************************************************************************/ + +/** + * preadjust_frame_size: Prepare for an operation that will change the + * frame size, setting up the secondary buffer plane pointers with the new + * size. Calling swap_buffers() will store the new size in the + * vframe_list_t structure. + * + * Parameters: + * vtd: Pointer to video frame data. + * new_w: New frame width. + * new_h: New frame height. + * Return value: + * None. + */ + +static void preadjust_frame_size(video_trans_data_t *vtd, int new_w, int new_h) +{ + int i; + + vtd->preadj_w = new_w; + vtd->preadj_h = new_h; + for (i = 0; i < vtd->nplanes-1; i++) { + int planesize = (new_w/vtd->width_div[i]) * (new_h/vtd->height_div[i]) + * vtd->Bpp; + vtd->tmpplanes[i+1] = vtd->tmpplanes[i] + planesize; + } +} + +/*************************************************************************/ + +/** + * swap_buffers: Swap current video frame buffer with free buffer. Also + * updates frame size if preadjust_frame_size() has been called. + * + * Parameters: + * vtd: Pointer to video frame data. + * Return value: + * None. + */ + +static void swap_buffers(video_trans_data_t *vtd) +{ + vtd->ptr->video_buf = vtd->ptr->video_buf_Y[vtd->ptr->free]; + vtd->ptr->free = (vtd->ptr->free==0) ? 1 : 0; + /* Install new width/height if preadjust_frame_size() was called */ + if (vtd->preadj_w && vtd->preadj_h) { + vtd->ptr->v_width = vtd->preadj_w; + vtd->ptr->v_height = vtd->preadj_h; + vtd->preadj_w = 0; + vtd->preadj_h = 0; + } + /* Set up plane pointers again */ + set_vtd(vtd, vtd->ptr); +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * do_process_frame: Perform video frame transformations based on global + * transcoding settings (derived from command-line arguments). + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +static int do_process_frame(vob_t *vob, vframe_list_t *ptr) +{ + video_trans_data_t vtd; /* for passing to subroutines */ + + + /**** Sanity check and initialization ****/ + + if (ptr->video_buf_Y[0] == ptr->video_buf_Y[1]) { + tc_log_error(__FILE__, "video frame has no temporary buffer!"); + return -1; + } + if (ptr->video_buf == ptr->video_buf_Y[ptr->free]) { + static int warned = 0; + if (!warned) { + tc_log_warn(__FILE__, "ptr->free points to wrong buffer" + " (BUG in transcode or modules)"); + warned = 1; + } + ptr->free = !ptr->free; + } + set_vtd(&vtd, ptr); + + /**** -j: clip frame (import) ****/ + + if (im_clip) { + preadjust_frame_size(&vtd, + ptr->v_width - vob->im_clip_left - vob->im_clip_right, + ptr->v_height - vob->im_clip_top - vob->im_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->im_clip_left / vtd.width_div[i], + vob->im_clip_right / vtd.width_div[i], + vob->im_clip_top / vtd.height_div[i], + vob->im_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /**** -I: deinterlace video frame ****/ + + if (vob->deinterlace > 0 + || ((ptr->attributes & TC_FRAME_IS_INTERLACED) && ptr->deinter_flag > 0) + ) { + int mode = (vob->deinterlace>0 ? vob->deinterlace : ptr->deinter_flag); + if (mode == 1) { + /* Simple linear interpolation */ + /* Note that for YUV, we can just leave U and V alone, since + * they already cover pairs of lines; thus instead of using + * PROCESS_FRAME, we just call tcv_deinterlace() on the Y/RGB + * plane, then copy the other two planes and swap_buffers(). */ + int i; + tcv_deinterlace(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + TCV_DEINTERLACE_INTERPOLATE); + for (i = 1; i < vtd.nplanes; i++) { + ac_memcpy(vtd.tmpplanes[i], vtd.planes[i], + (ptr->v_width/vtd.width_div[i]) + * (ptr->v_height/vtd.height_div[i])); + } + swap_buffers(&vtd); + } else if (mode == 3 || mode == 4) { + /* Drop every other line (and zoom back out in mode 3) */ + preadjust_frame_size(&vtd, ptr->v_width, ptr->v_height/2); + /* Drop the top or the bottom field? (Does it matter?) */ + PROCESS_FRAME(tcv_deinterlace, &vtd, + TCV_DEINTERLACE_DROP_FIELD_BOTTOM); + if (mode == 3) { + int w = ptr->v_width, h = ptr->v_height*2; + preadjust_frame_size(&vtd, w, h); + PROCESS_FRAME(tcv_zoom, &vtd, w / vtd.width_div[i], + h / vtd.height_div[i], vob->zoom_filter); + } + } else if (mode == 5) { + /* Linear blend; as for -I 1, only Y is processed in YUV mode */ + int i; + tcv_deinterlace(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + TCV_DEINTERLACE_LINEAR_BLEND); + for (i = 1; i < vtd.nplanes; i++) { + ac_memcpy(vtd.tmpplanes[i], vtd.planes[i], + (ptr->v_width/vtd.width_div[i]) + * (ptr->v_height/vtd.height_div[i])); + } + swap_buffers(&vtd); + } + /* else mode 2 (handled by encoder) or unknown: do nothing */ + ptr->attributes &= ~TC_FRAME_IS_INTERLACED; + } + + /**** -X: fast resize (up) ****/ + /**** -B: fast resize (down) ****/ + + if (resize1 || resize2) { + int width = ptr->v_width, height = ptr->v_height; + int resize_w = vob->hori_resize2 - vob->hori_resize1; + int resize_h = vob->vert_resize2 - vob->vert_resize1; + if (resize_h) { + preadjust_frame_size(&vtd, width, height+resize_h*8); + PROCESS_FRAME(tcv_resize, &vtd, 0, resize_h, 8/vtd.width_div[i], + 8/vtd.height_div[i]); + height += resize_h * 8; + } + if (resize_w) { + preadjust_frame_size(&vtd, width+resize_w*8, height); + PROCESS_FRAME(tcv_resize, &vtd, resize_w, 0, 8/vtd.width_div[i], + 8/vtd.height_div[i]); + } + } + + /**** -Z: zoom frame (slow resize) ****/ + + if (zoom) { + preadjust_frame_size(&vtd, vob->zoom_width, vob->zoom_height); + if (vob->zoom_interlaced) { + /* In YUV mode, only handle the first place as interlaced; + * the U and V planes are shared between both fields */ + int i; + tcv_zoom(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + vob->zoom_width, -vob->zoom_height, vob->zoom_filter); + for (i = 1; i < vtd.nplanes; i++) { + tcv_zoom(handle, vtd.planes[i], vtd.tmpplanes[i], + ptr->v_width / vtd.width_div[i], + ptr->v_height / vtd.height_div[i], vtd.Bpp, + vob->zoom_width / vtd.width_div[i], + vob->zoom_height / vtd.height_div[i], + vob->zoom_filter); + } + swap_buffers(&vtd); + } else { + PROCESS_FRAME(tcv_zoom, &vtd, vob->zoom_width / vtd.width_div[i], + vob->zoom_height / vtd.height_div[i], + vob->zoom_filter); + } + } + + /**** -Y: clip frame (export) ****/ + + if (ex_clip) { + preadjust_frame_size(&vtd, + ptr->v_width - vob->ex_clip_left-vob->ex_clip_right, + ptr->v_height - vob->ex_clip_top - vob->ex_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->ex_clip_left / vtd.width_div[i], + vob->ex_clip_right / vtd.width_div[i], + vob->ex_clip_top / vtd.height_div[i], + vob->ex_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /**** -r: rescale video frame ****/ + + if (rescale) { + preadjust_frame_size(&vtd, ptr->v_width / vob->reduce_w, + ptr->v_height / vob->reduce_h); + PROCESS_FRAME(tcv_reduce, &vtd, vob->reduce_w, vob->reduce_h); + } + + /**** -z: flip frame vertically ****/ + + if (flip) { + PROCESS_FRAME(tcv_flip_v, &vtd); + } + + /**** -l: flip flame horizontally (mirror) ****/ + + if (mirror) { + PROCESS_FRAME(tcv_flip_h, &vtd); + } + + /**** -k: red/blue swap ****/ + + if (rgbswap) { + if (ptr->v_codec == CODEC_RGB) { + int i; + for (i = 0; i < ptr->v_width * ptr->v_height; i++) { + uint8_t tmp = vtd.planes[0][i*3]; + vtd.planes[0][i*3] = vtd.planes[0][i*3+2]; + vtd.planes[0][i*3+2] = tmp; + } + } else { + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + ac_memcpy(vtd.tmpplanes[1], vtd.planes[1], UVsize); /* tmp<-U */ + ac_memcpy(vtd.planes[1], vtd.planes[2], UVsize); /* U<-V */ + ac_memcpy(vtd.planes[2], vtd.tmpplanes[1], UVsize); /* V<-tmp */ + } + } + + /**** -K: grayscale ****/ + + if (decolor) { + if (ptr->v_codec == CODEC_RGB) { + /* Convert to 8-bit grayscale, then back to RGB24. Just + * averaging the values won't give us the right intensity. */ + tcv_convert(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, IMG_RGB24, IMG_GRAY8); + tcv_convert(handle, vtd.tmpplanes[0], vtd.planes[0], + ptr->v_width, ptr->v_height, IMG_GRAY8, IMG_RGB24); + } else { + /* YUV is easy: just set U and V to 128 */ + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + memset(vtd.planes[1], 128, UVsize); + memset(vtd.planes[2], 128, UVsize); + } + } + + /**** -G: gamma correction ****/ + + if (dgamma) { + /* Only process the first plane (Y) for YUV; for RGB it's all in + * one plane anyway */ + tcv_gamma_correct(handle, ptr->video_buf, ptr->video_buf, + ptr->v_width, ptr->v_height, vtd.Bpp, vob->gamma); + } + + /**** -C: antialiasing ****/ + + if (vob->antialias) { + /* Only Y is antialiased; U and V remain the same */ + tcv_antialias(handle, vtd.planes[0], vtd.tmpplanes[0], + ptr->v_width, ptr->v_height, vtd.Bpp, + vob->aa_weight, vob->aa_bias); + if (ptr->v_codec != CODEC_RGB) { + int UVsize = (ptr->v_width / vtd.width_div[1]) + * (ptr->v_height / vtd.height_div[1]) * vtd.Bpp; + ac_memcpy(vtd.tmpplanes[1], vtd.planes[1], UVsize); + ac_memcpy(vtd.tmpplanes[2], vtd.planes[2], UVsize); + } + swap_buffers(&vtd); + } + + /**** End of processing ****/ + + return 0; +} + +/*************************************************************************/ +/*************************** Exported routines ***************************/ +/*************************************************************************/ + +/** + * process_vid_frame: Main video frame processing routine. The image is + * passed in ptr->video_buf; this can be updated as needed, e.g. to point + * to the secondary buffer after transformations. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +int process_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Check for pass-through mode or skipped frames */ + if (vob->pass_flag & TC_VIDEO) + return 0; + if (ptr->attributes & TC_FRAME_IS_SKIPPED) + return 0; + + /* It's a valid frame, check the colorspace for validity and process it */ + if (vob->im_v_codec == CODEC_RGB + || vob->im_v_codec == CODEC_YUV + || vob->im_v_codec == CODEC_YUV422 + ) { + ptr->v_codec = vob->im_v_codec; + return do_process_frame(vob, ptr); + } + + /* Invalid colorspace, bail out */ + tc_error("Oops, invalid colorspace video frame data"); + return -1; +} + +/*************************************************************************/ + +/** + * preprocess_vid_frame: Frame preprocessing routine. Checks for frame + * out of -c range and performs early clipping. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +int preprocess_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Allocate tcvideo handle if necessary */ + if (!handle) { + handle = tcv_init(); + if (!handle) { + tc_log_error(PACKAGE, "video_trans.c: tcv_init() failed!"); + return -1; + } + } + + /* Check for pass-through mode */ + if (vob->pass_flag & TC_VIDEO) + return 0; + + /* Check frame colorspace */ + if (vob->im_v_codec != CODEC_RGB + && vob->im_v_codec != CODEC_YUV + && vob->im_v_codec != CODEC_YUV422 + ) { + tc_error("Oops, invalid colorspace video frame data"); + return -1; + } + + /* Perform early clipping */ + if (pre_im_clip) { + video_trans_data_t vtd; + ptr->v_codec = vob->im_v_codec; + set_vtd(&vtd, ptr); + preadjust_frame_size(&vtd, + ptr->v_width - vob->pre_im_clip_left - vob->pre_im_clip_right, + ptr->v_height - vob->pre_im_clip_top - vob->pre_im_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->pre_im_clip_left / vtd.width_div[i], + vob->pre_im_clip_right / vtd.width_div[i], + vob->pre_im_clip_top / vtd.height_div[i], + vob->pre_im_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /* Finished with preprocessing */ + return 0; +} + +/*************************************************************************/ + +/** + * postprocess_vid_frame: Frame postprocessing routine. Performs final + * clipping and sanity checks. + * + * Parameters: + * vob: Global data pointer. + * ptr: Pointer to video frame buffer. + * Return value: + * 0 on success, -1 on failure. + */ + +/* Frame postprocessing routine. Performs final clipping and sanity + * checks. Parameters are as for process_vid_frame(). + */ + +int postprocess_vid_frame(vob_t *vob, vframe_list_t *ptr) +{ + /* Check parameter validity */ + if (!vob || !ptr) + return -1; + + /* Check for pass-through mode or skipped frames */ + if (vob->pass_flag & TC_VIDEO) + return 0; + if (ptr->attributes & TC_FRAME_IS_SKIPPED) + return 0; + + /* Check frame colorspace */ + if (vob->im_v_codec != CODEC_RGB + && vob->im_v_codec != CODEC_YUV + && vob->im_v_codec != CODEC_YUV422 + ) { + tc_error("Oops, invalid colorspace video frame data"); + return -1; + } + + /* Perform final clipping, if this isn't a cloned frame */ + if (post_ex_clip && !(ptr->attributes & TC_FRAME_WAS_CLONED)) { + video_trans_data_t vtd; + ptr->v_codec = vob->im_v_codec; + set_vtd(&vtd, ptr); + preadjust_frame_size(&vtd, + ptr->v_width - vob->post_ex_clip_left - vob->post_ex_clip_right, + ptr->v_height - vob->post_ex_clip_top - vob->post_ex_clip_bottom); + PROCESS_FRAME(tcv_clip, &vtd, + vob->post_ex_clip_left / vtd.width_div[i], + vob->post_ex_clip_right / vtd.width_div[i], + vob->post_ex_clip_top / vtd.height_div[i], + vob->post_ex_clip_bottom / vtd.height_div[i], + vtd.black_pixel[i]); + } + + /* Sanity check: make sure the frame size is what we're expecting */ + if (ptr->v_width != vob->ex_v_width || ptr->v_height != vob->ex_v_height) { + tc_log_msg(__FILE__, "width %d %d | height %d %d", + ptr->v_width, vob->ex_v_width, + ptr->v_height, vob->ex_v_height); + tc_error("Oops, frame parameter mismatch detected"); + } + + /* Finished with postprocessing */ + return 0; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ diff --git a/debian/transcode/transcode-1.1.7/src/video_trans.h b/debian/transcode/transcode-1.1.7/src/video_trans.h new file mode 100644 index 00000000..45880879 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/video_trans.h @@ -0,0 +1,37 @@ +/* + * video_trans.h - header for video frame transformation routines + * Written by Andrew Church <achurch@achurch.org> + * Based on code written by Thomas Oestreich. + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifndef _VIDEO_TRANS_H +#define _VIDEO_TRANS_H + +#include "transcode.h" + +/*************************************************************************/ + +/* Video frame processing functions. */ + +int process_vid_frame(vob_t *vob, vframe_list_t *ptr); +int preprocess_vid_frame(vob_t *vob, vframe_list_t *ptr); +int postprocess_vid_frame(vob_t *vob, vframe_list_t *ptr); + +/*************************************************************************/ + +#endif /* _VIDEO_TRANS_H */ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
