pax_global_header00006660000000000000000000000064125336371360014523gustar00rootroot0000000000000052 comment=56cc11d175b6cfdf33ed03ccd8368cb837d3992c v4l2loopback-0.9.1/000077500000000000000000000000001253363713600140345ustar00rootroot00000000000000v4l2loopback-0.9.1/.gitignore000066400000000000000000000001671253363713600160300ustar00rootroot00000000000000.tmp_versions/ *.cmd Module.symvers modules.order v4l2loopback.ko v4l2loopback.mod.c v4l2loopback.mod.o v4l2loopback.o v4l2loopback-0.9.1/AUTHORS000066400000000000000000000010521253363713600151020ustar00rootroot00000000000000Angus McInnes Aidan Thornton Anatolij Gutschin Anton Novikov Dmitry Eremin Gorinich Zmey IOhannes m zmoelnig Javier Infante Scott Maines Stefan Diewald Tasos Sahanidis tz Ted Mielczarek Vasily Levin Yusuke Ohshima v4l2loopback-0.9.1/COPYING000066400000000000000000000432541253363713600150770ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 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) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General Public License instead of this License. v4l2loopback-0.9.1/ChangeLog000066400000000000000000000323551253363713600156160ustar00rootroot00000000000000v4l2loopback (0.9.1) unstable; urgency=medium * Fixed module version -- IOhannes m zmölnig (Debian/GNU) Wed, 03 Jun 2015 19:47:23 +0200 v4l2loopback (0.9.0) unstable; urgency=medium [ IOhannes m zmölnig ] * formats * support more formats * support compressed formats * move formats-enumeration to separate file * tools to implement missing formats * controls * disable deprecated vidioc_*ctrl callbacks * register custom-controls * use ctrl_config information in (deprecated) queryctrl * fixed bugs * used static code analysis to find more bugs * more error checking * check timeperframe before setting it (Closes: #61) * make MAX_DEVICES/TIMEOUT/BUFFERS settable during build-process (Closes: #55) * check for errors returned by get_capture_buffer() * check whether there is at least 1 requestbuffer * unsigned comparision against <0 * avoid setting b->count to negative/null * ... * fixed typos * code formatting * standards compliancy * standard-conformant bus_info * pretend to not support {G,S,ENUM}_{IN,OUT}PUT depending on state * only pretend to not support IN/OUTPUT enumeration in exclusive-caps mode * test programs * for (de)queuing buffers * for writing interlaced video * compatibility with newer kernels * compatibility with older kernels * Updated documentation * Removed GFDL document * note where to get API documentation [ tatokis ] * Updated v4l2loopback.c to compile on >= 3.18 kernel [ tz ] * add ondemandcam [ Yusuke Ohshima ] * Fix issue #79 [ Tasos Sahanidis ] * Fix for kernel 4.0 -- IOhannes m zmölnig Tue, 02 Jun 2015 19:58:39 +0200 v4l2loopback (0.8.0) unstable; urgency=medium [ Dmitry Eremin ] * Add DKMS support. [ Angus McInnes ] * Make vidioc_g_fmt_out not change the format * Set correct output buffer type in vidioc_dqbuf [ Javier Infante ] * Added card_labels option when loading module. [ IOhannes m zmölnig ] * renamed 'card_labels' to 'card_label' * removed '-e' flag from call to 'depmod' (needs '-E' or '-F') * auto-detect new version * auto-update dkms.conf to new version -- IOhannes m zmölnig Tue, 10 Dec 2013 18:12:15 +0100 v4l2loopback (0.7.1) unstable; urgency=low [ Aidan Thornton ] * Linux 3.11 compatibility fix [ IOhannes m zmölnig ] * trying to keep pre-2.6.29 compatibility -- IOhannes m zmoelnig (gpg-key at iem) Mon, 16 Sep 2013 09:55:51 +0200 v4l2loopback (0.7.0) unstable; urgency=low [ IOhannes m zmölnig ] * don't implement STD-ioctls * Revert "dummy audio ioctl's that return EINVAL" * disable more STD-stuff based on V4L2LOOPBACK_WITH_STD * don't announce all caps capabilities * only announce capture/output capabilities if possible * 'exclusive_caps' parameter to control caps announcment * avoid duplicate setting of cardname * break lines * remove commented out code * updated AUTHORS information * fixed ChangeLog for 0.6.1 * updated NEWS for last releases [ Anatolij Gustschin ] * fix missing spin lock init * add newlines to debug statements [ Hans Verkuil ] * reformatting to kernel-standards -- IOhannes m zmoelnig (gpg-key at iem) Fri, 07 Jun 2013 11:24:34 +0200 v4l2loopback (0.6.3) unstable; urgency=low [ Ted Mielczarek ] * Fill in the "v4l2_capability::bus_info" field (Closes: #30) [ IOhannes m zmölnig ] * make "v4l2_capability::card" unique per device (Closes: #37) * fill in "video_device::vfl_dir" field on newer kernels (Closes: #35) * always provide format-string when using printf() * fixing update-changelog script -- IOhannes m zmoelnig (gpg-key at iem) Tue, 05 Feb 2013 10:03:28 +0100 v4l2loopback (0.6.2) unstable; urgency=low [ IOhannes m zmölnig ] * provide our own v4l2l_vzalloc * added missing includes * create unique names for the various devices * more verbose debugging output when capture DQBUF fails [ Anton Novikov ] * make v4l2loopback.ko a PHONY target * restarting-writer.sh runs on Ubuntu 11.10 * warning about disabled timeout when setting image * readpos2index -> bufpos2index * test different queue-sizes in restarting-writer.sh * fix buffer indices before dev->used_buffers update * fix ctl script (was hardcoded /dev/video0) [ yukkeorg ] * Fix error on compile in Linux kernel 3.6.1. -- IOhannes m zmoelnig Tue, 23 Oct 2012 14:38:02 +0200 v4l2loopback (0.6.1) UNRELEASED; urgency=low [ IOhannes m zmoelnig ] * Makefile fixes for debian -- IOhannes m zmoelnig (gpg-key at iem) Fri, 27 Apr 2012 17:22:25 +0200 v4l2loopback (0.6.0) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * added direct link to wiki * fixed typos * check for (devices<0) rather than (devices==-1) [ Anton Novikov ] * add .gitignore files * add 'format' sysfs attr * remove 'fourcc' sysfs attr * 'keep_format' ctrl * set_timeperframe(), dev->frame_jiffies * 'sustain_framerate' ctrl * add examples/restarting-writer.sh * reset write_position only when !ready_for_capture * handle arbitrary output QBUF index order * 'timeout' ctrl * add ability to do i/o on placeholder picture buf * add v4l2loopback-ctl script * installing v4l2loopback-ctl * fix dequeuing unused buffers * timeout_image_io cleaner memory handling * some documentation on controls * some v4l2loopback-ctl syntax&doc tweaks [ IOhannes m zmölnig ] * moved utility into utils/ * Updated copyright notice * use max image size to prevent insane allocations * in-code documentation of the format string * fixed description of 'debug' option * fixed closing comment * allow to set the max.framesize via module parameters * renamed 'v4l2loopback' target to 'v4l2loopback.ko' * added install-utils target [ Anton Novikov ] * script bugfix * v4l2loopback-ctl set-fps * more README [ IOhannes m zmölnig ] * initialize list in all cases * notes on how to do kernel-debugging * when dying, write to stderr * check for applications before using them * fix usage/version to make it fit for help2man * manpage for v4l2loopback-ctl * placeholder * simplified description * build and install manpages * deleted manage (it's generated automatically) * updated in-module AUTHORs * debugging printout * don't try to force a given format * clarify README about default device in ./test -- IOhannes m zmoelnig (gpg-key at iem) Fri, 27 Apr 2012 09:29:52 +0200 v4l2loopback (0.5.0) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * more (and better) debugging output * stefan diewald's ENUM_FRAMESIZES fix * simplifified framesize enumeration * stefan diewald's ENUM_FRAMEINTERVAL implementations * stefan diewald's buffer request logic * added Stefan Diewald to the authors * use sudo to rmmod/insmod kernel modules in Makefile * use unlocked_ioctl as suggested by salsaman * provide macros to simplify sysfs attrfile creation * added deviceattributes * implemented "video_nr" parameter to manually force device IDs * dummy audio ioctl's that return EINVAL * better output enumeration/format handling * trying to improve handling of std's * improve readability of vidioc_g_output * added note about video_nr param * fixed memleaks * allow per-device "max_openers" settings * warn if illegal number of max_openers is requested * prefix posts with "v4l2loopback" [ IOhannes m zmoelnig ] * simplistic ChangeLog generator -- zmoelnig Tue, 27 Dec 2011 19:01:25 +0100 v4l2loopback (0.4.1) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * yuv4mpeg producer to be used in conjunction with mplayer * added yuv4mpeg_to_v4l2 to the build targets * simplified Makefile; added clean target * protect newer pixel formats * fixed preprocessor expansion on linux<2.6.32 * made it compile on 2.6.28 and 2.6.27 * <=2.6.27 definitely won't work * allow S_PARM for fps * renamed opener->position to opener->read_position * added dummy VIDIOC_QUERYCTRL implementation (fix for linux-3.1) -- IOhannes m zmoelnig (gpg-key at iem) Thu, 24 Nov 2011 18:11:01 +0100 v4l2loopback (0.4.0) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * default debug-level should be 0 * cleanup version confusion * changed version to 0.3.0 * updated README to mention Debian packages * better internal format representation (as found in bttv-drivers) - still unused * trying to support I420 --- might be very unstable right now * dummy Makefile to allow "make" * allow to set device from cmdline * en/disable the readback test using defines * use FRAME_SIZE throughout * more experiments * more debugging messages * added rule to autoload the new v4l2loopback device * rewrote most of the mmap part in order to support I420 * cleanup to make it C90 compliant again * reordered formats a bit to make better default choices... * replace vloopback by v4l2loopback to avoid confusion * cleaned up code * properly initialize the timestampe in order to guarantee a monotic series * updated copyright * bumped to version 0.4.0 -- IOhannes m zmoelnig (gpg-key at iem) Tue, 29 Mar 2011 12:54:23 +0200 v4l2loopback (0.3) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * note on why gstreamer v4l2sink fails to write to such a device * enum_framesizes and enum_fmt_caps * hmm, this makes it more crashy than better * enable additional ioctls (eg. enum_output) * fixed typo: USAGE instead of USEAGE * remove stray #error * gcode reorganization; uniform comments * experiments with returning 0-size * offline documentation for v4l2 * allow all kinds of formats during negotiation * comment on which fields to set * better support for setting formats * add note about using application's bytesperline * set type to WRITER when caller calls enum_fmt_out * removed TODO as it has been done already * indentation * hopefully a bit more intelligent buffer-reallocation strategy * extra safety checks * print fourcc in fmt-enum * fallback formats for try_fmt_out * nicer format descriptions * use defines for size-limits * return EBUSY when trying to set fmt in CAPTURE mode when not ready * properly implement querycap * bytes_used in the mmap may be smaller than the page-size * some dummy functions for video-std settings * debug-level: 1 * terminate function call with ";" * getting rid of MEMLEAK warning (should be fixed now) * calculate bytesperline * only return dummy-format with G_FMT[out] when none has been set * nicer debugging * disable OVERLAY dummy * return 0-sized image by default * default max_buffers_number is 8 * getting rid of my prefix * pushed to version 0.0.2 * pumped to version 0.3 -- IOhannes m zmoelnig (gpg-key at iem) Sun, 10 Oct 2010 21:12:38 +0200 v4l2loopback (0.2) UNRELEASED; urgency=low [ IOhannes m zmölnig ] * acces /dev/video0 * variable number of pipes * nicer printout * proper cleanup * renamed "pipes" to "devices" * bumped version; added meself as co-author * removed files removed by "debian/rules clean" * removed examples * fixed debian/control debian/rules * postinst stuff * moved example into separate folder * MakefileS need not be executable * README, COPYING, AUTHORS * re-version to 0.2 * updated README * added Vasily Levin to the authors * removed debian stuff * added a README for the test * added vasily.levin to the authors * included linux/slab.h * license issues: this module is GPLv2 * added meself into the copyright header -- IOhannes m zmoelnig (gpg-key at iem) Sun, 10 Oct 2010 21:09:43 +0200 v4l2loopback (0.1) UNRELEASED; urgency=low [ gorinich.zmey ] * initial * first approach * removed autogenerated file * temproraly removed fps control and input from stdin handling * removed irrelevant changelog, changed readme * forgotten changes applued * modules.order delete * cleaned the mess with git-svn * added test file * added VIDIOC_G_PARM call * format handling improvment, current solution is a stub * temporarly removed mmap to keep code simple * compile fix * poll added, streaming started * small test refine * enum_input added * basic streaming, polish needed * first streaming working, mplayer gots a picture, yet crappy * readme add * readme rewrite * readme additions * mutex add * skype working * queue introduction, next step queue remove * first run is OK already * queues debugged * halfway of massive inner structure changes * compiles * pre multireader * style for linux kernel * indent * 80 width * module name changed and debianize start * debian * 2.6.28 support * almost works, just one bug left * debian * bug with two and more openers fixed * redebianized * removed files * license header add * freeing of unitialized pointer fixed, added nonblocking IO * sync with v4l-dvb tree * review responce * hans review * test improvments by Antonio Ospite * removed header * more small fixes [ Scott Maines ] * missing header for Fedora -- IOhannes m zmoelnig (gpg-key at iem) Sun, 10 Oct 2010 21:01:50 +0200 v4l2loopback-0.9.1/Makefile000066400000000000000000000041411253363713600154740ustar00rootroot00000000000000KERNELRELEASE ?= `uname -r` KERNEL_DIR ?= /lib/modules/$(KERNELRELEASE)/build PWD := $(shell pwd) obj-m := v4l2loopback.o PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man MAN1DIR = $(MANDIR)/man1 INSTALL = install INSTALL_PROGRAM = $(INSTALL) -p -m 755 INSTALL_DIR = $(INSTALL) -p -m 755 -d INSTALL_DATA = $(INSTALL) -m 644 MODULE_OPTIONS = devices=2 ########################################## # note on build targets # # module-assistant makes some assumptions about targets, namely # : must be present and build the module # .ko is not enough # install: must be present (and should only install the module) # # we therefore make a .PHONY alias to .ko # and remove utils-installation from 'install' # call 'make install-all' if you want to install everything ########################################## .PHONY: all install clean distclean .PHONY: install-all install-utils install-man .PHONY: modprobe v4l2loopback # we don't control the .ko file dependencies, as it is done by kernel # makefiles. therefore v4l2loopback.ko is a phony target actually .PHONY: v4l2loopback.ko all: v4l2loopback.ko v4l2loopback: v4l2loopback.ko v4l2loopback.ko: @echo "Building v4l2-loopback driver..." $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules install-all: install install-utils install-man install: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install depmod -a $(KERNELRELEASE) install-utils: utils/v4l2loopback-ctl $(INSTALL_DIR) "$(DESTDIR)$(BINDIR)" $(INSTALL_PROGRAM) $< "$(DESTDIR)$(BINDIR)" install-man: man/v4l2loopback-ctl.1 $(INSTALL_DIR) "$(DESTDIR)$(MAN1DIR)" $(INSTALL_DATA) $< "$(DESTDIR)$(MAN1DIR)" clean: rm -f *~ rm -f Module.symvers Module.markers modules.order $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean distclean: clean rm -f man/v4l2loopback-ctl.1 modprobe: v4l2loopback.ko chmod a+r v4l2loopback.ko sudo modprobe videodev -sudo rmmod v4l2loopback sudo insmod ./v4l2loopback.ko $(MODULE_OPTIONS) man/v4l2loopback-ctl.1: utils/v4l2loopback-ctl help2man -N --name "control v4l2 loopback devices" $^ > $@ v4l2loopback-0.9.1/Makefile.manual000066400000000000000000000042041253363713600167500ustar00rootroot00000000000000## DO NOT USE THIS MAKEFILE! ### this is created based on `make V=1 > make.log` and is used solely ### for creating build for static code analysis .PHONY: default KERNELRELEASE ?= `uname -r` KERNEL_SOURCE ?= /lib/modules/$(KERNELRELEASE)/build KERNEL_SOURCE_COMMON ?= /lib/modules/$(KERNELRELEASE)/source CFLAGS=-nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -I$(KERNEL_SOURCE_COMMON)/arch/x86/include -I$(KERNEL_SOURCE)/arch/x86/include/generated -I$(KERNEL_SOURCE_COMMON)/include -I$(KERNEL_SOURCE)/include -I$(KERNEL_SOURCE_COMMON)/arch/x86/include/uapi -I$(KERNEL_SOURCE)/arch/x86/include/generated/uapi -I$(KERNEL_SOURCE_COMMON)/include/uapi -I$(KERNEL_SOURCE)/include/generated/uapi -include $(KERNEL_SOURCE_COMMON)/include/linux/kconfig.h -I. -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks -O2 -m64 -mno-mmx -mno-sse -mpreferred-stack-boundary=3 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -maccumulate-outgoing-args -fstack-protector -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -Wframe-larger-than=2048 -Wno-unused-but-set-variable -fomit-frame-pointer -g -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -DCC_HAVE_ASM_GOTO default: $(CC) -Wp,-MD,./.v4l2loopback.o.d $(CFLAGS) -DMODULE -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(v4l2loopback)" -D"KBUILD_MODNAME=KBUILD_STR(v4l2loopback)" -c -o ./.tmp_v4l2loopback.o ./v4l2loopback.c $(CC) -Wp,-MD,./.v4l2loopback.mod.o.d $(CFLAGS) -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(v4l2loopback.mod)" -D"KBUILD_MODNAME=KBUILD_STR(v4l2loopback)" -DMODULE -c -o ./v4l2loopback.mod.o ./v4l2loopback.mod.c $(LD) -r -m elf_x86_64 -T $(KERNEL_SOURCE_COMMON)/scripts/module-common.lds --build-id -o ./v4l2loopback.ko ./v4l2loopback.o ./v4l2loopback.mod.o v4l2loopback-0.9.1/NEWS000066400000000000000000000105421253363713600145350ustar00rootroot00000000000000v4l2loopback-0.9.1 - Fixed module version -- IOhannes m zmölnig (Debian/GNU) Wed, 03 Jun 2015 19:47:23 +0200 v4l2loopback-0.9.0 - more formats - kernel compatibility fixed issues with kernel up to 4.0 -- IOhannes m zmölnig Tue, 02 Jun 2015 19:58:39 +0200 v4l2loopback-0.8.0 - DKMS config - 'card_label' option to manually set device names - fixes in format handling -- IOhannes m zmölnig Tue, 10 Dec 2013 18:12:15 +0100 v4l2loopback-0.7.1 - kernel compatibility fixed issues with kernel-3.11 fixed regression with kernel<2.6.29 -- IOhannes m zmoelnig (gpg-key at iem) Mon, 16 Sep 2013 09:55:51 +0200 v4l2loopback-0.7.0 - experimental'exclusive_caps' mode that only reports CAPTURE/OUTPUT capabilities exclusively (support for Chromium/WebRTC) - disabled fake STDs (e.g. ffmpeg support) -- IOhannes m zmoelnig (gpg-key at iem) Fri, 07 Jun 2013 11:08:10 +0200 v4l2loopback-0.6.3 - kernel compatibility fixed issues with kernel-3.7 - unique "Card Type" Names (fixes broken clients like flash) - create unique ID in "bus_info" field (fixes Firefox/WebRTC support) -- IOhannes m zmoelnig (gpg-key at iem) 2013-02-05 v4l2loopback-0.6.2 - kernel compatibility fixed issues with older kernels (<2.6.37) fixed issues with kernel-3.6.1 - distinct device names (fixes broken clients like google+) -- IOhannes m zmoelnig (gpg-key at iem) 2012-10-23 v4l2loopback-0.6.1 - Debian specific build-fixes -- IOhannes m zmoelnig (gpg-key at iem) 2012-04-27 v4l2loopback-0.6.0 - support fallback images (in case there is no live-stream) - utilities to interact with v4l2loopback devices -- IOhannes m zmoelnig (gpg-key at iem) 2012-04-27 v4l2loopback-0.5.0 - module parameters 'video_nr' manually sets device id(s) - device attributes via sysfs: /sys/devices/virtual/video4linux/video*/ 'max_openers' per device 'fourcc' queries currently selected format - ioctl fixes avoid the BIG KERNEL LOCK ENUM_FRAMEINTERVAL implementation fixed ENUM_FRAMESIZES (fixes skype compatibilitiy) fixes to buffer queue with multiple consumers minor fixes to handling of standards, output enumeration and output formats - more (and better) debugging output - fixed memleaks in the examples -- IOhannes m zmoelnig (gpg-key at iem) Tue, 27 Dec 2011 19:01:25 +0100 v4l2loopback-0.4.1 - yuv4mpeg producer example - kernel compatibility fixed issues with kernels<2.6.32 fixed issues with kernel-3.1 -- IOhannes m zmoelnig (gpg-key at iem) Thu, 24 Nov 2011 18:11:01 +0100 v4l2loopback-0.4 - fixed issues with non-trivial colorspaces (e.g. I420) this should allow for more clients to work out-of-the-box (e.g. no more caps-tweak with gstreamer) - fixed timestamps this allows playback with players that need monotonous timestamps (e.g. ffmpeg) - cleaned up code -- IOhannes m zmoelnig Tue, 29 Mar 2011 14:26:10 +0200 v4l2loopback-0.3 - tested writers: GStreamer's normal "v4l2sink" element (from plugins-good) GStreamer's "v4l2loopback" (deprecated by v4l2sink) pd/Gem(0.93svn) - tested readers: GStreamer's "v4l2src" pd/Gem(0.92) vlc xawtv (depending on image format) mplayer (with correct image format, e.g. rgb32) - code documentation - added v4l2-documentation for easier offline programming - added a lot of ioctls to meet the v4l2 standard -- IOhannes m zmoelnig Sun, 10 Oct 2010 21:18:22 +0200 v4l2loopback-0.2 - Linux 2.6.32 & 2.6.35 - tested with pd/Gem(0.93svn) and GStreamer's "v4l2loopback" - add support for multiple video devices - README, COPYING, AUTHORS - re-organized file layout -- IOhannes m zmoelnig Tue Sep 28 09:46:47 CEST 2010 v4l2loopback-0.1 - Linux 2.6.28 - Skype support - support for GStreamer's "v4l2loopback" element - test application - README - dev: nonblocking I/O - dev: VIDIOC_G_PARM -- Gorinich Zmey Wed Jun 16 12:19:59 CEST 2010 v4l2loopback-0.0 - initial -- Vasily Levin Tue Feb 3 10:56:28 CET 2009 v4l2loopback-0.9.1/README.md000066400000000000000000000161261253363713600153210ustar00rootroot00000000000000v4l2loopback - a kernel module to create V4L2 loopback devices ============================================================== this module allows you to create "virtual video devices" normal (v4l2) applications will read these devices as if they were ordinary video devices, but the video will not be read from e.g. a capture card but instead it is generated by another application. this allows you for instance to apply apply some nifty video effects on your Skype video... it also allows some more serious things (e.g. I've been using it to add streaming capabilities to an application by the means of hooking GStreamer into the loopback devices). # NEWS to get the main features of each new release, see the NEWS file. you could also have a look at the ChangeLog (which gets automatically generated and might only be of limited use... # ISSUES for current issues, checkout https://github.com/umlaeute/v4l2loopback/issues please use the issue-tracker for reporting any problems # DEPENDENCIES The v4l2loopback module is a *kernel module*. In order to build it, you *must have* the kernel headers installed that match the linux kernel with which you want to use the module (in most this will be the kernel that you are currently running). Please note, that kernel headers and kernel image must have *exactly the same* version. For example, `3.18.0-trunk-rpi` is a different version that `3.18.7-v7+`, even though the first few number are the same. (Modules will be incompatible if the versions don't match. If you are lucky, the module will simply refuse to load. If you are unlucky, your computer will spit in your eye or do worse.) # BUILD to build the kernel module run: $ make this should give you a file named "v4l2loopback.ko", which is the kernel module # INSTALL to install the module run "make install" (you might have to be 'root' to have all necessary permissions to install the module). if your system has "sudo", do: $ make && sudo make install if your system lacks "sudo", do: $ make $ su (enter root password) # make install # exit # RUN Load the v4l2loopback module as root : # modprobe v4l2loopback using sudo use: $ sudo modprobe v4l2loopback this will create an additional video-device, e.g. /dev/video0 (the number depends on whether you already had video devices on your system), which can be fed by various programs. tested feeders: - GStreamer-0.10: using the "v4l2sink" element - Gem(>=0.93) using the "recordV4L2" plugin in theory most programs capable of _writing to_ a v4l2 device should work. the data sent to the v4l2loopback device can then be read by any v4l2-capable application. you can find a number of scenarios on the wiki at http://github.com/umlaeute/v4l2loopback/wiki # OPTIONS if you need several independent loopback devices, you can pass the "devices" option, when loading the module; e.g. # modprobe v4l2loopback devices=4 will give you 4 loopback devices (e.g. `/dev/video1` ... `/dev/video5`) you can also specify the device IDs manually; e.g. # modprobe v4l2loopback video_nr=3,4,7 will create 3 devices (`/dev/video3`, `/dev/video4` & `/dev/video7`) # modprobe v4l2loopback video_nr=3,4,7 card_label="device number 3","the number four","the last one" will create 3 devices with the card names passed as the second parameter: - `/dev/video3` -> *device number 3* - `/dev/video4` -> *the number four* - `/dev/video7` -> *the last one* # ATTRIBUTES you can set and/or query some per-device attributes via sysfs, in a human readable format. see `/sys/devices/virtual/video4linux/video*/` also there are some V4L2 controls that you can list with $ v4l2-ctl -d /dev/video1 -l - `keep_format(0/1)`: while set to 1, once negotiated format will be fixed forever, until the setting is set back to 0 - `sustain_framerate(0/1)`: if set to 1, nominal device fps will be ensured by means of frame duplication when needed - `timeout(integer)`: if >0, will cause a timeout picture (a null frame, by default) to be displayed after (value) msecs of missing input - `timeout_image_io(0/1)`: if set to 1, the next opener will write to timeout frame buffer # FORCING FPS $ v4l2loopback-ctl set-fps 25 /dev/video0 or $ echo '@100' | sudo tee /sys/devices/virtual/video4linux/video0/format # FORCING A GSTREAMER (0.10) CAPS $ v4l2loopback-ctl set-caps "video/x-raw-yuv, width=640, height=480" /dev/video0 # SETTING STREAM TIMEOUT ~~~ $ v4l2-ctl -d /dev/video0 -c timeout=3000 (will output null frames by default) $ v4l2loopback-ctl set-timeout-image service-unavailable.png /dev/video0 this currently requires GStreamer 0.10 installed ~~~ # KERNELs the original module has been developed for linux-2.6.28; i don't have a system with such an old kernel anymore, so i don't know whether it still works. further development has been done mainly on linux-2.6.32 and linux-2.6.35, with newer kernels being continually tested as they enter debian. support: - <= 2.6.27 definitely will NOT work - 2.6.28 - 2.6.31 may work (seems to compile but i cannot test) - >= 2.6.32 should work - >= 3.0.0 should work as well # DISTRIBUTIONS v4l2loopack is now (since 2010-10-13) available as a Debian-package. https://packages.debian.org/source/stable/v4l2loopback This means, that it is also part of Debian-derived distributions, including Ubuntu (starting with natty). The most convenient way is to install the package "v4l2loopback-dkms": # aptitude install v4l2loopback-dkms This should automatically build and install the module for your current kernel (provided you have the matching kernel-headers installed). Another option is to install the "v4l2loopback-source" package. In this case you should be able to simply do (as root): # aptitude install v4l2loopback-source module-assistant # module-assistant auto-install v4l2loopback-source # DOWNLOAD the most up-to-date version of this module can be found at http://github.com/umlaeute/v4l2loopback/. # LICENSE/COPYING - Copyright (c) 2010-2015 IOhannes m zmoelnig - Copyright (c) 2014-2015 Tasos Sahanidis - Copyright (c) 2012-2015 Yusuke Ohshima - Copyright (c) 2015 Tom Zerucha - Copyright (c) 2013 Aidan Thornton - Copyright (c) 2013 Anatolij Gustschin - Copyright (c) 2012 Ted Mielczarek - Copyright (c) 2012 Anton Novikov - Copyright (c) 2011 Stefan Diewald - Copyright (c) 2010 Scott Maines - Copyright (c) 2009 Gorinich Zmey - Copyright (c) 2005-2009 Vasily Levin 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 program. If not, see . v4l2loopback-0.9.1/TODO000066400000000000000000000013521253363713600145250ustar00rootroot00000000000000TODO for v4l2loopback in no specific order - fix all bugs :-) - improve buffering (salsaman) - allow USERPTR buffers - allow to use the device without streaming i/o - pass 'v4l2-compliance' tests currently failing are: VIDIOC_G/S_PRIORITY VIDIOC_LOG_STATUS VIDIOC_ENUMAUDIO VIDIOC_G/S_AUDIO VIDIOC_ENUMAUDOUT VIDIOC_G/S/ENUMOUTPUT VIDIOC_G/S_CTRL VIDIOC_G/S/TRY_EXT_CTRLS VIDIOC_ENUM/G/S/QUERY_STD VIDIOC_ENUM/G/S/QUERY_DV_PRESETS VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS VIDIOC_G_FBUF VIDIOC_G_FMT VIDIOC_G_SLICED_VBI_CAP - it would be nice to have a way to communicate format requests from the consumer to the producer (though i see no way how to do that) - provide more producers for more colorspaces in the examples v4l2loopback-0.9.1/currentversion.sh000077500000000000000000000003431253363713600174630ustar00rootroot00000000000000#!/bin/sh grep "^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION" v4l2loopback.c \ | sed -e 's|^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION||' \ -e 's|^[^0-9]*||' -e 's|[^0-9]*$||' \ -e 's|[^0-9][^0-9]*|.|g' v4l2loopback-0.9.1/dkms.conf000066400000000000000000000004561253363713600156460ustar00rootroot00000000000000PACKAGE_NAME="v4l2loopback" PACKAGE_VERSION="0.9.1" # Items below here should not have to change with each driver version MAKE[0]="make KERNEL_DIR=${kernel_source_dir} all" CLEAN="make clean" BUILT_MODULE_NAME[0]="$PACKAGE_NAME" DEST_MODULE_LOCATION[0]="/extra" REMAKE_INITRD="no" AUTOINSTALL="yes" v4l2loopback-0.9.1/doc/000077500000000000000000000000001253363713600146015ustar00rootroot00000000000000v4l2loopback-0.9.1/doc/docs.txt000066400000000000000000000004551253363713600162760ustar00rootroot00000000000000obsolete V4L2-API: http://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html (an even more obsolete version 0.24 of this document used to be included here, but is no longer) current V4L2-API: http://linuxtv.org/downloads/v4l-dvb-apis/ (unfortunately this is multi-page) v4l2loopback-0.9.1/doc/kernel_debugging.txt000066400000000000000000000011171253363713600206350ustar00rootroot00000000000000some hints how to debug kernel panics https://wiki.ubuntu.com/Kernel/KernelDebuggingTricks basically it is: - run tests in a virtual machine (i use VirtualBox) - configure the vm to have a serial output VirtualBox-Settings/Serial Ports enable serial port COM1 port mode: Raw File file path: /tmp/vbox_serial.log - configure the vm's kernel to log to the kernel: add "console=tty console=ttyS0,9600" to the kernel-parms (i put that into grub) - reboot - raise the vm's kernel console verbosity: echo 7 > /proc/sys/kernel/printk - run - examine /tmp/vbox_serial.log on the host v4l2loopback-0.9.1/doc/makeformats.sh000077500000000000000000000010531253363713600174500ustar00rootroot00000000000000#!/bin/sh ## usage: # echo "V4L2_PIX_FMT_MPEG4 MPEG-4 part 2 ES" | $0 ## normally it's more like: # cat /usr/include/linux/videodev2.h \ # | grep "define V4L2_PIX_FMT" \ # | sed -e "s|^#define ||" -e "s|v4l2_fourcc('.', '.', '.', '.')||" -e 's|/\*||' -e 's|\*/||' \ # | $0 DEPTH=0 FLAGS=0 while read FOURCC NAME do echo "#ifdef ${FOURCC}" echo "{" echo " .name = \"${NAME}\"," echo " .fourcc = ${FOURCC}," echo " .depth = ${DEPTH}," echo " .flags = ${FLAGS}," echo " }," echo "#endif /* ${FOURCC} */" done v4l2loopback-0.9.1/doc/missingformats.h000066400000000000000000000160501253363713600200210ustar00rootroot00000000000000#ifdef V4L2_PIX_FMT_Y10BPACK }, { .name = "10 bpp Greyscale bit-packed", .fourcc = V4L2_PIX_FMT_Y10BPACK, .depth = 10, .flags = 0, #endif /* V4L2_PIX_FMT_Y10BPACK */ #ifdef V4L2_PIX_FMT_PAL8 }, { .name = "8 bpp 8-bit palette", .fourcc = V4L2_PIX_FMT_PAL8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_PAL8 */ #ifdef V4L2_PIX_FMT_UV8 }, { .name = "8 bpp UV 4:4", .fourcc = V4L2_PIX_FMT_UV8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_UV8 */ #ifdef V4L2_PIX_FMT_HI240 }, { .name = "8 bpp 8-bit color ", .fourcc = V4L2_PIX_FMT_HI240, .depth = 8, .flags = FORMAT_FLAGS_PLANAR, #endif /* V4L2_PIX_FMT_HI240 */ #ifdef V4L2_PIX_FMT_HM12 }, { .name = "8 bpp YUV 4:2:0 16x16 macroblocks", .fourcc = V4L2_PIX_FMT_HM12, .depth = 8, .flags = FORMAT_FLAGS_PLANAR, #endif /* V4L2_PIX_FMT_HM12 */ #ifdef V4L2_PIX_FMT_M420 }, { .name = "12 bpp YUV 4:2:0 2 lines y, 1 line uv interleaved", .fourcc = V4L2_PIX_FMT_M420, .depth = 12, .flags = FORMAT_FLAGS_PLANAR, #endif /* V4L2_PIX_FMT_M420 */ #ifdef V4L2_PIX_FMT_NV12 }, { .name = "12 bpp Y/CbCr 4:2:0 ", .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_NV12 */ #ifdef V4L2_PIX_FMT_NV21 }, { .name = "12 bpp Y/CrCb 4:2:0 ", .fourcc = V4L2_PIX_FMT_NV21, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_NV21 */ #ifdef V4L2_PIX_FMT_NV16 }, { .name = "16 bpp Y/CbCr 4:2:2 ", .fourcc = V4L2_PIX_FMT_NV16, .depth = 16, .flags = 0, #endif /* V4L2_PIX_FMT_NV16 */ #ifdef V4L2_PIX_FMT_NV61 }, { .name = "16 bpp Y/CrCb 4:2:2 ", .fourcc = V4L2_PIX_FMT_NV61, .depth = 16, .flags = 0, #endif /* V4L2_PIX_FMT_NV61 */ #ifdef V4L2_PIX_FMT_NV24 }, { .name = "24 bpp Y/CbCr 4:4:4 ", .fourcc = V4L2_PIX_FMT_NV24, .depth = 24, .flags = 0, #endif /* V4L2_PIX_FMT_NV24 */ #ifdef V4L2_PIX_FMT_NV42 }, { .name = "24 bpp Y/CrCb 4:4:4 ", .fourcc = V4L2_PIX_FMT_NV42, .depth = 24, .flags = 0, #endif /* V4L2_PIX_FMT_NV42 */ #ifdef V4L2_PIX_FMT_NV12M }, { .name = "12 bpp Y/CbCr 4:2:0 ", .fourcc = V4L2_PIX_FMT_NV12M, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_NV12M */ #ifdef V4L2_PIX_FMT_NV21M }, { .name = "21 bpp Y/CrCb 4:2:0 ", .fourcc = V4L2_PIX_FMT_NV21M, .depth = 21, .flags = 0, #endif /* V4L2_PIX_FMT_NV21M */ #ifdef V4L2_PIX_FMT_NV16M }, { .name = "16 bpp Y/CbCr 4:2:2 ", .fourcc = V4L2_PIX_FMT_NV16M, .depth = 16, .flags = 0, #endif /* V4L2_PIX_FMT_NV16M */ #ifdef V4L2_PIX_FMT_NV61M }, { .name = "16 bpp Y/CrCb 4:2:2 ", .fourcc = V4L2_PIX_FMT_NV61M, .depth = 16, .flags = 0, #endif /* V4L2_PIX_FMT_NV61M */ #ifdef V4L2_PIX_FMT_NV12MT }, { .name = "12 bpp Y/CbCr 4:2:0 64x32 macroblocks", .fourcc = V4L2_PIX_FMT_NV12MT, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_NV12MT */ #ifdef V4L2_PIX_FMT_NV12MT_16X16 }, { .name = "12 bpp Y/CbCr 4:2:0 16x16 macroblocks", .fourcc = V4L2_PIX_FMT_NV12MT_16X16, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_NV12MT_16X16 */ #ifdef V4L2_PIX_FMT_YUV420M }, { .name = "12 bpp YUV420 planar", .fourcc = V4L2_PIX_FMT_YUV420M, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_YUV420M */ #ifdef V4L2_PIX_FMT_YVU420M }, { .name = "12 bpp YVU420 planar", .fourcc = V4L2_PIX_FMT_YVU420M, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_YVU420M */ #ifdef V4L2_PIX_FMT_SBGGR8 }, { .name = "8 bpp BGBG.. GRGR..", .fourcc = V4L2_PIX_FMT_SBGGR8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR8 */ #ifdef V4L2_PIX_FMT_SGBRG8 }, { .name = "8 bpp GBGB.. RGRG..", .fourcc = V4L2_PIX_FMT_SGBRG8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_SGBRG8 */ #ifdef V4L2_PIX_FMT_SGRBG8 }, { .name = "8 bpp GRGR.. BGBG..", .fourcc = V4L2_PIX_FMT_SGRBG8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_SGRBG8 */ #ifdef V4L2_PIX_FMT_SRGGB8 }, { .name = "8 bpp RGRG.. GBGB..", .fourcc = V4L2_PIX_FMT_SRGGB8, .depth = 8, .flags = 0, #endif /* V4L2_PIX_FMT_SRGGB8 */ #ifdef V4L2_PIX_FMT_SBGGR10 }, { .name = "10 bpp BGBG.. GRGR..", .fourcc = V4L2_PIX_FMT_SBGGR10, .depth = 10, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR10 */ #ifdef V4L2_PIX_FMT_SGBRG10 }, { .name = "10 bpp GBGB.. RGRG..", .fourcc = V4L2_PIX_FMT_SGBRG10, .depth = 10, .flags = 0, #endif /* V4L2_PIX_FMT_SGBRG10 */ #ifdef V4L2_PIX_FMT_SGRBG10 }, { .name = "10 bpp GRGR.. BGBG..", .fourcc = V4L2_PIX_FMT_SGRBG10, .depth = 10, .flags = 0, #endif /* V4L2_PIX_FMT_SGRBG10 */ #ifdef V4L2_PIX_FMT_SRGGB10 }, { .name = "10 bpp RGRG.. GBGB..", .fourcc = V4L2_PIX_FMT_SRGGB10, .depth = 10, .flags = 0, #endif /* V4L2_PIX_FMT_SRGGB10 */ #ifdef V4L2_PIX_FMT_SBGGR12 }, { .name = "12 bpp BGBG.. GRGR..", .fourcc = V4L2_PIX_FMT_SBGGR12, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR12 */ #ifdef V4L2_PIX_FMT_SGBRG12 }, { .name = "12 bpp GBGB.. RGRG..", .fourcc = V4L2_PIX_FMT_SGBRG12, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_SGBRG12 */ #ifdef V4L2_PIX_FMT_SGRBG12 }, { .name = "12 bpp GRGR.. BGBG..", .fourcc = V4L2_PIX_FMT_SGRBG12, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_SGRBG12 */ #ifdef V4L2_PIX_FMT_SRGGB12 }, { .name = "12 bpp RGRG.. GBGB..", .fourcc = V4L2_PIX_FMT_SRGGB12, .depth = 12, .flags = 0, #endif /* V4L2_PIX_FMT_SRGGB12 */ #ifdef V4L2_PIX_FMT_SBGGR10ALAW8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SBGGR10ALAW8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR10ALAW8 */ #ifdef V4L2_PIX_FMT_SGBRG10ALAW8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SGBRG10ALAW8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SGBRG10ALAW8 */ #ifdef V4L2_PIX_FMT_SGRBG10ALAW8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SGRBG10ALAW8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SGRBG10ALAW8 */ #ifdef V4L2_PIX_FMT_SRGGB10ALAW8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SRGGB10ALAW8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SRGGB10ALAW8 */ #ifdef V4L2_PIX_FMT_SBGGR10DPCM8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SBGGR10DPCM8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR10DPCM8 */ #ifdef V4L2_PIX_FMT_SGBRG10DPCM8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SGBRG10DPCM8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SGBRG10DPCM8 */ #ifdef V4L2_PIX_FMT_SGRBG10DPCM8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SGRBG10DPCM8 */ #ifdef V4L2_PIX_FMT_SRGGB10DPCM8 }, { .name = "", .fourcc = V4L2_PIX_FMT_SRGGB10DPCM8, .depth = 0, .flags = 0, #endif /* V4L2_PIX_FMT_SRGGB10DPCM8 */ #ifdef V4L2_PIX_FMT_SBGGR16 }, { .name = "16 bpp BGBG.. GRGR..", .fourcc = V4L2_PIX_FMT_SBGGR16, .depth = 16, .flags = 0, #endif /* V4L2_PIX_FMT_SBGGR16 */ v4l2loopback-0.9.1/doc/v4l2_formats.txt000066400000000000000000000111211253363713600176600ustar00rootroot00000000000000/* RGB formats */ V4L2_PIX_FMT_RGB332 /* 8 RGB-3-3-2 */ V4L2_PIX_FMT_RGB444 /* 16 xxxxrrrr ggggbbbb */ V4L2_PIX_FMT_RGB555 /* 16 RGB-5-5-5 */ V4L2_PIX_FMT_RGB565 /* 16 RGB-5-6-5 */ V4L2_PIX_FMT_RGB555X /* 16 RGB-5-5-5 BE */ V4L2_PIX_FMT_RGB565X /* 16 RGB-5-6-5 BE */ V4L2_PIX_FMT_BGR666 /* 18 BGR-6-6-6 */ V4L2_PIX_FMT_BGR24 /* 24 BGR-8-8-8 */ V4L2_PIX_FMT_RGB24 /* 24 RGB-8-8-8 */ V4L2_PIX_FMT_BGR32 /* 32 BGR-8-8-8-8 */ V4L2_PIX_FMT_RGB32 /* 32 RGB-8-8-8-8 */ /* Grey formats */ V4L2_PIX_FMT_GREY /* 8 Greyscale */ V4L2_PIX_FMT_Y4 /* 4 Greyscale */ V4L2_PIX_FMT_Y6 /* 6 Greyscale */ V4L2_PIX_FMT_Y10 /* 10 Greyscale */ V4L2_PIX_FMT_Y12 /* 12 Greyscale */ V4L2_PIX_FMT_Y16 /* 16 Greyscale */ /* Grey bit-packed formats */ V4L2_PIX_FMT_Y10BPACK /* 10 Greyscale bit-packed */ /* Palette formats */ V4L2_PIX_FMT_PAL8 /* 8 8-bit palette */ /* Chrominance formats */ V4L2_PIX_FMT_UV8 /* 8 UV 4:4 */ /* Luminance+Chrominance formats */ V4L2_PIX_FMT_YVU410 /* 9 YVU 4:1:0 */ V4L2_PIX_FMT_YVU420 /* 12 YVU 4:2:0 */ V4L2_PIX_FMT_YUYV /* 16 YUV 4:2:2 */ V4L2_PIX_FMT_YYUV /* 16 YUV 4:2:2 */ V4L2_PIX_FMT_YVYU /* 16 YVU 4:2:2 */ V4L2_PIX_FMT_UYVY /* 16 YUV 4:2:2 */ V4L2_PIX_FMT_VYUY /* 16 YUV 4:2:2 */ V4L2_PIX_FMT_YUV422P /* 16 YVU422 planar */ V4L2_PIX_FMT_YUV411P /* 16 YVU411 planar */ V4L2_PIX_FMT_Y41P /* 12 YUV 4:1:1 */ V4L2_PIX_FMT_YUV444 /* 16 xxxxyyyy uuuuvvvv */ V4L2_PIX_FMT_YUV555 /* 16 YUV-5-5-5 */ V4L2_PIX_FMT_YUV565 /* 16 YUV-5-6-5 */ V4L2_PIX_FMT_YUV32 /* 32 YUV-8-8-8-8 */ V4L2_PIX_FMT_YUV410 /* 9 YUV 4:1:0 */ V4L2_PIX_FMT_YUV420 /* 12 YUV 4:2:0 */ V4L2_PIX_FMT_HI240 /* 8 8-bit color */ V4L2_PIX_FMT_HM12 /* 8 YUV 4:2:0 16x16 macroblocks */ V4L2_PIX_FMT_M420 /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ /* two planes -- one Y, one Cr + Cb interleaved */ V4L2_PIX_FMT_NV12 /* 12 Y/CbCr 4:2:0 */ V4L2_PIX_FMT_NV21 /* 12 Y/CrCb 4:2:0 */ V4L2_PIX_FMT_NV16 /* 16 Y/CbCr 4:2:2 */ V4L2_PIX_FMT_NV61 /* 16 Y/CrCb 4:2:2 */ V4L2_PIX_FMT_NV24 /* 24 Y/CbCr 4:4:4 */ V4L2_PIX_FMT_NV42 /* 24 Y/CrCb 4:4:4 */ /* two non contiguous planes - one Y, one Cr + Cb interleaved */ V4L2_PIX_FMT_NV12M /* 12 Y/CbCr 4:2:0 */ V4L2_PIX_FMT_NV21M /* 21 Y/CrCb 4:2:0 */ V4L2_PIX_FMT_NV16M /* 16 Y/CbCr 4:2:2 */ V4L2_PIX_FMT_NV61M /* 16 Y/CrCb 4:2:2 */ V4L2_PIX_FMT_NV12MT /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ V4L2_PIX_FMT_NV12MT_16X16 /* 12 Y/CbCr 4:2:0 16x16 macroblocks */ /* three non contiguous planes - Y, Cb, Cr */ V4L2_PIX_FMT_YUV420M /* 12 YUV420 planar */ V4L2_PIX_FMT_YVU420M /* 12 YVU420 planar */ /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */ V4L2_PIX_FMT_SBGGR8 /* 8 BGBG.. GRGR.. */ V4L2_PIX_FMT_SGBRG8 /* 8 GBGB.. RGRG.. */ V4L2_PIX_FMT_SGRBG8 /* 8 GRGR.. BGBG.. */ V4L2_PIX_FMT_SRGGB8 /* 8 RGRG.. GBGB.. */ V4L2_PIX_FMT_SBGGR10 /* 10 BGBG.. GRGR.. */ V4L2_PIX_FMT_SGBRG10 /* 10 GBGB.. RGRG.. */ V4L2_PIX_FMT_SGRBG10 /* 10 GRGR.. BGBG.. */ V4L2_PIX_FMT_SRGGB10 /* 10 RGRG.. GBGB.. */ V4L2_PIX_FMT_SBGGR12 /* 12 BGBG.. GRGR.. */ V4L2_PIX_FMT_SGBRG12 /* 12 GBGB.. RGRG.. */ V4L2_PIX_FMT_SGRBG12 /* 12 GRGR.. BGBG.. */ V4L2_PIX_FMT_SRGGB12 /* 12 RGRG.. GBGB.. */ /* 10bit raw bayer a-law compressed to 8 bits */ V4L2_PIX_FMT_SBGGR10ALAW8 V4L2_PIX_FMT_SGBRG10ALAW8 V4L2_PIX_FMT_SGRBG10ALAW8 V4L2_PIX_FMT_SRGGB10ALAW8 /* 10bit raw bayer DPCM compressed to 8 bits */ V4L2_PIX_FMT_SBGGR10DPCM8 V4L2_PIX_FMT_SGBRG10DPCM8 V4L2_PIX_FMT_SGRBG10DPCM8 V4L2_PIX_FMT_SRGGB10DPCM8 /* * 10bit raw bayer, expanded to 16 bits * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb... */ V4L2_PIX_FMT_SBGGR16 /* 16 BGBG.. GRGR.. */ /* compressed formats */ V4L2_PIX_FMT_MJPEG /* Motion-JPEG */ V4L2_PIX_FMT_JPEG /* JFIF JPEG */ V4L2_PIX_FMT_DV /* 1394 */ V4L2_PIX_FMT_MPEG /* MPEG-1/2/4 Multiplexed */ V4L2_PIX_FMT_H264 /* H264 with start codes */ V4L2_PIX_FMT_H264_NO_SC /* H264 without start codes */ V4L2_PIX_FMT_H264_MVC /* H264 MVC */ V4L2_PIX_FMT_H263 /* H263 */ V4L2_PIX_FMT_MPEG1 /* MPEG-1 ES */ V4L2_PIX_FMT_MPEG2 /* MPEG-2 ES */ V4L2_PIX_FMT_MPEG4 /* MPEG-4 part 2 ES */ V4L2_PIX_FMT_XVID /* Xvid */ V4L2_PIX_FMT_VC1_ANNEX_G /* SMPTE 421M Annex G compliant stream */ V4L2_PIX_FMT_VC1_ANNEX_L /* SMPTE 421M Annex L compliant stream */ V4L2_PIX_FMT_VP8 /* VP8 */ v4l2loopback-0.9.1/examples/000077500000000000000000000000001253363713600156525ustar00rootroot00000000000000v4l2loopback-0.9.1/examples/.gitignore000066400000000000000000000000261253363713600176400ustar00rootroot00000000000000test yuv4mpeg_to_v4l2 v4l2loopback-0.9.1/examples/Makefile000066400000000000000000000002631253363713600173130ustar00rootroot00000000000000TARGETS=test yuv4mpeg_to_v4l2 ondemandcam .PHONY: all clean all: $(TARGETS) ondemandcam: ondemandcam.c gcc -o ondemandcam ondemandcam.c -lrt -lpthread clean: -rm $(TARGETS) v4l2loopback-0.9.1/examples/README000066400000000000000000000024101253363713600165270ustar00rootroot00000000000000v4l2loopback tests ================== test ---- this small sample application will write an image into a v4l2loopback device. the image will be 640x480 pixels in UYVY colorspace (and since all pixels are set to "0" it will be green). the video-device defaults to /dev/video1 but you can specify another device on the commandline USAGE: $ make test $ ./test /dev/video2 & $ xawtv -c /dev/video2 if you want to use another device you need to modify the VIDEO_DEVICE define at the beginning of the code and recompile. yuv4mpeg_to_v4l2 ---------------- Copyright (C) 2011 Eric C. Cooper Example using mplayer as a producer for the v4l2loopback driver: $ mkfifo /tmp/pipe $ ./yuv4mpeg_to_v4l2 < /tmp/pipe & $ mplayer movie.mp4 -vo yuv4mpeg:file=/tmp/pipe ondemandcam ----------- Copyright 2015, tz@execpc.com, GPLv3. This will wait until something connects to pull frames then sends them. It uses two threads and semaphores to do the testing. It can setup and teardown the frame source. The example just sends a different color each second. It is 80x60 (it is a skeleton for hardware with this resolution) $ make ondemandcam $ ondemandcam /dev/videoX & # where X is the v4l2loopback device It can be viewed with: $ vlc v4l2:///dev/videoX # X is same device as above v4l2loopback-0.9.1/examples/ondemandcam.c000077500000000000000000000057161253363713600203000ustar00rootroot00000000000000#include #include #include #include /* low-level i/o */ #include #include #include #include #include #include #include #include #include #include #include static char *v4l2dev = "/dev/video1"; static int v4l2sink = -1; static int width = 80; //640; // Default for Flash static int height = 60; //480; // Default for Flash static char *vidsendbuf = NULL; static int vidsendsiz = 0; static void init_device() { } static void grab_frame() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); memset( vidsendbuf, 0, 3); switch( ts.tv_sec & 3 ) { case 0: vidsendbuf[0] = 255; break; case 1: vidsendbuf[0] = 255; vidsendbuf[1] = 255; break; case 2: vidsendbuf[1] = 255; break; case 3: vidsendbuf[2] = 255; break; } memcpy( vidsendbuf+3, vidsendbuf, vidsendsiz-3 ); } static void stop_device() { } static void open_vpipe() { v4l2sink = open(v4l2dev, O_WRONLY); if (v4l2sink < 0) { fprintf(stderr, "Failed to open v4l2sink device. (%s)\n", strerror(errno)); exit(-2); } // setup video for proper format struct v4l2_format v; int t; v.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; t = ioctl(v4l2sink, VIDIOC_G_FMT, &v); if( t < 0 ) exit(t); v.fmt.pix.width = width; v.fmt.pix.height = height; v.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; vidsendsiz = width * height * 3; v.fmt.pix.sizeimage = vidsendsiz; t = ioctl(v4l2sink, VIDIOC_S_FMT, &v); if( t < 0 ) exit(t); vidsendbuf = malloc( vidsendsiz ); } static pthread_t sender; static sem_t lock1,lock2; static void *sendvid(void *v) { for (;;) { sem_wait(&lock1); if (vidsendsiz != write(v4l2sink, vidsendbuf, vidsendsiz)) exit(-1); sem_post(&lock2); } } int main(int argc, char **argv) { struct timespec ts; if( argc == 2 ) v4l2dev = argv[1]; open_vpipe(); // open and lock response if (sem_init(&lock2, 0, 1) == -1) exit(-1); sem_wait(&lock2); if (sem_init(&lock1, 0, 1) == -1) exit(-1); pthread_create(&sender, NULL, sendvid, NULL); for (;;) { // wait until a frame can be written fprintf( stderr, "Waiting for sink\n" ); sem_wait(&lock2); // setup source init_device(); // open and setup SPI for (;;) { grab_frame(); // push it out sem_post(&lock1); clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 2; // wait for it to get written (or is blocking) if (sem_timedwait(&lock2, &ts)) break; } stop_device(); // close SPI } close(v4l2sink); return 0; } v4l2loopback-0.9.1/examples/restarting-writer.sh000077500000000000000000000017211253363713600217060ustar00rootroot00000000000000#!/bin/bash device=${1:-/dev/video0} echo Using $device run_writers() { declare -a nbufs nbufs=(10 9 4 5 6) #nbufs=(12 12 12 12 12) for i in `seq 0 4`; do sleep 1 gst-launch-0.10 videotestsrc horizontal-speed=1 num-buffers=90 ! v4l2sink queue-size=${nbufs[$i]} device=$device done } #v4l2-ctl -d $device -c keep_format=1 || exit 1 ./utils/v4l2loopback-ctl set-caps "video/x-raw-yuv, width=640, height=480, framerate=(fraction)25/1" $device || exit 1 v4l2-ctl -d $device -c sustain_framerate=0 || exit 1 v4l2-ctl -d $device -c timeout=2000 || exit 1 gst-launch-0.10 videotestsrc num-buffers=1 ! v4l2sink device=$device || exit 1 { run_writers sleep 10 # can see a flash of green here v4l2-ctl -d $device -c sustain_framerate=1 || exit 1 run_writers sleep 10 run_writers } >/dev/null & gst-launch-0.10 v4l2src queue-size=3 device=$device ! timeoverlay ! ffmpegcolorspace ! autovideosink kill $! 2>/dev/null wait v4l2loopback-0.9.1/examples/test.c000066400000000000000000000113641253363713600170020ustar00rootroot00000000000000/* * How to test v4l2loopback: * 1. launch this test program (even in background), it will initialize the * loopback device and keep it open so it won't loose the settings. * 2. Feed the video device with data according to the settings specified * below: size, pixelformat, etc. * For instance, you can try the default settings with this command: * mencoder video.avi -ovc raw -nosound -vf scale=640:480,format=yuy2 -o /dev/video1 * TODO: a command that limits the fps would be better :) * * Test the video in your favourite viewer, for instance: * luvcview -d /dev/video1 -f yuyv */ #include #include #include #include #include #include #include #include #define ROUND_UP_2(num) (((num)+1)&~1) #define ROUND_UP_4(num) (((num)+3)&~3) #define ROUND_UP_8(num) (((num)+7)&~7) #define ROUND_UP_16(num) (((num)+15)&~15) #define ROUND_UP_32(num) (((num)+31)&~31) #define ROUND_UP_64(num) (((num)+63)&~63) #if 0 # define CHECK_REREAD #endif #define VIDEO_DEVICE "/dev/video0" #if 1 # define FRAME_WIDTH 640 # define FRAME_HEIGHT 480 #else # define FRAME_WIDTH 512 # define FRAME_HEIGHT 512 #endif #if 0 # define FRAME_FORMAT V4L2_PIX_FMT_YUYV #else # define FRAME_FORMAT V4L2_PIX_FMT_YVU420 #endif static int debug=0; int format_properties(const unsigned int format, const unsigned int width, const unsigned int height, size_t*linewidth, size_t*framewidth) { size_t lw, fw; switch(format) { case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: lw = width; /* ??? */ fw = ROUND_UP_4 (width) * ROUND_UP_2 (height); fw += 2 * ((ROUND_UP_8 (width) / 2) * (ROUND_UP_2 (height) / 2)); break; case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_Y41P: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: lw = (ROUND_UP_2 (width) * 2); fw = lw * height; break; default: return 0; } if(linewidth)*linewidth=lw; if(framewidth)*framewidth=fw; return 1; } void print_format(struct v4l2_format*vid_format) { printf(" vid_format->type =%d\n", vid_format->type ); printf(" vid_format->fmt.pix.width =%d\n", vid_format->fmt.pix.width ); printf(" vid_format->fmt.pix.height =%d\n", vid_format->fmt.pix.height ); printf(" vid_format->fmt.pix.pixelformat =%d\n", vid_format->fmt.pix.pixelformat); printf(" vid_format->fmt.pix.sizeimage =%d\n", vid_format->fmt.pix.sizeimage ); printf(" vid_format->fmt.pix.field =%d\n", vid_format->fmt.pix.field ); printf(" vid_format->fmt.pix.bytesperline=%d\n", vid_format->fmt.pix.bytesperline ); printf(" vid_format->fmt.pix.colorspace =%d\n", vid_format->fmt.pix.colorspace ); } int main(int argc, char**argv) { struct v4l2_capability vid_caps; struct v4l2_format vid_format; size_t framesize; size_t linewidth; __u8*buffer; __u8*check_buffer; const char*video_device=VIDEO_DEVICE; int fdwr = 0; int ret_code = 0; int i; if(argc>1) { video_device=argv[1]; printf("using output device: %s\n", video_device); } fdwr = open(video_device, O_RDWR); assert(fdwr >= 0); ret_code = ioctl(fdwr, VIDIOC_QUERYCAP, &vid_caps); assert(ret_code != -1); memset(&vid_format, 0, sizeof(vid_format)); ret_code = ioctl(fdwr, VIDIOC_G_FMT, &vid_format); if(debug)print_format(&vid_format); vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; vid_format.fmt.pix.width = FRAME_WIDTH; vid_format.fmt.pix.height = FRAME_HEIGHT; vid_format.fmt.pix.pixelformat = FRAME_FORMAT; vid_format.fmt.pix.sizeimage = framesize; vid_format.fmt.pix.field = V4L2_FIELD_NONE; vid_format.fmt.pix.bytesperline = linewidth; vid_format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; if(debug)print_format(&vid_format); ret_code = ioctl(fdwr, VIDIOC_S_FMT, &vid_format); assert(ret_code != -1); if(debug)printf("frame: format=%d\tsize=%d\n", FRAME_FORMAT, framesize); print_format(&vid_format); if(!format_properties(vid_format.fmt.pix.pixelformat, vid_format.fmt.pix.width, vid_format.fmt.pix.height, &linewidth, &framesize)) { printf("unable to guess correct settings for format '%d'\n", FRAME_FORMAT); } buffer=(__u8*)malloc(sizeof(__u8)*framesize); check_buffer=(__u8*)malloc(sizeof(__u8)*framesize); memset(buffer, 0, framesize); memset(check_buffer, 0, framesize); for (i = 0; i < framesize; ++i) { //buffer[i] = i % 2; check_buffer[i] = 0; } write(fdwr, buffer, framesize); #ifdef CHECK_REREAD do { /* check if we get the same data on output */ int fdr = open(video_device, O_RDONLY); read(fdr, check_buffer, framesize); for (i = 0; i < framesize; ++i) { if (buffer[i] != check_buffer[i]) assert(0); } close(fdr); } while(0); #endif pause(); close(fdwr); free(buffer); free(check_buffer); return 0; } v4l2loopback-0.9.1/examples/yuv4mpeg_to_v4l2.c000066400000000000000000000057631253363713600211620ustar00rootroot00000000000000/* * Copy a YUV4MPEG stream to a v4l2 output device. * The stream is read from standard input. * The device can be specified as argument; it defaults to /dev/video0. * * Example using mplayer as a producer for the v4l2loopback driver: * * $ mkfifo /tmp/pipe * $ ./yuv4mpeg_to_v4l2 < /tmp/pipe & * $ mplayer movie.mp4 -vo yuv4mpeg:file=/tmp/pipe * * Copyright (C) 2011 Eric C. Cooper * Released under the GNU General Public License */ #include #include #include #include #include #include #include char *prog; char *device; int dev_fd; int frame_width; int frame_height; int frame_bytes; void usage(void) { fprintf(stderr, "Usage: %s [/dev/videoN]\n", prog); exit(1); } void process_args(int argc, char **argv) { prog = argv[0]; switch (argc) { case 1: device = "/dev/video0"; break; case 2: device = argv[1]; break; default: usage(); break; } } void sysfail(char *msg) { perror(msg); exit(1); } void fail(char *msg) { fprintf(stderr, "%s: %s\n", prog, msg); exit(1); } void bad_header(char *kind) { char msg[64]; sprintf(msg, "malformed %s header", kind); fail(msg); } void do_tag(char tag, char *value) { switch (tag) { case 'W': frame_width = strtoul(value, NULL, 10); break; case 'H': frame_height = strtoul(value, NULL, 10); break; } } int read_header(char *magic) { char *p, *q, *p0; size_t n; int first, done; p0 = NULL; if (getline(&p0, &n, stdin) == -1) { free(p0); return 0; } q = p = p0; first = 1; done = 0; while (!done) { while (*q != ' ' && *q != '\n') if (*q++ == '\0') bad_header(magic); done = (*q == '\n'); *q = '\0'; if (first) if (strcmp(p, magic) == 0) first = 0; else bad_header(magic); else do_tag(*p, p + 1); p = ++q; } free(p0); return 1; } void process_header(void) { if (!read_header("YUV4MPEG2")) fail("missing YUV4MPEG2 header"); frame_bytes = 3 * frame_width * frame_height / 2; if (frame_bytes == 0) fail("frame width or height is missing"); } void copy_frames(void) { char *frame; frame = malloc(frame_bytes); if (frame == NULL) fail("cannot malloc frame"); while (read_header("FRAME")) { if (fread(frame, 1, frame_bytes, stdin) != frame_bytes) { free(frame); fail("malformed frame"); } else if (write(dev_fd, frame, frame_bytes) != frame_bytes) { free(frame); sysfail("write"); } } free(frame); } #define vidioc(op, arg) \ if (ioctl(dev_fd, VIDIOC_##op, arg) == -1) \ sysfail(#op); \ else void open_video(void) { struct v4l2_format v; dev_fd = open(device, O_RDWR); if (dev_fd == -1) sysfail(device); v.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; vidioc(G_FMT, &v); v.fmt.pix.width = frame_width; v.fmt.pix.height = frame_height; v.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; v.fmt.pix.sizeimage = frame_bytes; vidioc(S_FMT, &v); } int main(int argc, char **argv) { process_args(argc, argv); process_header(); open_video(); copy_frames(); return 0; } v4l2loopback-0.9.1/man/000077500000000000000000000000001253363713600146075ustar00rootroot00000000000000v4l2loopback-0.9.1/man/touch000066400000000000000000000000001253363713600156420ustar00rootroot00000000000000v4l2loopback-0.9.1/release.sh000077500000000000000000000044211253363713600160140ustar00rootroot00000000000000#!/bin/sh #################################### # prepare package for release # DONE: get current version from module source # DONE: ChangeLog generator using git-dch # DONE: update dkms.conf # TODO: automatically update AUTHORS # TODO: automatically prepare NEWS (from ChangeLog) # TODO: automatically launch editors for ChangeLog/NEWS/AUTHORS # TODO: automatically tag (if all went well) CHANGELOG=ChangeLog AUTHORS=AUTHORS NEWS=NEWS error() { echo "$@" 1>&2 } fatal() { error "$@" exit 1 } usage() { fatal "usage: $0 [] " 1>&2 } getoldversion () { dpkg-parsechangelog --count 1 -l${CHANGELOG} | egrep "^Version:" | head -1 | cut -f2 -d' ' } getmoduleversion() { grep "^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION" v4l2loopback.c \ | sed -e 's|^#define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION||' \ -e 's|^[^0-9]*||' -e 's|[^0-9]*$||' \ -e 's|[^0-9][^0-9]*|.|g' } getgitbranch() { git rev-parse --abbrev-ref HEAD } if [ "$(getgitbranch)" != "master" ]; then fatal "current branch '$(getgitbranch)' is not 'master'" fi if [ "x$2" = "x" ]; then ## guess current version NEWVERSION=$1 OLDVERSION=$(getoldversion) else OLDVERSION=$1 NEWVERSION=$2 fi if [ "x${NEWVERSION}" = "x" ]; then NEWVERSION=$(getmoduleversion) fi if git tag -l v${OLDVERSION} | grep . >/dev/null then : else fatal "it seems like there is no tag 'v${OLDVERSION}'" fi if [ "x${OLDVERSION}" = "x" ]; then usage fi echo "updating from ${OLDVERSION}" if [ "x${NEWVERSION}" = "x" ]; then usage fi if dpkg --compare-versions ${OLDVERSION} ge ${NEWVERSION} then fatal "version mismatch: ${NEWVErSION} is not newer than ${OLDVERSION}" fi echo "updating to ${NEWVERSION}" OK=false mkdir debian cp ${CHANGELOG} debian/changelog gbp dch -R --since "v${OLDVERSION}" -N "${NEWVERSION}" && cat debian/changelog > ${CHANGELOG} && OK=true rm -rf debian if [ "x${OK}" = "xtrue" ]; then sed -e "s|^PACKAGE_VERSION=\".*\"$|PACKAGE_VERSION=\"${NEWVERSION}\"|" -i dkms.conf fi if [ "x${OK}" = "xtrue" ]; then echo "all went well" echo "" echo "- please check your ${CHANGELOG}" echo "- please check&edit your ${NEWS}" echo "- please check&edit your ${AUTHORS}" echo "- and don't forget to git-tag the new version as v${NEWVERSION}" echo " git tag v${NEWVERSION} -m \"\"" fi v4l2loopback-0.9.1/tests/000077500000000000000000000000001253363713600151765ustar00rootroot00000000000000v4l2loopback-0.9.1/tests/Makefile000066400000000000000000000000201253363713600166260ustar00rootroot00000000000000all: test_dqbuf v4l2loopback-0.9.1/tests/interlaced_w000077500000000000000000000013461253363713600175700ustar00rootroot00000000000000#!/bin/sh DEVICE=$1 WIDTH=320 HEIGHT=240 #WIDTH=1920 #HEIGHT=1080 ## uncomment the 'progressive' line to switch to non-interlaced INTERLACED=interlaced #INTERLACED=progressive INTERLACED=mixed error() { echo "$@" 1>&2 } debug() { error "$@" $@ } if [ "x${GSTLAUNCH}" = "x" ]; then GSTLAUNCH=$(which gst-launch-1.0) fi if [ "x${GSTLAUNCH}" = "x" ]; then error "need gst-launch-1.0" exit 1 fi if [ "x${DEVICE}" = "x" ]; then DEVICE=/dev/video0 fi if [ -c "${DEVICE}" ]; then : else error "illegal device ${DEVICE}" exit 1 fi debug ${GSTLAUNCH} videotestsrc horizontal-speed=16 \ ! video/x-raw, format=UYVY, width=${WIDTH}, height=${HEIGHT}, frame-rate=30000/1001, interlace-mode=${INTERLACED} \ ! v4l2sink device=${DEVICE} v4l2loopback-0.9.1/tests/test_dqbuf.c000066400000000000000000000057021253363713600175060ustar00rootroot00000000000000/* * v4l2loopback.c -- video4linux2 loopback driver * * Copyright (C) 2014 Nicolas Dufresne */ #include #include #include #include #include #include #include #include #include #include #include #define COUNT 4 #define sysfail(msg) { printf ("%s failed: %s\n", (msg), strerror (errno)); return -1; } void usage(const char*progname) { printf("usage: %s \n", progname); exit(1); } int main (int argc, char **argv) { struct v4l2_format fmt = { 0 }; struct v4l2_requestbuffers breq = { 0 }; struct v4l2_buffer bufs[COUNT]; void *data[COUNT] = { 0 }; int fd; int i; if(argc<2) usage(argv[0]); fd = open (argv[1], O_RDWR); if (fd < 0) sysfail("open"); fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; fmt.fmt.pix.width = 320; fmt.fmt.pix.height = 240; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; if (ioctl (fd, VIDIOC_S_FMT, &fmt) < 0) sysfail ("S_FMT"); breq.count = COUNT; breq.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; breq.memory = V4L2_MEMORY_MMAP; if (ioctl (fd, VIDIOC_REQBUFS, &breq) < 0) sysfail ("REQBUFS"); assert (breq.count == COUNT); memset (bufs, 0, sizeof (bufs)); for (i = 0; i < COUNT; i++) { int p; bufs[i].index = i; bufs[i].type = breq.type; bufs[i].memory = breq.memory; if (ioctl (fd, VIDIOC_QUERYBUF, &bufs[i]) < 0) sysfail ("QUERYBUF"); data[i] = mmap (NULL, bufs[i].length, PROT_WRITE, MAP_SHARED, fd, bufs[i].m.offset); if (data[i] == MAP_FAILED) sysfail ("mmap"); for (p = 0; p < (bufs[i].bytesused >> 2); p++) ((unsigned int*)data[i])[p] = 0xFF00FF00; } if (ioctl (fd, VIDIOC_QBUF, &bufs[0]) < 0) sysfail ("QBUF"); if ((bufs[0].flags & V4L2_BUF_FLAG_QUEUED) == 0) { printf ("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); bufs[0].flags |= V4L2_BUF_FLAG_QUEUED; } if (ioctl (fd, VIDIOC_STREAMON, &fmt.type) < 0) sysfail ("STREAMON"); i = 1; while (1) { struct v4l2_buffer buf = { 0 }; if (ioctl (fd, VIDIOC_QBUF, &bufs[i]) < 0) sysfail ("QBUF"); printf ("\tQUEUED=%d\tDONE=%d\n", bufs[i].flags & V4L2_BUF_FLAG_QUEUED, bufs[i].flags & V4L2_BUF_FLAG_DONE); if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { printf ("BUG #1: Driver should set the QUEUED flag before returning from QBUF\n"); bufs[i].flags |= V4L2_BUF_FLAG_QUEUED; } buf.type = breq.type; buf.memory = breq.memory; if (ioctl (fd, VIDIOC_DQBUF, &buf) < 0) sysfail ("DBBUF"); i = buf.index; if ((bufs[i].flags & V4L2_BUF_FLAG_QUEUED) == 0) { printf ("BUG #2: Driver should not dequeue a buffer that was not intially queued\n"); } #if 0 assert (bufs[i].flags & V4L2_BUF_FLAG_QUEUED); assert (!(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))); #endif bufs[i] = buf; } return 0; } v4l2loopback-0.9.1/utils/000077500000000000000000000000001253363713600151745ustar00rootroot00000000000000v4l2loopback-0.9.1/utils/v4l2loopback-ctl000077500000000000000000000102011253363713600201760ustar00rootroot00000000000000#!/bin/bash usage() { ## ugly linebreaks in order to make help2man more happy cat < /dev/videoX Set device fps (if format is already negotiated). set-timeout-image /dev/videoX Set timeout on device, and optionally the placeholder picture, instead of default null frame. set-caps /dev/videoX Fix format for the device (using GStreamer caps), so it will be maintained until reset, regardless of future readers/writers. Options: FPS frames per second, either as integer ('30') or fraction ('50/2') IMAGE image file ("no-input.png"), or 'videotestsrc' CAPS GStreamer caps ("video/x-raw-yuv, width=640, height=480"), or 'any' /dev/videoX the v4l2-loopback device file you want to act on ! exit 1 } version() { cat < Copyright (c) 2012 Anton Novikov (random.plant@gmail.com) ! exit 1 } die() { echo "$@" 1>&2 exit 1 } parse_device() { if [ -e "$1" ]; then device="$1" elif [ -e "/dev/video$1" ]; then device="/dev/video$1" else die "can't parse device" fi sysfs=/sys/devices/virtual/video4linux/${device##*/} } check_application() { which $1 > /dev/null || die "can't find $1" } set_fps() { fps="$1" parse_device "$2" [ -z "$fps" ] && usage echo "@$fps" | sudo tee "$sysfs/format" >/dev/null || die "Set fps failed" echo OK } set_caps() { caps="$1" parse_device "$2" [ -z "$caps" ] && usage [ "$caps" = 'any' ] && { check_application v4l2-ctl v4l2-ctl -d $device -c keep_format=0 return } [ -n "`cat $sysfs/format`" ] && die "Device is busy" declare -a params declare -a nofps_params IFS=',' read -ra params <<< "$caps" for p in "${params[@]}"; do if echo "$p" | grep -q framerate; then fps=`echo "$p" | sed -r 's/.*(=|\))//g'` echo "$fps" | grep -q '[^0-9/]' && die "could not parse framerate param" echo "@$fps" | sudo tee $sysfs/format >/dev/null || die "could not set fps attr" #echo "fps: $fps" else nofps_params=("${nofps_params[@]}" "$p") fi done { IFS=',' nofps_caps="${nofps_params[*]}" } #echo "nofps_caps: $nofps_caps" check_application v4l2-ctl check_application gst-launch-0.10 v4l2-ctl -d $device -c keep_format=1 || exit 1 v4l2-ctl -d $device -c sustain_framerate=1 || exit 1 gst-launch-0.10 videotestsrc num-buffers=1 ! "$nofps_caps" ! v4l2sink device=$device || die "output to $device failed" } set_timeout_image() { imagefile="$1" parse_device "$2" [ -z "$imagefile" ] && return [ -n "`cat $sysfs/format`" ] || die "Device has no format negotiated" check_application v4l2-ctl check_application gst-launch-0.10 v4l2-ctl -d $device -c timeout_image_io=1 if [ "$imagefile" = 'videotestsrc' ]; then gst-launch-0.10 videotestsrc num-buffers=1 ! v4l2sink device=$device else uri="file://`readlink -f $imagefile`" || die "no file" echo "Reading from $uri" gst-launch-0.10 uridecodebin uri=$uri ! ffmpegcolorspace ! videoscale ! imagefreeze ! identity error-after=2 ! v4l2sink show-preroll-frame=false device=$device fi timeout=`v4l2-ctl -d $device -C timeout | sed -n 's/^timeout: //p'` [ -n "$timeout" ] || die "couldn't get timeout" echo "timeout: $timeout" [ "$timeout" = 0 ] && { echo 'Timeout is currently disabled; you can set it to some positive value, e.g.:' echo "$ v4l2-ctl -d $device -c timeout=3000" } } command="$1" shift case "$command" in "-h"|"--help") usage ;; "-v"|"--version") version ;; "set-fps") set_fps "$@" ;; "set-caps") set_caps "$@" ;; "set-timeout-image") set_timeout_image "$@" ;; *) usage ;; esac v4l2loopback-0.9.1/v4l2loopback.c000066400000000000000000001777671253363713600165330ustar00rootroot00000000000000/* -*- c-file-style: "linux" -*- */ /* * v4l2loopback.c -- video4linux2 loopback driver * * Copyright (C) 2005-2009 Vasily Levin (vasaka@gmail.com) * Copyright (C) 2010-2014 IOhannes m zmoelnig (zmoelnig@iem.at) * Copyright (C) 2011 Stefan Diewald (stefan.diewald@mytum.de) * Copyright (C) 2012 Anton Novikov (random.plant@gmail.com) * * 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. * */ #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) # define HAVE__V4L2_DEVICE # include #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) # define HAVE__V4L2_CTRLS # include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,1) # define kstrtoul strict_strtoul #endif #define V4L2LOOPBACK_VERSION_CODE KERNEL_VERSION(0, 9, 1) MODULE_DESCRIPTION("V4L2 loopback video device"); MODULE_AUTHOR("Vasily Levin, " \ "IOhannes m zmoelnig ," \ "Stefan Diewald," \ "Anton Novikov" \ "et al." \ ); MODULE_LICENSE("GPL"); /* * helpers */ #define STRINGIFY(s) #s #define STRINGIFY2(s) STRINGIFY(s) #define dprintk(fmt, args...) \ do { if (debug > 0) { \ printk(KERN_INFO "v4l2-loopback[" STRINGIFY2(__LINE__) "]: " fmt, ##args); \ } } while (0) #define MARK() \ do { if (debug > 1) { \ printk(KERN_INFO "%s:%d[%s]\n", __FILE__, __LINE__, __func__); \ } } while (0) #define dprintkrw(fmt, args...) \ do { if (debug > 2) { \ printk(KERN_INFO "v4l2-loopback[" STRINGIFY2(__LINE__)"]: " fmt, ##args); \ } } while (0) /* * compatibility hacks */ #ifndef HAVE__V4L2_CTRLS struct v4l2_ctrl_handler { int error; }; struct v4l2_ctrl_config { void *ops; u32 id; const char *name; int type; s32 min; s32 max; u32 step; s32 def; }; int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler*hdl, unsigned nr_of_controls_hint) { hdl->error=0; return 0; } void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) { } void*v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_config*conf, void*priv) { return NULL; } #endif /* HAVE__V4L2_CTRLS */ #ifndef HAVE__V4L2_DEVICE /* dummy v4l2_device struct/functions */ # define V4L2_DEVICE_NAME_SIZE (20 + 16) struct v4l2_device { char name[V4L2_DEVICE_NAME_SIZE]; struct v4l2_ctrl_handler*ctrl_handler; }; static inline int v4l2_device_register (void *dev, void *v4l2_dev) { return 0; } static inline void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { return; } #endif /* HAVE__V4L2_DEVICE */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) # define v4l2_file_operations file_operations #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) void *v4l2l_vzalloc(unsigned long size) { void *data = vmalloc(size); memset(data, 0, size); return data; } #else # define v4l2l_vzalloc vzalloc #endif /* module constants * can be overridden during he build process using something like * make KCPPFLAGS="-DMAX_DEVICES=100" */ /* maximum number of v4l2loopback devices that can be created */ #ifndef MAX_DEVICES # define MAX_DEVICES 8 #endif /* when a producer is considered to have gone stale */ #ifndef MAX_TIMEOUT # define MAX_TIMEOUT (100 * 1000) /* in msecs */ #endif /* max buffers that can be mapped, actually they * are all mapped to max_buffers buffers */ #ifndef MAX_BUFFERS # define MAX_BUFFERS 32 #endif /* module parameters */ static int debug = 0; module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "debugging level (higher values == more verbose)"); static int max_buffers = 8; module_param(max_buffers, int, S_IRUGO); MODULE_PARM_DESC(max_buffers, "how many buffers should be allocated"); /* how many times a device can be opened * the per-module default value can be overridden on a per-device basis using * the /sys/devices interface * * note that max_openers should be at least 2 in order to get a working system: * one opener for the producer and one opener for the consumer * however, we leave that to the user */ static int max_openers = 10; module_param(max_openers, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_openers, "how many users can open loopback device"); static int devices = -1; module_param(devices, int, 0); MODULE_PARM_DESC(devices, "how many devices should be created"); static int video_nr[MAX_DEVICES] = { [0 ... (MAX_DEVICES - 1)] = -1 }; module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video device numbers (-1=auto, 0=/dev/video0, etc.)"); static char *card_label[MAX_DEVICES]; module_param_array(card_label, charp, NULL, 0000); MODULE_PARM_DESC(card_label, "card labels for every device"); static bool exclusive_caps[MAX_DEVICES] = { [0 ... (MAX_DEVICES - 1)] = 1 }; module_param_array(exclusive_caps, bool, NULL, 0444); /* FIXXME: wording */ MODULE_PARM_DESC(exclusive_caps, "whether to announce OUTPUT/CAPTURE capabilities exclusively or not"); /* format specifications */ #define V4L2LOOPBACK_SIZE_MIN_WIDTH 48 #define V4L2LOOPBACK_SIZE_MIN_HEIGHT 32 #define V4L2LOOPBACK_SIZE_MAX_WIDTH 8192 #define V4L2LOOPBACK_SIZE_MAX_HEIGHT 8192 #define V4L2LOOPBACK_SIZE_DEFAULT_WIDTH 640 #define V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT 480 static int max_width = V4L2LOOPBACK_SIZE_MAX_WIDTH; module_param(max_width, int, S_IRUGO); MODULE_PARM_DESC(max_width, "maximum frame width"); static int max_height = V4L2LOOPBACK_SIZE_MAX_HEIGHT; module_param(max_height, int, S_IRUGO); MODULE_PARM_DESC(max_height, "maximum frame height"); /* control IDs */ #ifndef HAVE__V4L2_CTRLS # define V4L2LOOPBACK_CID_BASE (V4L2_CID_PRIVATE_BASE) #else # define V4L2LOOPBACK_CID_BASE (V4L2_CID_USER_BASE | 0xf000) #endif #define CID_KEEP_FORMAT (V4L2LOOPBACK_CID_BASE + 0) #define CID_SUSTAIN_FRAMERATE (V4L2LOOPBACK_CID_BASE + 1) #define CID_TIMEOUT (V4L2LOOPBACK_CID_BASE + 2) #define CID_TIMEOUT_IMAGE_IO (V4L2LOOPBACK_CID_BASE + 3) static int v4l2loopback_s_ctrl(struct v4l2_ctrl *ctrl); static const struct v4l2_ctrl_ops v4l2loopback_ctrl_ops = { .s_ctrl = v4l2loopback_s_ctrl, }; static const struct v4l2_ctrl_config v4l2loopback_ctrl_keepformat = { .ops = &v4l2loopback_ctrl_ops, .id = CID_KEEP_FORMAT, .name = "keep_format", .type = V4L2_CTRL_TYPE_BOOLEAN, .min = 0, .max = 1, .step = 1, .def = 0, }; static const struct v4l2_ctrl_config v4l2loopback_ctrl_sustainframerate = { .ops = &v4l2loopback_ctrl_ops, .id = CID_SUSTAIN_FRAMERATE, .name = "sustain_framerate", .type = V4L2_CTRL_TYPE_BOOLEAN, .min = 0, .max = 1, .step = 1, .def = 0, }; static const struct v4l2_ctrl_config v4l2loopback_ctrl_timeout = { .ops = &v4l2loopback_ctrl_ops, .id = CID_TIMEOUT, .name = "timeout", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = MAX_TIMEOUT, .step = 1, .def = 0, }; static const struct v4l2_ctrl_config v4l2loopback_ctrl_timeoutimageio = { .ops = &v4l2loopback_ctrl_ops, .id = CID_TIMEOUT_IMAGE_IO, .name = "timeout_image_io", .type = V4L2_CTRL_TYPE_BOOLEAN, .min = 0, .max = 1, .step = 1, .def = 0, }; /* module structures */ struct v4l2loopback_private { int devicenr; }; /* TODO(vasaka) use typenames which are common to kernel, but first find out if * it is needed */ /* struct keeping state and settings of loopback device */ struct v4l2l_buffer { struct v4l2_buffer buffer; struct list_head list_head; int use_count; }; struct v4l2_loopback_device { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct video_device *vdev; /* pixel and stream format */ struct v4l2_pix_format pix_format; struct v4l2_captureparm capture_param; unsigned long frame_jiffies; /* ctrls */ int keep_format; /* CID_KEEP_FORMAT; stay ready_for_capture even when all openers close() the device */ int sustain_framerate; /* CID_SUSTAIN_FRAMERATE; duplicate frames to maintain (close to) nominal framerate */ /* buffers stuff */ u8 *image; /* pointer to actual buffers data */ unsigned long int imagesize; /* size of buffers data */ int buffers_number; /* should not be big, 4 is a good choice */ struct v4l2l_buffer buffers[MAX_BUFFERS]; /* inner driver buffers */ int used_buffers; /* number of the actually used buffers */ int max_openers; /* how many times can this device be opened */ int write_position; /* number of last written frame + 1 */ struct list_head outbufs_list; /* buffers in output DQBUF order */ int bufpos2index[MAX_BUFFERS]; /* mapping of (read/write_position % used_buffers) * to inner buffer index */ long buffer_size; /* sustain_framerate stuff */ struct timer_list sustain_timer; unsigned int reread_count; /* timeout stuff */ unsigned long timeout_jiffies; /* CID_TIMEOUT; 0 means disabled */ int timeout_image_io; /* CID_TIMEOUT_IMAGE_IO; next opener will * read/write to timeout_image */ u8 *timeout_image; /* copy of it will be captured when timeout passes */ struct v4l2l_buffer timeout_image_buffer; struct timer_list timeout_timer; int timeout_happened; /* sync stuff */ atomic_t open_count; int ready_for_capture;/* set to true when at least one writer opened * device and negotiated format */ int ready_for_output; /* set to true when no writer is currently attached * this differs slightly from !ready_for_capture, * e.g. when using fallback images */ int announce_all_caps;/* set to false, if device caps (OUTPUT/CAPTURE) * should only be announced if the resp. "ready" * flag is set; default=TRUE */ wait_queue_head_t read_event; spinlock_t lock; }; /* types of opener shows what opener wants to do with loopback */ enum opener_type { UNNEGOTIATED = 0, READER = 1, WRITER = 2, }; /* struct keeping state and type of opener */ struct v4l2_loopback_opener { enum opener_type type; int vidioc_enum_frameintervals_calls; int read_position; /* number of last processed frame + 1 or * write_position - 1 if reader went out of sync */ unsigned int reread_count; struct v4l2_buffer *buffers; int buffers_number; /* should not be big, 4 is a good choice */ int timeout_image_io; }; /* this is heavily inspired by the bttv driver found in the linux kernel */ struct v4l2l_format { char *name; int fourcc; /* video4linux 2 */ int depth; /* bit/pixel */ int flags; }; /* set the v4l2l_format.flags to PLANAR for non-packed formats */ #define FORMAT_FLAGS_PLANAR 0x01 #define FORMAT_FLAGS_COMPRESSED 0x02 static const struct v4l2l_format formats[] = { #include "v4l2loopback_formats.h" }; static const unsigned int FORMATS = ARRAY_SIZE(formats); static char *fourcc2str(unsigned int fourcc, char buf[4]) { buf[0] = (fourcc >> 0) & 0xFF; buf[1] = (fourcc >> 8) & 0xFF; buf[2] = (fourcc >> 16) & 0xFF; buf[3] = (fourcc >> 24) & 0xFF; return buf; } static const struct v4l2l_format *format_by_fourcc(int fourcc) { unsigned int i; for (i = 0; i < FORMATS; i++) { if (formats[i].fourcc == fourcc) return formats + i; } dprintk("unsupported format '%c%c%c%c'\n", (fourcc >> 0) & 0xFF, (fourcc >> 8) & 0xFF, (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF); return NULL; } static void pix_format_set_size(struct v4l2_pix_format *f, const struct v4l2l_format *fmt, unsigned int width, unsigned int height) { f->width = width; f->height = height; if (fmt->flags & FORMAT_FLAGS_PLANAR) { f->bytesperline = width; /* Y plane */ f->sizeimage = (width * height * fmt->depth) >> 3; } else if (fmt->flags & FORMAT_FLAGS_COMPRESSED) { /* doesn't make sense for compressed formats */ f->bytesperline = 0; f->sizeimage = (width * height * fmt->depth) >> 3; } else { f->bytesperline = (width * fmt->depth) >> 3; f->sizeimage = height * f->bytesperline; } } static int set_timeperframe(struct v4l2_loopback_device *dev, struct v4l2_fract *tpf) { if((tpf->denominator < 1) || (tpf->numerator < 1)) { return -EINVAL; } dev->capture_param.timeperframe = *tpf; dev->frame_jiffies = max(1UL, msecs_to_jiffies(1000) * tpf->numerator / tpf->denominator); return 0; } static struct v4l2_loopback_device *v4l2loopback_cd2dev(struct device *cd); /* device attributes */ /* available via sysfs: /sys/devices/virtual/video4linux/video* */ static ssize_t attr_show_format(struct device *cd, struct device_attribute *attr, char *buf) { /* gets the current format as "FOURCC:WxH@f/s", e.g. "YUYV:320x240@1000/30" */ struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); const struct v4l2_fract *tpf; char buf4cc[5], buf_fps[32]; if (!dev || !dev->ready_for_capture) return 0; tpf = &dev->capture_param.timeperframe; fourcc2str(dev->pix_format.pixelformat, buf4cc); if (tpf->numerator == 1) snprintf(buf_fps, sizeof(buf_fps), "%d", tpf->denominator); else snprintf(buf_fps, sizeof(buf_fps), "%d/%d", tpf->denominator, tpf->numerator); return sprintf(buf, "%4s:%dx%d@%s\n", buf4cc, dev->pix_format.width, dev->pix_format.height, buf_fps); } static ssize_t attr_store_format(struct device *cd, struct device_attribute *attr, const char *buf, size_t len) { struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); int fps_num = 0, fps_den = 1; /* only fps changing is supported */ if (sscanf(buf, "@%d/%d", &fps_num, &fps_den) > 0) { struct v4l2_fract f = { .numerator = fps_den, .denominator = fps_num }; int err=0; if((err=set_timeperframe(dev, &f)) < 0) return err; return len; } return -EINVAL; } static DEVICE_ATTR(format, S_IRUGO | S_IWUSR, attr_show_format, attr_store_format); static ssize_t attr_show_buffers(struct device *cd, struct device_attribute *attr, char *buf) { struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); return sprintf(buf, "%d\n", dev->used_buffers); } static DEVICE_ATTR(buffers, S_IRUGO, attr_show_buffers, NULL); static ssize_t attr_show_maxopeners(struct device *cd, struct device_attribute *attr, char *buf) { struct v4l2_loopback_device *dev = v4l2loopback_cd2dev(cd); return sprintf(buf, "%d\n", dev->max_openers); } static ssize_t attr_store_maxopeners(struct device *cd, struct device_attribute *attr, const char *buf, size_t len) { struct v4l2_loopback_device *dev = NULL; unsigned long curr = 0; if (kstrtoul(buf, 0, &curr)) return -EINVAL; dev = v4l2loopback_cd2dev(cd); if (dev->max_openers == curr) return len; if (dev->open_count.counter > curr) { /* request to limit to less openers as are currently attached to us */ return -EINVAL; } dev->max_openers = (int)curr; return len; } static DEVICE_ATTR(max_openers, S_IRUGO | S_IWUSR, attr_show_maxopeners, attr_store_maxopeners); static void v4l2loopback_remove_sysfs(struct video_device *vdev) { #define V4L2_SYSFS_DESTROY(x) device_remove_file(&vdev->dev, &dev_attr_##x) if (vdev) { V4L2_SYSFS_DESTROY(format); V4L2_SYSFS_DESTROY(buffers); V4L2_SYSFS_DESTROY(max_openers); /* ... */ } } static void v4l2loopback_create_sysfs(struct video_device *vdev) { int res = 0; #define V4L2_SYSFS_CREATE(x) res = device_create_file(&vdev->dev, &dev_attr_##x); if (res < 0) break if (!vdev) return; do { V4L2_SYSFS_CREATE(format); V4L2_SYSFS_CREATE(buffers); V4L2_SYSFS_CREATE(max_openers); /* ... */ } while (0); if (res >= 0) return; dev_err(&vdev->dev, "%s error: %d\n", __func__, res); } /* global module data */ struct v4l2_loopback_device *devs[MAX_DEVICES]; static struct v4l2_loopback_device *v4l2loopback_cd2dev(struct device *cd) { struct video_device *loopdev = to_video_device(cd); struct v4l2loopback_private *ptr = (struct v4l2loopback_private *)video_get_drvdata(loopdev); int nr = ptr->devicenr; if (nr < 0 || nr >= devices) { printk(KERN_ERR "v4l2-loopback: illegal device %d\n", nr); return NULL; } return devs[nr]; } static struct v4l2_loopback_device *v4l2loopback_getdevice(struct file *f) { struct video_device *loopdev = video_devdata(f); struct v4l2loopback_private *ptr = (struct v4l2loopback_private *)video_get_drvdata(loopdev); int nr = ptr->devicenr; if (nr < 0 || nr >= devices) { printk(KERN_ERR "v4l2-loopback: illegal device %d\n", nr); return NULL; } return devs[nr]; } /* forward declarations */ static void init_buffers(struct v4l2_loopback_device *dev); static int allocate_buffers(struct v4l2_loopback_device *dev); static int free_buffers(struct v4l2_loopback_device *dev); static void try_free_buffers(struct v4l2_loopback_device *dev); static int allocate_timeout_image(struct v4l2_loopback_device *dev); static void check_timers(struct v4l2_loopback_device *dev); static const struct v4l2_file_operations v4l2_loopback_fops; static const struct v4l2_ioctl_ops v4l2_loopback_ioctl_ops; /* Queue helpers */ /* next functions sets buffer flags and adjusts counters accordingly */ static inline void set_done(struct v4l2l_buffer *buffer) { buffer->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; buffer->buffer.flags |= V4L2_BUF_FLAG_DONE; } static inline void set_queued(struct v4l2l_buffer *buffer) { buffer->buffer.flags &= ~V4L2_BUF_FLAG_DONE; buffer->buffer.flags |= V4L2_BUF_FLAG_QUEUED; } static inline void unset_flags(struct v4l2l_buffer *buffer) { buffer->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; buffer->buffer.flags &= ~V4L2_BUF_FLAG_DONE; } /* V4L2 ioctl caps and params calls */ /* returns device capabilities * called on VIDIOC_QUERYCAP */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); int devnr = ((struct v4l2loopback_private *)video_get_drvdata(dev->vdev))->devicenr; strlcpy(cap->driver, "v4l2 loopback", sizeof(cap->driver)); if (card_label[devnr] != NULL) { snprintf(cap->card, sizeof(cap->card), card_label[devnr]); } else { snprintf(cap->card, sizeof(cap->card), "Dummy video device (0x%04X)", devnr); } snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:v4l2loopback-%03d", devnr); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) /* since 3.1.0, the v4l2-core system is supposed to set the version */ cap->version = V4L2LOOPBACK_VERSION_CODE; #endif cap->capabilities = #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) V4L2_CAP_DEVICE_CAPS | #endif V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; #ifdef V4L2_CAP_VIDEO_M2M cap->capabilities |= V4L2_CAP_VIDEO_M2M; #endif /* V4L2_CAP_VIDEO_M2M */ if (dev->announce_all_caps) { cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; } else { if (dev->ready_for_capture) { cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE; } if (dev->ready_for_output) { cap->capabilities |= V4L2_CAP_VIDEO_OUTPUT; } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) cap->device_caps = (cap->capabilities & ~V4L2_CAP_DEVICE_CAPS); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) cap->device_caps = cap->capabilities; cap->capabilities |= V4L2_CAP_DEVICE_CAPS; #endif memset(cap->reserved, 0, sizeof(cap->reserved)); return 0; } static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *argp) { struct v4l2_loopback_device *dev; /* LATER: what does the index really mean? * if it's about enumerating formats, we can safely ignore it * (CHECK) */ /* there can be only one... */ if (argp->index) return -EINVAL; dev = v4l2loopback_getdevice(file); if (dev->ready_for_capture) { /* format has already been negotiated * cannot change during runtime */ argp->type = V4L2_FRMSIZE_TYPE_DISCRETE; argp->discrete.width = dev->pix_format.width; argp->discrete.height = dev->pix_format.height; } else { /* if the format has not been negotiated yet, we accept anything */ argp->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; argp->stepwise.min_width = V4L2LOOPBACK_SIZE_MIN_WIDTH; argp->stepwise.min_height = V4L2LOOPBACK_SIZE_MIN_HEIGHT; argp->stepwise.max_width = max_width; argp->stepwise.max_height = max_height; argp->stepwise.step_width = 1; argp->stepwise.step_height = 1; } return 0; } /* returns frameinterval (fps) for the set resolution * called on VIDIOC_ENUM_FRAMEINTERVALS */ static int vidioc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *argp) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); struct v4l2_loopback_opener *opener = file->private_data; if (dev->ready_for_capture) { if (opener->vidioc_enum_frameintervals_calls > 0) return -EINVAL; if (argp->width == dev->pix_format.width && argp->height == dev->pix_format.height) { argp->type = V4L2_FRMIVAL_TYPE_DISCRETE; argp->discrete = dev->capture_param.timeperframe; opener->vidioc_enum_frameintervals_calls++; return 0; } return -EINVAL; } return 0; } /* ------------------ CAPTURE ----------------------- */ /* returns device formats * called on VIDIOC_ENUM_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE */ static int vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) { struct v4l2_loopback_device *dev; MARK(); dev = v4l2loopback_getdevice(file); if (f->index) return -EINVAL; if (dev->ready_for_capture) { const __u32 format = dev->pix_format.pixelformat; snprintf(f->description, sizeof(f->description), "[%c%c%c%c]", (format >> 0) & 0xFF, (format >> 8) & 0xFF, (format >> 16) & 0xFF, (format >> 24) & 0xFF); f->pixelformat = dev->pix_format.pixelformat; } else { return -EINVAL; } f->flags = 0; MARK(); return 0; } /* returns current video format format fmt * called on VIDIOC_G_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE */ static int vidioc_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_device *dev; MARK(); dev = v4l2loopback_getdevice(file); if (!dev->ready_for_capture) return -EINVAL; fmt->fmt.pix = dev->pix_format; MARK(); return 0; } /* checks if it is OK to change to format fmt; * actual check is done by inner_try_fmt_cap * just checking that pixelformat is OK and set other parameters, app should * obey this decision * called on VIDIOC_TRY_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE */ static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; char buf[5]; opener = file->private_data; opener->type = READER; dev = v4l2loopback_getdevice(file); if (0 == dev->ready_for_capture) { dprintk("setting fmt_cap not possible yet\n"); return -EBUSY; } if (fmt->fmt.pix.pixelformat != dev->pix_format.pixelformat) return -EINVAL; fmt->fmt.pix = dev->pix_format; buf[4] = 0; dprintk("capFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf)); return 0; } /* sets new output format, if possible * actually format is set by input and we even do not check it, just return * current one, but it is possible to set subregions of input TODO(vasaka) * called on VIDIOC_S_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE */ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt) { return vidioc_try_fmt_cap(file, priv, fmt); } /* ------------------ OUTPUT ----------------------- */ /* returns device formats; * LATER: allow all formats * called on VIDIOC_ENUM_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT */ static int vidioc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f) { struct v4l2_loopback_device *dev; const struct v4l2l_format *fmt; dev = v4l2loopback_getdevice(file); if (dev->ready_for_capture) { const __u32 format = dev->pix_format.pixelformat; /* format has been fixed by the writer, so only one single format is supported */ if (f->index) return -EINVAL; fmt = format_by_fourcc(format); if (NULL == fmt) return -EINVAL; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* f->flags = ??; */ snprintf(f->description, sizeof(f->description), "%s", fmt->name); f->pixelformat = dev->pix_format.pixelformat; } else { __u32 format; /* fill in a dummy format */ /* coverity[unsigned_compare] */ if (f->index < 0 || f->index >= FORMATS) return -EINVAL; fmt = &formats[f->index]; f->pixelformat = fmt->fourcc; format = f->pixelformat; /* strlcpy(f->description, "dummy OUT format", sizeof(f->description)); */ snprintf(f->description, sizeof(f->description), "%s", fmt->name); } f->flags = 0; return 0; } /* returns current video format format fmt */ /* NOTE: this is called from the producer * so if format has not been negotiated yet, * it should return ALL of available formats, * called on VIDIOC_G_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT */ static int vidioc_g_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; MARK(); dev = v4l2loopback_getdevice(file); opener = file->private_data; opener->type = WRITER; dev->ready_for_output = 1; /* * LATER: this should return the currently valid format * gstreamer doesn't like it, if this returns -EINVAL, as it * then concludes that there is _no_ valid format * CHECK whether this assumption is wrong, * or whether we have to always provide a valid format */ fmt->fmt.pix = dev->pix_format; return 0; } /* checks if it is OK to change to format fmt; * if format is negotiated do not change it * called on VIDIOC_TRY_FMT with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT */ static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; MARK(); opener = file->private_data; opener->type = WRITER; dev = v4l2loopback_getdevice(file); dev->ready_for_output = 1; /* TODO(vasaka) loopback does not care about formats writer want to set, * maybe it is a good idea to restrict format somehow */ if (dev->ready_for_capture) { fmt->fmt.pix = dev->pix_format; } else { __u32 w = fmt->fmt.pix.width; __u32 h = fmt->fmt.pix.height; __u32 pixfmt = fmt->fmt.pix.pixelformat; const struct v4l2l_format *format = format_by_fourcc(pixfmt); if (w > max_width) w = max_width; if (h > max_height) h = max_height; dprintk("trying image %dx%d\n", w, h); if (w < 1) w = V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; if (h < 1) h = V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; if (NULL == format) format = &formats[0]; pix_format_set_size(&fmt->fmt.pix, format, w, h); fmt->fmt.pix.pixelformat = format->fourcc; fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; if (V4L2_FIELD_ANY == fmt->fmt.pix.field) fmt->fmt.pix.field = V4L2_FIELD_NONE; /* FIXXME: try_fmt should never modify the device-state */ dev->pix_format = fmt->fmt.pix; } return 0; } /* sets new output format, if possible; * allocate data here because we do not know if it will be streaming or * read/write IO * called on VIDIOC_S_FMT with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT */ static int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt) { struct v4l2_loopback_device *dev; char buf[5]; int ret; MARK(); dev = v4l2loopback_getdevice(file); ret = vidioc_try_fmt_out(file, priv, fmt); dprintk("s_fmt_out(%d) %d...%d\n", ret, dev->ready_for_capture, dev->pix_format.sizeimage); buf[4] = 0; dprintk("outFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf)); if (ret < 0) return ret; if (!dev->ready_for_capture) { dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage); fmt->fmt.pix.sizeimage = dev->buffer_size; allocate_buffers(dev); } return ret; } /*#define V4L2L_OVERLAY*/ #ifdef V4L2L_OVERLAY /* ------------------ OVERLAY ----------------------- */ /* currently unsupported */ /* GSTreamer's v4l2sink is buggy, as it requires the overlay to work * while it should only require it, if overlay is requested * once the gstreamer element is fixed, remove the overlay dummies */ #warning OVERLAY dummies static int vidioc_g_fmt_overlay(struct file *file, void *priv, struct v4l2_format *fmt) { return 0; } static int vidioc_s_fmt_overlay(struct file *file, void *priv, struct v4l2_format *fmt) { return 0; } #endif /* V4L2L_OVERLAY */ /* ------------------ PARAMs ----------------------- */ /* get some data flow parameters, only capability, fps and readbuffers has * effect on this driver * called on VIDIOC_G_PARM */ static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { /* do not care about type of opener, hope this enums would always be * compatible */ struct v4l2_loopback_device *dev; MARK(); dev = v4l2loopback_getdevice(file); parm->parm.capture = dev->capture_param; return 0; } /* get some data flow parameters, only capability, fps and readbuffers has * effect on this driver * called on VIDIOC_S_PARM */ static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm) { struct v4l2_loopback_device *dev; int err=0; MARK(); dev = v4l2loopback_getdevice(file); dprintk("vidioc_s_parm called frate=%d/%d\n", parm->parm.capture.timeperframe.numerator, parm->parm.capture.timeperframe.denominator); switch (parm->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if ((err=set_timeperframe(dev, &parm->parm.capture.timeperframe)) < 0) return err; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: if ((err=set_timeperframe(dev, &parm->parm.capture.timeperframe)) < 0) return err; break; default: return -1; } parm->parm.capture = dev->capture_param; return 0; } #ifdef V4L2LOOPBACK_WITH_STD /* sets a tv standard, actually we do not need to handle this any special way * added to support effecttv * called on VIDIOC_S_STD */ static int vidioc_s_std(struct file *file, void *private_data, v4l2_std_id *_std) { v4l2_std_id req_std = 0, supported_std = 0; const v4l2_std_id all_std = V4L2_STD_ALL, no_std = 0; if (_std) { req_std = *_std; *_std = all_std; } /* we support everything in V4L2_STD_ALL, but not more... */ supported_std = (all_std & req_std); if (no_std == supported_std) return -EINVAL; return 0; } /* gets a fake video standard * called on VIDIOC_G_STD */ static int vidioc_g_std(struct file *file, void *private_data, v4l2_std_id *norm) { if (norm) *norm = V4L2_STD_ALL; return 0; } /* gets a fake video standard * called on VIDIOC_QUERYSTD */ static int vidioc_querystd(struct file *file, void *private_data, v4l2_std_id *norm) { if (norm) *norm = V4L2_STD_ALL; return 0; } #endif /* V4L2LOOPBACK_WITH_STD */ /* get ctrls info * called on VIDIOC_QUERYCTRL */ static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *q) { const struct v4l2_ctrl_config *cnf = 0; switch (q->id) { case CID_KEEP_FORMAT: cnf = &v4l2loopback_ctrl_keepformat; break; case CID_SUSTAIN_FRAMERATE: cnf = &v4l2loopback_ctrl_sustainframerate; break; case CID_TIMEOUT: cnf = &v4l2loopback_ctrl_timeout; break; case CID_TIMEOUT_IMAGE_IO: cnf = &v4l2loopback_ctrl_timeoutimageio; break; default: return -EINVAL; } if (!cnf) BUG(); strcpy(q->name, cnf->name); q->default_value=cnf->def; q->type = cnf->type; q->minimum = cnf->min; q->maximum = cnf->max; q->step = cnf->step; memset(q->reserved, 0, sizeof(q->reserved)); return 0; } static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); switch (c->id) { case CID_KEEP_FORMAT: c->value = dev->keep_format; break; case CID_SUSTAIN_FRAMERATE: c->value = dev->sustain_framerate; break; case CID_TIMEOUT: c->value = jiffies_to_msecs(dev->timeout_jiffies); break; case CID_TIMEOUT_IMAGE_IO: c->value = dev->timeout_image_io; break; default: return -EINVAL; } return 0; } static int v4l2loopback_set_ctrl( struct v4l2_loopback_device *dev, u32 id, s64 val) { switch (id) { case CID_KEEP_FORMAT: if (val < 0 || val > 1) return -EINVAL; dev->keep_format = val; try_free_buffers(dev); break; case CID_SUSTAIN_FRAMERATE: if (val < 0 || val > 1) return -EINVAL; spin_lock_bh(&dev->lock); dev->sustain_framerate = val; check_timers(dev); spin_unlock_bh(&dev->lock); break; case CID_TIMEOUT: if (val < 0 || val > MAX_TIMEOUT) return -EINVAL; spin_lock_bh(&dev->lock); dev->timeout_jiffies = msecs_to_jiffies(val); check_timers(dev); spin_unlock_bh(&dev->lock); allocate_timeout_image(dev); break; case CID_TIMEOUT_IMAGE_IO: if (val < 0 || val > 1) return -EINVAL; dev->timeout_image_io = val; break; default: return -EINVAL; } return 0; } static int v4l2loopback_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_loopback_device *dev = container_of(ctrl->handler, struct v4l2_loopback_device, ctrl_handler); return v4l2loopback_set_ctrl(dev, ctrl->id, ctrl->val); } static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); return v4l2loopback_set_ctrl(dev, c->id, c->value); } /* returns set of device outputs, in our case there is only one * called on VIDIOC_ENUMOUTPUT */ static int vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *outp) { __u32 index = outp->index; struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); MARK(); if (!dev->announce_all_caps && !dev->ready_for_output) return -ENOTTY; if (0 != index) return -EINVAL; /* clear all data (including the reserved fields) */ memset(outp, 0, sizeof(*outp)); outp->index = index; strlcpy(outp->name, "loopback in", sizeof(outp->name)); outp->type = V4L2_OUTPUT_TYPE_ANALOG; outp->audioset = 0; outp->modulator = 0; #ifdef V4L2LOOPBACK_WITH_STD outp->std = V4L2_STD_ALL; # ifdef V4L2_OUT_CAP_STD outp->capabilities |= V4L2_OUT_CAP_STD; # endif /* V4L2_OUT_CAP_STD */ #endif /* V4L2LOOPBACK_WITH_STD */ return 0; } /* which output is currently active, * called on VIDIOC_G_OUTPUT */ static int vidioc_g_output(struct file *file, void *fh, unsigned int *i) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); if (!dev->announce_all_caps && !dev->ready_for_output) return -ENOTTY; if (i) *i = 0; return 0; } /* set output, can make sense if we have more than one video src, * called on VIDIOC_S_OUTPUT */ static int vidioc_s_output(struct file *file, void *fh, unsigned int i) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); if (!dev->announce_all_caps && !dev->ready_for_output) return -ENOTTY; if (i) return -EINVAL; return 0; } /* returns set of device inputs, in our case there is only one, * but later I may add more * called on VIDIOC_ENUMINPUT */ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *inp) { __u32 index = inp->index; struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); MARK(); if (!dev->announce_all_caps && !dev->ready_for_capture) return -ENOTTY; if (0 != index) return -EINVAL; /* clear all data (including the reserved fields) */ memset(inp, 0, sizeof(*inp)); inp->index = index; strlcpy(inp->name, "loopback", sizeof(inp->name)); inp->type = V4L2_INPUT_TYPE_CAMERA; inp->audioset = 0; inp->tuner = 0; inp->status = 0; #ifdef V4L2LOOPBACK_WITH_STD inp->std = V4L2_STD_ALL; # ifdef V4L2_IN_CAP_STD inp->capabilities |= V4L2_IN_CAP_STD; # endif #endif /* V4L2LOOPBACK_WITH_STD */ return 0; } /* which input is currently active, * called on VIDIOC_G_INPUT */ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); if (!dev->announce_all_caps && !dev->ready_for_capture) return -ENOTTY; if (i) *i = 0; return 0; } /* set input, can make sense if we have more than one video src, * called on VIDIOC_S_INPUT */ static int vidioc_s_input(struct file *file, void *fh, unsigned int i) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); if (!dev->announce_all_caps && !dev->ready_for_capture) return -ENOTTY; if (i == 0) return 0; return -EINVAL; } /* --------------- V4L2 ioctl buffer related calls ----------------- */ /* negotiate buffer type * only mmap streaming supported * called on VIDIOC_REQBUFS */ static int vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; int i; MARK(); dev = v4l2loopback_getdevice(file); opener = file->private_data; dprintk("reqbufs: %d\t%d=%d\n", b->memory, b->count, dev->buffers_number); if (opener->timeout_image_io) { if (b->memory != V4L2_MEMORY_MMAP) return -EINVAL; b->count = 1; return 0; } init_buffers(dev); switch (b->memory) { case V4L2_MEMORY_MMAP: /* do nothing here, buffers are always allocated*/ if (b->count < 1 || dev->buffers_number < 1) return 0; if (b->count > dev->buffers_number) b->count = dev->buffers_number; /* make sure that outbufs_list contains buffers from 0 to used_buffers-1 * actually, it will have been already populated via v4l2_loopback_init() * at this point */ if (list_empty(&dev->outbufs_list)) { for (i = 0; i < dev->used_buffers; ++i) list_add_tail(&dev->buffers[i].list_head, &dev->outbufs_list); } /* also, if dev->used_buffers is going to be decreased, we should remove * out-of-range buffers from outbufs_list, and fix bufpos2index mapping */ if (b->count < dev->used_buffers) { struct v4l2l_buffer *pos, *n; list_for_each_entry_safe(pos, n, &dev->outbufs_list, list_head) { if (pos->buffer.index >= b->count) list_del(&pos->list_head); } /* after we update dev->used_buffers, buffers in outbufs_list will * correspond to dev->write_position + [0;b->count-1] range */ i = dev->write_position; list_for_each_entry(pos, &dev->outbufs_list, list_head) { dev->bufpos2index[i % b->count] = pos->buffer.index; ++i; } } opener->buffers_number = b->count; if (opener->buffers_number < dev->used_buffers) dev->used_buffers = opener->buffers_number; return 0; default: return -EINVAL; } } /* returns buffer asked for; * give app as many buffers as it wants, if it less than MAX, * but map them in our inner buffers * called on VIDIOC_QUERYBUF */ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { enum v4l2_buf_type type; int index; struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; MARK(); type = b->type; index = b->index; dev = v4l2loopback_getdevice(file); opener = file->private_data; if ((b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && (b->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) { return -EINVAL; } if (b->index > max_buffers) return -EINVAL; if (opener->timeout_image_io) *b = dev->timeout_image_buffer.buffer; else *b = dev->buffers[b->index % dev->used_buffers].buffer; b->type = type; b->index = index; dprintkrw("buffer type: %d (of %d with size=%ld)\n", b->memory, dev->buffers_number, dev->buffer_size); return 0; } static void buffer_written(struct v4l2_loopback_device *dev, struct v4l2l_buffer *buf) { del_timer_sync(&dev->sustain_timer); del_timer_sync(&dev->timeout_timer); spin_lock_bh(&dev->lock); dev->bufpos2index[dev->write_position % dev->used_buffers] = buf->buffer.index; list_move_tail(&buf->list_head, &dev->outbufs_list); ++dev->write_position; dev->reread_count = 0; check_timers(dev); spin_unlock_bh(&dev->lock); } /* put buffer to queue * called on VIDIOC_QBUF */ static int vidioc_qbuf(struct file *file, void *private_data, struct v4l2_buffer *buf) { struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; struct v4l2l_buffer *b; int index; dev = v4l2loopback_getdevice(file); opener = file->private_data; if (buf->index > max_buffers) return -EINVAL; if (opener->timeout_image_io) return 0; index = buf->index % dev->used_buffers; b = &dev->buffers[index]; switch (buf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: dprintkrw("capture QBUF index: %d\n", index); set_queued(b); return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: dprintkrw("output QBUF pos: %d index: %d\n", dev->write_position, index); do_gettimeofday(&b->buffer.timestamp); set_done(b); buffer_written(dev, b); wake_up_all(&dev->read_event); return 0; default: return -EINVAL; } } static int can_read(struct v4l2_loopback_device *dev, struct v4l2_loopback_opener *opener) { int ret; spin_lock_bh(&dev->lock); check_timers(dev); ret = dev->write_position > opener->read_position || dev->reread_count > opener->reread_count || dev->timeout_happened; spin_unlock_bh(&dev->lock); return ret; } static int get_capture_buffer(struct file *file) { struct v4l2_loopback_device *dev = v4l2loopback_getdevice(file); struct v4l2_loopback_opener *opener = file->private_data; int pos, ret; int timeout_happened; if ((file->f_flags & O_NONBLOCK) && (dev->write_position <= opener->read_position && dev->reread_count <= opener->reread_count && !dev->timeout_happened)) return -EAGAIN; wait_event_interruptible(dev->read_event, can_read(dev, opener)); spin_lock_bh(&dev->lock); if (dev->write_position == opener->read_position) { if (dev->reread_count > opener->reread_count + 2) opener->reread_count = dev->reread_count - 1; ++opener->reread_count; pos = (opener->read_position + dev->used_buffers - 1) % dev->used_buffers; } else { opener->reread_count = 0; if (dev->write_position > opener->read_position + 2) opener->read_position = dev->write_position - 1; pos = opener->read_position % dev->used_buffers; ++opener->read_position; } timeout_happened = dev->timeout_happened; dev->timeout_happened = 0; spin_unlock_bh(&dev->lock); ret = dev->bufpos2index[pos]; if (timeout_happened) { /* although allocated on-demand, timeout_image is freed only * in free_buffers(), so we don't need to worry about it being * deallocated suddenly */ memcpy(dev->image + dev->buffers[ret].buffer.m.offset, dev->timeout_image, dev->buffer_size); } return ret; } /* put buffer to dequeue * called on VIDIOC_DQBUF */ static int vidioc_dqbuf(struct file *file, void *private_data, struct v4l2_buffer *buf) { struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; int index; struct v4l2l_buffer *b; dev = v4l2loopback_getdevice(file); opener = file->private_data; if (opener->timeout_image_io) { *buf = dev->timeout_image_buffer.buffer; return 0; } switch (buf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: index = get_capture_buffer(file); if (index < 0) return index; dprintkrw("capture DQBUF pos: %d index: %d\n", opener->read_position - 1, index); if (!(dev->buffers[index].buffer.flags&V4L2_BUF_FLAG_MAPPED)) { dprintk("trying to return not mapped buf[%d]\n", index); return -EINVAL; } unset_flags(&dev->buffers[index]); *buf = dev->buffers[index].buffer; return 0; case V4L2_BUF_TYPE_VIDEO_OUTPUT: b = list_entry(dev->outbufs_list.next, struct v4l2l_buffer, list_head); list_move_tail(&b->list_head, &dev->outbufs_list); dprintkrw("output DQBUF index: %d\n", b->buffer.index); unset_flags(b); *buf = b->buffer; buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; return 0; default: return -EINVAL; } } /* ------------- STREAMING ------------------- */ /* start streaming * called on VIDIOC_STREAMON */ static int vidioc_streamon(struct file *file, void *private_data, enum v4l2_buf_type type) { struct v4l2_loopback_device *dev; int ret; MARK(); dev = v4l2loopback_getdevice(file); switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT: dev->ready_for_output = 0; if (!dev->ready_for_capture) { ret = allocate_buffers(dev); if (ret < 0) return ret; dev->ready_for_capture = 1; } return 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (!dev->ready_for_capture) return -EIO; return 0; default: return -EINVAL; } } /* stop streaming * called on VIDIOC_STREAMOFF */ static int vidioc_streamoff(struct file *file, void *private_data, enum v4l2_buf_type type) { MARK(); dprintk("%d\n", type); return 0; } #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *fh, struct video_mbuf *p) { struct v4l2_loopback_device *dev; MARK(); dev = v4l2loopback_getdevice(file); p->frames = dev->buffers_number; p->offsets[0] = 0; p->offsets[1] = 0; p->size = dev->buffer_size; return 0; } #endif /* file operations */ static void vm_open(struct vm_area_struct *vma) { struct v4l2l_buffer *buf; MARK(); buf = vma->vm_private_data; buf->use_count++; } static void vm_close(struct vm_area_struct *vma) { struct v4l2l_buffer *buf; MARK(); buf = vma->vm_private_data; buf->use_count--; } static struct vm_operations_struct vm_ops = { .open = vm_open, .close = vm_close, }; static int v4l2_loopback_mmap(struct file *file, struct vm_area_struct *vma) { int i; unsigned long addr; unsigned long start; unsigned long size; struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; struct v4l2l_buffer *buffer = NULL; MARK(); start = (unsigned long) vma->vm_start; size = (unsigned long) (vma->vm_end - vma->vm_start); dev = v4l2loopback_getdevice(file); opener = file->private_data; if (size > dev->buffer_size) { dprintk("userspace tries to mmap too much, fail\n"); return -EINVAL; } if (opener->timeout_image_io) { /* we are going to map the timeout_image_buffer */ if ((vma->vm_pgoff << PAGE_SHIFT) != dev->buffer_size * MAX_BUFFERS) { dprintk("invalid mmap offset for timeout_image_io mode\n"); return -EINVAL; } } else if ((vma->vm_pgoff << PAGE_SHIFT) > dev->buffer_size * (dev->buffers_number - 1)) { dprintk("userspace tries to mmap too far, fail\n"); return -EINVAL; } /* FIXXXXXME: allocation should not happen here! */ if (NULL == dev->image) if (allocate_buffers(dev) < 0) return -EINVAL; if (opener->timeout_image_io) { buffer = &dev->timeout_image_buffer; addr = (unsigned long)dev->timeout_image; } else { for (i = 0; i < dev->buffers_number; ++i) { buffer = &dev->buffers[i]; if ((buffer->buffer.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } if (NULL == buffer) return -EINVAL; addr = (unsigned long) dev->image + (vma->vm_pgoff << PAGE_SHIFT); } while (size > 0) { struct page *page; page = (void *)vmalloc_to_page((void *)addr); if (vm_insert_page(vma, start, page) < 0) return -EAGAIN; start += PAGE_SIZE; addr += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &vm_ops; vma->vm_private_data = buffer; buffer->buffer.flags |= V4L2_BUF_FLAG_MAPPED; vm_open(vma); MARK(); return 0; } static unsigned int v4l2_loopback_poll(struct file *file, struct poll_table_struct *pts) { struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; int ret_mask = 0; MARK(); opener = file->private_data; dev = v4l2loopback_getdevice(file); switch (opener->type) { case WRITER: ret_mask = POLLOUT | POLLWRNORM; break; case READER: poll_wait(file, &dev->read_event, pts); if (can_read(dev, opener)) ret_mask = POLLIN | POLLRDNORM; break; default: ret_mask = -POLLERR; } MARK(); return ret_mask; } /* do not want to limit device opens, it can be as many readers as user want, * writers are limited by means of setting writer field */ static int v4l2_loopback_open(struct file *file) { struct v4l2_loopback_device *dev; struct v4l2_loopback_opener *opener; MARK(); dev = v4l2loopback_getdevice(file); if (dev->open_count.counter >= dev->max_openers) return -EBUSY; /* kfree on close */ opener = kzalloc(sizeof(*opener), GFP_KERNEL); if (opener == NULL) return -ENOMEM; file->private_data = opener; atomic_inc(&dev->open_count); opener->timeout_image_io = dev->timeout_image_io; dev->timeout_image_io = 0; if (opener->timeout_image_io) { int r = allocate_timeout_image(dev); if (r < 0) { dprintk("timeout image allocation failed\n"); return r; } } dprintk("opened dev:%p with image:%p\n", dev, dev ? dev->image : NULL); MARK(); return 0; } static int v4l2_loopback_close(struct file *file) { struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; int iswriter=0; MARK(); opener = file->private_data; dev = v4l2loopback_getdevice(file); if(WRITER == opener->type) iswriter = 1; atomic_dec(&dev->open_count); if (dev->open_count.counter == 0) { del_timer_sync(&dev->sustain_timer); del_timer_sync(&dev->timeout_timer); } try_free_buffers(dev); kfree(opener); if(iswriter) { dev->ready_for_output = 1; } MARK(); return 0; } static ssize_t v4l2_loopback_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int read_index; struct v4l2_loopback_opener *opener; struct v4l2_loopback_device *dev; MARK(); opener = file->private_data; dev = v4l2loopback_getdevice(file); read_index = get_capture_buffer(file); if (read_index < 0) return read_index; if (count > dev->buffer_size) count = dev->buffer_size; if (copy_to_user((void *)buf, (void *)(dev->image + dev->buffers[read_index].buffer.m.offset), count)) { printk(KERN_ERR "v4l2-loopback: failed copy_from_user() in write buf\n"); return -EFAULT; } dprintkrw("leave v4l2_loopback_read()\n"); return count; } static ssize_t v4l2_loopback_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct v4l2_loopback_device *dev; int write_index; struct v4l2_buffer *b; int ret; MARK(); dev = v4l2loopback_getdevice(file); /* there's at least one writer, so don'stop announcing output capabilities */ dev->ready_for_output = 0; if (!dev->ready_for_capture) { ret = allocate_buffers(dev); if (ret < 0) return ret; dev->ready_for_capture = 1; } dprintkrw("v4l2_loopback_write() trying to write %zu bytes\n", count); if (count > dev->buffer_size) count = dev->buffer_size; write_index = dev->write_position % dev->used_buffers; b = &dev->buffers[write_index].buffer; if (copy_from_user((void *)(dev->image + b->m.offset), (void *)buf, count)) { printk(KERN_ERR "v4l2-loopback: failed copy_from_user() in write buf, could not write %zu\n", count); return -EFAULT; } do_gettimeofday(&b->timestamp); b->sequence = dev->write_position; buffer_written(dev, &dev->buffers[write_index]); wake_up_all(&dev->read_event); dprintkrw("leave v4l2_loopback_write()\n"); return count; } /* init functions */ /* frees buffers, if already allocated */ static int free_buffers(struct v4l2_loopback_device *dev) { MARK(); dprintk("freeing image@%p for dev:%p\n", dev ? dev->image : NULL, dev); if (dev->image) { vfree(dev->image); dev->image = NULL; } if (dev->timeout_image) { vfree(dev->timeout_image); dev->timeout_image = NULL; } dev->imagesize = 0; return 0; } /* frees buffers, if they are no longer needed */ static void try_free_buffers(struct v4l2_loopback_device *dev) { MARK(); if (0 == dev->open_count.counter && !dev->keep_format) { free_buffers(dev); dev->ready_for_capture = 0; dev->buffer_size = 0; dev->write_position = 0; } } /* allocates buffers, if buffer_size is set */ static int allocate_buffers(struct v4l2_loopback_device *dev) { MARK(); /* vfree on close file operation in case no open handles left */ if (0 == dev->buffer_size) return -EINVAL; if (dev->image) { dprintk("allocating buffers again: %ld %ld\n", dev->buffer_size * dev->buffers_number, dev->imagesize); /* FIXME: prevent double allocation more intelligently! */ if (dev->buffer_size * dev->buffers_number == dev->imagesize) return 0; /* if there is only one writer, no problem should occur */ if (dev->open_count.counter == 1) free_buffers(dev); else return -EINVAL; } dev->imagesize = dev->buffer_size * dev->buffers_number; dprintk("allocating %ld = %ldx%d\n", dev->imagesize, dev->buffer_size, dev->buffers_number); dev->image = vmalloc(dev->imagesize); if (dev->timeout_jiffies > 0) allocate_timeout_image(dev); if (dev->image == NULL) return -ENOMEM; dprintk("vmallocated %ld bytes\n", dev->imagesize); MARK(); init_buffers(dev); return 0; } /* init inner buffers, they are capture mode and flags are set as * for capture mod buffers */ static void init_buffers(struct v4l2_loopback_device *dev) { int i; int buffer_size; int bytesused; MARK(); buffer_size = dev->buffer_size; bytesused = dev->pix_format.sizeimage; for (i = 0; i < dev->buffers_number; ++i) { struct v4l2_buffer *b = &dev->buffers[i].buffer; b->index = i; b->bytesused = bytesused; b->length = buffer_size; b->field = V4L2_FIELD_NONE; b->flags = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1) b->input = 0; #endif b->m.offset = i * buffer_size; b->memory = V4L2_MEMORY_MMAP; b->sequence = 0; b->timestamp.tv_sec = 0; b->timestamp.tv_usec = 0; b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; do_gettimeofday(&b->timestamp); } dev->timeout_image_buffer = dev->buffers[0]; dev->timeout_image_buffer.buffer.m.offset = MAX_BUFFERS * buffer_size; MARK(); } static int allocate_timeout_image(struct v4l2_loopback_device *dev) { MARK(); if (dev->buffer_size <= 0) return -EINVAL; if (dev->timeout_image == NULL) { dev->timeout_image = v4l2l_vzalloc(dev->buffer_size); if (dev->timeout_image == NULL) return -ENOMEM; } return 0; } /* fills and register video device */ static void init_vdev(struct video_device *vdev, int nr) { MARK(); snprintf(vdev->name, sizeof(vdev->name), "Loopback video device %X", nr); #ifdef V4L2LOOPBACK_WITH_STD vdev->tvnorms = V4L2_STD_ALL; vdev->current_norm = V4L2_STD_ALL; #endif /* V4L2LOOPBACK_WITH_STD */ vdev->vfl_type = VFL_TYPE_GRABBER; vdev->fops = &v4l2_loopback_fops; vdev->ioctl_ops = &v4l2_loopback_ioctl_ops; vdev->release = &video_device_release; vdev->minor = -1; if (debug > 1) #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 20, 0) vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; #else vdev->dev_debug = V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG; #endif /* since kernel-3.7, there is a new field 'vfl_dir' that has to be * set to VFL_DIR_M2M for bidrectional devices */ #ifdef VFL_DIR_M2M vdev->vfl_dir = VFL_DIR_M2M; #endif MARK(); } /* init default capture parameters, only fps may be changed in future */ static void init_capture_param(struct v4l2_captureparm *capture_param) { MARK(); capture_param->capability = 0; capture_param->capturemode = 0; capture_param->extendedmode = 0; capture_param->readbuffers = max_buffers; capture_param->timeperframe.numerator = 1; capture_param->timeperframe.denominator = 30; } static void check_timers(struct v4l2_loopback_device *dev) { if (!dev->ready_for_capture) return; if (dev->timeout_jiffies > 0 && !timer_pending(&dev->timeout_timer)) mod_timer(&dev->timeout_timer, jiffies + dev->timeout_jiffies); if (dev->sustain_framerate && !timer_pending(&dev->sustain_timer)) mod_timer(&dev->sustain_timer, jiffies + dev->frame_jiffies * 3 / 2); } static void sustain_timer_clb(unsigned long nr) { struct v4l2_loopback_device *dev = devs[nr]; spin_lock(&dev->lock); if (dev->sustain_framerate) { dev->reread_count++; dprintkrw("reread: %d %d\n", dev->write_position, dev->reread_count); if (dev->reread_count == 1) mod_timer(&dev->sustain_timer, jiffies + max(1UL, dev->frame_jiffies / 2)); else mod_timer(&dev->sustain_timer, jiffies + dev->frame_jiffies); wake_up_all(&dev->read_event); } spin_unlock(&dev->lock); } static void timeout_timer_clb(unsigned long nr) { struct v4l2_loopback_device *dev = devs[nr]; spin_lock(&dev->lock); if (dev->timeout_jiffies > 0) { dev->timeout_happened = 1; mod_timer(&dev->timeout_timer, jiffies + dev->timeout_jiffies); wake_up_all(&dev->read_event); } spin_unlock(&dev->lock); } /* init loopback main structure */ static int v4l2_loopback_init(struct v4l2_loopback_device *dev, int nr) { int ret; struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "v4l2loopback-%03d", nr); ret = v4l2_device_register(NULL, &dev->v4l2_dev); if (ret) return ret; MARK(); dev->vdev = video_device_alloc(); if (dev->vdev == NULL) { ret=-ENOMEM; goto error; } video_set_drvdata(dev->vdev, kzalloc(sizeof(struct v4l2loopback_private), GFP_KERNEL)); if (video_get_drvdata(dev->vdev) == NULL) { ret=-ENOMEM; goto error; } ((struct v4l2loopback_private *)video_get_drvdata(dev->vdev))->devicenr = nr; init_vdev(dev->vdev, nr); dev->vdev->v4l2_dev = &dev->v4l2_dev; init_capture_param(&dev->capture_param); set_timeperframe(dev, &dev->capture_param.timeperframe); dev->keep_format = 0; dev->sustain_framerate = 0; dev->buffers_number = max_buffers; dev->used_buffers = max_buffers; dev->max_openers = max_openers; dev->write_position = 0; spin_lock_init(&dev->lock); INIT_LIST_HEAD(&dev->outbufs_list); if (list_empty(&dev->outbufs_list)) { int i; for (i = 0; i < dev->used_buffers; ++i) list_add_tail(&dev->buffers[i].list_head, &dev->outbufs_list); } memset(dev->bufpos2index, 0, sizeof(dev->bufpos2index)); atomic_set(&dev->open_count, 0); dev->ready_for_capture = 0; dev->ready_for_output = 1; dev->announce_all_caps = (!exclusive_caps[nr]); dev->buffer_size = 0; dev->image = NULL; dev->imagesize = 0; setup_timer(&dev->sustain_timer, sustain_timer_clb, nr); dev->reread_count = 0; setup_timer(&dev->timeout_timer, timeout_timer_clb, nr); dev->timeout_jiffies = 0; dev->timeout_image = NULL; dev->timeout_happened = 0; ret = v4l2_ctrl_handler_init(hdl, 1); if(ret) goto error; v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_keepformat, NULL); v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_sustainframerate, NULL); v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_timeout, NULL); v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_timeoutimageio, NULL); if (hdl->error) { ret = hdl->error; goto error; } dev->v4l2_dev.ctrl_handler = hdl; /* FIXME set buffers to 0 */ /* Set initial format */ dev->pix_format.width = 0; /* V4L2LOOPBACK_SIZE_DEFAULT_WIDTH; */ dev->pix_format.height = 0; /* V4L2LOOPBACK_SIZE_DEFAULT_HEIGHT; */ dev->pix_format.pixelformat = formats[0].fourcc; dev->pix_format.colorspace = V4L2_COLORSPACE_SRGB; /* do we need to set this ? */ dev->pix_format.field = V4L2_FIELD_NONE; dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage); dprintk("buffer_size = %ld (=%d)\n", dev->buffer_size, dev->pix_format.sizeimage); allocate_buffers(dev); init_waitqueue_head(&dev->read_event); MARK(); return 0; error: v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); kfree(dev->vdev); return ret; }; /* LINUX KERNEL */ static const struct v4l2_file_operations v4l2_loopback_fops = { .owner = THIS_MODULE, .open = v4l2_loopback_open, .release = v4l2_loopback_close, .read = v4l2_loopback_read, .write = v4l2_loopback_write, .poll = v4l2_loopback_poll, .mmap = v4l2_loopback_mmap, .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops v4l2_loopback_ioctl_ops = { .vidioc_querycap = &vidioc_querycap, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) .vidioc_enum_framesizes = &vidioc_enum_framesizes, .vidioc_enum_frameintervals = &vidioc_enum_frameintervals, #endif #ifndef HAVE__V4L2_CTRLS .vidioc_queryctrl = &vidioc_queryctrl, .vidioc_g_ctrl = &vidioc_g_ctrl, .vidioc_s_ctrl = &vidioc_s_ctrl, #endif /* HAVE__V4L2_CTRLS */ .vidioc_enum_output = &vidioc_enum_output, .vidioc_g_output = &vidioc_g_output, .vidioc_s_output = &vidioc_s_output, .vidioc_enum_input = &vidioc_enum_input, .vidioc_g_input = &vidioc_g_input, .vidioc_s_input = &vidioc_s_input, .vidioc_enum_fmt_vid_cap = &vidioc_enum_fmt_cap, .vidioc_g_fmt_vid_cap = &vidioc_g_fmt_cap, .vidioc_s_fmt_vid_cap = &vidioc_s_fmt_cap, .vidioc_try_fmt_vid_cap = &vidioc_try_fmt_cap, .vidioc_enum_fmt_vid_out = &vidioc_enum_fmt_out, .vidioc_s_fmt_vid_out = &vidioc_s_fmt_out, .vidioc_g_fmt_vid_out = &vidioc_g_fmt_out, .vidioc_try_fmt_vid_out = &vidioc_try_fmt_out, #ifdef V4L2L_OVERLAY .vidioc_s_fmt_vid_overlay = &vidioc_s_fmt_overlay, .vidioc_g_fmt_vid_overlay = &vidioc_g_fmt_overlay, #endif #ifdef V4L2LOOPBACK_WITH_STD .vidioc_s_std = &vidioc_s_std, .vidioc_g_std = &vidioc_g_std, .vidioc_querystd = &vidioc_querystd, #endif /* V4L2LOOPBACK_WITH_STD */ .vidioc_g_parm = &vidioc_g_parm, .vidioc_s_parm = &vidioc_s_parm, .vidioc_reqbufs = &vidioc_reqbufs, .vidioc_querybuf = &vidioc_querybuf, .vidioc_qbuf = &vidioc_qbuf, .vidioc_dqbuf = &vidioc_dqbuf, .vidioc_streamon = &vidioc_streamon, .vidioc_streamoff = &vidioc_streamoff, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = &vidiocgmbuf, #endif }; static void zero_devices(void) { int i; MARK(); for (i = 0; i < MAX_DEVICES; i++) devs[i] = NULL; } static void free_devices(void) { int i; MARK(); for (i = 0; i < devices; i++) { if (NULL != devs[i]) { free_buffers(devs[i]); v4l2loopback_remove_sysfs(devs[i]->vdev); kfree(video_get_drvdata(devs[i]->vdev)); video_unregister_device(devs[i]->vdev); v4l2_device_unregister(&devs[i]->v4l2_dev); v4l2_ctrl_handler_free(&devs[i]->ctrl_handler); kfree(devs[i]); devs[i] = NULL; } } } int __init init_module(void) { int ret; int i; MARK(); zero_devices(); if (devices < 0) { devices = 1; /* try guessing the devices from the "video_nr" parameter */ for (i = MAX_DEVICES - 1; i >= 0; i--) { if (video_nr[i] >= 0) { devices = i + 1; break; } } } if (devices > MAX_DEVICES) { devices = MAX_DEVICES; printk(KERN_INFO "v4l2loopback: number of devices is limited to: %d\n", MAX_DEVICES); } if (max_buffers > MAX_BUFFERS) { max_buffers = MAX_BUFFERS; printk(KERN_INFO "v4l2loopback: number of buffers is limited to: %d\n", MAX_BUFFERS); } if (max_openers < 0) { printk(KERN_INFO "v4l2loopback: allowing %d openers rather than %d\n", 2, max_openers); max_openers = 2; } if (max_width < 1) { max_width = V4L2LOOPBACK_SIZE_MAX_WIDTH; printk(KERN_INFO "v4l2loopback: using max_width %d\n", max_width); } if (max_height < 1) { max_height = V4L2LOOPBACK_SIZE_MAX_HEIGHT; printk(KERN_INFO "v4l2loopback: using max_height %d\n", max_height); } /* kfree on module release */ for (i = 0; i < devices; i++) { dprintk("creating v4l2loopback-device #%d\n", i); devs[i] = kzalloc(sizeof(*devs[i]), GFP_KERNEL); if (devs[i] == NULL) { free_devices(); return -ENOMEM; } ret = v4l2_loopback_init(devs[i], i); if (ret < 0) { free_devices(); return ret; } /* register the device -> it creates /dev/video* */ if (video_register_device(devs[i]->vdev, VFL_TYPE_GRABBER, video_nr[i]) < 0) { video_device_release(devs[i]->vdev); printk(KERN_ERR "v4l2loopback: failed video_register_device()\n"); free_devices(); return -EFAULT; } v4l2loopback_create_sysfs(devs[i]->vdev); } dprintk("module installed\n"); printk(KERN_INFO "v4l2loopback driver version %d.%d.%d loaded\n", (V4L2LOOPBACK_VERSION_CODE >> 16) & 0xff, (V4L2LOOPBACK_VERSION_CODE >> 8) & 0xff, (V4L2LOOPBACK_VERSION_CODE) & 0xff); return 0; } void __exit cleanup_module(void) { MARK(); /* unregister the device -> it deletes /dev/video* */ free_devices(); dprintk("module removed\n"); } /* * fake usage of unused functions */ #ifdef HAVE__V4L2_CTRLS static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *q) __attribute__ ((unused)); static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) __attribute__ ((unused)); static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) __attribute__ ((unused)); #endif /* HAVE__V4L2_CTRLS */ v4l2loopback-0.9.1/v4l2loopback_formats.h000066400000000000000000000204221253363713600202420ustar00rootroot00000000000000 /* here come the packed formats */ { .name = "32 bpp RGB, le", .fourcc = V4L2_PIX_FMT_BGR32, .depth = 32, .flags = 0, }, { .name = "32 bpp RGB, be", .fourcc = V4L2_PIX_FMT_RGB32, .depth = 32, .flags = 0, }, { .name = "24 bpp RGB, le", .fourcc = V4L2_PIX_FMT_BGR24, .depth = 24, .flags = 0, }, { .name = "24 bpp RGB, be", .fourcc = V4L2_PIX_FMT_RGB24, .depth = 24, .flags = 0, }, #ifdef V4L2_PIX_FMT_RGB332 { .name = "8 bpp RGB-3-3-2", .fourcc = V4L2_PIX_FMT_RGB332, .depth = 8, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB332 */ #ifdef V4L2_PIX_FMT_RGB444 { .name = "16 bpp RGB (xxxxrrrr ggggbbbb)", .fourcc = V4L2_PIX_FMT_RGB444, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB444 */ #ifdef V4L2_PIX_FMT_RGB555 { .name = "16 bpp RGB-5-5-5", .fourcc = V4L2_PIX_FMT_RGB555, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB555 */ #ifdef V4L2_PIX_FMT_RGB565 { .name = "16 bpp RGB-5-6-5", .fourcc = V4L2_PIX_FMT_RGB565, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB565 */ #ifdef V4L2_PIX_FMT_RGB555X { .name = "16 bpp RGB-5-5-5 BE", .fourcc = V4L2_PIX_FMT_RGB555X, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB555X */ #ifdef V4L2_PIX_FMT_RGB565X { .name = "16 bpp RGB-5-6-5 BE", .fourcc = V4L2_PIX_FMT_RGB565X, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_RGB565X */ #ifdef V4L2_PIX_FMT_BGR666 { .name = "18 bpp BGR-6-6-6", .fourcc = V4L2_PIX_FMT_BGR666, .depth = 18, .flags = 0, }, #endif /* V4L2_PIX_FMT_BGR666 */ { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .flags = 0, }, { .name = "4:2:2, packed, UYVY", .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16, .flags = 0, }, #ifdef V4L2_PIX_FMT_YVYU { .name = "4:2:2, packed YVYU", .fourcc = V4L2_PIX_FMT_YVYU, .depth = 16, .flags = 0, }, #endif #ifdef V4L2_PIX_FMT_VYUY { .name = "4:2:2, packed VYUY", .fourcc = V4L2_PIX_FMT_VYUY, .depth = 16, .flags = 0, }, #endif { .name = "4:2:2, packed YYUV", .fourcc = V4L2_PIX_FMT_YYUV, .depth = 16, .flags = 0, }, { .name = "YUV-8-8-8-8", .fourcc = V4L2_PIX_FMT_YUV32, .depth = 32, .flags = 0, }, { .name = "8 bpp, Greyscale", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8, .flags = 0, }, #ifdef V4L2_PIX_FMT_Y4 { .name = "4 bpp Greyscale", .fourcc = V4L2_PIX_FMT_Y4, .depth = 4, .flags = 0, }, #endif /* V4L2_PIX_FMT_Y4 */ #ifdef V4L2_PIX_FMT_Y6 { .name = "6 bpp Greyscale", .fourcc = V4L2_PIX_FMT_Y6, .depth = 6, .flags = 0, }, #endif /* V4L2_PIX_FMT_Y6 */ #ifdef V4L2_PIX_FMT_Y10 { .name = "10 bpp Greyscale", .fourcc = V4L2_PIX_FMT_Y10, .depth = 10, .flags = 0, }, #endif /* V4L2_PIX_FMT_Y10 */ #ifdef V4L2_PIX_FMT_Y12 { .name = "12 bpp Greyscale", .fourcc = V4L2_PIX_FMT_Y12, .depth = 12, .flags = 0, }, #endif /* V4L2_PIX_FMT_Y12 */ { .name = "16 bpp, Greyscale", .fourcc = V4L2_PIX_FMT_Y16, .depth = 16, .flags = 0, }, #ifdef V4L2_PIX_FMT_YUV444 { .name = "16 bpp xxxxyyyy uuuuvvvv", .fourcc = V4L2_PIX_FMT_YUV444, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_YUV444 */ #ifdef V4L2_PIX_FMT_YUV555 { .name = "16 bpp YUV-5-5-5", .fourcc = V4L2_PIX_FMT_YUV555, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_YUV555 */ #ifdef V4L2_PIX_FMT_YUV565 { .name = "16 bpp YUV-5-6-5", .fourcc = V4L2_PIX_FMT_YUV565, .depth = 16, .flags = 0, }, #endif /* V4L2_PIX_FMT_YUV565 */ /* here come the planar formats */ { .name = "4:1:0, planar, Y-Cr-Cb", .fourcc = V4L2_PIX_FMT_YVU410, .depth = 9, .flags = FORMAT_FLAGS_PLANAR, }, { .name = "4:2:0, planar, Y-Cr-Cb", .fourcc = V4L2_PIX_FMT_YVU420, .depth = 12, .flags = FORMAT_FLAGS_PLANAR, }, { .name = "4:1:0, planar, Y-Cb-Cr", .fourcc = V4L2_PIX_FMT_YUV410, .depth = 9, .flags = FORMAT_FLAGS_PLANAR, }, { .name = "4:2:0, planar, Y-Cb-Cr", .fourcc = V4L2_PIX_FMT_YUV420, .depth = 12, .flags = FORMAT_FLAGS_PLANAR, }, #ifdef V4L2_PIX_FMT_YUV422P { .name = "16 bpp YVU422 planar", .fourcc = V4L2_PIX_FMT_YUV422P, .depth = 16, .flags = FORMAT_FLAGS_PLANAR, }, #endif /* V4L2_PIX_FMT_YUV422P */ #ifdef V4L2_PIX_FMT_YUV411P { .name = "16 bpp YVU411 planar", .fourcc = V4L2_PIX_FMT_YUV411P, .depth = 16, .flags = FORMAT_FLAGS_PLANAR, }, #endif /* V4L2_PIX_FMT_YUV411P */ #ifdef V4L2_PIX_FMT_Y41P { .name = "12 bpp YUV 4:1:1", .fourcc = V4L2_PIX_FMT_Y41P, .depth = 12, .flags = FORMAT_FLAGS_PLANAR, }, #endif /* V4L2_PIX_FMT_Y41P */ /* here come the compressed formats */ #ifdef V4L2_PIX_FMT_MJPEG { .name = "Motion-JPEG", .fourcc = V4L2_PIX_FMT_MJPEG, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_MJPEG */ #ifdef V4L2_PIX_FMT_JPEG { .name = "JFIF JPEG", .fourcc = V4L2_PIX_FMT_JPEG, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_JPEG */ #ifdef V4L2_PIX_FMT_DV { .name = "DV1394", .fourcc = V4L2_PIX_FMT_DV, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_DV */ #ifdef V4L2_PIX_FMT_MPEG { .name = "MPEG-1/2/4 Multiplexed", .fourcc = V4L2_PIX_FMT_MPEG, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, #endif /* V4L2_PIX_FMT_MPEG */ #ifdef V4L2_PIX_FMT_H264 }, { .name = "H264 with start codes", .fourcc = V4L2_PIX_FMT_H264, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_H264 */ #ifdef V4L2_PIX_FMT_H264_NO_SC { .name = "H264 without start codes", .fourcc = V4L2_PIX_FMT_H264_NO_SC, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_H264_NO_SC */ #ifdef V4L2_PIX_FMT_H264_MVC { .name = "H264 MVC", .fourcc = V4L2_PIX_FMT_H264_MVC, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_H264_MVC */ #ifdef V4L2_PIX_FMT_H263 { .name = "H263", .fourcc = V4L2_PIX_FMT_H263, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_H263 */ #ifdef V4L2_PIX_FMT_MPEG1 { .name = "MPEG-1 ES", .fourcc = V4L2_PIX_FMT_MPEG1, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_MPEG1 */ #ifdef V4L2_PIX_FMT_MPEG2 { .name = "MPEG-2 ES", .fourcc = V4L2_PIX_FMT_MPEG2, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_MPEG2 */ #ifdef V4L2_PIX_FMT_MPEG4 { .name = "MPEG-4 part 2 ES", .fourcc = V4L2_PIX_FMT_MPEG4, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_MPEG4 */ #ifdef V4L2_PIX_FMT_XVID { .name = "Xvid", .fourcc = V4L2_PIX_FMT_XVID, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_XVID */ #ifdef V4L2_PIX_FMT_VC1_ANNEX_G { .name = "SMPTE 421M Annex G compliant stream", .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_VC1_ANNEX_G */ #ifdef V4L2_PIX_FMT_VC1_ANNEX_L { .name = "SMPTE 421M Annex L compliant stream", .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_VC1_ANNEX_L */ #ifdef V4L2_PIX_FMT_VP8 { .name = "VP8", .fourcc = V4L2_PIX_FMT_VP8, .depth = 32, .flags = FORMAT_FLAGS_COMPRESSED, }, #endif /* V4L2_PIX_FMT_VP8 */