# HG changeset patch
# User KennethLavrsen
# Date 1175404963 0
# Node ID 5f21a4dddc0cec070d1bd6a90369f83ffa4a00c7
Initial checkin
diff -r 000000000000 -r 5f21a4dddc0c COPYING
--- /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.
+
+
+ Copyright (C) 19yy
+
+ 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.
+
+ , 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.
diff -r 000000000000 -r 5f21a4dddc0c Makefile
--- /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
diff -r 000000000000 -r 5f21a4dddc0c Modules.symvers
diff -r 000000000000 -r 5f21a4dddc0c README
--- /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
diff -r 000000000000 -r 5f21a4dddc0c debian/README.Debian
--- /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
+ modules_image`
+
+No matter where you downloaded vloopback, you can use the absolute path to the
+source for
+
+If you downloaded vloopback to `/usr/src/modules/s`', you can
+use just for
+
+When `make-kpkg` is finished, you should find
+'vloopback-modules-_+_.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-_+_.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 `
+
+It's not yet clear to me whether you must download vloopback to
+`/usr/src/module/`, whether you can use
+`MODULE_LOC= module-assistant build ` if
+vloopback is downloaded to `//`
+
+Consult the module-assistant documentation or contact the module-assistant
+maintainer for more information
+
+ -- Jack Bates
+
+[0] http://packages.debian.org/kernel-package
+[1] http://packages.debian.org/module-assistant
+[2] http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
diff -r 000000000000 -r 5f21a4dddc0c debian/changelog
--- /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 Wed, 30 Aug 2006 09:40:36 -0700
+
+vloopback (0.97-snap3-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Jack Bates Mon, 1 May 2006 19:48:36 -0700
+
+vloopback (0.97-snap1-1) unstable; urgency=low
+
+ * Initial release
+
+ -- Jack Bates Thu, 8 Dec 2005 16:43:57 -0800
diff -r 000000000000 -r 5f21a4dddc0c debian/compat
--- /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
diff -r 000000000000 -r 5f21a4dddc0c debian/control.modules.in
--- /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
+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.
diff -r 000000000000 -r 5f21a4dddc0c debian/copyright
--- /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'.
diff -r 000000000000 -r 5f21a4dddc0c debian/rules
--- /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
diff -r 000000000000 -r 5f21a4dddc0c example/Makefile
--- /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
diff -r 000000000000 -r 5f21a4dddc0c example/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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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; iname, "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; iframes; 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);
+}
diff -r 000000000000 -r 5f21a4dddc0c example/feed.c
--- /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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+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);
+}
diff -r 000000000000 -r 5f21a4dddc0c example/invert.c
--- /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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+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);
+}
diff -r 000000000000 -r 5f21a4dddc0c example/resize.c
--- /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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+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 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 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 /* >= 2.6.14 LINUX_VERSION_CODE */
+#include
+#include
+#include
+#include
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+#include
+#endif
+
+#include
+#include
+#include
+
+/* Include files which are unique to versions */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+ #include
+ #include
+ #include
+ #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)<
+ #include
+ #include
+#else
+ #include
+ #include
+ #include
+ #include
+#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-pipesusedvloopin->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; ibuflength;
+ 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; ivloopin->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; ivloopin->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);
diff -r 000000000000 -r 5f21a4dddc0c vloopback.html
--- /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 @@
+
+
+
+ Video4Linux loopback API
+
+
+ Video4Linux loopback API
+
+
+
+
+ Author: Jeroen Vreeken (pe1rxq@amsat.org)
+ Version: 0.90
+ Date: 31-01-2001
+
+
+
+ Introduction:
+
+ 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 'pusher'.
+ 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
+ 'client'.
+ The calls and ioctls mentioned in this document refer to those
+ as described in the Video4Linux API.
+
+ The loopback device has two operating modes:
+
+ -
+ A simple one-copy mode in which pusher specifies the
+ size of the images and the used palette and uses write to
+ push its images to the pipe.
+ This mode is mostly for feeding fixed size images without any
+ knowledge about client.
+
+ -
+ A zero-copy mode in which pusher regularly polls the
+ device if client does an ioctl.
+ In this mode pusher 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.
+
+
+
+
+
+ Locating a free pipe
+
+ In order to find an unused pipe pusher will have to scan
+ the contents of /proc/video/vloopback/
+ Each pipe will have its own entry in the form of vloopback0 to
+ vloopbackN, N being the total number of available pipes-1.
+ There will also be a general vloopbacks file which will contain
+ information on all entries.
+
+ 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.
+
+ Once pusher has found a free pipe it can claim it by simply
+ opening the input device.
+
+
+
+ One-copy mode
+
+ In this mode pusher only has to provide images using the
+ write() call,
+ the driver will handle the communication with client or
+ will drop the images if the output is unused.
+ To client the device will closely resemble a webcam driver.
+
+ In order to use it pusher will open the input device.
+ Before writing it will first have to set the palette it is going to use
+ by calling VIDIOCSPICT, and the size by calling
+ VIDIOCSWIN.
+ After this it can call write() for each frame it wants to send
+ to the pipe.
+
+ 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.
+ Note: when client is using read() instead of mmap() the driver will
+ actually use a double copy.
+
+
+
+ Zero-copy mode
+
+ In this mode the driver will forward nearly all ioctls to
+ pusher.
+
+ To initiate this mode pusher will have to call mmap()
+ with the size of the requested buffer.
+ The driver will allocate memory for this buffer and pusher will
+ gain access to it.
+ 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 VIDIOCMCAPTURE and VIDIOCSYNC.
+
+
+ Handling ioctls
+
+
+ When client has issued an ioctl pusher will receive a
+ SIGIO signal.
+ Pusher may check to see if it is comming from vloopback by calling
+ poll() first.
+ It then has to respond by calling read()
+ with a large enough buffer for the largest possible ioctl data
+ structure plus sizeof(unsigned long int). (The largest ioctl data structure is 280 bytes
+ in linux kernel 2.4.0-test10pre1, a buffer of 1024 bytes is recommended)
+
+ 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 client.
+ Pusher will now have to handle this ioctl.
+
+ If it is an ioctl requesting data pusher will answer it by
+ calling the ioctl with the requested data.
+ If it is an ioctl setting data pusher will call the ioctl with
+ the exact same data to accept it.
+
+
+ Handling read
+
+
+ Pusher will not need to handle any read requests because the
+ kernel module will fake an mmap and sync call for it.
+
+
+ Starting and stopping capture
+
+
+ The first time VIDIOCMCAPTURE is called pusher should
+ initialize capture and start capturing of the requested frames into
+ the mmapped buffer.
+ When client closes its device an 'ioctl' 0 will be send with
+ no data, pusher will tell the hardware to stop.
+
+
+
+
\ No newline at end of file