Mercurial > vloopback
changeset 0:5f21a4dddc0c
Initial checkin
author | KennethLavrsen |
---|---|
date | Sun, 01 Apr 2007 05:22:43 +0000 |
parents | |
children | bcf5fe83f332 |
files | COPYING Makefile Modules.symvers README debian/README.Debian debian/changelog debian/compat debian/control.modules.in debian/copyright debian/rules example/Makefile example/dummy.c example/feed.c example/invert.c example/resize.c vloopback.c vloopback.html |
diffstat | 16 files changed, 3053 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,40 @@ +ifneq ($(KERNELRELEASE),) + +obj-m := vloopback.o + +else + +KVER := $(shell uname -r) +KLINK := $(shell test -e /lib/modules/${KVER}/source/ && echo source || echo "build") +KSRC := /lib/modules/$(KVER)/$(KLINK) +PWD := $(shell pwd) +DEST := /lib/modules/$(KVER)/kernel/drivers/misc + +# Fix some problem with suse < 9.2 and suse >= 9.2 +is_suse := $(shell test -e /etc/SuSE-release && echo 1 || echo 0) +ifeq ($(is_suse),1) + suse_version := $(shell grep VERSION /etc/SuSE-release | cut -f 3 -d " "| tr -d .) + is_suse_92_or_greater := $(shell test $(suse_version) -ge 92 && echo 1) + ifeq ($(is_suse_92_or_greater),1) + KSRC := /lib/modules/$(KVER)/build + endif +endif + + + +all default: + $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules + +install: + install -d $(DEST) + install -m 644 -c vloopback.ko $(DEST) + -/sbin/depmod -a + + +uninstall: + rm -f $(DEST)/vloopback.ko + -/sbin/depmod -a +clean: + rm -f .*.cmd *.o *.mod.c *.ko .v* *~ core + rm -rf .tmp_versions/ +endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,68 @@ +vloopback is a video4linux driver providing video pipes. +With the driver you can use the output of a user program to feed a program that +would normally communicate with a video4linux device. +To achieve this a video pipe consists out of two video4linux devices: +one for the generating program to write its data to and one for a normal +video4linux program to read from. + +At the moment there are only few programs that can feed the input of the pipe: +invert and resize, the example programs with the driver and +motion, my motion detection program. + +Just type 'make' and then 'insmod vloopback'. +If you want more pipes use 'insmod vloopback pipes=N' with N between 1 and 16. +If you want to create spare pipes use 'spares=N'. + +Type 'dmesg' to see which video devices have been created as input and output. +For example: if you have a camera on /dev/video0 the input pipe will most likely +be /dev/video1 and the output on /dev/video2. +e.g. if you want to watch an inverted image of the camera you would start invert +with /dev/video0 as its input and /dev/video1 as its output. +Then start a viewing app (such as camstream or xawtv) with as input /dev/video2 +and you can see yourself inverted. + +This is a very experimental device driver! +SMP should work from 0.2 on..... but is untested! +If it troubles you fix it or tell me how to fix it :) + +Using vloopback with xawtv: +As of 0.83 xawtv will work with vloopback outputs, but you will have to specify +the size to use. For example: 'xawtv -c /dev/video2 -geometry 320x240' + +Jeroen Vreeken, pe1rxq@amsat.org + +------------------------------------------------------------------------------- + +Module has experimental support for kernel 2.6.x . +The current version 1.0 has been tested with kernel 2.6.x ( not with >= 2.6.16 ). + +To compile and install : + +make ; su - ; make install +/sbin/modprobe videodev +/sbin/modprobe vloopback + +To uninstall : + +make uninstall ( as root ). + +- Problems : + +* If you got this message after 'insmod ./vloopback.ko' : + + "insmod: error inserting './vloopback.ko': -1 Unknown symbol in module" + + Solution : You must load videodev module : + + 'insmod videodev ; insmod ./vloopback.ko' + +* If you got this message after 'modprobe vloopback [options]' : + + "FATAL: Error inserting vloopback (/lib/modules/2.6.8-1-k7/kernel/drivers/misc/vloopback.ko): Too many open files in system" + + You probably used dev_offset= as a option , but the video device that vloopback was trying to register was already in use, maybe the value used for dev_offeset was too high. + + Solution : Try to use a lower value for dev_offset= or even better don't use it . + +Angel Carpintero, ack@telefonica.net +Kenneth Lavrsen, kenneth@lavrsen.dk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/README.Debian Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,61 @@ +I added as little glue as possible to the vloopback source to build with the +Debian kernel module tools kernel-package [0] or module-assistant [1] + + +kernel-package +============== + +kernel-package is a utility for building Linux kernel related Debian packages +just by running `make-kpkg kernel_image` + +Documentation on building extra kernel modules like vloopback is in the +kernel-package package in `/usr/share/doc/kernel-package/README.modules` + +Essentially, you must download the vloopback source, which comes with the +necessary Debian glue [2] + +Now you can add to the `make-kpkg` command line `--added-modules +<vloopback_location> modules_image` + +No matter where you downloaded vloopback, you can use the absolute path to the +source for <vloopback_location> + +If you downloaded vloopback to `/usr/src/modules/<vloopback_source>s`', you can +use just <vloopback_source> for <vloopback_location> + +When `make-kpkg` is finished, you should find +'vloopback-modules-<kernel_version>_<vloopback_version>+<kernel_revision>_<architecture>.deb' +in the directory below that which you ran `make-kpkg` + +Use this package however you would ordinarily use a Debian package - for +instance: `dpkg -i +vloopback-modules-<kernel_version>_<vloopback_version>+<kernel_revision>_<architecture>.deb` + + +module-assistant +================ + +The module-assistant tool helps users & package maintainers with managing +external kernel modules packaged for Debian + +Documentation on building modules with module-assistant is in the +module-assistant package in `/usr/share/doc/module-assistant/HOWTO` + +Essentially, you must download the vloopback source, as with kernel-package + +Then you can run `module-assistant build <vloopback_source>` + +It's not yet clear to me whether you must download vloopback to +`/usr/src/module/<vloopback_source>`, whether you can use +`MODULE_LOC=<other_path> module-assistant build <vloopback_source>` if +vloopback is downloaded to `<other_path>/<vloopback_source` or whether you can +run `module-assistant build <other_path>/<vloopback_source>` + +Consult the module-assistant documentation or contact the module-assistant +maintainer for more information + + -- Jack Bates <ms419@freezone.co.uk> + +[0] http://packages.debian.org/kernel-package +[1] http://packages.debian.org/module-assistant +[2] http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/changelog Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,17 @@ +vloopback (1.0-1) unstable; urgency=low + + * New upstream release + + -- Jack Bates <ms419@freezone.co.uk> Wed, 30 Aug 2006 09:40:36 -0700 + +vloopback (0.97-snap3-1) unstable; urgency=low + + * New upstream release + + -- Jack Bates <ms419@freezone.co.uk> Mon, 1 May 2006 19:48:36 -0700 + +vloopback (0.97-snap1-1) unstable; urgency=low + + * Initial release + + -- Jack Bates <ms419@freezone.co.uk> Thu, 8 Dec 2005 16:43:57 -0800
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/compat Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,1 @@ +4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/control.modules.in Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,14 @@ +Source: vloopback +Section: unknown +Priority: optional +Maintainer: Jack Bates <ms419@freezone.co.uk> +Build-Depends: debhelper (>> 4.0.0) # module-assistant? +Standards-Version: 3.6.2 + +Package: vloopback-modules-_KVERS_ +Architecture: any +Provides: vloopback-modules +Description: vloopback modules for Linux _KVERS_ + vloopback is a video4linux driver providing video pipes. With this driver you + can use the output of a user program as input to another program which would + normally communicate with a video4linux device.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/copyright Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,23 @@ +Copyright Holder: + + Angel Carpintero, ack@telefonica.net + Kenneth Lavrsen, kenneth@lavrsen.dk + +License: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/rules Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/make -f + +# Uncomment to turn on verbose mode +#export DH_VERBOSE = 1 + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +# Prefix of the target package name +PACKAGE = vloopback-modules + +# Load generic variable handling +-include /usr/share/modass/include/generic.make + +# Load default rules, including kdist, kdist_image, ... +-include /usr/share/modass/include/common-rules.make + +binary-modules: + dh_testdir + dh_testroot + dh_installdirs lib/modules/$(KVERS)/misc + + # Build the module + $(MAKE) -C $(KSRC) M=$(PWD) modules + + # Install the module + $(MAKE) -C $(KSRC) M=$(PWD) \ + DEST=debian/$(PKGNAME)/lib/modules/$(KVERS)/misc modules_install + + dh_installdocs README vloopback.html + dh_installmodules + dh_installchangelogs + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol -- -v$(VERSION) + dh_md5sums + dh_builddeb --destdir=$(DEB_DESTDIR) + +kdist_clean: + -$(MAKE) clean + + # kdist_clean may be run before prep-deb-files + -dh_clean + rm -f debian/control + +kdist_config: prep-deb-files
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/example/Makefile Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,14 @@ +CFLAGS=-g -Wall + +all: invert resize feed dummy + +clean: + rm -f dummy resize invert feed *o *~ + +invert: invert.c + +resize: resize.c + +feed: feed.c + +dummy: dummy.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/example/dummy.c Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,435 @@ +/* dummy.c + * + * Example program for using a videoloopback device in zero-copy mode. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright 2005 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/poll.h> +#include <dirent.h> +#include <sys/utsname.h> +#include <linux/videodev.h> + +/* all seem reasonable, or not? */ +#define MAXIOCTL 1024 +#define MAXWIDTH 640 +#define MAXHEIGHT 480 +int width; +int height; +int fmt=0; +char ioctlbuf[MAXIOCTL]; +int v4ldev; +char *image_out; + + +int get_frame(void) +{ + int i; + char colour = 0; + + memset(image_out, 0x128, width*height*3); + + for (i=10; i<width-10; i++) { + image_out[10*width*3+i*3]=colour++; + image_out[10*width*3+i*3+1]=0; + image_out[10*width*3+i*3+2]=-colour; + } + for (i=10; i<width-10; i++) { + image_out[(height-10)*width*3+i*3]=colour; + image_out[(height-10)*width*3+i*3+1]=0; + image_out[(height-10)*width*3+i*3+2]=-colour++; + } + /* + */ + usleep(500); /* BIG XXX */ + return 0; +} + +char *v4l_create (int dev, int memsize) +{ + char *map; + + map=mmap(0, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + if ((unsigned char *)-1 == (unsigned char *)map) + return NULL; + return map; +} + +int v4l_ioctl(unsigned long int cmd, void *arg) +{ + int i; + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *vidcap=arg; + + sprintf(vidcap->name, "Jeroen's dummy v4l driver"); + vidcap->type= VID_TYPE_CAPTURE; + vidcap->channels=1; + vidcap->audios=0; + vidcap->maxwidth=MAXWIDTH; + vidcap->maxheight=MAXHEIGHT; + vidcap->minwidth=20; + vidcap->minheight=20; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *vidchan= (struct video_channel *)arg; + + printf("VIDIOCGCHAN called\n"); + if (vidchan->channel!=0) + ;//return 1; + vidchan->channel=0; + vidchan->flags=0; + vidchan->tuners=0; + vidchan->norm=0; + vidchan->type=VIDEO_TYPE_CAMERA; + strcpy(vidchan->name, "Loopback"); + + return 0; + } + case VIDIOCSCHAN: + { + int *v=arg; + + if (v[0]!=0) + return 1; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + + if(v->tuner) { + printf("VIDIOCGTUNER: Invalid Tuner, was %d\n", v->tuner); + //return -EINVAL; + } + v->tuner=0; + strcpy(v->name, "Format"); + v->rangelow=0; + v->rangehigh=0; + v->flags=0; + v->mode=VIDEO_MODE_AUTO; + return 1; + } + case VIDIOCGPICT: + { + struct video_picture *vidpic=arg; + + vidpic->colour=0x8000; + vidpic->hue=0x8000; + vidpic->brightness=0x8000; + vidpic->contrast=0x8000; + vidpic->whiteness=0x8000; + vidpic->depth=0x8000; + vidpic->palette=fmt; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *vidpic=arg; + + if (vidpic->palette!=fmt) + return 1; + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vidwin=arg; + + vidwin->x=0; + vidwin->y=0; + vidwin->width=width; + vidwin->height=height; + vidwin->chromakey=0; + vidwin->flags=0; + vidwin->clipcount=0; + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vidwin=arg; + + if (vidwin->width > MAXWIDTH || + vidwin->height > MAXHEIGHT ) + return 1; + if (vidwin->flags) + return 1; + width=vidwin->width; + height=vidwin->height; + printf("new size: %dx%d\n", width, height); + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vidmbuf=arg; + + vidmbuf->size=width*height*3; + vidmbuf->frames=1; + for (i=0; i<vidmbuf->frames; i++) + vidmbuf->offsets[i]=i*vidmbuf->size; + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vidmmap=arg; + + //return 0; + if (vidmmap->height>MAXHEIGHT || + vidmmap->width>MAXWIDTH || + vidmmap->format!=fmt ) + return 1; + if (vidmmap->height!=height || + vidmmap->width!=width) { + height=vidmmap->height; + width=vidmmap->width; + printf("new size: %dx%d\n", width, height); + } + // check if 'vidmmap->frame' is valid + // initiate capture for 'vidmmap->frame' frames + return 0; + } + case VIDIOCSYNC: + { + //struct video_mmap *vidmmap=arg; + + // check if frames are ready. + // wait until ready. + get_frame(); + return 0; + } + default: + { + printf("unknown ioctl: %ld\n", cmd & 0xff); + return 1; + } + } + return 0; +} + +#define VIDIOCSINVALID _IO('v',BASE_VIDIOCPRIVATE+1) + +void sighandler(int signo) +{ + int size, ret; + unsigned long int cmd; + struct pollfd ufds; + + if (signo!=SIGIO) + return; + ufds.fd=v4ldev; + ufds.events=POLLIN; + ufds.revents=0; + poll(&ufds, 1, 1000); + if (!ufds.revents & POLLIN) { + printf("Received signal but got negative on poll?!?!?!?\n"); + return; + } + size=read(v4ldev, ioctlbuf, MAXIOCTL); + if (size >= sizeof(unsigned long int)) { + memcpy(&cmd, ioctlbuf, sizeof(unsigned long int)); + if (cmd==0) { + printf("Client closed device\n"); + return; + } + ret=v4l_ioctl(cmd, ioctlbuf+sizeof(unsigned long int)); + if (ret) { + memset(ioctlbuf+sizeof(unsigned long int), MAXIOCTL-sizeof(unsigned long int), 0xff); + printf("ioctl %lx unsuccesfull, lets issue VIDIOCSINVALID (%x)\n", cmd, VIDIOCSINVALID); + ioctl(v4ldev, VIDIOCSINVALID); + } else + ioctl(v4ldev, cmd, ioctlbuf+sizeof(unsigned long int)); + } + return; +} + +int open_vidpipe(void) +{ + int pipe_fd = -1; + FILE *vloopbacks; + char pipepath[255]; + char buffer[255]; + char *loop; + char *input; + char *istatus; + char *output; + char *ostatus; + char *major; + char *minor; + struct utsname uts; + + if (uname(&uts) < 0) { + printf("Unable to execute uname\nError[%s]\n",strerror(errno)); + return -1; + } + + major = strtok(uts.release, "."); + minor = strtok(NULL, "."); + if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { + printf("Unable to decipher OS version\n"); + return -1; + } + + if (strcmp(minor, "5") < 0) { + + vloopbacks=fopen("/proc/video/vloopback/vloopbacks", "r"); + if (!vloopbacks) { + printf ("Failed to open '/proc/video/vloopback/vloopbacks"); + return -1; + } + /* Read vloopback version */ + fgets(buffer, 255, vloopbacks); + printf("%s", buffer); + /* Read explaination line */ + fgets(buffer, 255, vloopbacks); + while (fgets(buffer, 255, vloopbacks)) { + if (strlen(buffer)>1) { + buffer[strlen(buffer)-1]=0; + loop=strtok(buffer, "\t"); + input=strtok(NULL, "\t"); + istatus=strtok(NULL, "\t"); + output=strtok(NULL, "\t"); + ostatus=strtok(NULL, "\t"); + if (istatus[0]=='-') { + sprintf(pipepath, "/dev/%s", input); + pipe_fd=open(pipepath, O_RDWR); + if (pipe_fd>=0) { + printf("Input: /dev/%s\n", input); + printf("Output: /dev/%s\n", output); + return pipe_fd; + } + } + } + } + + }else{ + DIR *dir; + struct dirent *dirp; + const char prefix[]="/sys/class/video4linux/"; + char *ptr, *io; + int fd; + int low=9999; + int tfd; + int tnum; + + if ((dir=opendir(prefix))== NULL) { + printf( "Failed to open '%s'", prefix); + return -1; + } + + while ((dirp=readdir(dir)) != NULL) { + if (!strncmp(dirp->d_name, "video", 5)) { + strcpy(buffer, prefix); + strcat(buffer, dirp->d_name); + strcat(buffer, "/name"); + if ((fd=open(buffer, O_RDONLY)) >= 0) { + if ((read(fd, buffer, sizeof(buffer)-1))<0) { + close(fd); + continue; + } + ptr = strtok(buffer, " "); + if (strcmp(ptr,"Video")) { + close(fd); + continue; + } + major = strtok(NULL, " "); + minor = strtok(NULL, " "); + io = strtok(NULL, " \n"); + if (strcmp(major, "loopback") || strcmp(io, "input")) { + close(fd); + continue; + } + if ((ptr=strtok(buffer, " "))==NULL) { + close(fd); + continue; + } + tnum = atoi(minor); + if (tnum < low) { + strcpy(buffer, "/dev/"); + strcat(buffer, dirp->d_name); + if ((tfd=open(buffer, O_RDWR))>=0) { + strcpy(pipepath, buffer); + if (pipe_fd>=0) { + close(pipe_fd); + } + pipe_fd = tfd; + low = tnum; + } + } + close(fd); + } + } + } + + + closedir(dir); + if (pipe_fd >= 0) + printf("Opened input of %s", pipepath); + } + + return pipe_fd; +} + +int main (int argc, char **argv) +{ + char palette[10]={'\0'}; + + if (argc != 3) { + printf("dummy.c\n"); + printf("A example for using a video4linux loopback in zero-copy mode\n"); + printf("Written by Jeroen Vreeken, 2000\n"); + printf("Updated to vloopback API v0.97\n\n"); + printf("Usage:\n\n"); + printf("dummy widthxheight rgb24|yuv420p\n\n"); + printf("example: dummy 352x288 yuv420p\n\n"); + exit(1); + } + + sscanf(argv[1], "%dx%d", &width, &height); + sscanf(argv[2], "%s", palette); + + if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24; + else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P; + else fmt = VIDEO_PALETTE_RGB24; + + /* Default startup values, nothing special + width=352; + height=288; + */ + + v4ldev=open_vidpipe(); + if (v4ldev < 0) { + printf ("Failed to open video loopback device\nError[%s]\n",strerror(errno)); + exit(1); + } + image_out=v4l_create(v4ldev, MAXWIDTH*MAXHEIGHT*3); + if (!image_out) { + exit(1); + printf ("Failed to set device to zero-copy mode\nError[%s]\n",strerror(errno)); + } + + signal (SIGIO, sighandler); + + printf("\nListening.\n"); + while (1) { + sleep(1000); + } + + close (v4ldev); + free(image_out); + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/example/feed.c Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,217 @@ +/* feed.c + * + * Example program for videoloopback device. + * Copyright 2006 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <linux/videodev.h> + + +int fmt=0; +int noexit = 1; +int read_img=0; + +char *start_capture (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_mbuf vid_buf; + char *map; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (NULL); + } + if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY; + if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { + fprintf(stderr, "no mmap falling back on read\n"); + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + read_img=1; + map=malloc(width*height*3); + return (map); + } + /* If we are going to capture greyscale we need room to blow the image up */ + if (fmt==VIDEO_PALETTE_GREY) + map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + else + map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + + if ((unsigned char *)-1 == (unsigned char *)map) + return (NULL); + return map; +} + +int start_pipe (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_picture vid_pic; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno)); + return (1); + } + vid_pic.palette=fmt; + if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN"); + return (1); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN"); + return (1); + } + return 0; +} + +char *next_capture (int dev, char *map, int width, int height) +{ + int i; + char *grey, *rgb; + struct video_mmap vid_mmap; + + sigset_t set, old; + + if (read_img) { + if (fmt==VIDEO_PALETTE_GREY) { + if (read(dev, map, width*height) != width*height) + return NULL; + } else { + if (read(dev, map, width*height*3) != width*height*3) + return NULL; + } + } else { + vid_mmap.format=fmt; + vid_mmap.frame=0; + vid_mmap.width=width; + vid_mmap.height=height; + + sigemptyset (&set); //BTTV hates signals during IOCTL + sigaddset (&set, SIGCHLD); //block SIGCHLD & SIGALRM + sigaddset (&set, SIGALRM); //for the time of ioctls + sigprocmask (SIG_BLOCK, &set, &old); + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + + sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking + } + /* Blow up a grey */ + if (fmt==VIDEO_PALETTE_GREY) { + i=width*height; + grey=map+i-1; + rgb=map+i*3; + for (; i>=0; i--, grey--) { + *(rgb--)=*grey; + *(rgb--)=*grey; + *(rgb--)=*grey; + } + } + return map; +} + +int put_image(int dev, char *image, int width, int height) +{ + if (write(dev, image, width*height*3)!=width*height*3) { + printf("Error writing image to pipe!\nError[%s]\n",strerror(errno)); + return 0; + } + return 1; +} + +void sig_handler(int signo) +{ + noexit = 0; +} + + +int main (int argc, char **argv) +{ + int devin, devout; + int width; + int height; + char *image_new; + char palette[10]={'\0'}; + + if (argc != 5) { + printf("Usage:\n\n"); + printf("feed input output widthxheight(in) rgb24|yuv420p\n\n"); + printf("example: feed /dev/video0 /dev/video1 352x288 yuv420p\n\n"); + exit(1); + } + sscanf(argv[3], "%dx%d", &width, &height); + sscanf(argv[4], "%s", palette); + + if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24; + else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P; + else fmt = VIDEO_PALETTE_RGB24; + + devin=open (argv[1], O_RDWR); + if (devin < 0) { + printf ("Failed to open video device %s\nError[%s]\n",argv[1],strerror(errno)); + exit(1); + } + + devout=open (argv[2], O_RDWR); + if (devout < 0) { + printf ("Failed to open video device%s \nError[%s]\n",argv[2],strerror(errno)); + exit(1); + } + + image_new=start_capture (devin, width, height); + if (!image_new) { + printf("Capture error\nError[%s]\n",strerror(errno)); + exit(1); + } + + start_pipe(devout, width, height); + + signal(SIGTERM, sig_handler); + + printf("Starting video stream.\n"); + while ( (next_capture(devin, image_new, width, height)) && (noexit)) { + if (put_image(devout, image_new, width, height)==0) + exit(1); + } + printf("You bought vaporware!\nError[%s]\n",strerror(errno)); + close (devin); + close (devout); + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/example/invert.c Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,223 @@ +/* invert.c + * + * Example program for videoloopback device. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright 2005 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <linux/videodev.h> + + +int fmt=0; +int noexit = 1; +int read_img=0; + +char *start_capture (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_mbuf vid_buf; + char *map; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (NULL); + } + if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY; + if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { + fprintf(stderr, "no mmap falling back on read\n"); + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + read_img=1; + map=malloc(width*height*3); + return (map); + } + /* If we are going to capture greyscale we need room to blow the image up */ + if (fmt==VIDEO_PALETTE_GREY) + map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + else + map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + + if ((unsigned char *)-1 == (unsigned char *)map) + return (NULL); + return map; +} + +int start_pipe (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_picture vid_pic; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno)); + return (1); + } + vid_pic.palette=fmt; + if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno)); + return (1); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno)); + return (1); + } + return 0; +} + +char *next_capture (int dev, char *map, int width, int height) +{ + int i; + char *grey, *rgb; + struct video_mmap vid_mmap; + + sigset_t set, old; + + if (read_img) { + if (fmt==VIDEO_PALETTE_GREY) { + if (read(dev, map, width*height) != width*height) + return NULL; + } else { + if (read(dev, map, width*height*3) != width*height*3) + return NULL; + } + } else { + vid_mmap.format=fmt; + vid_mmap.frame=0; + vid_mmap.width=width; + vid_mmap.height=height; + + sigemptyset (&set); //BTTV hates signals during IOCTL + sigaddset (&set, SIGCHLD); //block SIGCHLD & SIGALRM + sigaddset (&set, SIGALRM); //for the time of ioctls + sigprocmask (SIG_BLOCK, &set, &old); + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + + sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking + } + /* Blow up a grey */ + if (fmt==VIDEO_PALETTE_GREY) { + i=width*height; + grey=map+i-1; + rgb=map+i*3; + for (; i>=0; i--, grey--) { + *(rgb--)=*grey; + *(rgb--)=*grey; + *(rgb--)=*grey; + } + } + return map; +} + +int put_image(int dev, char *image, int width, int height) +{ + if (write(dev, image, width*height*3)!=width*height*3) { + printf("Error writing image to pipe!\nError[%s]\n",strerror(errno)); + return 0; + } + return 1; +} + + +void sig_handler(int signo) +{ + noexit = 0; +} + +int main (int argc, char **argv) +{ + int i, devin, devout; + int width; + int height; + char *image_out, *image_new; + char palette[10]={'\0'}; + + if (argc != 5) { + printf("Usage:\n\n"); + printf("invert input output widthxheight rgb24|yuv420p\n\n"); + printf("example: invert /dev/video0 /dev/video1 352x288 yuv420p\n\n"); + exit(1); + } + sscanf(argv[3], "%dx%d", &width, &height); + sscanf(argv[4], "%s", palette); + + if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24; + else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P; + else fmt = VIDEO_PALETTE_RGB24; + + + image_out=malloc(width*height*3); + + devin=open (argv[1], O_RDWR); + if (devin < 0) { + printf("Failed to open input video device [%s]\nError:[%s]\n",argv[1],strerror(errno)); + exit(1); + } + + devout=open (argv[2], O_RDWR); + if (devout < 0) { + printf ("Failed to open output video device [%s]\nError:[%s]\n",argv[2],strerror(errno)); + exit(1); + } + + image_new=start_capture (devin, width, height); + if (!image_new) { + printf("Capture error \n Error[%s]\n",strerror(errno)); + exit(1); + } + + start_pipe(devout, width, height); + + signal(SIGTERM, sig_handler); + + printf("Starting video stream.\n"); + while ( (next_capture(devin, image_new, width, height)) && (noexit) ){ + for (i=width*height*3; i>=0; i--) image_out[i]=-image_new[i]; + if (put_image(devout, image_out, width, height)==0) + exit(1); + } + printf("You bought vaporware!\nError[%s]\n",strerror(errno)); + close (devin); + close (devout); + free(image_out); + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/example/resize.c Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,265 @@ +/* resize.c + * + * Example program for videoloopback device. + * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) + * Copyright 2005 by Angel Carpintero (ack@telefonica.net) + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <signal.h> +#include <sys/wait.h> +#include <linux/videodev.h> + + +int fmt=0; +int noexit = 1; +int read_img=0; + +char *start_capture (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_mbuf vid_buf; + char *map; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (NULL); + } + if (vid_caps.type & VID_TYPE_MONOCHROME) fmt=VIDEO_PALETTE_GREY; + if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { + fprintf(stderr, "no mmap falling back on read\n"); + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno)); + return (NULL); + } + read_img=1; + map=malloc(width*height*3); + return (map); + } + /* If we are going to capture greyscale we need room to blow the image up */ + if (fmt==VIDEO_PALETTE_GREY) + map=mmap(0, vid_buf.size*3, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + else + map=mmap(0, vid_buf.size, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); + + if ((unsigned char *)-1 == (unsigned char *)map) + return (NULL); + return map; +} + +int start_pipe (int dev, int width, int height) +{ + struct video_capability vid_caps; + struct video_window vid_win; + struct video_picture vid_pic; + + if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) { + printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno)); + return (1); + } + vid_pic.palette=fmt; + if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) { + printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno)); + return (1); + } + if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCGWIN"); + return (1); + } + vid_win.width=width; + vid_win.height=height; + if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) { + printf ("ioctl VIDIOCSWIN"); + return (1); + } + return 0; +} + +char *next_capture (int dev, char *map, int width, int height) +{ + int i; + char *grey, *rgb; + struct video_mmap vid_mmap; + + sigset_t set, old; + + if (read_img) { + if (fmt==VIDEO_PALETTE_GREY) { + if (read(dev, map, width*height) != width*height) + return NULL; + } else { + if (read(dev, map, width*height*3) != width*height*3) + return NULL; + } + } else { + vid_mmap.format=fmt; + vid_mmap.frame=0; + vid_mmap.width=width; + vid_mmap.height=height; + + sigemptyset (&set); //BTTV hates signals during IOCTL + sigaddset (&set, SIGCHLD); //block SIGCHLD & SIGALRM + sigaddset (&set, SIGALRM); //for the time of ioctls + sigprocmask (SIG_BLOCK, &set, &old); + + if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + if (ioctl(dev, VIDIOCSYNC, &vid_mmap) == -1) { + sigprocmask (SIG_UNBLOCK, &old, NULL); + return (NULL); + } + + sigprocmask (SIG_UNBLOCK, &old, NULL); //undo the signal blocking + } + /* Blow up a grey */ + if (fmt==VIDEO_PALETTE_GREY) { + i=width*height; + grey=map+i-1; + rgb=map+i*3; + for (; i>=0; i--, grey--) { + *(rgb--)=*grey; + *(rgb--)=*grey; + *(rgb--)=*grey; + } + } + return map; +} + +int put_image(int dev, char *image, int width, int height) +{ + if (write(dev, image, width*height*3)!=width*height*3) { + printf("Error writing image to pipe!\nError[%s]\n",strerror(errno)); + return 0; + } + return 1; +} + +void sig_handler(int signo) +{ + noexit = 0; +} + +int main (int argc, char **argv) +{ + int x, y, devin, devout; + int width, realwidth; + int height; + int widthout, realwidthout; + int heightout; + int **newy, **newx, **line; + char *image_out, *image_new; + char palette[10]={'\0'}; + + if (argc != 6) { + printf("Usage:\n\n"); + printf("resize input output widthxheight(in) widthxheight(out) rgb24|yuv420p\n\n"); + printf("example: resize /dev/video0 /dev/video1 352x288 176x144 yuv420p\n\n"); + exit(1); + } + sscanf(argv[3], "%dx%d", &width, &height); + sscanf(argv[4], "%dx%d", &widthout, &heightout); + sscanf(argv[5], "%s", palette); + + if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24; + else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P; + else fmt = VIDEO_PALETTE_RGB24; + + realwidth=width*3; + realwidthout=widthout*3; + + image_out=malloc(widthout*heightout*3); + line=malloc(sizeof(int*)*heightout); + newy=malloc(sizeof(int*)*heightout); + newx=malloc(sizeof(int*)*realwidthout); + for (y=0; y<heightout; y++) { + line[y]=malloc(sizeof(int)); + line[y][0]=y*realwidthout; + newy[y]=malloc(sizeof(int)); + newy[y][0]=y*height/heightout*realwidth; + } + for (x=0; x<widthout; x++) { + newx[x*3]=malloc(sizeof(int)); + newx[x*3+1]=malloc(sizeof(int)); + newx[x*3+2]=malloc(sizeof(int)); + newx[x*3][0]=x*width/widthout*3; + newx[x*3+1][0]=x*width/widthout*3+1; + newx[x*3+2][0]=x*width/widthout*3+2; + } + + devin=open (argv[1], O_RDWR); + if (devin < 0) { + printf ("Failed to open video device %s\nError[%s]\n",argv[1],strerror(errno)); + exit(1); + } + + devout=open (argv[2], O_RDWR); + if (devout < 0) { + printf ("Failed to open video device%s \nError[%s]\n",argv[2],strerror(errno)); + exit(1); + } + + image_new=start_capture (devin, width, height); + if (!image_new) { + printf("Capture error\nError[%s]\n",strerror(errno)); + exit(1); + } + + start_pipe(devout, widthout, heightout); + + signal(SIGTERM, sig_handler); + + printf("Starting video stream.\n"); + while ( (next_capture(devin, image_new, width, height)) && (noexit)) { + for (y=0; y<heightout; y++) { + for (x=0; x<realwidthout; x++) { + image_out[line[y][0]+x]= + image_new[newy[y][0]+newx[x][0]]; + } + } + if (put_image(devout, image_out, widthout, heightout)==0) + exit(1); + } + printf("You bought vaporware!\nError[%s]\n",strerror(errno)); + close (devin); + close (devout); + + for (y=0; y<heightout; y++) { + free(line[y]); + free(newy[y]); + } + for (x=0; x<widthout; x++) { + free(newx[x*3]); + free(newx[x*3+1]); + free(newx[x*3+2]); + } + + free(line); + free(newx); + free(newy); + + free(image_out); + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vloopback.c Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,1136 @@ +/* + * vloopback.c + * + * Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000 + * Additional copyright by the contributing authors in the + * change history below, 2000-2007 + * + * Published under the GNU Public License. + * + * The Video Loopback Device is no longer systematically maintained. + * The project is a secondary project for the project "motion" found at + * http://motion.sourceforge.net/ and + * http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome + * and with the vloopback stored at + * http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice + * + * CHANGE HISTORY + * + * UPDATED: Jeroen Vreeken. + * Added locks for smp machines. UNTESTED! + * Made the driver much more cpu friendly by using + * a wait queue. + * Went from vmalloc to rvmalloc (yes, I stole the code + * like everybody else) and implemented mmap. + * Implemented VIDIOCGUNIT and removed size/palette checks + * in VIDIOCSYNC. + * Cleaned up a lot of code. + * Changed locks to semaphores. + * Disabled changing size while somebody is using mmap + * Changed mapped check to open check, also don't allow + * a open for write while somebody is reading. + * Added /proc support + * Set dumped count to zero at open. + * Modified /proc layout (added vloopbacks entry) + * + * 05.10.00 (MTS) Added Linux 2.2 support + * 06.10.00 (J Vreeken) Fixed 2.2 support to make things work under 2.4 again. + * 17.10.00 (J Vreeken) Added zero copy mode + * 19.10.00 (J Vreeken) Added SIGIO on device close. + * 24.10.00 (J Vreeken) Modified 2.2 stuff and removed spinlock.h + * released 0.81 + * 27.10.00 (J Vreeken) Implemented poll + * released 0.82 + * 17.01.01 (J Vreeken) support for xawtv + * Implemented VIDIOCGFBUF + * Additional checks on framebuffer freeing. + * released 0.83 + * 31.01.01 (J Vreeken) Removed need for 'struct ioctl', use _IOC_SIZE() and + * IOC_IN instead. + * Change the ioctlnr passing to 'unsigned long int' + * Instead of just one byte. + * THIS BREAKS COMPATIBILITY WITH PREVIOUS VERSIONS!!! + * 29.06.01 (J Vreeken) Added dev_offset module option + * Made vloopback_template sane + * Added double buffering support + * Made vloopback less verbose + * 20.11.01 (tibit) Made dev_offset option sane + * "Fixed" zerocopy mode by defining the ioctl + * VIDIOCSINVALID. An application which provides data + * has to issue it when it encounters an error in + * ioctl processing. See dummy.c for examples. + * 26.11.03 (Kenneth Lavrsen) + * released 0.91 + * 0.91 is the combination of the 0.90-tibit by + * Tilmann Bitterberg and an update of the Makefile by + * Roberto Carvajal. + * 23.01.05 (W Brack) + * (don't know what happened to the comments for 0.92 + * and 0.93, but I tentatively named this one as 0.99) + * enhanced for linux-2.6, with #ifdef to keep it + * compatible with linux-2.4. For linux versions + * > 2.5, I changed the memory management + * routines to the "more modern" way, most of it + * shamelessly copied from other drivers. I also + * added in the code necessary to avoid the "videodev + * has no release callback" message when installing. + * For versions < 2.5, I updated the routines to be + * closer to several other drivers. + * + * 04.02.05 (Angel Carpintero) + * Fixed version number to 0.93-pre1. + * Fixed warning for interruptible_sleep_on() deprecated and added + * wait_event_interruptible compatible with 2.6.x and 2.7. + * Fixed memory manager for kernel version > 2.6.9. + * + * 07.02.05 (Kenneth Lavrsen) + * Changed version to 0.94. + * Released as formal released version + * + * 20.02.05 (W Brack) + * Fixed error with wait_event_interruptible. + * Fixed crash when pipe source was stopped before dest. + * + * 20.02.05 (Angel Carpintero) + * Added install and uninstall in Makefile. + * + * + * 25.04.05 (Angel Carpintero) + * Included Samuel Audet's patch, it checks if the input is already + * opened in write mode. + * + * 02.05.05 (Kenneth Lavrsen) + * Released 0.95-snap2 formerly as 0.95 + * + * 10.05.05 (Angel Carpintero) + * Added MODULE_VERSION(), fixed create_pipes when video_register_device() returns + * -ENFILE . + * Fix warnings about checking return value from copy_to_user() and copy_from_user() functions. + * + * 14.11.05 (Angel Carpintero) + * Added <linux/version.h> that includes LINUX_VERSION_CODE and KERNEL_VERSION to fix + * compilation agains kernel 2.6.14 , change version to 0.97-snap1 + * + * 19.12.05 (Angel Carpintero) + * Added to example option to choose between rgb24 or yuv420p palettes. + * + * 31.12.05 (Angel Carpintero) + * Fixed examples, remove perror calls and add support to dummy.c for sysfs. + * + * 04.06.06 (Angel Carpintero) + * Add module_param() for kernel > 2.5 because MODULE_PARAM() macro is obsolete. + * + * 17.06.06 (Angel Carpintero) + * Release version 1.0 with some fixes and code clean up. Added a Jack Bates contribution + * to allow build a kernel module in debian way. + * + * 26.06.06 (Angel Carpintero) + * Added some improvements in Makefile. Fix a problem to compile in Suse. + * + * + * 02.11.06 (Angel Carpintero) + * Make compatible with new kernel stable version 2.6.18, Many functions and declarations has + * been moved to media/v42l-dev.h and remove from videodev.h/videodev2.h + * + * 18.01.07 (Angel Carpintero) + * Change -ENOIOCTLCMD by more appropiate error -ENOTTY. + */ + + +#define VLOOPBACK_VERSION "1.1-rc1" + +/* Include files common to 2.4 and 2.6 versions */ +#include <linux/version.h> /* >= 2.6.14 LINUX_VERSION_CODE */ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pagemap.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) +#include <media/v4l2-common.h> +#endif + +#include <linux/videodev.h> +#include <linux/vmalloc.h> +#include <linux/wait.h> + +/* Include files which are unique to versions */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + #include <asm/ioctl.h> + #include <asm/page.h> + #include <asm/pgtable.h> + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + #ifndef remap_pfn_range + #define remap_pfn_range(a,b,c,d,e) \ + remap_page_range((a),(b),(c)<<PAGE_SHIFT,(d),(e)) + #endif + #ifndef vmalloc_to_pfn + #define vmalloc_to_pfn(a) page_to_pfn(vmalloc_to_page((a))) + #endif + #endif + #include <asm/uaccess.h> + #include <linux/init.h> + #include <linux/device.h> +#else + #include <linux/mm.h> + #include <linux/slab.h> + #include <linux/wrapper.h> + #include <asm/io.h> +#endif + +#define VIDIOCSINVALID _IO('v',BASE_VIDIOCPRIVATE+1) + +#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg) + +struct vloopback_private { + int pipenr; + int in; /* bool */ +}; +typedef struct vloopback_private *priv_ptr; + +struct vloopback_pipe { + struct video_device *vloopin; + struct video_device *vloopout; + char *buffer; + unsigned long buflength; + unsigned int width, height; + unsigned int palette; + unsigned long frameswrite; + unsigned long framesread; + unsigned long framesdumped; + unsigned int wopen; + unsigned int ropen; + struct semaphore lock; + wait_queue_head_t wait; + unsigned int frame; + unsigned int pid; + unsigned int zerocopy; + unsigned long int ioctlnr; + unsigned int invalid_ioctl; /* 0 .. none invalid; 1 .. invalid */ + unsigned int ioctllength; + char *ioctldata; + char *ioctlretdata; +}; + +#define MAX_PIPES 16 +#define N_BUFFS 2 /* Number of buffers used for pipes */ + +static struct vloopback_pipe *loops[MAX_PIPES]; +static int nr_o_pipes=0; +static int pipes=-1; +static int spares=0; +static int pipesused=0; +static int dev_offset=-1; + +/********************************************************************** + * + * Memory management - revised for 2.6 kernels + * + **********************************************************************/ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva; + + kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + return __pa(kva); +} +#endif + +static void *rvmalloc(unsigned long size) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + struct page *page; +#endif + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + page = vmalloc_to_page((void *)adr); + mem_map_reserve(page); +#else + SetPageReserved(vmalloc_to_page((void *)adr)); +#endif + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + struct page *page; +#endif + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + page = vmalloc_to_page((void *)adr); + mem_map_unreserve(page); +#else + ClearPageReserved(vmalloc_to_page((void *)adr)); +#endif + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + + +static int create_pipe(int nr); + +static int fake_ioctl(int nr, unsigned long int cmd, void *arg) +{ + unsigned long fw; + + loops[nr]->ioctlnr=cmd; + memcpy(loops[nr]->ioctldata, arg, _IOC_SIZE(cmd)); + loops[nr]->ioctllength=_IOC_SIZE(cmd); + kill_proc(loops[nr]->pid, SIGIO, 1); /* Signal the pipe feeder */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + fw = loops[nr]->frameswrite; + wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); +#else + interruptible_sleep_on(&loops[nr]->wait); +#endif + if (cmd & IOC_IN) { + if (memcmp (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd))) + return 1; + } else { + memcpy (arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd)); + } + return 0; +} + +static int vloopback_open(struct inode *inod, struct file *f) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + + + /* Only allow a output to be opened if there is someone feeding + * the pipe. + */ + if (!ptr->in) { + if (loops[nr]->buffer==NULL) { + return -EINVAL; + } + loops[nr]->framesread=0; + loops[nr]->ropen=1; + } else { + if (loops[nr]->ropen || loops[nr]->wopen) + return -EBUSY; + loops[nr]->framesdumped=0; + loops[nr]->frameswrite=0; + loops[nr]->wopen=1; + loops[nr]->zerocopy=0; + loops[nr]->ioctlnr=-1; + pipesused++; + if (nr_o_pipes-pipesused<spares) { + if (!create_pipe(nr_o_pipes)) { + info("Creating extra spare pipe"); + info("Loopback %d registered, input: video%d, output: video%d", + nr_o_pipes, + loops[nr_o_pipes]->vloopin->minor, + loops[nr_o_pipes]->vloopout->minor + ); + nr_o_pipes++; + } + } + loops[nr]->pid=current->pid; + } + return 0; +} + +static int vloopback_release(struct inode * inod, struct file *f) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + + if (ptr->in) { + down(&loops[nr]->lock); + if (loops[nr]->buffer && !loops[nr]->ropen) { + rvfree(loops[nr]->buffer, + loops[nr]->buflength*N_BUFFS); + loops[nr]->buffer=NULL; + } + up(&loops[nr]->lock); + loops[nr]->frameswrite++; + if (waitqueue_active(&loops[nr]->wait)) + wake_up(&loops[nr]->wait); + + loops[nr]->width=0; + loops[nr]->height=0; + loops[nr]->palette=0; + loops[nr]->wopen=0; + pipesused--; + } else { + down(&loops[nr]->lock); + if (loops[nr]->buffer && !loops[nr]->wopen) { + rvfree(loops[nr]->buffer, + loops[nr]->buflength*N_BUFFS); + loops[nr]->buffer=NULL; + } + up(&loops[nr]->lock); + loops[nr]->ropen=0; + if (loops[nr]->zerocopy && loops[nr]->buffer) { + loops[nr]->ioctlnr=0; + loops[nr]->ioctllength=0; + kill_proc(loops[nr]->pid, SIGIO, 1); + } + } + + return 0; +} + +static ssize_t vloopback_write(struct file *f, const char *buf, + size_t count, loff_t *offset) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + unsigned long realcount=count; + + if (!ptr->in) + return -EINVAL; + if (loops[nr]->zerocopy) + return -EINVAL; + + if (loops[nr]->buffer==NULL) { + return -EINVAL; + } + + /* Anybody want some pictures??? */ + if (!waitqueue_active(&loops[nr]->wait)) { + loops[nr]->framesdumped++; + return realcount; + } + + down(&loops[nr]->lock); + if (!loops[nr]->buffer) { + up(&loops[nr]->lock); + return -EINVAL; + } + if (realcount > loops[nr]->buflength) { + realcount = loops[nr]->buflength; + info("Too much data! Only %ld bytes used.", realcount); + } + + if (copy_from_user( + loops[nr]->buffer+loops[nr]->frame*loops[nr]->buflength, + buf, realcount + )) return -EFAULT; + + loops[nr]->frame=0; + up(&loops[nr]->lock); + + loops[nr]->frameswrite++; + wake_up(&loops[nr]->wait); + + return realcount; +} + +static ssize_t vloopback_read (struct file * f, char * buf, size_t count, loff_t *offset) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + unsigned long realcount=count; + + if (loops[nr]->zerocopy) { + if (ptr->in) { + if (realcount > loops[nr]->ioctllength+sizeof(unsigned long int)) + realcount=loops[nr]->ioctllength+sizeof(unsigned long int); + if (copy_to_user(buf , &loops[nr]->ioctlnr, sizeof(unsigned long int))) + return -EFAULT; + if (copy_to_user(buf+sizeof(unsigned long int) , loops[nr]->ioctldata, + realcount-sizeof(unsigned long int))) + return -EFAULT; + if (loops[nr]->ioctlnr==0) + loops[nr]->ioctlnr=-1; + return realcount; + } else { + struct video_window vidwin; + struct video_mmap vidmmap; + struct video_picture vidpic; + + fake_ioctl(nr, VIDIOCGWIN, &vidwin); + fake_ioctl(nr, VIDIOCGPICT, &vidpic); + + vidmmap.height=vidwin.height; + vidmmap.width=vidwin.width; + vidmmap.format=vidpic.palette; + vidmmap.frame=0; + if (fake_ioctl(nr, VIDIOCMCAPTURE, &vidmmap)) + return 0; + if (fake_ioctl(nr, VIDIOCSYNC, &vidmmap)) + return 0; + realcount=vidwin.height*vidwin.width*vidpic.depth; + } + } + if (ptr->in) + return -EINVAL; + + if (realcount > loops[nr]->buflength) { + realcount = loops[nr]->buflength; + info("Not so much data in buffer!"); + } + + if (!loops[nr]->zerocopy) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + unsigned long fw=loops[nr]->frameswrite; + + wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); +#else + interruptible_sleep_on(&loops[nr]->wait); +#endif + } + + down(&loops[nr]->lock); + if (!loops[nr]->buffer) { + up(&loops[nr]->lock); + return 0; + } + if (copy_to_user(buf, loops[nr]->buffer, realcount)) + return -EFAULT; + up(&loops[nr]->lock); + + loops[nr]->framesread++; + return realcount; +} + +static int vloopback_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + unsigned long start = (unsigned long)vma->vm_start; + long size = vma->vm_end - vma->vm_start; + unsigned long page, pos; + + down(&loops[nr]->lock); + if (ptr->in) { + loops[nr]->zerocopy=1; + if (loops[nr]->ropen) { + info("Can't change size while opened for read"); + up(&loops[nr]->lock); + return -EINVAL; + } + if (!size) { + up(&loops[nr]->lock); + return -EINVAL; + } + if (loops[nr]->buffer) + rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); + loops[nr]->buflength=size; + loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); + } + if (loops[nr]->buffer == NULL) { + up(&loops[nr]->lock); + return -EINVAL; + } + + if (size > (((N_BUFFS * loops[nr]->buflength) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + up(&loops[nr]->lock); + return -EINVAL; + } + + pos = (unsigned long)loops[nr]->buffer; + while (size > 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) + page = kvirt_to_pa(pos); + if (remap_page_range(vma,start, page, PAGE_SIZE, PAGE_SHARED)) { +#else + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, + PAGE_SHARED)) { +#endif + up(&loops[nr]->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + up(&loops[nr]->lock); + + return 0; +} + +static int vloopback_ioctl(struct inode *inod, struct file *f, unsigned int cmd, unsigned long arg) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + int i; + + if (loops[nr]->zerocopy) { + if (!ptr->in) { + loops[nr]->ioctlnr=cmd; + loops[nr]->ioctllength=_IOC_SIZE(cmd); + /* info("DEBUG: vl_ioctl: !loop->in"); */ + /* info("DEBUG: vl_ioctl: cmd %lu", cmd); */ + /* info("DEBUG: vl_ioctl: len %lu", loops[nr]->ioctllength); */ + if(copy_from_user(loops[nr]->ioctldata, (void*)arg, _IOC_SIZE(cmd))) + return -EFAULT; + kill_proc(loops[nr]->pid, SIGIO, 1); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + wait_event_interruptible(loops[nr]->wait, loops[nr]->ioctlnr==-1); +#else + interruptible_sleep_on(&loops[nr]->wait); +#endif + + if (loops[nr]->invalid_ioctl) { + //info ("DEBUG: There was an invalid ioctl"); + loops[nr]->invalid_ioctl = 0; + return -ENOTTY; + } + if (cmd & IOC_IN && !(cmd & IOC_OUT)) { + //info("DEBUG: vl_ioctl: cmd & IOC_IN 1"); + if (memcmp(loops[nr]->ioctlretdata, loops[nr]->ioctldata, _IOC_SIZE(cmd))) { + return -EINVAL; + } + //info("DEBUG: vl_ioctl: cmd & IOC_IN 2"); + return 0; + } else { + if (copy_to_user((void*)arg, loops[nr]->ioctlretdata, _IOC_SIZE(cmd))) + return -EFAULT; + //info("DEBUG: vl_ioctl: !(cmd & IOC_IN) 1"); + return 0; + } + } else { + if ( (loops[nr]->ioctlnr!=cmd) && (cmd != (VIDIOCSINVALID))) { + /* wrong ioctl */ + info("DEBUG: vo_ioctl: Wrong IOCTL"); + return 0; + } + if (cmd == VIDIOCSINVALID) { + loops[nr]->invalid_ioctl = 1; + } else { + if (copy_from_user(loops[nr]->ioctlretdata, (void*)arg, loops[nr]->ioctllength)) + return -EFAULT; + } + loops[nr]->ioctlnr=-1; + if (waitqueue_active(&loops[nr]->wait)) + wake_up(&loops[nr]->wait); + return 0; + } + } + switch(cmd) + { + /* Get capabilities */ + case VIDIOCGCAP: + { + struct video_capability b; + if (ptr->in) { + sprintf(b.name, "Video loopback %d input", + ptr->pipenr); + b.type = 0; + } else { + sprintf(b.name, "Video loopback %d output", + ptr->pipenr); + b.type = VID_TYPE_CAPTURE; + } + b.channels=1; + b.audios=0; + b.maxwidth=loops[nr]->width; + b.maxheight=loops[nr]->height; + b.minwidth=20; + b.minheight=20; + if(copy_to_user((void*)arg, &b, sizeof(b))) + return -EFAULT; + return 0; + } + /* Get channel info (sources) */ + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&v, (void*)arg, sizeof(v))) + return -EFAULT; + if(v.channel!=0) { + info("VIDIOCGCHAN: Invalid Channel, was %d", v.channel); + v.channel=0; + //return -EINVAL; + } + v.flags=0; + v.tuners=0; + v.norm=0; + v.type = VIDEO_TYPE_CAMERA; + /*strcpy(v.name, "Loopback"); -- tibit */ + strcpy(v.name, "Composite1"); + if(copy_to_user((void*)arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + /* Set channel */ + case VIDIOCSCHAN: + { + int v; + if(copy_from_user(&v, (void*)arg, sizeof(v))) + return -EFAULT; + if(v!=0) { + info("VIDIOCSCHAN: Invalid Channel, was %d", v); + return -EINVAL; + } + return 0; + } + /* Get tuner abilities */ + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, (void*)arg, sizeof(v))!=0) + return -EFAULT; + if(v.tuner) { + info("VIDIOCGTUNER: Invalid Tuner, was %d", v.tuner); + return -EINVAL; + } + strcpy(v.name, "Format"); + v.rangelow=0; + v.rangehigh=0; + v.flags=0; + v.mode=VIDEO_MODE_AUTO; + if(copy_to_user((void*)arg,&v, sizeof(v))!=0) + return -EFAULT; + return 0; + } + /* Get picture properties */ + case VIDIOCGPICT: + { + struct video_picture p; + p.colour=0x8000; + p.hue=0x8000; + p.brightness=0x8000; + p.contrast=0x8000; + p.whiteness=0x8000; + p.depth=0x8000; + p.palette=loops[nr]->palette; + if(copy_to_user((void*)arg, &p, sizeof(p))) + return -EFAULT; + return 0; + + } + /* Set picture properties */ + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, (void*)arg, sizeof(p))) + return -EFAULT; + if (!ptr->in) { + if (p.palette!=loops[nr]->palette) + return -EINVAL; + } else + loops[nr]->palette=p.palette; + return 0; + } + /* Get the video overlay window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x=0; + vw.y=0; + vw.width=loops[nr]->width; + vw.height=loops[nr]->height; + vw.chromakey=0; + vw.flags=0; + vw.clipcount=0; + if(copy_to_user((void*)arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ + case VIDIOCSWIN: + { + struct video_window vw; + + if(copy_from_user(&vw, (void*)arg, sizeof(vw))) + return -EFAULT; + if(vw.flags) + return -EINVAL; + if(vw.clipcount) + return -EINVAL; + if (loops[nr]->height==vw.height && + loops[nr]->width==vw.width) + return 0; + if(!ptr->in) { + return -EINVAL; + } else { + loops[nr]->height=vw.height; + loops[nr]->width=vw.width; + /* Make sure nobody is using the buffer while we + fool around with it. + We are also not allowing changes while + somebody using mmap has the output open. + */ + down(&loops[nr]->lock); + if (loops[nr]->ropen) { + info("Can't change size while opened for read"); + up(&loops[nr]->lock); + return -EINVAL; + } + if (loops[nr]->buffer) + rvfree(loops[nr]->buffer, loops[nr]->buflength*N_BUFFS); + loops[nr]->buflength=vw.width*vw.height*4; + loops[nr]->buffer=rvmalloc(loops[nr]->buflength*N_BUFFS); + up(&loops[nr]->lock); + } + return 0; + } + /* Memory map buffer info */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + vm.size=loops[nr]->buflength*N_BUFFS; + vm.frames=N_BUFFS; + for (i=0; i<vm.frames; i++) + vm.offsets[i]=i*loops[nr]->buflength; + if(copy_to_user((void*)arg, &vm, sizeof(vm))) + return -EFAULT; + return 0; + } + /* Grab frames */ + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (ptr->in) + return -EINVAL; + if (!loops[nr]->buffer) + return -EINVAL; + if (copy_from_user(&vm, (void*)arg, sizeof(vm))) + return -EFAULT; + if (vm.format!=loops[nr]->palette) + return -EINVAL; + if (vm.frame > N_BUFFS) + return -EINVAL; + return 0; + } + /* Sync with mmap grabbing */ + case VIDIOCSYNC: + { + int frame; + unsigned long fw; + + if (copy_from_user((void *)&frame, (void*)arg, sizeof(int))) + return -EFAULT; + if (ptr->in) + return -EINVAL; + if (!loops[nr]->buffer) + return -EINVAL; + /* Ok, everything should be alright since the program + should have called VIDIOMCAPTURE and we are ready to + do the 'capturing' */ + if (frame > 1) + return -EINVAL; + loops[nr]->frame=frame; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + fw = loops[nr]->frameswrite; + wait_event_interruptible(loops[nr]->wait, fw!=loops[nr]->frameswrite); +#else + interruptible_sleep_on(&loops[nr]->wait); +#endif + if (!loops[nr]->buffer) /* possibly released during sleep */ + return -EINVAL; + loops[nr]->framesread++; + return 0; + } + /* Get attached units */ + case VIDIOCGUNIT: + { + struct video_unit vu; + + if (ptr->in) + vu.video=loops[nr]->vloopout->minor; + else + vu.video=loops[nr]->vloopin->minor; + vu.vbi=VIDEO_NO_UNIT; + vu.radio=VIDEO_NO_UNIT; + vu.audio=VIDEO_NO_UNIT; + vu.teletext=VIDEO_NO_UNIT; + if (copy_to_user((void*)arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + /* Get frame buffer */ + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base=NULL; + + if(copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + /* Start, end capture */ + case VIDIOCCAPTURE: + { + int start; + if (copy_from_user(&start, (void*)arg, sizeof(int))) + return -EFAULT; + if (start) info ("Capture started"); + else info ("Capture stopped"); + + return 0; + } + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + case VIDIOCKEY: + return 0; + default: + return -ENOTTY; + //return -ENOIOCTLCMD; + } + return 0; +} + +static unsigned int vloopback_poll(struct file *f, struct poll_table_struct *wait) +{ + struct video_device *loopdev=video_devdata(f); + priv_ptr ptr=(priv_ptr)loopdev->priv; + int nr=ptr->pipenr; + + if (loopdev==NULL) + return -EFAULT; + if (!ptr->in) + return 0; + + if (loops[nr]->ioctlnr!=-1) { + if (loops[nr]->zerocopy) { + return (POLLIN | POLLPRI | POLLOUT | POLLRDNORM); + } else { + return (POLLOUT); + } + } + return 0; +} + +static struct file_operations fileops_template= +{ + owner: THIS_MODULE, + open: vloopback_open, + release: vloopback_release, + read: vloopback_read, + write: vloopback_write, + poll: vloopback_poll, + ioctl: vloopback_ioctl, + mmap: vloopback_mmap, +}; + +static struct video_device vloopback_template= +{ + owner: THIS_MODULE, + name: "Video Loopback", + type: VID_TYPE_CAPTURE, + fops: &fileops_template, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + release: video_device_release, +#endif +}; + +static int create_pipe(int nr) +{ + int minor_in, minor_out , ret; + + if (dev_offset == -1) + minor_in = minor_out = -1; /* autoassign */ + else { + minor_in = 2*nr + dev_offset; + minor_out = 2*nr+1 + dev_offset; + } + + /* allocate space for this pipe */ + loops[nr]= kmalloc(sizeof(struct vloopback_pipe), GFP_KERNEL); + if (!loops[nr]) + return -ENOMEM; + /* set up a new video device plus our private area */ + loops[nr]->vloopin= video_device_alloc(); + if (loops[nr]->vloopin == NULL) + return -ENOMEM; + *loops[nr]->vloopin = vloopback_template; + loops[nr]->vloopin->priv= kmalloc(sizeof(struct vloopback_private), + GFP_KERNEL); + if (loops[nr]->vloopin->priv == NULL) { + kfree(loops[nr]->vloopin); + return -ENOMEM; + } + /* repeat for the output device */ + loops[nr]->vloopout= video_device_alloc(); + if (loops[nr]->vloopout == NULL) { + kfree(loops[nr]->vloopin->priv); + kfree(loops[nr]->vloopin); + return -ENOMEM; + } + *loops[nr]->vloopout = vloopback_template; + loops[nr]->vloopout->priv= kmalloc(sizeof(struct vloopback_private), + GFP_KERNEL); + if (loops[nr]->vloopout->priv == NULL) { + kfree(loops[nr]->vloopin->priv); + kfree(loops[nr]->vloopin); + kfree(loops[nr]->vloopout); + return -ENOMEM; + } + + ((priv_ptr)loops[nr]->vloopin->priv)->pipenr=nr; + ((priv_ptr)loops[nr]->vloopout->priv)->pipenr=nr; + loops[nr]->invalid_ioctl = 0; /* tibit */ + loops[nr]->buffer=NULL; + loops[nr]->width=0; + loops[nr]->height=0; + loops[nr]->palette=0; + loops[nr]->frameswrite=0; + loops[nr]->framesread=0; + loops[nr]->framesdumped=0; + loops[nr]->wopen=0; + loops[nr]->ropen=0; + loops[nr]->frame=0; + + ((priv_ptr)loops[nr]->vloopin->priv)->in=1; + ((priv_ptr)loops[nr]->vloopout->priv)->in=0; + loops[nr]->vloopin->type=0; + sprintf(loops[nr]->vloopin->name, "Video loopback %d input", nr); + loops[nr]->vloopout->type=VID_TYPE_CAPTURE; + sprintf(loops[nr]->vloopout->name, "Video loopback %d output", nr); + init_waitqueue_head(&loops[nr]->wait); + init_MUTEX(&loops[nr]->lock); + + ret = video_register_device(loops[nr]->vloopin, VFL_TYPE_GRABBER,minor_in); + + if ((ret == -1 ) || ( ret == -23 )) { + info("error registering device %s",loops[nr]->vloopin->name); + kfree(loops[nr]->vloopin->priv); + kfree(loops[nr]->vloopin); + kfree(loops[nr]->vloopout->priv); + kfree(loops[nr]->vloopout); + kfree(loops[nr]); + loops[nr]=NULL; + return ret; + } + + ret = video_register_device(loops[nr]->vloopout, VFL_TYPE_GRABBER,minor_out); + + if ((ret ==-1) || (ret == -23)) { + info("error registering device %s", loops[nr]->vloopout->name); + kfree(loops[nr]->vloopin->priv); + video_unregister_device(loops[nr]->vloopin); + kfree(loops[nr]->vloopout->priv); + kfree(loops[nr]->vloopout); + kfree(loops[nr]); + loops[nr]=NULL; + return ret; + } + + loops[nr]->ioctldata=kmalloc(1024, GFP_KERNEL); + loops[nr]->ioctlretdata=kmalloc(1024, GFP_KERNEL); + return 0; +} + + +/**************************************************************************** + * init stuff + ****************************************************************************/ + + +MODULE_AUTHOR("J.B. Vreeken (pe1rxq@amsat.org)"); +MODULE_DESCRIPTION("Video4linux loopback device."); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +module_param(pipes, int, 000); +#else +MODULE_PARM(pipes, "i"); +#endif + +MODULE_PARM_DESC(pipes, "Nr of pipes to create (each pipe uses two video devices)"); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +module_param(spares, int, 000); +#else +MODULE_PARM(spares, "i"); +#endif + +MODULE_PARM_DESC(spares, "Nr of spare pipes that should be created"); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) +module_param(dev_offset, int, 000); +#else +MODULE_PARM(dev_offset_param, "i"); +#endif + +MODULE_PARM_DESC(dev_offset, "Prefered offset for video device numbers"); +MODULE_LICENSE("GPL"); +MODULE_VERSION( VLOOPBACK_VERSION ); + +static int __init vloopback_init(void) +{ + int i,ret; + + info("Video4linux loopback driver v"VLOOPBACK_VERSION); + + if (pipes==-1) pipes=1; + if (pipes > MAX_PIPES) { + pipes=MAX_PIPES; + info("Nr of pipes is limited to: %d", MAX_PIPES); + } + + for (i=0; i<pipes; i++) { + + ret = create_pipe(i); + + if (ret == 0) { + info("Loopback %d registered, input: video%d," + "output: video%d", + i, loops[i]->vloopin->minor, + loops[i]->vloopout->minor); + nr_o_pipes=i+1; + }else{ + return ret; + } + } + return 0; +} + +static void __exit cleanup_vloopback_module(void) +{ + int i; + + info("Unregistering video4linux loopback devices"); + for (i=0; i<nr_o_pipes; i++) if (loops[i]) { + kfree(loops[i]->vloopin->priv); + video_unregister_device(loops[i]->vloopin); + kfree(loops[i]->vloopout->priv); + video_unregister_device(loops[i]->vloopout); + if (loops[i]->buffer) rvfree(loops[i]->buffer, loops[i]->buflength*N_BUFFS); + kfree(loops[i]->ioctldata); + kfree(loops[i]->ioctlretdata); + kfree(loops[i]); + } +} + +module_init(vloopback_init); +module_exit(cleanup_vloopback_module);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vloopback.html Sun Apr 01 05:22:43 2007 +0000 @@ -0,0 +1,148 @@ +<HTML> +<HEAD> + <TITLE> + Video4Linux loopback API + </TITLE> + <H1> + Video4Linux loopback API + </H1> +</HEAD> +<BODY bgcolor="white"> + <P> + Author: Jeroen Vreeken (pe1rxq@amsat.org)<BR> + Version: 0.90<BR> + Date: 31-01-2001<BR> + </P> + <P> + <H2> + Introduction: + </H2> + This document describes the API for the Video4Linux loopback driver. + The driver implements a video pipe using two video4linux devices. + The first device is used by the program supplying the data, + from now on this program will be refered to with <I>'pusher'</I>.<BR> + The second device acts as if it were a normall video4linux device, + it should be usable by any application that honours the video4linux + specifications. This application will from now on be refered to as + <I>'client'</I>.<BR> + The calls and ioctls mentioned in this document refer to those + as described in the Video4Linux API. + <BR> + The loopback device has two operating modes: + <UL> + <LI> + A simple one-copy mode in which <I>pusher</I> specifies the + size of the images and the used palette and uses write to + push its images to the pipe.<BR> + This mode is mostly for feeding fixed size images without any + knowledge about <I>client</I>. + </LI> + <LI> + A zero-copy mode in which <I>pusher</I> regularly polls the + device if <I>client</I> does an ioctl.<BR> + In this mode <I>pusher</I> has almost complete control over the + devices behaviour and it will be mainly used to implement + complex multiple tuner/channel/size configurations. + With this mode it should be possible to use the Xvideo + extensions as normal video4linux capture devices. + </LI> + </UL> + </P> + <P align=left> + <H2> + Locating a free pipe + </H2> + In order to find an unused pipe <I>pusher</I> will have to scan + the contents of /proc/video/vloopback/<BR> + Each pipe will have its own entry in the form of <I>vloopback0</I> to + <I>vloopbackN</I>, N being the total number of available pipes-1.<BR> + There will also be a general <I>vloopbacks</I> file which will contain + information on all entries. + <BR> + Each of these files will have references to the input and output + video device and will also indicate if either of them is currently + in use.<BR> + <BR> + Once <I>pusher</I> has found a free pipe it can claim it by simply + opening the input device. + </P> + <P align=left> + <H2> + One-copy mode + </H2> + In this mode <I>pusher</I> only has to provide images using the + <B>write()</B> call, + the driver will handle the communication with <I>client</I> or + will drop the images if the output is unused. + To <I>client</I> the device will closely resemble a webcam driver.<BR> + <BR> + In order to use it <I>pusher</I> will open the input device. + Before writing it will first have to set the palette it is going to use + by calling <B>VIDIOCSPICT</B>, and the size by calling + <B>VIDIOCSWIN</B>. + After this it can call <B>write()</B> for each frame it wants to send + to the pipe.<BR> + <BR> + When there is no application using the device the driver will simply + drop the frames which will result in a 'no-copy' situation while + writing to an unused pipe.<BR> + <I>Note: when client is using read() instead of mmap() the driver will + actually use a double copy.</I> + </P> + <P align=left> + <H2> + Zero-copy mode + </H2> + In this mode the driver will forward nearly all ioctls to + <I>pusher</I>.<BR> + <BR> + To initiate this mode <I>pusher</I> will have to call <B>mmap()</B> + with the size of the requested buffer. + The driver will allocate memory for this buffer and <I>pusher</I> will + gain access to it.<BR> + <I>Note: as the allocated memory might be in use by client, pusher is + NOT allowed to touch it under any circumstances with the only exeption + being between <B>VIDIOCMCAPTURE</B> and <B>VIDIOCSYNC</B>.</I><BR> + <BR> + <B> + Handling ioctls + </B><BR> + <BR> + When <I>client</I> has issued an ioctl <I>pusher</I> will receive a + <B>SIGIO</B> signal. + Pusher may check to see if it is comming from vloopback by calling + <B>poll()</B> first. + It then has to respond by calling <B>read()</B> + with a large enough buffer for the largest possible ioctl data + structure plus <B>sizeof(unsigned long int)</B>. <I>(The largest ioctl data structure is 280 bytes + in linux kernel 2.4.0-test10pre1, a buffer of 1024 bytes is recommended) + </I><BR> + The first bytes of this buffer will be the ioctl number. + This number is an unsigned long int, the remaining data is the data supplied by <I>client</I>. + <I>Pusher</I> will now have to handle this ioctl.<BR> + <BR> + If it is an ioctl requesting data <I>pusher</I> will answer it by + calling the ioctl with the requested data.<BR> + If it is an ioctl setting data <I>pusher</I> will call the ioctl with + the exact same data to accept it.<BR> + <BR> + <B> + Handling read + </B><BR> + <BR> + <I>Pusher</I> will not need to handle any read requests because the + kernel module will fake an mmap and sync call for it.<BR> + <BR> + <B> + Starting and stopping capture + </B><BR> + <BR> + The first time <B>VIDIOCMCAPTURE</B> is called <I>pusher</I> should + initialize capture and start capturing of the requested frames into + the mmapped buffer.<BR> + When <I>client</I> closes its device an 'ioctl' 0 will be send with + no data, <I>pusher</I> will tell the hardware to stop. + <BR> + </P> +</BODY> +</HTML> \ No newline at end of file