Originally committed as revision 17769 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.6
| @@ -18,15 +18,6 @@ Applications | |||
| ffmpeg: | |||
| ffmpeg.c Michael Niedermayer | |||
| Video Hooks: | |||
| vhook | |||
| vhook/watermark.c Marcus Engene | |||
| vhook/ppm.c | |||
| vhook/drawtext.c | |||
| vhook/fish.c | |||
| vhook/null.c | |||
| vhook/imlib2.c | |||
| ffplay: | |||
| ffplay.c Michael Niedermayer | |||
| @@ -32,10 +32,8 @@ FF_LDFLAGS := $(FFLDFLAGS) | |||
| FF_EXTRALIBS := $(FFEXTRALIBS) | |||
| FF_DEP_LIBS := $(DEP_LIBS) | |||
| ALL_TARGETS-$(CONFIG_VHOOK) += videohook | |||
| ALL_TARGETS-$(BUILD_DOC) += documentation | |||
| INSTALL_TARGETS-$(CONFIG_VHOOK) += install-vhook | |||
| ifneq ($(PROGS),) | |||
| INSTALL_TARGETS-yes += install-progs install-data | |||
| INSTALL_TARGETS-$(BUILD_DOC) += install-man | |||
| @@ -81,41 +79,8 @@ cmdutils.o cmdutils.d: version.h | |||
| alltools: $(addsuffix $(EXESUF),$(addprefix tools/, cws2fws pktdumper qt-faststart trasher)) | |||
| VHOOKCFLAGS += $(filter-out -mdynamic-no-pic,$(CFLAGS)) | |||
| BASEHOOKS = fish null watermark | |||
| ALLHOOKS = $(BASEHOOKS) drawtext imlib2 ppm | |||
| ALLHOOKS_SRCS = $(addprefix vhook/, $(addsuffix .c, $(ALLHOOKS))) | |||
| HOOKS-$(HAVE_FORK) += ppm | |||
| HOOKS-$(HAVE_IMLIB2) += imlib2 | |||
| HOOKS-$(HAVE_FREETYPE2) += drawtext | |||
| HOOKS = $(addprefix vhook/, $(addsuffix $(SLIBSUF), $(BASEHOOKS) $(HOOKS-yes))) | |||
| VHOOKCFLAGS-$(HAVE_IMLIB2) += `imlib2-config --cflags` | |||
| LIBS_imlib2$(SLIBSUF) = `imlib2-config --libs` | |||
| VHOOKCFLAGS-$(HAVE_FREETYPE2) += `freetype-config --cflags` | |||
| LIBS_drawtext$(SLIBSUF) = `freetype-config --libs` | |||
| VHOOKCFLAGS += $(VHOOKCFLAGS-yes) | |||
| vhook/%.o vhook/%.d: CFLAGS:=$(VHOOKCFLAGS) | |||
| # vhooks compile fine without libav*, but need them nonetheless. | |||
| videohook: $(FF_DEP_LIBS) $(HOOKS) | |||
| $(eval VHOOKSHFLAGS=$(VHOOKSHFLAGS)) | |||
| vhook/%$(SLIBSUF): vhook/%.o | |||
| $(CC) $(LDFLAGS) -o $@ $(VHOOKSHFLAGS) $< $(VHOOKLIBS) $(LIBS_$(@F)) | |||
| VHOOK_DEPS = $(HOOKS:$(SLIBSUF)=.d) | |||
| depend dep: $(VHOOK_DEPS) | |||
| documentation: $(addprefix doc/, ffmpeg-doc.html faq.html ffserver-doc.html \ | |||
| ffplay-doc.html general.html hooks.html \ | |||
| $(ALLMANPAGES)) | |||
| ffplay-doc.html general.html $(ALLMANPAGES)) | |||
| doc/%.html: doc/%.texi | |||
| texi2html -monolithic -number $< | |||
| @@ -141,11 +106,7 @@ install-man: $(MANPAGES) | |||
| install -d "$(MANDIR)/man1" | |||
| install -m 644 $(MANPAGES) "$(MANDIR)/man1" | |||
| install-vhook: videohook | |||
| install -d "$(SHLIBDIR)/vhook" | |||
| install -m 755 $(HOOKS) "$(SHLIBDIR)/vhook" | |||
| uninstall: uninstall-progs uninstall-data uninstall-man uninstall-vhook | |||
| uninstall: uninstall-progs uninstall-data uninstall-man | |||
| uninstall-progs: | |||
| rm -f $(addprefix "$(BINDIR)/", $(ALLPROGS)) | |||
| @@ -156,10 +117,6 @@ uninstall-data: | |||
| uninstall-man: | |||
| rm -f $(addprefix "$(MANDIR)/man1/",$(ALLMANPAGES)) | |||
| uninstall-vhook: | |||
| rm -f $(addprefix "$(SHLIBDIR)/",$(ALLHOOKS_SRCS:.c=$(SLIBSUF))) | |||
| -rmdir "$(SHLIBDIR)/vhook/" | |||
| testclean: | |||
| rm -rf tests/vsynth1 tests/vsynth2 tests/data tests/asynth1.sw tests/*~ | |||
| @@ -168,10 +125,9 @@ clean:: testclean | |||
| rm -f doc/*.html doc/*.pod doc/*.1 | |||
| rm -f $(addprefix tests/,$(addsuffix $(EXESUF),audiogen videogen rotozoom seek_test tiny_psnr)) | |||
| rm -f $(addprefix tools/,$(addsuffix $(EXESUF),cws2fws pktdumper qt-faststart trasher)) | |||
| rm -f vhook/*.o vhook/*~ vhook/*.so vhook/*.dylib vhook/*.dll | |||
| distclean:: | |||
| rm -f version.h config.* vhook/*.d | |||
| rm -f version.h config.* | |||
| # regression tests | |||
| @@ -355,6 +311,4 @@ tests/seek_test$(EXESUF): tests/seek_test.c $(FF_DEP_LIBS) | |||
| $(CC) $(FF_LDFLAGS) $(CFLAGS) -o $@ $< $(FF_EXTRALIBS) | |||
| .PHONY: lib videohook documentation *test regtest-* swscale-error zlib-error alltools check | |||
| -include $(VHOOK_DEPS) | |||
| .PHONY: lib documentation *test regtest-* swscale-error zlib-error alltools check | |||
| @@ -82,9 +82,8 @@ show_help(){ | |||
| echo " --disable-ffserver disable ffserver build" | |||
| echo " --enable-postproc enable GPLed postprocessing support [no]" | |||
| echo " --enable-swscale enable GPLed software scaler support [no]" | |||
| echo " --enable-avfilter video filter support (replaces vhook) [no]" | |||
| echo " --enable-avfilter video filter support [no]" | |||
| echo " --enable-avfilter-lavf video filters dependent on avformat [no]" | |||
| echo " --disable-vhook disable video hooking support" | |||
| echo " --enable-beosthreads use BeOS threads [no]" | |||
| echo " --enable-os2threads use OS/2 threads [no]" | |||
| echo " --enable-pthreads use pthreads [no]" | |||
| @@ -799,7 +798,6 @@ CONFIG_LIST=" | |||
| static | |||
| swscale | |||
| vdpau | |||
| vhook | |||
| x11grab | |||
| zlib | |||
| " | |||
| @@ -873,11 +871,9 @@ HAVE_LIST=" | |||
| fast_cmov | |||
| fast_unaligned | |||
| fork | |||
| freetype2 | |||
| gethrtime | |||
| GetProcessTimes | |||
| getrusage | |||
| imlib2 | |||
| inet_aton | |||
| inline_asm | |||
| libdc1394_1 | |||
| @@ -1133,7 +1129,6 @@ movie_filter_deps="avfilter_lavf" | |||
| ffplay_deps="sdl" | |||
| ffserver_deps="ffm_muxer rtp_protocol rtsp_demuxer" | |||
| ffserver_extralibs='$ldl' | |||
| vhook_extralibs='$ldl' | |||
| # default parameters | |||
| @@ -1180,12 +1175,10 @@ enable optimizations | |||
| enable protocols | |||
| enable static | |||
| enable stripping | |||
| vhook="default" | |||
| # build settings | |||
| add_cflags -D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112 | |||
| SHFLAGS='-shared -Wl,-soname,$$(@F)' | |||
| VHOOKSHFLAGS='$(SHFLAGS)' | |||
| FFSERVERLDFLAGS=-Wl,-E | |||
| LIBPREF="lib" | |||
| LIBSUF=".a" | |||
| @@ -1560,7 +1553,6 @@ case $target_os in | |||
| darwin) | |||
| disable need_memalign | |||
| SHFLAGS='-dynamiclib -Wl,-single_module -Wl,-install_name,$(SHLIBDIR)/$(SLIBNAME),-current_version,$(LIBVERSION),-compatibility_version,$(LIBMAJOR) -Wl,-read_only_relocs,suppress' | |||
| VHOOKSHFLAGS='-dynamiclib -Wl,-single_module -flat_namespace -undefined suppress -Wl,-install_name,$(SHLIBDIR)/vhook/$$(@F)' | |||
| strip="strip -x" | |||
| FFLDFLAGS="-Wl,-dynamic,-search_paths_first" | |||
| SLIBSUF=".dylib" | |||
| @@ -1578,12 +1570,6 @@ case $target_os in | |||
| LIBTARGET=x64 | |||
| fi | |||
| shlibdir_default="$bindir_default" | |||
| VHOOKSHFLAGS='-shared -L$(BUILD_ROOT)/libavformat -L$(BUILD_ROOT)/libavcodec -L$(BUILD_ROOT)/libavutil' | |||
| VHOOKLIBS='-lavformat$(BUILDSUF) -lavcodec$(BUILDSUF) -lavutil$(BUILDSUF) $(EXTRALIBS)' | |||
| if enabled swscale; then | |||
| VHOOKSHFLAGS="$VHOOKSHFLAGS -L\$(BUILD_ROOT)/libswscale" | |||
| VHOOKLIBS="$VHOOKLIBS -lswscale\$(BUILDSUF)" | |||
| fi | |||
| disable ffserver | |||
| SLIBPREF="" | |||
| SLIBSUF=".dll" | |||
| @@ -1607,12 +1593,6 @@ case $target_os in | |||
| cygwin*) | |||
| target_os=cygwin | |||
| shlibdir_default="$bindir_default" | |||
| VHOOKSHFLAGS='-shared -L$(BUILD_ROOT)/libavformat -L$(BUILD_ROOT)/libavcodec -L$(BUILD_ROOT)/libavutil' | |||
| VHOOKLIBS='-lavformat$(BUILDSUF) -lavcodec$(BUILDSUF) -lavutil$(BUILDSUF) $(EXTRALIBS)' | |||
| if enabled swscale; then | |||
| VHOOKSHFLAGS="$VHOOKSHFLAGS -L\$(BUILD_ROOT)/libswscale" | |||
| VHOOKLIBS="$VHOOKLIBS -lswscale\$(BUILDSUF)" | |||
| fi | |||
| SLIBPREF="cyg" | |||
| SLIBSUF=".dll" | |||
| SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' | |||
| @@ -1622,7 +1602,7 @@ case $target_os in | |||
| enable dos_paths | |||
| ;; | |||
| *-dos|freedos|opendos) | |||
| disable ffplay ffserver vhook | |||
| disable ffplay ffserver | |||
| disable $INDEV_LIST $OUTDEV_LIST | |||
| network_extralibs="-lsocket" | |||
| objformat="coff" | |||
| @@ -1656,12 +1636,8 @@ case $target_os in | |||
| emximp -o $(SUBDIR)$(LIBPREF)$(NAME)_dll.lib $(SUBDIR)$(NAME).def;' | |||
| SLIB_INSTALL_EXTRA_CMD='install -m 644 $(SUBDIR)$(LIBPREF)$(NAME)_dll.a $(SUBDIR)$(LIBPREF)$(NAME)_dll.lib "$(LIBDIR)"' | |||
| SLIB_UNINSTALL_EXTRA_CMD='rm -f "$(LIBDIR)"/$(LIBPREF)$(NAME)_dll.a "$(LIBDIR)"/$(LIBPREF)$(NAME)_dll.lib' | |||
| disable vhook | |||
| enable dos_paths | |||
| ;; | |||
| interix) | |||
| disable vhook | |||
| ;; | |||
| *) | |||
| die "Unknown OS '$target_os'." | |||
| @@ -2040,24 +2016,6 @@ void foo(char * $restrict_keyword p); | |||
| EOF | |||
| done | |||
| test "$vhook" = "default" && vhook="$dlopen" | |||
| if test "$target_os" = cygwin -o "$target_os" = mingw32 && enabled_all static vhook ; then | |||
| disable vhook | |||
| echo | |||
| echo "At the moment vhooks don't work on Cygwin or MinGW static builds." | |||
| echo "Patches welcome." | |||
| echo | |||
| fi | |||
| if enabled vhook; then | |||
| check_ldflags -rdynamic | |||
| check_ldflags -export-dynamic | |||
| fi | |||
| check_foo_config imlib2 imlib2 Imlib2.h imlib_load_font | |||
| check_foo_config freetype2 freetype ft2build.h FT_Init_FreeType | |||
| ########################################## | |||
| # SDL check | |||
| @@ -2225,8 +2183,6 @@ if enabled gprof; then | |||
| add_ldflags -p | |||
| fi | |||
| VHOOKCFLAGS="-fPIC" | |||
| # Find out if the .align argument is a power of two or not. | |||
| check_asm asmalign_pot '".align 3"' | |||
| @@ -2312,11 +2268,6 @@ echo "postprocessing support ${postproc-no}" | |||
| echo "software scaler enabled ${swscale-no}" | |||
| echo "new filter support ${avfilter-no}" | |||
| echo "filters using lavformat ${avfilter_lavf-no}" | |||
| echo "video hooking ${vhook-no}" | |||
| if enabled vhook; then | |||
| echo "Imlib2 support ${imlib2-no}" | |||
| echo "FreeType support ${freetype2-no}" | |||
| fi | |||
| echo "network support ${network-no}" | |||
| if enabled network; then | |||
| echo "IPv6 support ${ipv6-no}" | |||
| @@ -2395,13 +2346,10 @@ enabled stripping && | |||
| echo "STRIP=echo ignoring strip" >> config.mak | |||
| echo "OPTFLAGS=$CFLAGS" >> config.mak | |||
| echo "VHOOKCFLAGS=$VHOOKCFLAGS" >> config.mak | |||
| echo "LDFLAGS=$LDFLAGS" >> config.mak | |||
| echo "FFSERVERLDFLAGS=$FFSERVERLDFLAGS" >> config.mak | |||
| echo "SHFLAGS=$SHFLAGS" >> config.mak | |||
| echo "YASMFLAGS=$YASMFLAGS" >> config.mak | |||
| echo "VHOOKSHFLAGS=$VHOOKSHFLAGS" >> config.mak | |||
| echo "VHOOKLIBS=$VHOOKLIBS" >> config.mak | |||
| echo "LIBOBJFLAGS=$LIBOBJFLAGS" >> config.mak | |||
| echo "BUILD_STATIC=$static" >> config.mak | |||
| echo "BUILDSUF=$build_suffix" >> config.mak | |||
| @@ -2529,7 +2477,6 @@ if enabled source_path_used; then | |||
| libswscale \ | |||
| tests \ | |||
| tools \ | |||
| vhook \ | |||
| " | |||
| FILES="\ | |||
| Makefile \ | |||
| @@ -660,9 +660,6 @@ Calculate PSNR of compressed frames. | |||
| Dump video coding statistics to @file{vstats_HHMMSS.log}. | |||
| @item -vstats_file @var{file} | |||
| Dump video coding statistics to @var{file}. | |||
| @item -vhook @var{module} | |||
| Insert video processing @var{module}. @var{module} contains the module | |||
| name and its parameters separated by spaces. | |||
| @item -top @var{n} | |||
| top=1/bottom=0/auto=-1 field first | |||
| @item -dc @var{precision} | |||
| @@ -691,10 +691,6 @@ Notes: | |||
| @itemize | |||
| @item In order to compile vhooks, you must have a POSIX-compliant libdl in | |||
| your MinGW system. Get dlfcn-win32 from | |||
| @url{http://code.google.com/p/dlfcn-win32}. | |||
| @item In order to compile FFplay, you must have the MinGW development library | |||
| of SDL. Get it from @url{http://www.libsdl.org}. | |||
| Edit the @file{bin/sdl-config} script so that it points to the correct prefix | |||
| @@ -1,299 +0,0 @@ | |||
| \input texinfo @c -*- texinfo -*- | |||
| @settitle Video Hook Documentation | |||
| @titlepage | |||
| @sp 7 | |||
| @center @titlefont{Video Hook Documentation} | |||
| @sp 3 | |||
| @end titlepage | |||
| @chapter Introduction | |||
| @var{Please be aware that vhook is deprecated, and hence its development is | |||
| frozen (bug fixes are still accepted). | |||
| The substitute will be 'libavfilter', the result of our 'Video Filter API' | |||
| Google Summer of Code project. You may monitor its progress by subscribing to | |||
| the ffmpeg-soc mailing list at | |||
| @url{http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc}.} | |||
| The video hook functionality is designed (mostly) for live video. It allows | |||
| the video to be modified or examined between the decoder and the encoder. | |||
| Any number of hook modules can be placed inline, and they are run in the | |||
| order that they were specified on the ffmpeg command line. | |||
| The video hook modules are provided for use as a base for your own modules, | |||
| and are described below. | |||
| Modules are loaded using the -vhook option to ffmpeg. The value of this parameter | |||
| is a space separated list of arguments. The first is the module name, and the rest | |||
| are passed as arguments to the Configure function of the module. | |||
| The modules are dynamic libraries: They have different suffixes (.so, .dll, .dylib) | |||
| depending on your platform. And your platform dictates if they need to be | |||
| somewhere in your PATH, or in your LD_LIBRARY_PATH. Otherwise you will need to | |||
| specify the full path of the vhook file that you are using. | |||
| @section null.c | |||
| This does nothing. Actually it converts the input image to RGB24 and then converts | |||
| it back again. This is meant as a sample that you can use to test your setup. | |||
| @section fish.c | |||
| This implements a 'fish detector'. Essentially it converts the image into HSV | |||
| space and tests whether more than a certain percentage of the pixels fall into | |||
| a specific HSV cuboid. If so, then the image is saved into a file for processing | |||
| by other bits of code. | |||
| Why use HSV? It turns out that HSV cuboids represent a more compact range of | |||
| colors than would an RGB cuboid. | |||
| @section imlib2.c | |||
| This module implements a text overlay for a video image. Currently it | |||
| supports a fixed overlay or reading the text from a file. The string | |||
| is passed through strftime() so that it is easy to imprint the date and | |||
| time onto the image. | |||
| This module depends on the external library imlib2, available on | |||
| Sourceforge, among other places, if it is not already installed on | |||
| your system. | |||
| You may also overlay an image (even semi-transparent) like TV stations do. | |||
| You may move either the text or the image around your video to create | |||
| scrolling credits, for example. | |||
| The font file used is looked for in a FONTPATH environment variable, and | |||
| prepended to the point size as a command line option and can be specified | |||
| with the full path to the font file, as in: | |||
| @example | |||
| -F /usr/X11R6/lib/X11/fonts/TTF/VeraBd.ttf/20 | |||
| @end example | |||
| where 20 is the point size. | |||
| You can specify the filename to read RGB color names from. If it is not | |||
| specified, these defaults are used: @file{/usr/share/X11/rgb.txt} and | |||
| @file{/usr/lib/X11/rgb.txt} | |||
| Options: | |||
| @multitable @columnfractions .2 .8 | |||
| @item @option{-C <rgb.txt>} @tab The filename to read RGB color names from | |||
| @item @option{-c <color>} @tab The color of the text | |||
| @item @option{-F <fontname>} @tab The font face and size | |||
| @item @option{-t <text>} @tab The text | |||
| @item @option{-f <filename>} @tab The filename to read text from | |||
| @item @option{-x <expression>}@tab x coordinate of text or image | |||
| @item @option{-y <expression>}@tab y coordinate of text or image | |||
| @item @option{-i <filename>} @tab The filename to read a image from | |||
| @item @option{-R <expression>}@tab Value for R color | |||
| @item @option{-G <expression>}@tab Value for G color | |||
| @item @option{-B <expression>}@tab Value for B color | |||
| @item @option{-A <expression>}@tab Value for Alpha channel | |||
| @end multitable | |||
| Expressions are functions of these variables: | |||
| @multitable @columnfractions .2 .8 | |||
| @item @var{N} @tab frame number (starting at zero) | |||
| @item @var{H} @tab frame height | |||
| @item @var{W} @tab frame width | |||
| @item @var{h} @tab image height | |||
| @item @var{w} @tab image width | |||
| @item @var{X} @tab previous x coordinate of text or image | |||
| @item @var{Y} @tab previous y coordinate of text or image | |||
| @end multitable | |||
| You may also use the constants @var{PI}, @var{E}, and the math functions available at the | |||
| FFmpeg formula evaluator at (@url{ffmpeg-doc.html#SEC13}), except @var{bits2qp(bits)} | |||
| and @var{qp2bits(qp)}. | |||
| Usage examples: | |||
| @example | |||
| # Remember to set the path to your fonts | |||
| FONTPATH="/cygdrive/c/WINDOWS/Fonts/" | |||
| FONTPATH="$FONTPATH:/usr/share/imlib2/data/fonts/" | |||
| FONTPATH="$FONTPATH:/usr/X11R6/lib/X11/fonts/TTF/" | |||
| export FONTPATH | |||
| # Bulb dancing in a Lissajous pattern | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.dll -x W*(0.5+0.25*sin(N/47*PI))-w/2 -y H*(0.5+0.50*cos(N/97*PI))-h/2 -i /usr/share/imlib2/data/images/bulb.png' \ | |||
| -acodec copy -sameq output.avi | |||
| # Text scrolling | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.dll -c red -F Vera.ttf/20 -x 150+0.5*N -y 70+0.25*N -t Hello' \ | |||
| -acodec copy -sameq output.avi | |||
| # Date and time stamp, security-camera style: | |||
| ffmpeg -r 29.97 -s 320x256 -f video4linux -i /dev/video0 \ | |||
| -vhook 'vhook/imlib2.so -x 0 -y 0 -i black-260x20.png' \ | |||
| -vhook 'vhook/imlib2.so -c white -F VeraBd.ttf/12 -x 0 -y 0 -t %A-%D-%T' \ | |||
| output.avi | |||
| In this example the video is captured from the first video capture card as a | |||
| 320x256 AVI, and a black 260 by 20 pixel PNG image is placed in the upper | |||
| left corner, with the day, date and time overlaid on it in Vera Bold 12 | |||
| point font. A simple black PNG file 260 pixels wide and 20 pixels tall | |||
| was created in the GIMP for this purpose. | |||
| # Scrolling credits from a text file | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.so -c white -F VeraBd.ttf/16 -x 100 -y -1.0*N -f credits.txt' \ | |||
| -sameq output.avi | |||
| In this example, the text is stored in a file, and is positioned 100 | |||
| pixels from the left hand edge of the video. The text is scrolled from the | |||
| bottom up. Making the y factor positive will scroll from the top down. | |||
| Increasing the magnitude of the y factor makes the text scroll faster, | |||
| decreasing it makes it scroll slower. Hint: Blank lines containing only | |||
| a newline are treated as end-of-file. To create blank lines, use lines | |||
| that consist of space characters only. | |||
| # Scrolling credits with custom color from a text file | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.so -C rgb.txt -c CustomColor1 -F VeraBd.ttf/16 -x 100 -y -1.0*N -f credits.txt' \ | |||
| -sameq output.avi | |||
| This example does the same as the one above, but specifies an rgb.txt file | |||
| to be used, which has a custom-made color in it. | |||
| # Variable colors | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.so -t Hello -R abs(255*sin(N/47*PI)) -G abs(255*sin(N/47*PI)) -B abs(255*sin(N/47*PI))' \ | |||
| -sameq output.avi | |||
| In this example, the color for the text goes up and down from black to | |||
| white. | |||
| # Text fade-out | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.so -t Hello -A max(0,255-exp(N/47))' \ | |||
| -sameq output.avi | |||
| In this example, the text fades out in about 10 seconds for a 25 fps input | |||
| video file. | |||
| # scrolling credits from a graphics file | |||
| ffmpeg -sameq -i input.avi \ | |||
| -vhook 'vhook/imlib2.so -x 0 -y -1.0*N -i credits.png' output.avi | |||
| In this example, a transparent PNG file the same width as the video | |||
| (e.g. 320 pixels), but very long, (e.g. 3000 pixels), was created, and | |||
| text, graphics, brushstrokes, etc, were added to the image. The image | |||
| is then scrolled up, from the bottom of the frame. | |||
| @end example | |||
| @section ppm.c | |||
| It's basically a launch point for a PPM pipe, so you can use any | |||
| executable (or script) which consumes a PPM on stdin and produces a PPM | |||
| on stdout (and flushes each frame). The Netpbm utilities are a series of | |||
| such programs. | |||
| A list of them is here: | |||
| @url{http://netpbm.sourceforge.net/doc/directory.html} | |||
| Usage example: | |||
| @example | |||
| ffmpeg -i input -vhook "/path/to/ppm.so some-ppm-filter args" output | |||
| @end example | |||
| @section drawtext.c | |||
| This module implements a text overlay for a video image. Currently it | |||
| supports a fixed overlay or reading the text from a file. The string | |||
| is passed through strftime() so that it is easy to imprint the date and | |||
| time onto the image. | |||
| Features: | |||
| @itemize @minus | |||
| @item TrueType, Type1 and others via the FreeType2 library | |||
| @item Font kerning (better output) | |||
| @item Line Wrap (put the text that doesn't fit one line on the next line) | |||
| @item Background box (currently in development) | |||
| @item Outline | |||
| @end itemize | |||
| Options: | |||
| @multitable @columnfractions .2 .8 | |||
| @item @option{-c <color>} @tab Foreground color of the text ('internet' way) <#RRGGBB> [default #FFFFFF] | |||
| @item @option{-C <color>} @tab Background color of the text ('internet' way) <#RRGGBB> [default #000000] | |||
| @item @option{-f <font-filename>} @tab font file to use | |||
| @item @option{-t <text>} @tab text to display | |||
| @item @option{-T <filename>} @tab file to read text from | |||
| @item @option{-x <pos>} @tab x coordinate of the start of the text | |||
| @item @option{-y <pos>} @tab y coordinate of the start of the text | |||
| @end multitable | |||
| Text fonts are being looked for in a FONTPATH environment variable. | |||
| If the FONTPATH environment variable is not available, or is not checked by | |||
| your target (i.e. Cygwin), then specify the full path to the font file as in: | |||
| @example | |||
| -f /usr/X11R6/lib/X11/fonts/TTF/VeraBd.ttf | |||
| @end example | |||
| Usage Example: | |||
| @example | |||
| # Remember to set the path to your fonts | |||
| FONTPATH="/cygdrive/c/WINDOWS/Fonts/" | |||
| FONTPATH="$FONTPATH:/usr/share/imlib2/data/fonts/" | |||
| FONTPATH="$FONTPATH:/usr/X11R6/lib/X11/fonts/TTF/" | |||
| export FONTPATH | |||
| # Time and date display | |||
| ffmpeg -f video4linux2 -i /dev/video0 \ | |||
| -vhook 'vhook/drawtext.so -f VeraBd.ttf -t %A-%D-%T' movie.mpg | |||
| This example grabs video from the first capture card and outputs it to an | |||
| MPEG video, and places "Weekday-dd/mm/yy-hh:mm:ss" at the top left of the | |||
| frame, updated every second, using the Vera Bold TrueType Font, which | |||
| should exist in: /usr/X11R6/lib/X11/fonts/TTF/ | |||
| @end example | |||
| Check the man page for strftime() for all the various ways you can format | |||
| the date and time. | |||
| @section watermark.c | |||
| Command Line options: | |||
| @multitable @columnfractions .2 .8 | |||
| @item @option{-m [0|1]} @tab Mode (default: 0, see below) | |||
| @item @option{-t 000000 - FFFFFF} @tab Threshold, six digit hex number | |||
| @item @option{-f <filename>} @tab Watermark image filename, must be specified! | |||
| @end multitable | |||
| MODE 0: | |||
| The watermark picture works like this (assuming color intensities 0..0xFF): | |||
| Per color do this: | |||
| If mask color is 0x80, no change to the original frame. | |||
| If mask color is < 0x80 the absolute difference is subtracted from the | |||
| frame. If result < 0, result = 0. | |||
| If mask color is > 0x80 the absolute difference is added to the | |||
| frame. If result > 0xFF, result = 0xFF. | |||
| You can override the 0x80 level with the -t flag. E.g. if threshold is | |||
| 000000 the color value of watermark is added to the destination. | |||
| This way a mask that is visible both in light and dark pictures can be made | |||
| (e.g. by using a picture generated by the Gimp and the bump map tool). | |||
| An example watermark file is at: | |||
| @url{http://engene.se/ffmpeg_watermark.gif} | |||
| MODE 1: | |||
| Per color do this: | |||
| If mask color > threshold color then the watermark pixel is used. | |||
| Example usage: | |||
| @example | |||
| ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif' -an out.mov | |||
| ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif -m 1 -t 222222' -an out.mov | |||
| @end example | |||
| @bye | |||
| @@ -34,7 +34,6 @@ | |||
| #include "libavformat/avformat.h" | |||
| #include "libavdevice/avdevice.h" | |||
| #include "libswscale/swscale.h" | |||
| #include "libavformat/framehook.h" | |||
| #include "libavcodec/opt.h" | |||
| #include "libavcodec/audioconvert.h" | |||
| #include "libavutil/fifo.h" | |||
| @@ -203,7 +202,6 @@ static int audio_volume = 256; | |||
| static int exit_on_error = 0; | |||
| static int using_stdin = 0; | |||
| static int using_vhook = 0; | |||
| static int verbose = 1; | |||
| static int thread_count= 1; | |||
| static int q_pressed = 0; | |||
| @@ -736,7 +734,7 @@ static void pre_process_video_frame(AVInputStream *ist, AVPicture *picture, void | |||
| dec = ist->st->codec; | |||
| /* deinterlace : must be done before any resize */ | |||
| if (do_deinterlace || using_vhook) { | |||
| if (do_deinterlace) { | |||
| int size; | |||
| /* create temporary picture */ | |||
| @@ -764,10 +762,6 @@ static void pre_process_video_frame(AVInputStream *ist, AVPicture *picture, void | |||
| picture2 = picture; | |||
| } | |||
| if (CONFIG_VHOOK) | |||
| frame_hook_process(picture2, dec->pix_fmt, dec->width, dec->height, | |||
| 1000000 * ist->pts / AV_TIME_BASE); | |||
| if (picture != picture2) | |||
| *picture = *picture2; | |||
| *bufp = buf; | |||
| @@ -1772,10 +1766,6 @@ static int av_encode(AVFormatContext **output_files, | |||
| codec->block_align= 0; | |||
| break; | |||
| case CODEC_TYPE_VIDEO: | |||
| if(using_vhook) { | |||
| fprintf(stderr,"-vcodec copy and -vhook are incompatible (frames are not decoded)\n"); | |||
| av_exit(1); | |||
| } | |||
| codec->pix_fmt = icodec->pix_fmt; | |||
| codec->width = icodec->width; | |||
| codec->height = icodec->height; | |||
| @@ -2651,29 +2641,6 @@ static void opt_video_tag(const char *arg) | |||
| video_codec_tag= arg[0] + (arg[1]<<8) + (arg[2]<<16) + (arg[3]<<24); | |||
| } | |||
| #if CONFIG_VHOOK | |||
| static void add_frame_hooker(const char *arg) | |||
| { | |||
| int argc = 0; | |||
| char *argv[64]; | |||
| int i; | |||
| char *args = av_strdup(arg); | |||
| using_vhook = 1; | |||
| argv[0] = strtok(args, " "); | |||
| while (argc < 62 && (argv[++argc] = strtok(NULL, " "))) { | |||
| } | |||
| i = frame_hook_add(argc, argv); | |||
| if (i != 0) { | |||
| fprintf(stderr, "Failed to add video hook function: %s\n", arg); | |||
| av_exit(1); | |||
| } | |||
| } | |||
| #endif | |||
| static void opt_video_codec(const char *arg) | |||
| { | |||
| opt_codec(&video_stream_copy, &video_codec_name, CODEC_TYPE_VIDEO, arg); | |||
| @@ -3840,9 +3807,6 @@ static const OptionDef options[] = { | |||
| { "psnr", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, {(void*)&do_psnr}, "calculate PSNR of compressed frames" }, | |||
| { "vstats", OPT_EXPERT | OPT_VIDEO, {(void*)&opt_vstats}, "dump video coding statistics to file" }, | |||
| { "vstats_file", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_vstats_file}, "dump video coding statistics to file", "file" }, | |||
| #if CONFIG_VHOOK | |||
| { "vhook", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)add_frame_hooker}, "insert video processing module", "module" }, | |||
| #endif | |||
| { "intra_matrix", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_intra_matrix}, "specify intra matrix coeffs", "matrix" }, | |||
| { "inter_matrix", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_inter_matrix}, "specify inter matrix coeffs", "matrix" }, | |||
| { "top", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_top_field_first}, "top=1/bottom=0/auto=-1 field first", "" }, | |||
| @@ -228,8 +228,6 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o | |||
| OBJS-$(CONFIG_LIBNUT_DEMUXER) += libnut.o riff.o | |||
| OBJS-$(CONFIG_LIBNUT_MUXER) += libnut.o riff.o | |||
| OBJS-$(CONFIG_VHOOK) += framehook.o | |||
| # protocols I/O | |||
| OBJS+= avio.o aviobuf.o | |||
| @@ -1,115 +0,0 @@ | |||
| /* | |||
| * Video processing hooks | |||
| * Copyright (c) 2000, 2001 Fabrice Bellard | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <errno.h> | |||
| #include "config.h" | |||
| #include "avformat.h" | |||
| #include "framehook.h" | |||
| #if HAVE_DLFCN_H | |||
| #include <dlfcn.h> | |||
| #endif | |||
| typedef struct FrameHookEntry { | |||
| struct FrameHookEntry *next; | |||
| FrameHookConfigureFn Configure; | |||
| FrameHookProcessFn Process; | |||
| FrameHookReleaseFn Release; | |||
| void *ctx; | |||
| } FrameHookEntry; | |||
| static FrameHookEntry *first_hook; | |||
| /* Returns 0 on OK */ | |||
| int frame_hook_add(int argc, char *argv[]) | |||
| { | |||
| void *loaded; | |||
| FrameHookEntry *fhe, **fhep; | |||
| if (argc < 1) { | |||
| return ENOENT; | |||
| } | |||
| loaded = dlopen(argv[0], RTLD_NOW); | |||
| if (!loaded) { | |||
| av_log(NULL, AV_LOG_ERROR, "%s\n", dlerror()); | |||
| return -1; | |||
| } | |||
| fhe = av_mallocz(sizeof(*fhe)); | |||
| if (!fhe) { | |||
| return AVERROR(ENOMEM); | |||
| } | |||
| fhe->Configure = dlsym(loaded, "Configure"); | |||
| fhe->Process = dlsym(loaded, "Process"); | |||
| fhe->Release = dlsym(loaded, "Release"); /* Optional */ | |||
| if (!fhe->Process) { | |||
| av_log(NULL, AV_LOG_ERROR, "Failed to find Process entrypoint in %s\n", argv[0]); | |||
| return AVERROR(ENOENT); | |||
| } | |||
| if (!fhe->Configure && argc > 1) { | |||
| av_log(NULL, AV_LOG_ERROR, "Failed to find Configure entrypoint in %s\n", argv[0]); | |||
| return AVERROR(ENOENT); | |||
| } | |||
| if (argc > 1 || fhe->Configure) { | |||
| if (fhe->Configure(&fhe->ctx, argc, argv)) { | |||
| av_log(NULL, AV_LOG_ERROR, "Failed to Configure %s\n", argv[0]); | |||
| return AVERROR(EINVAL); | |||
| } | |||
| } | |||
| for (fhep = &first_hook; *fhep; fhep = &((*fhep)->next)) { | |||
| } | |||
| *fhep = fhe; | |||
| return 0; | |||
| } | |||
| void frame_hook_process(AVPicture *pict, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| if (first_hook) { | |||
| FrameHookEntry *fhe; | |||
| for (fhe = first_hook; fhe; fhe = fhe->next) { | |||
| fhe->Process(fhe->ctx, pict, pix_fmt, width, height, pts); | |||
| } | |||
| } | |||
| } | |||
| void frame_hook_release(void) | |||
| { | |||
| FrameHookEntry *fhe; | |||
| FrameHookEntry *fhenext; | |||
| for (fhe = first_hook; fhe; fhe = fhenext) { | |||
| fhenext = fhe->next; | |||
| if (fhe->Release) | |||
| fhe->Release(fhe->ctx); | |||
| av_free(fhe); | |||
| } | |||
| first_hook = NULL; | |||
| } | |||
| @@ -1,52 +0,0 @@ | |||
| /* | |||
| * video processing hooks | |||
| * copyright (c) 2000, 2001 Fabrice Bellard | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #ifndef AVFORMAT_FRAMEHOOK_H | |||
| #define AVFORMAT_FRAMEHOOK_H | |||
| #warning VHOOK is deprecated. Please help finishing libavfilter instead of wasting your time writing new filters for this crappy filter system. | |||
| /* | |||
| * Prototypes for interface to .so that implement a video processing hook | |||
| */ | |||
| #include "libavcodec/avcodec.h" | |||
| /* Function must be called 'Configure' */ | |||
| typedef int (FrameHookConfigure)(void **ctxp, int argc, char *argv[]); | |||
| typedef FrameHookConfigure *FrameHookConfigureFn; | |||
| extern FrameHookConfigure Configure; | |||
| /* Function must be called 'Process' */ | |||
| typedef void (FrameHookProcess)(void *ctx, struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height, int64_t pts); | |||
| typedef FrameHookProcess *FrameHookProcessFn; | |||
| extern FrameHookProcess Process; | |||
| /* Function must be called 'Release' */ | |||
| typedef void (FrameHookRelease)(void *ctx); | |||
| typedef FrameHookRelease *FrameHookReleaseFn; | |||
| extern FrameHookRelease Release; | |||
| int frame_hook_add(int argc, char *argv[]); | |||
| void frame_hook_process(struct AVPicture *pict, enum PixelFormat pix_fmt, int width, int height, int64_t pts); | |||
| void frame_hook_release(void); | |||
| #endif /* AVFORMAT_FRAMEHOOK_H */ | |||
| @@ -1,531 +0,0 @@ | |||
| /* | |||
| * drawtext.c: print text over the screen | |||
| ****************************************************************************** | |||
| * Options: | |||
| * -f <filename> font filename (MANDATORY!!!) | |||
| * -s <pixel_size> font size in pixels [default 16] | |||
| * -b print background | |||
| * -o outline glyphs (use the bg color) | |||
| * -x <pos> x position ( >= 0) [default 0] | |||
| * -y <pos> y position ( >= 0) [default 0] | |||
| * -t <text> text to print (will be passed to strftime()) | |||
| * MANDATORY: will be used even when -T is used. | |||
| * in this case, -t will be used if some error | |||
| * occurs | |||
| * -T <filename> file with the text (re-read every frame) | |||
| * -c <#RRGGBB> foreground color ('internet' way) [default #ffffff] | |||
| * -C <#RRGGBB> background color ('internet' way) [default #000000] | |||
| * | |||
| ****************************************************************************** | |||
| * Features: | |||
| * - True Type, Type1 and others via FreeType2 library | |||
| * - Font kerning (better output) | |||
| * - Line Wrap (if the text doesn't fit, the next char go to the next line) | |||
| * - Background box | |||
| * - Outline | |||
| ****************************************************************************** | |||
| * Author: Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br> | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #define MAXSIZE_TEXT 1024 | |||
| #include "libavformat/framehook.h" | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <fcntl.h> | |||
| #include <stdarg.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #undef time | |||
| #include <sys/time.h> | |||
| #include <time.h> | |||
| #include <ft2build.h> | |||
| #include FT_FREETYPE_H | |||
| #include FT_GLYPH_H | |||
| #define SCALEBITS 10 | |||
| #define ONE_HALF (1 << (SCALEBITS - 1)) | |||
| #define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5)) | |||
| #define RGB_TO_YUV(rgb_color, yuv_color) do { \ | |||
| yuv_color[0] = (FIX(0.29900) * rgb_color[0] + FIX(0.58700) * rgb_color[1] + FIX(0.11400) * rgb_color[2] + ONE_HALF) >> SCALEBITS; \ | |||
| yuv_color[2] = ((FIX(0.50000) * rgb_color[0] - FIX(0.41869) * rgb_color[1] - FIX(0.08131) * rgb_color[2] + ONE_HALF - 1) >> SCALEBITS) + 128; \ | |||
| yuv_color[1] = ((- FIX(0.16874) * rgb_color[0] - FIX(0.33126) * rgb_color[1] + FIX(0.50000) * rgb_color[2] + ONE_HALF - 1) >> SCALEBITS) + 128; \ | |||
| } while (0) | |||
| #define COPY_3(dst,src) { \ | |||
| dst[0]=src[0]; \ | |||
| dst[1]=src[1]; \ | |||
| dst[2]=src[2]; \ | |||
| } | |||
| #define SET_PIXEL(picture, yuv_color, x, y) { \ | |||
| picture->data[0][ (x) + (y)*picture->linesize[0] ] = yuv_color[0]; \ | |||
| picture->data[1][ ((x/2) + (y/2)*picture->linesize[1]) ] = yuv_color[1]; \ | |||
| picture->data[2][ ((x/2) + (y/2)*picture->linesize[2]) ] = yuv_color[2]; \ | |||
| } | |||
| #define GET_PIXEL(picture, yuv_color, x, y) { \ | |||
| yuv_color[0] = picture->data[0][ (x) + (y)*picture->linesize[0] ]; \ | |||
| yuv_color[1] = picture->data[1][ (x/2) + (y/2)*picture->linesize[1] ]; \ | |||
| yuv_color[2] = picture->data[2][ (x/2) + (y/2)*picture->linesize[2] ]; \ | |||
| } | |||
| typedef struct { | |||
| unsigned char *text; | |||
| char *file; | |||
| unsigned int x; | |||
| unsigned int y; | |||
| int bg; | |||
| int outline; | |||
| unsigned char bgcolor[3]; /* YUV */ | |||
| unsigned char fgcolor[3]; /* YUV */ | |||
| FT_Library library; | |||
| FT_Face face; | |||
| FT_Glyph glyphs[ 255 ]; | |||
| FT_Bitmap bitmaps[ 255 ]; | |||
| int advance[ 255 ]; | |||
| int bitmap_left[ 255 ]; | |||
| int bitmap_top[ 255 ]; | |||
| unsigned int glyphs_index[ 255 ]; | |||
| int text_height; | |||
| int baseline; | |||
| int use_kerning; | |||
| } ContextInfo; | |||
| void Release(void *ctx) | |||
| { | |||
| if (ctx) | |||
| av_free(ctx); | |||
| } | |||
| static int ParseColor(char *text, unsigned char yuv_color[3]) | |||
| { | |||
| char tmp[3]; | |||
| unsigned char rgb_color[3]; | |||
| int i; | |||
| tmp[2] = '\0'; | |||
| if ((!text) || (strlen(text) != 7) || (text[0] != '#') ) | |||
| return -1; | |||
| for (i=0; i < 3; i++) | |||
| { | |||
| tmp[0] = text[i*2+1]; | |||
| tmp[1] = text[i*2+2]; | |||
| rgb_color[i] = strtol(tmp, NULL, 16); | |||
| } | |||
| RGB_TO_YUV(rgb_color, yuv_color); | |||
| return 0; | |||
| } | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| int c; | |||
| int error; | |||
| ContextInfo *ci=NULL; | |||
| char *font=NULL; | |||
| unsigned int size=16; | |||
| FT_BBox bbox; | |||
| int yMax, yMin; | |||
| *ctxp = av_mallocz(sizeof(ContextInfo)); | |||
| ci = (ContextInfo *) *ctxp; | |||
| /* configure Context Info */ | |||
| ci->text = NULL; | |||
| ci->file = NULL; | |||
| ci->x = ci->y = 0; | |||
| ci->fgcolor[0]=255; | |||
| ci->fgcolor[1]=128; | |||
| ci->fgcolor[2]=128; | |||
| ci->bgcolor[0]=0; | |||
| ci->fgcolor[1]=128; | |||
| ci->fgcolor[2]=128; | |||
| ci->bg = 0; | |||
| ci->outline = 0; | |||
| ci->text_height = 0; | |||
| optind = 1; | |||
| while ((c = getopt(argc, argv, "f:t:T:x:y:s:c:C:bo")) > 0) { | |||
| switch (c) { | |||
| case 'f': | |||
| font = optarg; | |||
| break; | |||
| case 't': | |||
| ci->text = av_strdup(optarg); | |||
| break; | |||
| case 'T': | |||
| ci->file = av_strdup(optarg); | |||
| break; | |||
| case 'x': | |||
| ci->x = (unsigned int) atoi(optarg); | |||
| break; | |||
| case 'y': | |||
| ci->y = (unsigned int) atoi(optarg); | |||
| break; | |||
| case 's': | |||
| size = (unsigned int) atoi(optarg); | |||
| break; | |||
| case 'c': | |||
| if (ParseColor(optarg, ci->fgcolor) == -1) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "Invalid foreground color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -c #ffffff (for white foreground)\n", optarg); | |||
| return -1; | |||
| } | |||
| break; | |||
| case 'C': | |||
| if (ParseColor(optarg, ci->bgcolor) == -1) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "Invalid background color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -C #ffffff (for white background)\n", optarg); | |||
| return -1; | |||
| } | |||
| break; | |||
| case 'b': | |||
| ci->bg=1; | |||
| break; | |||
| case 'o': | |||
| ci->outline=1; | |||
| break; | |||
| case '?': | |||
| av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]); | |||
| return -1; | |||
| } | |||
| } | |||
| if (!ci->text) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "No text provided (-t text)\n"); | |||
| return -1; | |||
| } | |||
| if (ci->file) | |||
| { | |||
| FILE *fp; | |||
| if ((fp=fopen(ci->file, "r")) == NULL) | |||
| { | |||
| av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be opened. Using text provided with -t switch: %s", strerror(errno)); | |||
| } | |||
| else | |||
| { | |||
| fclose(fp); | |||
| } | |||
| } | |||
| if (!font) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "No font file provided! (-f filename)\n"); | |||
| return -1; | |||
| } | |||
| if ((error = FT_Init_FreeType(&(ci->library))) != 0) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "Could not load FreeType (error# %d).\n", error); | |||
| return -1; | |||
| } | |||
| if ((error = FT_New_Face( ci->library, font, 0, &(ci->face) )) != 0) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "Could not load face: %s (error# %d).\n", font, error); | |||
| return -1; | |||
| } | |||
| if ((error = FT_Set_Pixel_Sizes( ci->face, 0, size)) != 0) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "Could not set font size to %d pixels (error# %d).\n", size, error); | |||
| return -1; | |||
| } | |||
| ci->use_kerning = FT_HAS_KERNING(ci->face); | |||
| /* load and cache glyphs */ | |||
| yMax = -32000; | |||
| yMin = 32000; | |||
| for (c=0; c < 256; c++) | |||
| { | |||
| /* Load char */ | |||
| error = FT_Load_Char( ci->face, (unsigned char) c, FT_LOAD_RENDER | FT_LOAD_MONOCHROME ); | |||
| if (error) continue; /* ignore errors */ | |||
| /* Save bitmap */ | |||
| ci->bitmaps[c] = ci->face->glyph->bitmap; | |||
| /* Save bitmap left */ | |||
| ci->bitmap_left[c] = ci->face->glyph->bitmap_left; | |||
| /* Save bitmap top */ | |||
| ci->bitmap_top[c] = ci->face->glyph->bitmap_top; | |||
| /* Save advance */ | |||
| ci->advance[c] = ci->face->glyph->advance.x >> 6; | |||
| /* Save glyph */ | |||
| error = FT_Get_Glyph( ci->face->glyph, &(ci->glyphs[c]) ); | |||
| /* Save glyph index */ | |||
| ci->glyphs_index[c] = FT_Get_Char_Index( ci->face, (unsigned char) c ); | |||
| /* Measure text height to calculate text_height (or the maximum text height) */ | |||
| FT_Glyph_Get_CBox( ci->glyphs[ c ], ft_glyph_bbox_pixels, &bbox ); | |||
| if (bbox.yMax > yMax) | |||
| yMax = bbox.yMax; | |||
| if (bbox.yMin < yMin) | |||
| yMin = bbox.yMin; | |||
| } | |||
| ci->text_height = yMax - yMin; | |||
| ci->baseline = yMax; | |||
| return 0; | |||
| } | |||
| static inline void draw_glyph(AVPicture *picture, FT_Bitmap *bitmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned char yuv_fgcolor[3], unsigned char yuv_bgcolor[3], int outline) | |||
| { | |||
| int r, c; | |||
| int spixel, dpixel[3], in_glyph=0; | |||
| if (bitmap->pixel_mode == ft_pixel_mode_mono) | |||
| { | |||
| in_glyph = 0; | |||
| for (r=0; (r < bitmap->rows) && (r+y < height); r++) | |||
| { | |||
| for (c=0; (c < bitmap->width) && (c+x < width); c++) | |||
| { | |||
| /* pixel in the picture (destination) */ | |||
| GET_PIXEL(picture, dpixel, (c+x), (y+r)); | |||
| /* pixel in the glyph bitmap (source) */ | |||
| spixel = bitmap->buffer[r*bitmap->pitch +c/8] & (0x80>>(c%8)); | |||
| if (spixel) | |||
| COPY_3(dpixel, yuv_fgcolor); | |||
| if (outline) | |||
| { | |||
| /* border detection: */ | |||
| if ( (!in_glyph) && (spixel) ) | |||
| /* left border detected */ | |||
| { | |||
| in_glyph = 1; | |||
| /* draw left pixel border */ | |||
| if (c-1 >= 0) | |||
| SET_PIXEL(picture, yuv_bgcolor, (c+x-1), (y+r)); | |||
| } | |||
| else if ( (in_glyph) && (!spixel) ) | |||
| /* right border detected */ | |||
| { | |||
| in_glyph = 0; | |||
| /* 'draw' right pixel border */ | |||
| COPY_3(dpixel, yuv_bgcolor); | |||
| } | |||
| if (in_glyph) | |||
| /* see if we have a top/bottom border */ | |||
| { | |||
| /* top */ | |||
| if ( (r-1 >= 0) && (! bitmap->buffer[(r-1)*bitmap->pitch +c/8] & (0x80>>(c%8))) ) | |||
| /* we have a top border */ | |||
| SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r-1)); | |||
| /* bottom */ | |||
| if ( (r+1 < height) && (! bitmap->buffer[(r+1)*bitmap->pitch +c/8] & (0x80>>(c%8))) ) | |||
| /* we have a bottom border */ | |||
| SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r+1)); | |||
| } | |||
| } | |||
| SET_PIXEL(picture, dpixel, (c+x), (y+r)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| static inline void draw_box(AVPicture *picture, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned char yuv_color[3]) | |||
| { | |||
| int i, j; | |||
| for (j = 0; (j < height); j++) | |||
| for (i = 0; (i < width); i++) | |||
| { | |||
| SET_PIXEL(picture, yuv_color, (i+x), (y+j)); | |||
| } | |||
| } | |||
| void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| FT_Face face = ci->face; | |||
| FT_GlyphSlot slot = face->glyph; | |||
| unsigned char *text = ci->text; | |||
| unsigned char c; | |||
| int x = 0, y = 0, i=0, size=0; | |||
| unsigned char buff[MAXSIZE_TEXT]; | |||
| unsigned char tbuff[MAXSIZE_TEXT]; | |||
| time_t now = time(0); | |||
| int str_w, str_w_max; | |||
| FT_Vector pos[MAXSIZE_TEXT]; | |||
| FT_Vector delta; | |||
| if (ci->file) | |||
| { | |||
| int fd = open(ci->file, O_RDONLY); | |||
| if (fd < 0) | |||
| { | |||
| text = ci->text; | |||
| av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be opened. Using text provided with -t switch: %s", strerror(errno)); | |||
| } | |||
| else | |||
| { | |||
| int l = read(fd, tbuff, sizeof(tbuff) - 1); | |||
| if (l >= 0) | |||
| { | |||
| tbuff[l] = 0; | |||
| text = tbuff; | |||
| } | |||
| else | |||
| { | |||
| text = ci->text; | |||
| av_log(NULL, AV_LOG_INFO, "WARNING: The file could not be read. Using text provided with -t switch: %s", strerror(errno)); | |||
| } | |||
| close(fd); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| text = ci->text; | |||
| } | |||
| strftime(buff, sizeof(buff), text, localtime(&now)); | |||
| text = buff; | |||
| size = strlen(text); | |||
| /* measure string size and save glyphs position*/ | |||
| str_w = str_w_max = 0; | |||
| x = ci->x; | |||
| y = ci->y; | |||
| for (i=0; i < size; i++) | |||
| { | |||
| c = text[i]; | |||
| /* kerning */ | |||
| if ( (ci->use_kerning) && (i > 0) && (ci->glyphs_index[c]) ) | |||
| { | |||
| FT_Get_Kerning( ci->face, | |||
| ci->glyphs_index[ text[i-1] ], | |||
| ci->glyphs_index[c], | |||
| ft_kerning_default, | |||
| &delta ); | |||
| x += delta.x >> 6; | |||
| } | |||
| if (( (x + ci->advance[ c ]) >= width ) || ( c == '\n' )) | |||
| { | |||
| str_w = width - ci->x - 1; | |||
| y += ci->text_height; | |||
| x = ci->x; | |||
| } | |||
| /* save position */ | |||
| pos[i].x = x + ci->bitmap_left[c]; | |||
| pos[i].y = y - ci->bitmap_top[c] + ci->baseline; | |||
| x += ci->advance[c]; | |||
| if (str_w > str_w_max) | |||
| str_w_max = str_w; | |||
| } | |||
| if (ci->bg) | |||
| { | |||
| /* Check if it doesn't pass the limits */ | |||
| if ( str_w_max + ci->x >= width ) | |||
| str_w_max = width - ci->x - 1; | |||
| if ( y >= height ) | |||
| y = height - 1 - 2*ci->y; | |||
| /* Draw Background */ | |||
| draw_box( picture, ci->x, ci->y, str_w_max, y - ci->y, ci->bgcolor ); | |||
| } | |||
| /* Draw Glyphs */ | |||
| for (i=0; i < size; i++) | |||
| { | |||
| c = text[i]; | |||
| if ( | |||
| ( (c == '_') && (text == ci->text) ) || /* skip '_' (consider as space) | |||
| IF text was specified in cmd line | |||
| (which doesn't like nested quotes) */ | |||
| ( c == '\n' ) /* Skip new line char, just go to new line */ | |||
| ) | |||
| continue; | |||
| /* now, draw to our target surface */ | |||
| draw_glyph( picture, | |||
| &(ci->bitmaps[ c ]), | |||
| pos[i].x, | |||
| pos[i].y, | |||
| width, | |||
| height, | |||
| ci->fgcolor, | |||
| ci->bgcolor, | |||
| ci->outline ); | |||
| /* increment pen position */ | |||
| x += slot->advance.x >> 6; | |||
| } | |||
| } | |||
| @@ -1,382 +0,0 @@ | |||
| /* | |||
| * Fish Detector Hook | |||
| * Copyright (c) 2002 Philip Gladstone | |||
| * | |||
| * This file implements a fish detector. It is used to see when a | |||
| * goldfish passes in front of the camera. It does this by counting | |||
| * the number of input pixels that fall within a particular HSV | |||
| * range. | |||
| * | |||
| * It takes a multitude of arguments: | |||
| * | |||
| * -h <num>-<num> the range of H values that are fish | |||
| * -s <num>-<num> the range of S values that are fish | |||
| * -v <num>-<num> the range of V values that are fish | |||
| * -z zap all non-fish values to black | |||
| * -l <num> limit the number of saved files to <num> | |||
| * -i <num> only check frames every <num> seconds | |||
| * -t <num> the threshold for the amount of fish pixels (range 0-1) | |||
| * -d turn debugging on | |||
| * -D <directory> where to put the fish images | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <stdlib.h> | |||
| #include <fcntl.h> | |||
| #include <unistd.h> | |||
| #include <stdarg.h> | |||
| #include <string.h> | |||
| #include <time.h> | |||
| #include <stdio.h> | |||
| #include <dirent.h> | |||
| #include "libavformat/avformat.h" | |||
| #include "libavformat/framehook.h" | |||
| #include "libavcodec/dsputil.h" | |||
| #include "libswscale/swscale.h" | |||
| #undef fprintf | |||
| static int sws_flags = SWS_BICUBIC; | |||
| #define SCALEBITS 10 | |||
| #define ONE_HALF (1 << (SCALEBITS - 1)) | |||
| #define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5)) | |||
| #define YUV_TO_RGB1_CCIR(cb1, cr1)\ | |||
| {\ | |||
| cb = (cb1) - 128;\ | |||
| cr = (cr1) - 128;\ | |||
| r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;\ | |||
| g_add = - FIX(0.34414*255.0/224.0) * cb - FIX(0.71414*255.0/224.0) * cr + \ | |||
| ONE_HALF;\ | |||
| b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;\ | |||
| } | |||
| #define YUV_TO_RGB2_CCIR(r, g, b, y1)\ | |||
| {\ | |||
| yt = ((y1) - 16) * FIX(255.0/219.0);\ | |||
| r = cm[(yt + r_add) >> SCALEBITS];\ | |||
| g = cm[(yt + g_add) >> SCALEBITS];\ | |||
| b = cm[(yt + b_add) >> SCALEBITS];\ | |||
| } | |||
| typedef struct { | |||
| int h; /* 0 .. 360 */ | |||
| int s; /* 0 .. 255 */ | |||
| int v; /* 0 .. 255 */ | |||
| } HSV; | |||
| typedef struct { | |||
| int zapping; | |||
| int threshold; | |||
| HSV dark, bright; | |||
| char *dir; | |||
| int file_limit; | |||
| int debug; | |||
| int min_interval; | |||
| int64_t next_pts; | |||
| int inset; | |||
| int min_width; | |||
| struct SwsContext *toRGB_convert_ctx; | |||
| } ContextInfo; | |||
| static void dorange(const char *s, int *first, int *second, int maxval) | |||
| { | |||
| sscanf(s, "%d-%d", first, second); | |||
| if (*first > maxval) | |||
| *first = maxval; | |||
| if (*second > maxval) | |||
| *second = maxval; | |||
| } | |||
| void Release(void *ctx) | |||
| { | |||
| ContextInfo *ci; | |||
| ci = (ContextInfo *) ctx; | |||
| if (ctx) { | |||
| sws_freeContext(ci->toRGB_convert_ctx); | |||
| av_free(ctx); | |||
| } | |||
| } | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| ContextInfo *ci; | |||
| int c; | |||
| *ctxp = av_mallocz(sizeof(ContextInfo)); | |||
| ci = (ContextInfo *) *ctxp; | |||
| optind = 1; | |||
| ci->dir = av_strdup("/tmp"); | |||
| ci->threshold = 100; | |||
| ci->file_limit = 100; | |||
| ci->min_interval = 1000000; | |||
| ci->inset = 10; /* Percent */ | |||
| while ((c = getopt(argc, argv, "w:i:dh:s:v:zl:t:D:")) > 0) { | |||
| switch (c) { | |||
| case 'h': | |||
| dorange(optarg, &ci->dark.h, &ci->bright.h, 360); | |||
| break; | |||
| case 's': | |||
| dorange(optarg, &ci->dark.s, &ci->bright.s, 255); | |||
| break; | |||
| case 'v': | |||
| dorange(optarg, &ci->dark.v, &ci->bright.v, 255); | |||
| break; | |||
| case 'z': | |||
| ci->zapping = 1; | |||
| break; | |||
| case 'l': | |||
| ci->file_limit = atoi(optarg); | |||
| break; | |||
| case 'i': | |||
| ci->min_interval = 1000000 * atof(optarg); | |||
| break; | |||
| case 't': | |||
| ci->threshold = atof(optarg) * 1000; | |||
| if (ci->threshold > 1000 || ci->threshold < 0) { | |||
| av_log(NULL, AV_LOG_ERROR, "Invalid threshold value '%s' (range is 0-1)\n", optarg); | |||
| return -1; | |||
| } | |||
| break; | |||
| case 'w': | |||
| ci->min_width = atoi(optarg); | |||
| break; | |||
| case 'd': | |||
| ci->debug++; | |||
| break; | |||
| case 'D': | |||
| ci->dir = av_strdup(optarg); | |||
| break; | |||
| default: | |||
| av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]); | |||
| return -1; | |||
| } | |||
| } | |||
| av_log(NULL, AV_LOG_INFO, "Fish detector configured:\n"); | |||
| av_log(NULL, AV_LOG_INFO, " HSV range: %d,%d,%d - %d,%d,%d\n", | |||
| ci->dark.h, | |||
| ci->dark.s, | |||
| ci->dark.v, | |||
| ci->bright.h, | |||
| ci->bright.s, | |||
| ci->bright.v); | |||
| av_log(NULL, AV_LOG_INFO, " Threshold is %d%% pixels\n", ci->threshold / 10); | |||
| return 0; | |||
| } | |||
| static void get_hsv(HSV *hsv, int r, int g, int b) | |||
| { | |||
| int i, v, x, f; | |||
| x = (r < g) ? r : g; | |||
| if (b < x) | |||
| x = b; | |||
| v = (r > g) ? r : g; | |||
| if (b > v) | |||
| v = b; | |||
| if (v == x) { | |||
| hsv->h = 0; | |||
| hsv->s = 0; | |||
| hsv->v = v; | |||
| return; | |||
| } | |||
| if (r == v) { | |||
| f = g - b; | |||
| i = 0; | |||
| } else if (g == v) { | |||
| f = b - r; | |||
| i = 2 * 60; | |||
| } else { | |||
| f = r - g; | |||
| i = 4 * 60; | |||
| } | |||
| hsv->h = i + (60 * f) / (v - x); | |||
| if (hsv->h < 0) | |||
| hsv->h += 360; | |||
| hsv->s = (255 * (v - x)) / v; | |||
| hsv->v = v; | |||
| return; | |||
| } | |||
| void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; | |||
| int rowsize = picture->linesize[0]; | |||
| #if 0 | |||
| av_log(NULL, AV_LOG_DEBUG, "pix_fmt = %d, width = %d, pts = %lld, ci->next_pts = %lld\n", | |||
| pix_fmt, width, pts, ci->next_pts); | |||
| #endif | |||
| if (pts < ci->next_pts) | |||
| return; | |||
| if (width < ci->min_width) | |||
| return; | |||
| ci->next_pts = pts + 1000000; | |||
| if (pix_fmt == PIX_FMT_YUV420P) { | |||
| uint8_t *y, *u, *v; | |||
| int width2 = width >> 1; | |||
| int inrange = 0; | |||
| int pixcnt; | |||
| int h; | |||
| int h_start, h_end; | |||
| int w_start, w_end; | |||
| h_end = 2 * ((ci->inset * height) / 200); | |||
| h_start = height - h_end; | |||
| w_end = (ci->inset * width2) / 100; | |||
| w_start = width2 - w_end; | |||
| pixcnt = ((h_start - h_end) >> 1) * (w_start - w_end); | |||
| y = picture->data[0] + h_end * picture->linesize[0] + w_end * 2; | |||
| u = picture->data[1] + h_end * picture->linesize[1] / 2 + w_end; | |||
| v = picture->data[2] + h_end * picture->linesize[2] / 2 + w_end; | |||
| for (h = h_start; h > h_end; h -= 2) { | |||
| int w; | |||
| for (w = w_start; w > w_end; w--) { | |||
| unsigned int r,g,b; | |||
| HSV hsv; | |||
| int cb, cr, yt, r_add, g_add, b_add; | |||
| YUV_TO_RGB1_CCIR(u[0], v[0]); | |||
| YUV_TO_RGB2_CCIR(r, g, b, y[0]); | |||
| get_hsv(&hsv, r, g, b); | |||
| if (ci->debug > 1) | |||
| av_log(NULL, AV_LOG_DEBUG, "(%d,%d,%d) -> (%d,%d,%d)\n", | |||
| r,g,b,hsv.h,hsv.s,hsv.v); | |||
| if (hsv.h >= ci->dark.h && hsv.h <= ci->bright.h && | |||
| hsv.s >= ci->dark.s && hsv.s <= ci->bright.s && | |||
| hsv.v >= ci->dark.v && hsv.v <= ci->bright.v) { | |||
| inrange++; | |||
| } else if (ci->zapping) { | |||
| y[0] = y[1] = y[rowsize] = y[rowsize + 1] = 16; | |||
| u[0] = 128; | |||
| v[0] = 128; | |||
| } | |||
| y+= 2; | |||
| u++; | |||
| v++; | |||
| } | |||
| y += picture->linesize[0] * 2 - (w_start - w_end) * 2; | |||
| u += picture->linesize[1] - (w_start - w_end); | |||
| v += picture->linesize[2] - (w_start - w_end); | |||
| } | |||
| if (ci->debug) | |||
| av_log(NULL, AV_LOG_INFO, "Fish: Inrange=%d of %d = %d threshold\n", inrange, pixcnt, 1000 * inrange / pixcnt); | |||
| if (inrange * 1000 / pixcnt >= ci->threshold) { | |||
| /* Save to file */ | |||
| int size; | |||
| char *buf; | |||
| AVPicture picture1; | |||
| static int frame_counter; | |||
| static int foundfile; | |||
| if ((frame_counter++ % 20) == 0) { | |||
| /* Check how many files we have */ | |||
| DIR *d; | |||
| foundfile = 0; | |||
| d = opendir(ci->dir); | |||
| if (d) { | |||
| struct dirent *dent; | |||
| while ((dent = readdir(d))) { | |||
| if (strncmp("fishimg", dent->d_name, 7) == 0) { | |||
| if (strcmp(".ppm", dent->d_name + strlen(dent->d_name) - 4) == 0) { | |||
| foundfile++; | |||
| } | |||
| } | |||
| } | |||
| closedir(d); | |||
| } | |||
| } | |||
| if (foundfile < ci->file_limit) { | |||
| FILE *f; | |||
| char fname[256]; | |||
| size = avpicture_get_size(PIX_FMT_RGB24, width, height); | |||
| buf = av_malloc(size); | |||
| avpicture_fill(&picture1, buf, PIX_FMT_RGB24, width, height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| width, height, pix_fmt, | |||
| width, height, PIX_FMT_RGB24, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, height, | |||
| picture1.data, picture1.linesize); | |||
| /* Write out the PPM file */ | |||
| snprintf(fname, sizeof(fname), "%s/fishimg%ld_%"PRId64".ppm", ci->dir, (long)(av_gettime() / 1000000), pts); | |||
| f = fopen(fname, "w"); | |||
| if (f) { | |||
| fprintf(f, "P6 %d %d 255\n", width, height); | |||
| if (!fwrite(buf, width * height * 3, 1, f)) | |||
| av_log(ctx, AV_LOG_ERROR, "Couldn't write to PPM file %s\n", fname); | |||
| fclose(f); | |||
| } | |||
| av_free(buf); | |||
| ci->next_pts = pts + ci->min_interval; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,493 +0,0 @@ | |||
| /* | |||
| * imlib2 based hook | |||
| * Copyright (c) 2002 Philip Gladstone | |||
| * | |||
| * This module is very much intended as an example of what could be done. | |||
| * | |||
| * One caution is that this is an expensive process -- in particular the | |||
| * conversion of the image into RGB and back is time consuming. For some | |||
| * special cases -- e.g. painting black text -- it would be faster to paint | |||
| * the text into a bitmap and then combine it directly into the YUV | |||
| * image. However, this code is fast enough to handle 10 fps of 320x240 on a | |||
| * 900MHz Duron in maybe 15% of the CPU. | |||
| * See further statistics on Pentium4, 3GHz, FFMpeg is SVN-r6798 | |||
| * Input movie is 20.2 seconds of PAL DV on AVI | |||
| * Output movie is DVD compliant VOB. | |||
| * | |||
| ffmpeg -i input.avi -target pal-dvd out.vob | |||
| # 13.516s just transcode | |||
| ffmpeg -i input.avi -vhook /usr/local/bin/vhook/null.dll -target pal-dvd out.vob | |||
| # 23.546s transcode and img_convert | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.dll -c red -F Vera/20 -x 150-0.5*N -y 70+0.25*N -t Hello_person' \ | |||
| -target pal-dvd out.vob | |||
| # 21.454s transcode, img_convert and move text around | |||
| ffmpeg -i input.avi -vhook \ | |||
| 'vhook/imlib2.dll -x 150-0.5*N -y 70+0.25*N -i /usr/share/imlib2/data/images/bulb.png' \ | |||
| -target pal-dvd out.vob | |||
| # 20.828s transcode, img_convert and move image around | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavformat/framehook.h" | |||
| #include "libswscale/swscale.h" | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <fcntl.h> | |||
| #include <stdarg.h> | |||
| #include <string.h> | |||
| #include <strings.h> | |||
| #include <unistd.h> | |||
| #undef time | |||
| #include <sys/time.h> | |||
| #include <time.h> | |||
| #include <Imlib2.h> | |||
| #include "libavcodec/eval.h" | |||
| const char *const_names[]={ | |||
| "PI", | |||
| "E", | |||
| "N", // frame number (starting at zero) | |||
| "H", // frame height | |||
| "W", // frame width | |||
| "h", // image height | |||
| "w", // image width | |||
| "X", // previous x | |||
| "Y", // previous y | |||
| NULL | |||
| }; | |||
| static int sws_flags = SWS_BICUBIC; | |||
| typedef struct { | |||
| int dummy; | |||
| Imlib_Font fn; | |||
| char *text; | |||
| char *file; | |||
| int r, g, b, a; | |||
| AVEvalExpr *eval_r, *eval_g, *eval_b, *eval_a; | |||
| char *expr_R, *expr_G, *expr_B, *expr_A; | |||
| int eval_colors; | |||
| double x, y; | |||
| char *fileImage; | |||
| struct CachedImage *cache; | |||
| Imlib_Image imageOverlaid; | |||
| AVEvalExpr *eval_x, *eval_y; | |||
| char *expr_x, *expr_y; | |||
| int frame_number; | |||
| int imageOverlaid_width, imageOverlaid_height; | |||
| // This vhook first converts frame to RGB ... | |||
| struct SwsContext *toRGB_convert_ctx; | |||
| // ... and then converts back frame from RGB to initial format | |||
| struct SwsContext *fromRGB_convert_ctx; | |||
| } ContextInfo; | |||
| typedef struct CachedImage { | |||
| struct CachedImage *next; | |||
| Imlib_Image image; | |||
| int width; | |||
| int height; | |||
| } CachedImage; | |||
| void Release(void *ctx) | |||
| { | |||
| ContextInfo *ci; | |||
| ci = (ContextInfo *) ctx; | |||
| if (ci->cache) { | |||
| imlib_context_set_image(ci->cache->image); | |||
| imlib_free_image(); | |||
| av_free(ci->cache); | |||
| } | |||
| if (ctx) { | |||
| if (ci->imageOverlaid) { | |||
| imlib_context_set_image(ci->imageOverlaid); | |||
| imlib_free_image(); | |||
| } | |||
| ff_eval_free(ci->eval_x); | |||
| ff_eval_free(ci->eval_y); | |||
| ff_eval_free(ci->eval_r); | |||
| ff_eval_free(ci->eval_g); | |||
| ff_eval_free(ci->eval_b); | |||
| ff_eval_free(ci->eval_a); | |||
| av_free(ci->expr_x); | |||
| av_free(ci->expr_y); | |||
| av_free(ci->expr_R); | |||
| av_free(ci->expr_G); | |||
| av_free(ci->expr_B); | |||
| av_free(ci->expr_A); | |||
| sws_freeContext(ci->toRGB_convert_ctx); | |||
| sws_freeContext(ci->fromRGB_convert_ctx); | |||
| av_free(ctx); | |||
| } | |||
| } | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| int c; | |||
| ContextInfo *ci; | |||
| char *rgbtxt = 0; | |||
| const char *font = "LucidaSansDemiBold/16"; | |||
| char *fp = getenv("FONTPATH"); | |||
| char *color = 0; | |||
| FILE *f; | |||
| char *p; | |||
| const char *error; | |||
| *ctxp = av_mallocz(sizeof(ContextInfo)); | |||
| ci = (ContextInfo *) *ctxp; | |||
| ci->x = 0.0; | |||
| ci->y = 0.0; | |||
| ci->expr_x = "0.0"; | |||
| ci->expr_y = "0.0"; | |||
| optind = 1; | |||
| /* Use ':' to split FONTPATH */ | |||
| if (fp) | |||
| while ((p = strchr(fp, ':'))) { | |||
| *p = 0; | |||
| imlib_add_path_to_font_path(fp); | |||
| fp = p + 1; | |||
| } | |||
| if ((fp) && (*fp)) | |||
| imlib_add_path_to_font_path(fp); | |||
| while ((c = getopt(argc, argv, "R:G:B:A:C:c:f:F:t:x:y:i:")) > 0) { | |||
| switch (c) { | |||
| case 'R': | |||
| ci->expr_R = av_strdup(optarg); | |||
| ci->eval_colors = 1; | |||
| break; | |||
| case 'G': | |||
| ci->expr_G = av_strdup(optarg); | |||
| ci->eval_colors = 1; | |||
| break; | |||
| case 'B': | |||
| ci->expr_B = av_strdup(optarg); | |||
| ci->eval_colors = 1; | |||
| break; | |||
| case 'A': | |||
| ci->expr_A = av_strdup(optarg); | |||
| break; | |||
| case 'C': | |||
| rgbtxt = optarg; | |||
| break; | |||
| case 'c': | |||
| color = optarg; | |||
| break; | |||
| case 'F': | |||
| font = optarg; | |||
| break; | |||
| case 't': | |||
| ci->text = av_strdup(optarg); | |||
| break; | |||
| case 'f': | |||
| ci->file = av_strdup(optarg); | |||
| break; | |||
| case 'x': | |||
| ci->expr_x = av_strdup(optarg); | |||
| break; | |||
| case 'y': | |||
| ci->expr_y = av_strdup(optarg); | |||
| break; | |||
| case 'i': | |||
| ci->fileImage = av_strdup(optarg); | |||
| break; | |||
| case '?': | |||
| av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]); | |||
| return -1; | |||
| } | |||
| } | |||
| if (ci->eval_colors && !(ci->expr_R && ci->expr_G && ci->expr_B)) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "You must specify expressions for all or no colors.\n"); | |||
| return -1; | |||
| } | |||
| if (ci->text || ci->file) { | |||
| ci->fn = imlib_load_font(font); | |||
| if (!ci->fn) { | |||
| av_log(NULL, AV_LOG_ERROR, "Failed to load font '%s'\n", font); | |||
| return -1; | |||
| } | |||
| imlib_context_set_font(ci->fn); | |||
| imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT); | |||
| } | |||
| if (color) { | |||
| char buff[256]; | |||
| int done = 0; | |||
| if (ci->eval_colors) | |||
| { | |||
| av_log(NULL, AV_LOG_ERROR, "You must not specify both a color name and expressions for the colors.\n"); | |||
| return -1; | |||
| } | |||
| if (rgbtxt) | |||
| f = fopen(rgbtxt, "r"); | |||
| else | |||
| { | |||
| f = fopen("/usr/share/X11/rgb.txt", "r"); | |||
| if (!f) | |||
| f = fopen("/usr/lib/X11/rgb.txt", "r"); | |||
| } | |||
| if (!f) { | |||
| av_log(NULL, AV_LOG_ERROR, "Failed to find RGB color names file\n"); | |||
| return -1; | |||
| } | |||
| while (fgets(buff, sizeof(buff), f)) { | |||
| int r, g, b; | |||
| char colname[80]; | |||
| if (sscanf(buff, "%d %d %d %64s", &r, &g, &b, colname) == 4 && | |||
| strcasecmp(colname, color) == 0) { | |||
| ci->r = r; | |||
| ci->g = g; | |||
| ci->b = b; | |||
| /* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */ | |||
| done = 1; | |||
| break; | |||
| } | |||
| } | |||
| fclose(f); | |||
| if (!done) { | |||
| av_log(NULL, AV_LOG_ERROR, "Unable to find color '%s' in rgb.txt\n", color); | |||
| return -1; | |||
| } | |||
| } else if (ci->eval_colors) { | |||
| if (!(ci->eval_r = ff_parse(ci->expr_R, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse R expression '%s': %s\n", ci->expr_R, error); | |||
| return -1; | |||
| } | |||
| if (!(ci->eval_g = ff_parse(ci->expr_G, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse G expression '%s': %s\n", ci->expr_G, error); | |||
| return -1; | |||
| } | |||
| if (!(ci->eval_b = ff_parse(ci->expr_B, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse B expression '%s': %s\n", ci->expr_B, error); | |||
| return -1; | |||
| } | |||
| } | |||
| if (ci->expr_A) { | |||
| if (!(ci->eval_a = ff_parse(ci->expr_A, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse A expression '%s': %s\n", ci->expr_A, error); | |||
| return -1; | |||
| } | |||
| } else { | |||
| ci->a = 255; | |||
| } | |||
| if (!(ci->eval_colors || ci->eval_a)) | |||
| imlib_context_set_color(ci->r, ci->g, ci->b, ci->a); | |||
| /* load the image (for example, credits for a movie) */ | |||
| if (ci->fileImage) { | |||
| ci->imageOverlaid = imlib_load_image_immediately(ci->fileImage); | |||
| if (!(ci->imageOverlaid)){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't load image '%s'\n", ci->fileImage); | |||
| return -1; | |||
| } | |||
| imlib_context_set_image(ci->imageOverlaid); | |||
| ci->imageOverlaid_width = imlib_image_get_width(); | |||
| ci->imageOverlaid_height = imlib_image_get_height(); | |||
| } | |||
| if (!(ci->eval_x = ff_parse(ci->expr_x, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse x expression '%s': %s\n", ci->expr_x, error); | |||
| return -1; | |||
| } | |||
| if (!(ci->eval_y = ff_parse(ci->expr_y, const_names, NULL, NULL, NULL, NULL, &error))){ | |||
| av_log(NULL, AV_LOG_ERROR, "Couldn't parse y expression '%s': %s\n", ci->expr_y, error); | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| static Imlib_Image get_cached_image(ContextInfo *ci, int width, int height) | |||
| { | |||
| CachedImage *cache; | |||
| for (cache = ci->cache; cache; cache = cache->next) { | |||
| if (width == cache->width && height == cache->height) | |||
| return cache->image; | |||
| } | |||
| return NULL; | |||
| } | |||
| static void put_cached_image(ContextInfo *ci, Imlib_Image image, int width, int height) | |||
| { | |||
| CachedImage *cache = av_mallocz(sizeof(*cache)); | |||
| cache->image = image; | |||
| cache->width = width; | |||
| cache->height = height; | |||
| cache->next = ci->cache; | |||
| ci->cache = cache; | |||
| } | |||
| void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| AVPicture picture1; | |||
| Imlib_Image image; | |||
| DATA32 *data; | |||
| image = get_cached_image(ci, width, height); | |||
| if (!image) { | |||
| image = imlib_create_image(width, height); | |||
| put_cached_image(ci, image, width, height); | |||
| } | |||
| imlib_context_set_image(image); | |||
| data = imlib_image_get_data(); | |||
| avpicture_fill(&picture1, (uint8_t *) data, PIX_FMT_RGB32, width, height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| width, height, pix_fmt, | |||
| width, height, PIX_FMT_RGB32, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, height, | |||
| picture1.data, picture1.linesize); | |||
| imlib_image_set_has_alpha(0); | |||
| { | |||
| int wid, hig, h_a, v_a; | |||
| char buff[1000]; | |||
| char tbuff[1000]; | |||
| const char *tbp = ci->text; | |||
| time_t now = time(0); | |||
| char *p, *q; | |||
| int y; | |||
| double const_values[]={ | |||
| M_PI, | |||
| M_E, | |||
| ci->frame_number, // frame number (starting at zero) | |||
| height, // frame height | |||
| width, // frame width | |||
| ci->imageOverlaid_height, // image height | |||
| ci->imageOverlaid_width, // image width | |||
| ci->x, // previous x | |||
| ci->y, // previous y | |||
| 0 | |||
| }; | |||
| if (ci->file) { | |||
| int fd = open(ci->file, O_RDONLY); | |||
| if (fd < 0) { | |||
| tbp = "[File not found]"; | |||
| } else { | |||
| int l = read(fd, tbuff, sizeof(tbuff) - 1); | |||
| if (l >= 0) { | |||
| tbuff[l] = 0; | |||
| tbp = tbuff; | |||
| } else { | |||
| tbp = "[I/O Error]"; | |||
| } | |||
| close(fd); | |||
| } | |||
| } | |||
| if (tbp) | |||
| strftime(buff, sizeof(buff), tbp, localtime(&now)); | |||
| else if (!(ci->imageOverlaid)) | |||
| strftime(buff, sizeof(buff), "[No data]", localtime(&now)); | |||
| ci->x = ff_parse_eval(ci->eval_x, const_values, ci); | |||
| ci->y = ff_parse_eval(ci->eval_y, const_values, ci); | |||
| y = ci->y; | |||
| if (ci->eval_a) { | |||
| ci->a = ff_parse_eval(ci->eval_a, const_values, ci); | |||
| } | |||
| if (ci->eval_colors) { | |||
| ci->r = ff_parse_eval(ci->eval_r, const_values, ci); | |||
| ci->g = ff_parse_eval(ci->eval_g, const_values, ci); | |||
| ci->b = ff_parse_eval(ci->eval_b, const_values, ci); | |||
| } | |||
| if (ci->eval_colors || ci->eval_a) { | |||
| imlib_context_set_color(ci->r, ci->g, ci->b, ci->a); | |||
| } | |||
| if (!(ci->imageOverlaid)) | |||
| for (p = buff; p; p = q) { | |||
| q = strchr(p, '\n'); | |||
| if (q) | |||
| *q++ = 0; | |||
| imlib_text_draw_with_return_metrics(ci->x, y, p, &wid, &hig, &h_a, &v_a); | |||
| y += v_a; | |||
| } | |||
| if (ci->imageOverlaid) { | |||
| imlib_context_set_image(image); | |||
| imlib_blend_image_onto_image(ci->imageOverlaid, 0, | |||
| 0, 0, ci->imageOverlaid_width, ci->imageOverlaid_height, | |||
| ci->x, ci->y, ci->imageOverlaid_width, ci->imageOverlaid_height); | |||
| } | |||
| } | |||
| ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, | |||
| width, height, PIX_FMT_RGB32, | |||
| width, height, pix_fmt, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->fromRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the fromRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->fromRGB_convert_ctx, | |||
| picture1.data, picture1.linesize, 0, height, | |||
| picture->data, picture->linesize); | |||
| ci->frame_number++; | |||
| } | |||
| @@ -1,116 +0,0 @@ | |||
| /* | |||
| * Null Video Hook | |||
| * Copyright (c) 2002 Philip Gladstone | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <stdio.h> | |||
| #include "libavformat/framehook.h" | |||
| #include "libswscale/swscale.h" | |||
| static int sws_flags = SWS_BICUBIC; | |||
| typedef struct { | |||
| int dummy; | |||
| // This vhook first converts frame to RGB ... | |||
| struct SwsContext *toRGB_convert_ctx; | |||
| // ... and later converts back frame from RGB to initial format | |||
| struct SwsContext *fromRGB_convert_ctx; | |||
| } ContextInfo; | |||
| void Release(void *ctx) | |||
| { | |||
| ContextInfo *ci; | |||
| ci = (ContextInfo *) ctx; | |||
| if (ctx) { | |||
| sws_freeContext(ci->toRGB_convert_ctx); | |||
| sws_freeContext(ci->fromRGB_convert_ctx); | |||
| av_free(ctx); | |||
| } | |||
| } | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| av_log(NULL, AV_LOG_DEBUG, "Called with argc=%d\n", argc); | |||
| *ctxp = av_mallocz(sizeof(ContextInfo)); | |||
| return 0; | |||
| } | |||
| void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| char *buf = 0; | |||
| AVPicture picture1; | |||
| AVPicture *pict = picture; | |||
| (void) ci; | |||
| if (pix_fmt != PIX_FMT_RGB24) { | |||
| int size; | |||
| size = avpicture_get_size(PIX_FMT_RGB24, width, height); | |||
| buf = av_malloc(size); | |||
| avpicture_fill(&picture1, buf, PIX_FMT_RGB24, width, height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| width, height, pix_fmt, | |||
| width, height, PIX_FMT_RGB24, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, height, | |||
| picture1.data, picture1.linesize); | |||
| pict = &picture1; | |||
| } | |||
| /* Insert filter code here */ | |||
| if (pix_fmt != PIX_FMT_RGB24) { | |||
| ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, | |||
| width, height, PIX_FMT_RGB24, | |||
| width, height, pix_fmt, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->fromRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the fromRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->fromRGB_convert_ctx, | |||
| picture1.data, picture1.linesize, 0, height, | |||
| picture->data, picture->linesize); | |||
| } | |||
| av_free(buf); | |||
| } | |||
| @@ -1,374 +0,0 @@ | |||
| /* | |||
| * PPM Video Hook | |||
| * Copyright (c) 2003 Charles Yates | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <stdio.h> | |||
| #include <unistd.h> | |||
| #include <fcntl.h> | |||
| #include <sys/types.h> | |||
| #include <sys/wait.h> | |||
| #include <ctype.h> | |||
| #include "libavutil/avstring.h" | |||
| #include "libavformat/framehook.h" | |||
| #include "libavformat/avformat.h" | |||
| #include "libswscale/swscale.h" | |||
| #undef fprintf | |||
| static int sws_flags = SWS_BICUBIC; | |||
| /** Bi-directional pipe structure. | |||
| */ | |||
| typedef struct rwpipe | |||
| { | |||
| int pid; | |||
| FILE *reader; | |||
| FILE *writer; | |||
| } | |||
| rwpipe; | |||
| /** Create a bidirectional pipe for the given command. | |||
| */ | |||
| static rwpipe *rwpipe_open( int argc, char *argv[] ) | |||
| { | |||
| rwpipe *this = av_mallocz( sizeof( rwpipe ) ); | |||
| if ( this != NULL ) | |||
| { | |||
| int input[ 2 ]; | |||
| int output[ 2 ]; | |||
| if (!pipe( input )) | |||
| return NULL; | |||
| if (!pipe( output )) | |||
| return NULL; | |||
| this->pid = fork(); | |||
| if ( this->pid == 0 ) | |||
| { | |||
| #define COMMAND_SIZE 10240 | |||
| char *command = av_mallocz( COMMAND_SIZE ); | |||
| int i; | |||
| strcpy( command, "" ); | |||
| for ( i = 0; i < argc; i ++ ) | |||
| { | |||
| av_strlcat( command, argv[ i ], COMMAND_SIZE ); | |||
| av_strlcat( command, " ", COMMAND_SIZE ); | |||
| } | |||
| dup2( output[ 0 ], STDIN_FILENO ); | |||
| dup2( input[ 1 ], STDOUT_FILENO ); | |||
| close( input[ 0 ] ); | |||
| close( input[ 1 ] ); | |||
| close( output[ 0 ] ); | |||
| close( output[ 1 ] ); | |||
| execl("/bin/sh", "sh", "-c", command, (char*)NULL ); | |||
| _exit( 255 ); | |||
| } | |||
| else | |||
| { | |||
| close( input[ 1 ] ); | |||
| close( output[ 0 ] ); | |||
| this->reader = fdopen( input[ 0 ], "r" ); | |||
| this->writer = fdopen( output[ 1 ], "w" ); | |||
| } | |||
| } | |||
| return this; | |||
| } | |||
| /** Read data from the pipe. | |||
| */ | |||
| static FILE *rwpipe_reader( rwpipe *this ) | |||
| { | |||
| if ( this != NULL ) | |||
| return this->reader; | |||
| else | |||
| return NULL; | |||
| } | |||
| /** Write data to the pipe. | |||
| */ | |||
| static FILE *rwpipe_writer( rwpipe *this ) | |||
| { | |||
| if ( this != NULL ) | |||
| return this->writer; | |||
| else | |||
| return NULL; | |||
| } | |||
| /* Read a number from the pipe - assumes PNM style headers. | |||
| */ | |||
| static int rwpipe_read_number( rwpipe *rw ) | |||
| { | |||
| int value = 0; | |||
| int c = 0; | |||
| FILE *in = rwpipe_reader( rw ); | |||
| do | |||
| { | |||
| c = fgetc( in ); | |||
| while( c != EOF && !isdigit( c ) && c != '#' ) | |||
| c = fgetc( in ); | |||
| if ( c == '#' ) | |||
| while( c != EOF && c != '\n' ) | |||
| c = fgetc( in ); | |||
| } | |||
| while ( c != EOF && !isdigit( c ) ); | |||
| while( c != EOF && isdigit( c ) ) | |||
| { | |||
| value = value * 10 + ( c - '0' ); | |||
| c = fgetc( in ); | |||
| } | |||
| return value; | |||
| } | |||
| /** Read a PPM P6 header. | |||
| */ | |||
| static int rwpipe_read_ppm_header( rwpipe *rw, int *width, int *height ) | |||
| { | |||
| char line[ 3 ]; | |||
| FILE *in = rwpipe_reader( rw ); | |||
| int max; | |||
| if (!fgets( line, 3, in )) | |||
| return -1; | |||
| if ( !strncmp( line, "P6", 2 ) ) | |||
| { | |||
| *width = rwpipe_read_number( rw ); | |||
| *height = rwpipe_read_number( rw ); | |||
| max = rwpipe_read_number( rw ); | |||
| return max != 255 || *width <= 0 || *height <= 0; | |||
| } | |||
| return 1; | |||
| } | |||
| /** Close the pipe and process. | |||
| */ | |||
| static void rwpipe_close( rwpipe *this ) | |||
| { | |||
| if ( this != NULL ) | |||
| { | |||
| fclose( this->reader ); | |||
| fclose( this->writer ); | |||
| waitpid( this->pid, NULL, 0 ); | |||
| av_free( this ); | |||
| } | |||
| } | |||
| /** Context info for this vhook - stores the pipe and image buffers. | |||
| */ | |||
| typedef struct | |||
| { | |||
| rwpipe *rw; | |||
| int size1; | |||
| char *buf1; | |||
| int size2; | |||
| char *buf2; | |||
| // This vhook first converts frame to RGB ... | |||
| struct SwsContext *toRGB_convert_ctx; | |||
| // ... then processes it via a PPM command pipe ... | |||
| // ... and finally converts back frame from RGB to initial format | |||
| struct SwsContext *fromRGB_convert_ctx; | |||
| } | |||
| ContextInfo; | |||
| /** Initialise the context info for this vhook. | |||
| */ | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| if ( argc > 1 ) | |||
| { | |||
| *ctxp = av_mallocz(sizeof(ContextInfo)); | |||
| if ( *ctxp != NULL && argc > 1 ) | |||
| { | |||
| ContextInfo *info = (ContextInfo *)*ctxp; | |||
| info->rw = rwpipe_open( argc - 1, &argv[ 1 ] ); | |||
| return 0; | |||
| } | |||
| } | |||
| return 1; | |||
| } | |||
| /** Process a frame. | |||
| */ | |||
| void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) | |||
| { | |||
| int err = 0; | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| AVPicture picture1; | |||
| AVPicture picture2; | |||
| AVPicture *pict = picture; | |||
| int out_width; | |||
| int out_height; | |||
| int i; | |||
| uint8_t *ptr = NULL; | |||
| FILE *in = rwpipe_reader( ci->rw ); | |||
| FILE *out = rwpipe_writer( ci->rw ); | |||
| /* Check that we have a pipe to talk to. */ | |||
| if ( in == NULL || out == NULL ) | |||
| err = 1; | |||
| /* Convert to RGB24 if necessary */ | |||
| if ( !err && pix_fmt != PIX_FMT_RGB24 ) | |||
| { | |||
| int size = avpicture_get_size(PIX_FMT_RGB24, width, height); | |||
| if ( size != ci->size1 ) | |||
| { | |||
| av_free( ci->buf1 ); | |||
| ci->buf1 = av_malloc(size); | |||
| ci->size1 = size; | |||
| err = ci->buf1 == NULL; | |||
| } | |||
| if ( !err ) | |||
| { | |||
| avpicture_fill(&picture1, ci->buf1, PIX_FMT_RGB24, width, height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| width, height, pix_fmt, | |||
| width, height, PIX_FMT_RGB24, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, height, | |||
| picture1.data, picture1.linesize); | |||
| pict = &picture1; | |||
| } | |||
| } | |||
| /* Write out the PPM */ | |||
| if ( !err ) | |||
| { | |||
| ptr = pict->data[ 0 ]; | |||
| fprintf( out, "P6\n%d %d\n255\n", width, height ); | |||
| for ( i = 0; !err && i < height; i ++ ) | |||
| { | |||
| err = !fwrite( ptr, width * 3, 1, out ); | |||
| ptr += pict->linesize[ 0 ]; | |||
| } | |||
| if ( !err ) | |||
| err = fflush( out ); | |||
| } | |||
| /* Read the PPM returned. */ | |||
| if ( !err && !rwpipe_read_ppm_header( ci->rw, &out_width, &out_height ) ) | |||
| { | |||
| int size = avpicture_get_size(PIX_FMT_RGB24, out_width, out_height); | |||
| if ( size != ci->size2 ) | |||
| { | |||
| av_free( ci->buf2 ); | |||
| ci->buf2 = av_malloc(size); | |||
| ci->size2 = size; | |||
| err = ci->buf2 == NULL; | |||
| } | |||
| if ( !err ) | |||
| { | |||
| avpicture_fill(&picture2, ci->buf2, PIX_FMT_RGB24, out_width, out_height); | |||
| ptr = picture2.data[ 0 ]; | |||
| for ( i = 0; !err && i < out_height; i ++ ) | |||
| { | |||
| err = !fread( ptr, out_width * 3, 1, in ); | |||
| ptr += picture2.linesize[ 0 ]; | |||
| } | |||
| } | |||
| } | |||
| /* Convert the returned PPM back to the input format */ | |||
| if ( !err ) | |||
| { | |||
| /* The out_width/out_height returned from the PPM | |||
| * filter won't necessarily be the same as width and height | |||
| * but it will be scaled anyway to width/height. | |||
| */ | |||
| av_log(NULL, AV_LOG_DEBUG, | |||
| "PPM vhook: Input dimensions: %d x %d Output dimensions: %d x %d\n", | |||
| width, height, out_width, out_height); | |||
| ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, | |||
| out_width, out_height, PIX_FMT_RGB24, | |||
| width, height, pix_fmt, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->fromRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the fromRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->fromRGB_convert_ctx, | |||
| picture2.data, picture2.linesize, 0, out_height, | |||
| picture->data, picture->linesize); | |||
| } | |||
| } | |||
| /** Clean up the effect. | |||
| */ | |||
| void Release(void *ctx) | |||
| { | |||
| ContextInfo *ci; | |||
| ci = (ContextInfo *) ctx; | |||
| if (ctx) | |||
| { | |||
| rwpipe_close( ci->rw ); | |||
| av_free( ci->buf1 ); | |||
| av_free( ci->buf2 ); | |||
| sws_freeContext(ci->toRGB_convert_ctx); | |||
| sws_freeContext(ci->fromRGB_convert_ctx); | |||
| av_free(ctx); | |||
| } | |||
| } | |||
| @@ -1,655 +0,0 @@ | |||
| /* | |||
| * Watermark Hook | |||
| * Copyright (c) 2005 Marcus Engene myfirstname(at)mylastname.se | |||
| * | |||
| * parameters for watermark: | |||
| * -m nbr = nbr is 0..1. 0 is the default mode, see below. | |||
| * -t nbr = nbr is six digit hex. Threshold. | |||
| * -f file = file is the watermark image filename. You must specify this! | |||
| * | |||
| * MODE 0: | |||
| * The watermark picture works like this (assuming color intensities 0..0xff): | |||
| * Per color do this: | |||
| * If mask color is 0x80, no change to the original frame. | |||
| * If mask color is < 0x80 the abs difference is subtracted from the frame. If | |||
| * result < 0, result = 0 | |||
| * If mask color is > 0x80 the abs difference is added to the frame. If result | |||
| * > 0xff, result = 0xff | |||
| * | |||
| * You can override the 0x80 level with the -t flag. E.g. if threshold is | |||
| * 000000 the color value of watermark is added to the destination. | |||
| * | |||
| * This way a mask that is visible both in light pictures and in dark can be | |||
| * made (fex by using a picture generated by Gimp and the bump map tool). | |||
| * | |||
| * An example watermark file is at | |||
| * http://engene.se/ffmpeg_watermark.gif | |||
| * | |||
| * MODE 1: | |||
| * Per color do this: | |||
| * If mask color > threshold color then the watermark pixel is used. | |||
| * | |||
| * Example usage: | |||
| * ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif' -an out.mov | |||
| * ffmpeg -i infile -vhook '/path/watermark.so -f wm.gif -m 1 -t 222222' -an out.mov | |||
| * | |||
| * Note that the entire vhook argument is encapsulated in ''. This | |||
| * way, arguments to the vhook won't be mixed up with those for ffmpeg. | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <stdlib.h> | |||
| //#include <fcntl.h> | |||
| #include <unistd.h> | |||
| #include <stdarg.h> | |||
| #include "libavutil/common.h" | |||
| #include "libavformat/avformat.h" | |||
| #include "libavformat/framehook.h" | |||
| #include "libswscale/swscale.h" | |||
| static int sws_flags = SWS_BICUBIC; | |||
| typedef struct { | |||
| char filename[2000]; | |||
| int x_size; | |||
| int y_size; | |||
| /* get_watermark_picture() variables */ | |||
| AVFormatContext *pFormatCtx; | |||
| const char *p_ext; | |||
| int videoStream; | |||
| int frameFinished; | |||
| AVCodecContext *pCodecCtx; | |||
| AVCodec *pCodec; | |||
| AVFrame *pFrame; | |||
| AVPacket packet; | |||
| int numBytes; | |||
| uint8_t *buffer; | |||
| int i; | |||
| AVInputFormat *file_iformat; | |||
| AVStream *st; | |||
| int is_done; | |||
| AVFrame *pFrameRGB; | |||
| int thrR; | |||
| int thrG; | |||
| int thrB; | |||
| int mode; | |||
| // This vhook first converts frame to RGB ... | |||
| struct SwsContext *toRGB_convert_ctx; | |||
| // ... then converts a watermark and applies it to the RGB frame ... | |||
| struct SwsContext *watermark_convert_ctx; | |||
| // ... and finally converts back frame from RGB to initial format | |||
| struct SwsContext *fromRGB_convert_ctx; | |||
| } ContextInfo; | |||
| int get_watermark_picture(ContextInfo *ci, int cleanup); | |||
| /**************************************************************************** | |||
| * | |||
| ****************************************************************************/ | |||
| void Release(void *ctx) | |||
| { | |||
| ContextInfo *ci; | |||
| ci = (ContextInfo *) ctx; | |||
| if (ci) { | |||
| get_watermark_picture(ci, 1); | |||
| sws_freeContext(ci->toRGB_convert_ctx); | |||
| sws_freeContext(ci->watermark_convert_ctx); | |||
| sws_freeContext(ci->fromRGB_convert_ctx); | |||
| } | |||
| av_free(ctx); | |||
| } | |||
| /**************************************************************************** | |||
| * | |||
| ****************************************************************************/ | |||
| int Configure(void **ctxp, int argc, char *argv[]) | |||
| { | |||
| ContextInfo *ci; | |||
| int c; | |||
| int tmp = 0; | |||
| if (0 == (*ctxp = av_mallocz(sizeof(ContextInfo)))) return -1; | |||
| ci = (ContextInfo *) *ctxp; | |||
| optind = 1; | |||
| // Struct is mallocz:ed so no need to reset. | |||
| ci->thrR = 0x80; | |||
| ci->thrG = 0x80; | |||
| ci->thrB = 0x80; | |||
| while ((c = getopt(argc, argv, "f:m:t:")) > 0) { | |||
| switch (c) { | |||
| case 'f': | |||
| strncpy(ci->filename, optarg, 1999); | |||
| ci->filename[1999] = 0; | |||
| break; | |||
| case 'm': | |||
| ci->mode = atoi(optarg); | |||
| break; | |||
| case 't': | |||
| if (1 != sscanf(optarg, "%x", &tmp)) { | |||
| av_log(NULL, AV_LOG_ERROR, "Watermark: argument to -t must be a 6 digit hex number\n"); | |||
| return -1; | |||
| } | |||
| ci->thrR = (tmp >> 16) & 0xff; | |||
| ci->thrG = (tmp >> 8) & 0xff; | |||
| ci->thrB = (tmp >> 0) & 0xff; | |||
| break; | |||
| default: | |||
| av_log(NULL, AV_LOG_ERROR, "Watermark: Unrecognized argument '%s'\n", argv[optind]); | |||
| return -1; | |||
| } | |||
| } | |||
| // | |||
| if (0 == ci->filename[0]) { | |||
| av_log(NULL, AV_LOG_ERROR, "Watermark: There is no filename specified.\n"); | |||
| return -1; | |||
| } | |||
| av_register_all(); | |||
| return get_watermark_picture(ci, 0); | |||
| } | |||
| /**************************************************************************** | |||
| * For mode 0 (the original one) | |||
| ****************************************************************************/ | |||
| static void Process0(void *ctx, | |||
| AVPicture *picture, | |||
| enum PixelFormat pix_fmt, | |||
| int src_width, | |||
| int src_height, | |||
| int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| char *buf = 0; | |||
| AVPicture picture1; | |||
| AVPicture *pict = picture; | |||
| AVFrame *pFrameRGB; | |||
| int xm_size; | |||
| int ym_size; | |||
| int x; | |||
| int y; | |||
| int offs, offsm; | |||
| int mpoffs; | |||
| uint32_t *p_pixel = 0; | |||
| uint32_t pixel_meck; | |||
| uint32_t pixel; | |||
| uint32_t pixelm; | |||
| int tmp; | |||
| int thrR = ci->thrR; | |||
| int thrG = ci->thrG; | |||
| int thrB = ci->thrB; | |||
| if (pix_fmt != PIX_FMT_RGB32) { | |||
| int size; | |||
| size = avpicture_get_size(PIX_FMT_RGB32, src_width, src_height); | |||
| buf = av_malloc(size); | |||
| avpicture_fill(&picture1, buf, PIX_FMT_RGB32, src_width, src_height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| src_width, src_height, pix_fmt, | |||
| src_width, src_height, PIX_FMT_RGB32, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, src_height, | |||
| picture1.data, picture1.linesize); | |||
| pict = &picture1; | |||
| } | |||
| /* Insert filter code here */ /* ok */ | |||
| // Get me next frame | |||
| if (0 > get_watermark_picture(ci, 0)) { | |||
| return; | |||
| } | |||
| // These are the three original static variables in the ffmpeg hack. | |||
| pFrameRGB = ci->pFrameRGB; | |||
| xm_size = ci->x_size; | |||
| ym_size = ci->y_size; | |||
| // I'll do the *4 => <<2 crap later. Most compilers understand that anyway. | |||
| // According to avcodec.h PIX_FMT_RGB32 is handled in endian specific manner. | |||
| for (y=0; y<src_height; y++) { | |||
| offs = y * (src_width * 4); | |||
| offsm = (((y * ym_size) / src_height) * 4) * xm_size; // offsm first in maskline. byteoffs! | |||
| for (x=0; x<src_width; x++) { | |||
| mpoffs = offsm + (((x * xm_size) / src_width) * 4); | |||
| p_pixel = (uint32_t *)&((pFrameRGB->data[0])[mpoffs]); | |||
| pixelm = *p_pixel; | |||
| p_pixel = (uint32_t *)&((pict->data[0])[offs]); | |||
| pixel = *p_pixel; | |||
| // pixelm = *((uint32_t *)&(pFrameRGB->data[mpoffs])); | |||
| pixel_meck = pixel & 0xff000000; | |||
| // R | |||
| tmp = (int)((pixel >> 16) & 0xff) + (int)((pixelm >> 16) & 0xff) - thrR; | |||
| if (tmp > 255) tmp = 255; | |||
| if (tmp < 0) tmp = 0; | |||
| pixel_meck |= (tmp << 16) & 0xff0000; | |||
| // G | |||
| tmp = (int)((pixel >> 8) & 0xff) + (int)((pixelm >> 8) & 0xff) - thrG; | |||
| if (tmp > 255) tmp = 255; | |||
| if (tmp < 0) tmp = 0; | |||
| pixel_meck |= (tmp << 8) & 0xff00; | |||
| // B | |||
| tmp = (int)((pixel >> 0) & 0xff) + (int)((pixelm >> 0) & 0xff) - thrB; | |||
| if (tmp > 255) tmp = 255; | |||
| if (tmp < 0) tmp = 0; | |||
| pixel_meck |= (tmp << 0) & 0xff; | |||
| // test: | |||
| //pixel_meck = pixel & 0xff000000; | |||
| //pixel_meck |= (pixelm & 0x00ffffff); | |||
| *p_pixel = pixel_meck; | |||
| offs += 4; | |||
| } // foreach X | |||
| } // foreach Y | |||
| if (pix_fmt != PIX_FMT_RGB32) { | |||
| ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, | |||
| src_width, src_height, PIX_FMT_RGB32, | |||
| src_width, src_height, pix_fmt, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->fromRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the fromRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->fromRGB_convert_ctx, | |||
| picture1.data, picture1.linesize, 0, src_height, | |||
| picture->data, picture->linesize); | |||
| } | |||
| av_free(buf); | |||
| } | |||
| /**************************************************************************** | |||
| * For mode 1 (the original one) | |||
| ****************************************************************************/ | |||
| static void Process1(void *ctx, | |||
| AVPicture *picture, | |||
| enum PixelFormat pix_fmt, | |||
| int src_width, | |||
| int src_height, | |||
| int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| char *buf = 0; | |||
| AVPicture picture1; | |||
| AVPicture *pict = picture; | |||
| AVFrame *pFrameRGB; | |||
| int xm_size; | |||
| int ym_size; | |||
| int x; | |||
| int y; | |||
| int offs, offsm; | |||
| int mpoffs; | |||
| uint32_t *p_pixel = 0; | |||
| uint32_t pixel; | |||
| uint32_t pixelm; | |||
| if (pix_fmt != PIX_FMT_RGB32) { | |||
| int size; | |||
| size = avpicture_get_size(PIX_FMT_RGB32, src_width, src_height); | |||
| buf = av_malloc(size); | |||
| avpicture_fill(&picture1, buf, PIX_FMT_RGB32, src_width, src_height); | |||
| // if we already got a SWS context, let's realloc if is not re-useable | |||
| ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, | |||
| src_width, src_height, pix_fmt, | |||
| src_width, src_height, PIX_FMT_RGB32, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->toRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the toRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->toRGB_convert_ctx, | |||
| picture->data, picture->linesize, 0, src_height, | |||
| picture1.data, picture1.linesize); | |||
| pict = &picture1; | |||
| } | |||
| /* Insert filter code here */ /* ok */ | |||
| // Get me next frame | |||
| if (0 > get_watermark_picture(ci, 0)) { | |||
| return; | |||
| } | |||
| // These are the three original static variables in the ffmpeg hack. | |||
| pFrameRGB = ci->pFrameRGB; | |||
| xm_size = ci->x_size; | |||
| ym_size = ci->y_size; | |||
| // I'll do the *4 => <<2 crap later. Most compilers understand that anyway. | |||
| // According to avcodec.h PIX_FMT_RGB32 is handled in endian specific manner. | |||
| for (y=0; y<src_height; y++) { | |||
| offs = y * (src_width * 4); | |||
| offsm = (((y * ym_size) / src_height) * 4) * xm_size; // offsm first in maskline. byteoffs! | |||
| for (x=0; x<src_width; x++) { | |||
| mpoffs = offsm + (((x * xm_size) / src_width) * 4); | |||
| p_pixel = (uint32_t *)&((pFrameRGB->data[0])[mpoffs]); | |||
| pixelm = *p_pixel; /* watermark pixel */ | |||
| p_pixel = (uint32_t *)&((pict->data[0])[offs]); | |||
| pixel = *p_pixel; | |||
| if (((pixelm >> 16) & 0xff) > ci->thrR || | |||
| ((pixelm >> 8) & 0xff) > ci->thrG || | |||
| ((pixelm >> 0) & 0xff) > ci->thrB) | |||
| { | |||
| *p_pixel = pixelm; | |||
| } else { | |||
| *p_pixel = pixel; | |||
| } | |||
| offs += 4; | |||
| } // foreach X | |||
| } // foreach Y | |||
| if (pix_fmt != PIX_FMT_RGB32) { | |||
| ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, | |||
| src_width, src_height, PIX_FMT_RGB32, | |||
| src_width, src_height, pix_fmt, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->fromRGB_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the fromRGB conversion context\n"); | |||
| return; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->fromRGB_convert_ctx, | |||
| picture1.data, picture1.linesize, 0, src_height, | |||
| picture->data, picture->linesize); | |||
| } | |||
| av_free(buf); | |||
| } | |||
| /**************************************************************************** | |||
| * This is the function ffmpeg.c callbacks. | |||
| ****************************************************************************/ | |||
| void Process(void *ctx, | |||
| AVPicture *picture, | |||
| enum PixelFormat pix_fmt, | |||
| int src_width, | |||
| int src_height, | |||
| int64_t pts) | |||
| { | |||
| ContextInfo *ci = (ContextInfo *) ctx; | |||
| if (1 == ci->mode) { | |||
| Process1(ctx, picture, pix_fmt, src_width, src_height, pts); | |||
| } else { | |||
| Process0(ctx, picture, pix_fmt, src_width, src_height, pts); | |||
| } | |||
| } | |||
| /**************************************************************************** | |||
| * When cleanup == 0, we try to get the next frame. If no next frame, nothing | |||
| * is done. | |||
| * | |||
| * This code follows the example on | |||
| * http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html | |||
| * | |||
| * 0 = ok, -1 = error | |||
| ****************************************************************************/ | |||
| int get_watermark_picture(ContextInfo *ci, int cleanup) | |||
| { | |||
| if (1 == ci->is_done && 0 == cleanup) return 0; | |||
| // Yes, *pFrameRGB arguments must be null the first time otherwise it's not good.. | |||
| // This block is only executed the first time we enter this function. | |||
| if (0 == ci->pFrameRGB && | |||
| 0 == cleanup) | |||
| { | |||
| /* | |||
| * The last three parameters specify the file format, buffer size and format | |||
| * parameters; by simply specifying NULL or 0 we ask libavformat to auto-detect | |||
| * the format and use a default buffer size. (Didn't work!) | |||
| */ | |||
| if (av_open_input_file(&ci->pFormatCtx, ci->filename, NULL, 0, NULL) != 0) { | |||
| // Martin says this should not be necessary but it failed for me sending in | |||
| // NULL instead of file_iformat to av_open_input_file() | |||
| ci->i = strlen(ci->filename); | |||
| if (0 == ci->i) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() No filename to watermark vhook\n"); | |||
| return -1; | |||
| } | |||
| while (ci->i > 0) { | |||
| if (ci->filename[ci->i] == '.') { | |||
| ci->i++; | |||
| break; | |||
| } | |||
| ci->i--; | |||
| } | |||
| ci->p_ext = &(ci->filename[ci->i]); | |||
| ci->file_iformat = av_find_input_format (ci->p_ext); | |||
| if (0 == ci->file_iformat) { | |||
| av_log(NULL, AV_LOG_INFO, "get_watermark_picture() attempt to use image2 for [%s]\n", ci->p_ext); | |||
| ci->file_iformat = av_find_input_format ("image2"); | |||
| } | |||
| if (0 == ci->file_iformat) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Really failed to find iformat [%s]\n", ci->p_ext); | |||
| return -1; | |||
| } | |||
| // now continues the Martin template. | |||
| if (av_open_input_file(&ci->pFormatCtx, ci->filename, ci->file_iformat, 0, NULL)!=0) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open input file [%s]\n", ci->filename); | |||
| return -1; | |||
| } | |||
| } | |||
| /* | |||
| * This fills the streams field of the AVFormatContext with valid information. | |||
| */ | |||
| if(av_find_stream_info(ci->pFormatCtx)<0) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find stream info\n"); | |||
| return -1; | |||
| } | |||
| /* | |||
| * As mentioned in the introduction, we'll handle only video streams, not audio | |||
| * streams. To make things nice and easy, we simply use the first video stream we | |||
| * find. | |||
| */ | |||
| ci->videoStream=-1; | |||
| for(ci->i = 0; ci->i < ci->pFormatCtx->nb_streams; ci->i++) | |||
| if(ci->pFormatCtx->streams[ci->i]->codec->codec_type==CODEC_TYPE_VIDEO) | |||
| { | |||
| ci->videoStream = ci->i; | |||
| break; | |||
| } | |||
| if(ci->videoStream == -1) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any video stream\n"); | |||
| return -1; | |||
| } | |||
| ci->st = ci->pFormatCtx->streams[ci->videoStream]; | |||
| ci->x_size = ci->st->codec->width; | |||
| ci->y_size = ci->st->codec->height; | |||
| // Get a pointer to the codec context for the video stream | |||
| ci->pCodecCtx = ci->pFormatCtx->streams[ci->videoStream]->codec; | |||
| /* | |||
| * OK, so now we've got a pointer to the so-called codec context for our video | |||
| * stream, but we still have to find the actual codec and open it. | |||
| */ | |||
| // Find the decoder for the video stream | |||
| ci->pCodec = avcodec_find_decoder(ci->pCodecCtx->codec_id); | |||
| if(ci->pCodec == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to find any codec\n"); | |||
| return -1; | |||
| } | |||
| // Open codec | |||
| if(avcodec_open(ci->pCodecCtx, ci->pCodec)<0) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to open codec\n"); | |||
| return -1; | |||
| } | |||
| // Hack to correct wrong frame rates that seem to be generated by some | |||
| // codecs | |||
| if (ci->pCodecCtx->time_base.den>1000 && ci->pCodecCtx->time_base.num==1) | |||
| ci->pCodecCtx->time_base.num=1000; | |||
| /* | |||
| * Allocate a video frame to store the decoded images in. | |||
| */ | |||
| ci->pFrame = avcodec_alloc_frame(); | |||
| /* | |||
| * The RGB image pFrameRGB (of type AVFrame *) is allocated like this: | |||
| */ | |||
| // Allocate an AVFrame structure | |||
| ci->pFrameRGB=avcodec_alloc_frame(); | |||
| if(ci->pFrameRGB==NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, "get_watermark_picture() Failed to alloc pFrameRGB\n"); | |||
| return -1; | |||
| } | |||
| // Determine required buffer size and allocate buffer | |||
| ci->numBytes = avpicture_get_size(PIX_FMT_RGB32, ci->pCodecCtx->width, | |||
| ci->pCodecCtx->height); | |||
| ci->buffer = av_malloc(ci->numBytes); | |||
| // Assign appropriate parts of buffer to image planes in pFrameRGB | |||
| avpicture_fill((AVPicture *)ci->pFrameRGB, ci->buffer, PIX_FMT_RGB32, | |||
| ci->pCodecCtx->width, ci->pCodecCtx->height); | |||
| } | |||
| // TODO loop, pingpong etc? | |||
| if (0 == cleanup) | |||
| { | |||
| // av_log(NULL, AV_LOG_DEBUG, "get_watermark_picture() Get a frame\n"); | |||
| while(av_read_frame(ci->pFormatCtx, &ci->packet)>=0) | |||
| { | |||
| // Is this a packet from the video stream? | |||
| if(ci->packet.stream_index == ci->videoStream) | |||
| { | |||
| // Decode video frame | |||
| avcodec_decode_video(ci->pCodecCtx, ci->pFrame, &ci->frameFinished, | |||
| ci->packet.data, ci->packet.size); | |||
| // Did we get a video frame? | |||
| if(ci->frameFinished) | |||
| { | |||
| // Convert the image from its native format to RGB32 | |||
| ci->watermark_convert_ctx = | |||
| sws_getCachedContext(ci->watermark_convert_ctx, | |||
| ci->pCodecCtx->width, ci->pCodecCtx->height, ci->pCodecCtx->pix_fmt, | |||
| ci->pCodecCtx->width, ci->pCodecCtx->height, PIX_FMT_RGB32, | |||
| sws_flags, NULL, NULL, NULL); | |||
| if (ci->watermark_convert_ctx == NULL) { | |||
| av_log(NULL, AV_LOG_ERROR, | |||
| "Cannot initialize the watermark conversion context\n"); | |||
| return -1; | |||
| } | |||
| // img_convert parameters are 2 first destination, then 4 source | |||
| // sws_scale parameters are context, 4 first source, then 2 destination | |||
| sws_scale(ci->watermark_convert_ctx, | |||
| ci->pFrame->data, ci->pFrame->linesize, 0, ci->pCodecCtx->height, | |||
| ci->pFrameRGB->data, ci->pFrameRGB->linesize); | |||
| // Process the video frame (save to disk etc.) | |||
| //fprintf(stderr,"banan() New frame!\n"); | |||
| //DoSomethingWithTheImage(ci->pFrameRGB); | |||
| return 0; | |||
| } | |||
| } | |||
| // Free the packet that was allocated by av_read_frame | |||
| av_free_packet(&ci->packet); | |||
| } | |||
| ci->is_done = 1; | |||
| return 0; | |||
| } // if 0 != cleanup | |||
| if (0 != cleanup) | |||
| { | |||
| // Free the RGB image | |||
| av_freep(&ci->buffer); | |||
| av_freep(&ci->pFrameRGB); | |||
| // Close the codec | |||
| if (0 != ci->pCodecCtx) { | |||
| avcodec_close(ci->pCodecCtx); | |||
| ci->pCodecCtx = 0; | |||
| } | |||
| // Close the video file | |||
| if (0 != ci->pFormatCtx) { | |||
| av_close_input_file(ci->pFormatCtx); | |||
| ci->pFormatCtx = 0; | |||
| } | |||
| ci->is_done = 0; | |||
| } | |||
| return 0; | |||
| } | |||
| void parse_arg_file(const char *filename) | |||
| { | |||
| } | |||