├── 1080P50EDID.txt ├── LICENSE ├── Makefile ├── README ├── run.sh ├── uvc-setup.sh ├── uvc.h ├── v4l2-mmal-uvc.c └── v4l2-mmal-uvc.h /1080P50EDID.txt: -------------------------------------------------------------------------------- 1 | 00ffffffffffff005262888800888888 2 | 1c150103800000780aEE91A3544C9926 3 | 0F505400000001010101010101010101 4 | 010101010101011d007251d01e206e28 5 | 5500c48e2100001e8c0ad08a20e02d10 6 | 103e9600138e2100001e000000fc0054 7 | 6f73686962612d4832430a20000000FD 8 | 003b3d0f2e0f1e0a2020202020200100 9 | 020321434e841303021211012021223c 10 | 3d3e1f2309070766030c00300080E300 11 | 7F8c0ad08a20e02d10103e9600c48e21 12 | 0000188c0ad08a20e02d10103e960013 13 | 8e210000188c0aa01451f01600267c43 14 | 00138e21000098000000000000000000 15 | 00000000000000000000000000000000 16 | 00000000000000000000000000000000 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE ?= 2 | ARCH ?= x86 3 | KERNEL_DIR ?= /usr/src/linux 4 | 5 | CC := $(CROSS_COMPILE)gcc 6 | KERNEL_INCLUDE := -I$(KERNEL_DIR)/include -I$(KERNEL_DIR)/arch/$(ARCH)/include 7 | CFLAGS := -W -Wall -Wno-unused-function -g -I/opt/vc/include -pipe 8 | #CFLAGS := -W -Wall -Wno-unused-function -O2 -I/opt/vc/include -pipe 9 | LDFLAGS := 10 | 11 | LIBS := -L/opt/vc/lib -lrt -lbcm_host -lvcos -lvchiq_arm -pthread -lmmal_core -lmmal_util -lmmal_vc_client -lvcsm 12 | src = v4l2-mmal-uvc.c #$(wildcard *.c) 13 | obj = $(src:.c=.o) 14 | 15 | %.o : %.c 16 | $(CC) $(CFLAGS) -o $@ -c $< 17 | 18 | all: v4l2-mmal-uvc 19 | 20 | v4l2-mmal-uvc: $(obj) 21 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 22 | 23 | clean: 24 | rm -f *.o 25 | rm -f v4l2-mmal-uvc 26 | 27 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | TC35874 to UVC gadget application README 2 | by Kin Wei Lee September 2020 3 | 4 | 5 | Orignal Work 6 | ============= 7 | UVC original work by 8 | Laurent Pinchart ideasonboard.com 9 | and patches by Bhupesh Sharma 10 | but conveniently collated by jdonald (github.com/jdonald/uvc-gadget) 11 | V4L2 MMAL original work by 12 | 6by9 github.com/6by9/v4l2_mmal 13 | 14 | Overview 15 | ========= 16 | This software uses a Raspberry Pi Zero and a TC35874 bridge to convert an HDMI 17 | source signal and output the video over USB so that it can be used as a USB 18 | webcam. 19 | 20 | 21 | Introduction 22 | ============= 23 | 24 | This README file documents the v4l2-mmal-uvc application available 25 | here git://github.com/kinweilee/v4l2-mmal-uvc, which can be used to 26 | create a UVC webcam gadget using a HDMI input as the source video. 27 | HDMI conversion utilises a TC35874 HDMI to CSI-2 converter. 28 | 29 | The TC35874 outputs UYVY or RGB24 data to the CSI-2 camera port and is 30 | incompatible with UVC gadget, which requires either YUYV or MJPEG formatted data. 31 | 32 | By using the MMAL framework, supported by the Raspberry Pi Zero, hardware 33 | conversion of UYVY/RG24 data to YUYV or MJPEG can be performed. 34 | 35 | Prequisites 36 | =========== 37 | Raspberry Pi Zero or Raspberry Pi Zero 2 W (other boards should also work) 38 | TC358743 based converter board 39 | Raspberry Pi OS Lite (Legacy) or Raspberry Pi OS (Legacy) with desktop. 40 | Unfortunately this software is currently incompatible with Bullseye based 41 | images from Raspberry Pi. 42 | 43 | Setup 44 | ====== 45 | 1. Use raspi-config to enable 'CAMERA' and 'I2C' 46 | 47 | 2. Add to /boot/config.txt the following two lines: 48 | dtoverlay=tc358743,i2c_pins_28_29=1 49 | dtoverlay=dwc2 50 | 51 | 3. Add to /boot/cmdline.txt after rootwait the following commands: 52 | modules-load=dwc2,libcomposite cmd=32M 53 | 54 | 4. TC358743 must be setup up with the following commnads 55 | v4l2-ctl --set-edid=file=./1080P50EDID.txt --fix-edid-checksums 56 | v4l2-ctl --query-dv-timings 57 | v4l2-ctl --set-dv-bt-timings query 58 | 59 | 5. Setup the ConfigFS using the uvc-setup.sh script 60 | The UVC gadget can be used in either RAW YUYV or MJPEG mode. MJPEG will give 61 | better frame rates (30fps) but will use compressed images. 62 | 63 | 6.1 Setup using YUYV 64 | sudo ./uvc-setup.sh start yuyv 65 | ./v4l2-mmal-uvc -v /dev/video0 -u /dev/video1 -n3 -f0 # -v is camera device -u is g_webcam device 66 | 67 | 6.2 Setup using MJPEG 68 | sudo ./uvc-setup.sh start mjpeg 69 | ./v4l2-mmal-uvc -v /dev/video0 -u /dev/video1 -n3 -f1 70 | 71 | ConfigFS can be unloaded using 72 | sudo ./uvc-setup.sh stop 73 | 74 | Resolution 75 | ========== 76 | The output resolution is fixed at 1280x720, this will give about 30fps when 77 | using MJPEG or about 8.8fps when using YUYV RAW. 78 | 79 | NOTES 80 | ====== 81 | 82 | Audio is NOT handled. 83 | 84 | This software basically splices (hacked and diced with a very blunt blade) the 85 | V4L2 mmal part of v4l2-mmal by 6by9 and with the UVC part of uvc-gadget by 86 | Laurent Pinchart. 87 | v4l2-mmal has video output and H264 encoding but this has been removed, as we 88 | only need the ISP to do the RGB24/UYVY to YUYV format conversion. 89 | 90 | V4L2 capture uses V4L2_MEMORY_MMAP, DMA is used to transfer this to VC 91 | (Raspberry Pi video core) and UVC output uses V4L2_MEMORY_USERPTR. 92 | 93 | Internally the code will select UYVY output from TC35874 (RGB24 can be used 94 | too). 95 | 96 | Resolution: 97 | Resolution presented to UVC is fixed at 1280x720. 98 | Only raw YUYV data is presented to UVC, MJPEG is not used. 99 | ISOCH USB transfers work. 100 | BULK mode transfer does not work. 101 | 102 | Framerate: 103 | The max framerate is about 8.8fps, (I think) it's limited to UBR microframe 104 | period and streaming_maxpacket size for g_webcam. 105 | microframe period = 125us (ISOCH transfer) 106 | image size = 1280*720*2 107 | streaming_maxpacket = 2048 108 | framerate = 1/(1280*720*2/2048*125e-6) = 8.8fps 109 | 110 | ConfigFS is used to setup the UVC USB gadget. g_webcam can also work. 111 | 112 | Pipe Setup: 113 | Two different pipe setups are used for RAW YUYV, and compressed MJPEG formats. 114 | RAW YUYV 115 | V4L2 -> ISP -> UVC 116 | MJPEG 117 | V4L2 -> ISP -> Encoder -> UVC 118 | 119 | The software may or may not work for you and it may kill your hardware. My first 120 | PiZero's USB OTG functionality died during development. Essentially it no longer 121 | works in gadget mode. The cause could have come from power coming from two 122 | different sources into the Pi Zero. 123 | 124 | Issues 125 | ======= 126 | 127 | Stopping and restarting a stream doesn't work. It may be necessary to quit 128 | v4l2-mmal-uvc and the webcam viewer (e.g. QuickTime player) before a new stream 129 | can be viewed. 130 | 131 | Compiling reveals alot of warnings. 132 | 133 | Test Setup 134 | =========== 135 | Hardware 136 | -H2C-RPI0-B01 HDMI to CSI-2 Bridge using TC358743 (same foot print as Pi Zero) 137 | -Pi Zero W (4.19.122+ recompiled from source using rpi-4.19.y branch) 138 | -Pi Zero 2 W using stock Raspberry Pi OS Lite (Legacy) 139 | 140 | Input HDMI sources: 141 | -PC 1280x720 and 1920x1080 ok 142 | -EOS M - 720x480 interlaced signal needs code modification to use interlace mode 143 | (V4L2_FIELD_INTERLACED) and the source must be RGB24. (use Magic Lantern for clean HDMI!) 144 | -EOS M5 - 1920x1080 signal ok 145 | 146 | Software on MacBook Pro (Retina, 13-inch, Late 2013) 147 | -Facetime (Cataline 10.15.5) working (!) 148 | -QuickTime Player working 149 | -VLC (macOS 3.0.11.1) - selecting webcam gadget fails on first try and reverts 150 | to the internal webcam, on second select webcam gadget -> facetime camera -> 151 | webcam gadget to get it working. VLC however has a large latency. 152 | 153 | Windows 10 154 | - Skype working use UVC Camera as input and MJPEG mode, YUYV didn't work 155 | - VLC doesn't work error 'Your input couldn't be opened' 156 | 157 | On Pi Zero (first version) CPU usage is typically 45-50% using htop. 158 | On Pi Zero 2 W CPU usage is less than 15% on any core. 159 | 160 | Things to work on 161 | ================== 162 | Frame rate could be improved but streaming_maxpacket seems to be limited to 163 | 2048. USB_Video_Class 1.1 states that 3072 bytes should work however setting 164 | 3072 causes a USB disconnect error. It could be a dwc2 issue or a webcam.c 165 | issue. 166 | Bulk mode cannot be used since f_uvc.c does not implement any bulk modes. 167 | uvc-setup.sh needs a clean up 168 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./v4l2-mmal-uvc -v /dev/video0 -u /dev/video1 -n3 -r0 3 | ./v4l2-mmal-uvc -v /dev/video0 -u /dev/video1 -zUYVY -b1500000 -n3 -r1 -f1 4 | -------------------------------------------------------------------------------- /uvc-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SPDX-License-Identifier: MIT 3 | 4 | #set -e 5 | #set -x 6 | OP=$1 7 | PLAT=$2 8 | 9 | CONFIGFS="/sys/kernel/config" 10 | GADGET="$CONFIGFS/usb_gadget" 11 | VID="0x1d6b" 12 | PID="0x0104" 13 | SERIAL="0123456789" 14 | MANUF=$(hostname) 15 | PRODUCT="pizero" 16 | #echo "pizero USB Device UVC" > $GADGET_CONFIGFS_ROOT/strings/0x409/product 17 | 18 | BOARD=$(strings /proc/device-tree/model) 19 | 20 | case $BOARD in 21 | *) 22 | UDC=`ls /sys/class/udc` # will identify the 'first' UDC 23 | UDC_ROLE=/dev/null # Not generic 24 | ;; 25 | esac 26 | 27 | echo "Detecting platform:" 28 | echo " board : $BOARD" 29 | echo " udc : $UDC" 30 | 31 | create_eth() { 32 | 33 | CONFIG=$1 34 | FUNCTION=$2 35 | echo " Creating ECM.0 (ethernet) gadget functionality : $FUNCTION" 36 | cd $GADGET/pizero 37 | mkdir -p functions/$FUNCTION 38 | # first byte of address must be even 39 | HOST="48:6f:73:74:50:43" # "HostPC" 40 | SELF="42:61:64:55:53:42" # "BadUSB" 41 | echo $HOST > functions/$FUNCTION/host_addr 42 | echo $SELF > functions/$FUNCTION/dev_addr 43 | ln -s functions/$FUNCTION configs/c.1 44 | 45 | } 46 | create_serial() { 47 | 48 | CONFIG=$1 49 | FUNCTION=$2 50 | echo " Creating ACM (serial) gadget functionality : $FUNCTION" 51 | cd $GADGET/pizero 52 | mkdir -p functions/$FUNCTION 53 | # first byte of address must be even 54 | ln -s functions/$FUNCTION configs/c.1 55 | 56 | } 57 | create_frame() { 58 | # Example usage: 59 | # create_frame 60 | 61 | FUNCTION=$1 62 | WIDTH=$2 63 | HEIGHT=$3 64 | FORMAT=$4 65 | NAME=$5 66 | 67 | wdir=functions/$FUNCTION/streaming/$FORMAT/$NAME/${HEIGHT}p 68 | mkdir -p $wdir 69 | #temp new values 70 | #WIDTH=640 71 | #HEIGHT=360 72 | echo $WIDTH > $wdir/wWidth 73 | echo $HEIGHT > $wdir/wHeight 74 | #echo $(( $WIDTH * $HEIGHT * 1 )) > $wdir/dwMaxVideoFrameBufferSize 75 | # each frame is 100ns 76 | echo 500000 > $wdir/dwDefaultFrameInterval 77 | cat < $wdir/dwFrameInterval 78 | 166666 79 | 200000 80 | 333333 81 | 500000 82 | EOF 83 | echo $(( $WIDTH * $HEIGHT * 80 )) > $wdir/dwMinBitRate 84 | echo $(( $WIDTH * $HEIGHT * 160 )) > $wdir/dwMaxBitRate 85 | 86 | } 87 | 88 | create_uvc() { 89 | # create_uvc 90 | # create_uvc config/c.1 uvc.0 91 | CONFIG=$1 92 | FUNCTION=$2 93 | 94 | echo " Creating UVC gadget functionality : $FUNCTION" 95 | mkdir functions/$FUNCTION 96 | 97 | #create_frame $FUNCTION 640 360 uncompressed u 98 | #create_frame $FUNCTION 736 480 uncompressed u 99 | case "$PLAT" in 100 | test) 101 | create_frame $FUNCTION 1920 1080 mjpeg m 102 | ;; 103 | mjpeg) 104 | create_frame $FUNCTION 1280 720 mjpeg m 105 | ;; 106 | yuyv) 107 | create_frame $FUNCTION 1280 720 uncompressed u 108 | ;; 109 | all) 110 | create_frame $FUNCTION 1280 720 uncompressed u 111 | create_frame $FUNCTION 1280 720 mjpeg m 112 | ;; 113 | *) 114 | echo "unknown format, exiting" 115 | exit 1 116 | #create_frame $FUNCTION 1280 720 uncompressed u 117 | #create_frame $FUNCTION 1280 720 mjpeg m 118 | ;; 119 | esac 120 | 121 | mkdir functions/$FUNCTION/streaming/header/h 122 | cd functions/$FUNCTION/streaming/header/h 123 | 124 | ln -s ../../uncompressed/u 125 | ln -s ../../mjpeg/m 126 | cd ../../class/fs 127 | ln -s ../../header/h 128 | cd ../../class/hs 129 | ln -s ../../header/h 130 | 131 | cd ../../../control 132 | mkdir header/h 133 | ln -s header/h class/fs 134 | #ln -s header/h class/ss 135 | cd ../../../ 136 | 137 | # Set the packet size: uvc gadget max size is 3k... 138 | #echo 3072 > functions/$FUNCTION/streaming_maxpacket .. yeah but ain't working 139 | echo 2048 > functions/$FUNCTION/streaming_maxpacket 140 | # echo 1024 > functions/$FUNCTION/streaming_maxpacket .. only if you want slow fps 141 | 142 | #echo 15 > functions/$FUNCTION/streaming_maxburst 143 | echo 1 > functions/$FUNCTION/streaming_interval 144 | ln -s functions/$FUNCTION configs/c.1 145 | } 146 | 147 | delete_uvc() { 148 | # delete_uvc 149 | # delete_uvc config/c.1 uvc.0 150 | CONFIG=$1 151 | FUNCTION=$2 152 | 153 | echo " Deleting UVC gadget functionality : $FUNCTION" 154 | #rm $CONFIG/$FUNCTION 155 | rmdir functions/$FUNCTION 156 | 157 | rm functions/$FUNCTION/control/class/*/h 158 | rm functions/$FUNCTION/streaming/class/*/h 159 | rm functions/$FUNCTION/streaming/header/h/m 160 | rm functions/$FUNCTION/streaming/header/h/u 161 | 162 | rmdir functions/$FUNCTION/streaming/uncompressed/u/*/ 163 | rmdir functions/$FUNCTION/streaming/uncompressed/u 164 | 165 | rmdir functions/$FUNCTION/streaming/mjpeg/m/*/ 166 | rmdir functions/$FUNCTION/streaming/mjpeg/m 167 | 168 | rmdir functions/$FUNCTION/streaming/header/h 169 | rmdir functions/$FUNCTION/control/header/h 170 | 171 | rmdir functions/$FUNCTION 172 | rm $CONFIG/$FUNCTION 173 | #rm configs/c.1/uvc.0 174 | } 175 | delete_ethernet(){ 176 | CONFIG=$1 177 | FUNCTION=$2 178 | echo "function/$FUNCTION" 179 | rmdir functions/$FUNCTION 180 | rm $CONFIG/$FUNCTION 181 | } 182 | case "$OP" in 183 | start) 184 | echo "Creating the USB gadget" 185 | #echo "Loading composite module" 186 | #modprobe libcomposite 187 | 188 | echo "Creating gadget directory pizero" 189 | mkdir -p $GADGET/pizero 190 | 191 | cd $GADGET/pizero 192 | if [ $? -ne 0 ]; then 193 | echo "Error creating usb gadget in configfs" 194 | exit 1; 195 | else 196 | echo "OK" 197 | fi 198 | 199 | echo "Setting Vendor and Product ID's" 200 | echo $VID > idVendor 201 | echo $PID > idProduct 202 | echo 0x0100 > bcdDevice 203 | echo 0x0200 > bcdUSB 204 | echo 0xEF > bDeviceClass 205 | echo 0x02 > bDeviceSubClass 206 | echo 0x01 > bDeviceProtocol 207 | echo 64 > bMaxPacketSize0 208 | echo "OK" 209 | 210 | echo "Setting English strings" 211 | mkdir -p strings/0x409 212 | echo $SERIAL > strings/0x409/serialnumber 213 | echo $MANUF > strings/0x409/manufacturer 214 | echo $PRODUCT > strings/0x409/product 215 | echo "OK" 216 | 217 | echo "Creating Config" 218 | mkdir configs/c.1 219 | echo 500 > configs/c.1/MaxPower 220 | mkdir configs/c.1/strings/0x409 221 | 222 | echo "Creating functions..." 223 | 224 | create_uvc configs/c.1 uvc.0 225 | create_eth configs/c.1 ecm.usb0 226 | #create_serial configs/c.1 acm.usb0 227 | echo "OK" 228 | 229 | echo "Binding USB Device Controller" 230 | udevadm settle -t 5 || : 231 | sleep 2 232 | echo $UDC > UDC 233 | echo peripheral > $UDC_ROLE 234 | cat $UDC_ROLE 235 | echo "OK" 236 | ;; 237 | 238 | stop) 239 | echo "Stopping the USB gadget" 240 | echo "Sorry this doesn't work well" 241 | echo "Please reboot Pi to fully stop USB gadget" 242 | 243 | set +e # Ignore all errors here on a best effort 244 | 245 | cd $GADGET/pizero 246 | 247 | if [ $? -ne 0 ]; then 248 | echo "Error: no configfs gadget found" 249 | exit 1; 250 | fi 251 | 252 | echo "Unbinding USB Device Controller" 253 | grep $UDC UDC && echo "" > UDC 254 | echo "OK" 255 | 256 | #delete_uvc configs/c.1 uvc.0 257 | #delete_ethernet configs/c.1 ecm.usb0 258 | 259 | echo "Clearing English strings" 260 | rmdir strings/0x409 261 | echo "OK" 262 | 263 | delete_uvc configs/c.1 uvc.0 264 | delete_ethernet configs/c.1 ecm.usb0 265 | 266 | 267 | echo "Cleaning up configuration" 268 | rmdir configs/c.1/strings/0x409 269 | #rm configs/c.1/uvc.0 270 | rmdir configs/c.1 271 | 272 | rmdir functions/uvc.0 #try to put this in delete_uvc 273 | rmdir functions/ecm.usb0 274 | #rm string/0x409 275 | echo "OK" 276 | 277 | echo "Removing gadget directory" 278 | cd $GADGET 279 | rmdir pizero 280 | cd / 281 | echo "OK" 282 | 283 | #echo "Disable composite USB gadgets" 284 | #modprobe -r libcomposite 285 | #echo "OK" 286 | ;; 287 | *) 288 | echo "Usage : $0 {start mjpeg|start yuyv|stop}" 289 | esac 290 | -------------------------------------------------------------------------------- /uvc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uvc_gadget.h -- USB Video Class Gadget driver 3 | * 4 | * Copyright (C) 2009-2010 5 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | */ 12 | 13 | #ifndef _UVC_GADGET_H_ 14 | #define _UVC_GADGET_H_ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0) 21 | #define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0) 22 | #define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1) 23 | #define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2) 24 | #define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3) 25 | #define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4) 26 | #define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5) 27 | #define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5) 28 | 29 | struct uvc_request_data 30 | { 31 | __s32 length; 32 | __u8 data[60]; 33 | }; 34 | 35 | struct uvc_event 36 | { 37 | union { 38 | enum usb_device_speed speed; 39 | struct usb_ctrlrequest req; 40 | struct uvc_request_data data; 41 | }; 42 | }; 43 | 44 | #define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data) 45 | 46 | #define UVC_INTF_CONTROL 0 47 | #define UVC_INTF_STREAMING 1 48 | 49 | /* ------------------------------------------------------------------------ 50 | * Debugging, printing and logging 51 | */ 52 | 53 | #ifdef __KERNEL__ 54 | 55 | #include /* For usb_endpoint_* */ 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | #include "uvc_queue.h" 64 | 65 | #define UVC_TRACE_PROBE (1 << 0) 66 | #define UVC_TRACE_DESCR (1 << 1) 67 | #define UVC_TRACE_CONTROL (1 << 2) 68 | #define UVC_TRACE_FORMAT (1 << 3) 69 | #define UVC_TRACE_CAPTURE (1 << 4) 70 | #define UVC_TRACE_CALLS (1 << 5) 71 | #define UVC_TRACE_IOCTL (1 << 6) 72 | #define UVC_TRACE_FRAME (1 << 7) 73 | #define UVC_TRACE_SUSPEND (1 << 8) 74 | #define UVC_TRACE_STATUS (1 << 9) 75 | 76 | #define UVC_WARN_MINMAX 0 77 | #define UVC_WARN_PROBE_DEF 1 78 | 79 | extern unsigned int uvc_gadget_trace_param; 80 | 81 | #define uvc_trace(flag, msg...) \ 82 | do { \ 83 | if (uvc_gadget_trace_param & flag) \ 84 | printk(KERN_DEBUG "uvcvideo: " msg); \ 85 | } while (0) 86 | 87 | #define uvc_warn_once(dev, warn, msg...) \ 88 | do { \ 89 | if (!test_and_set_bit(warn, &dev->warnings)) \ 90 | printk(KERN_INFO "uvcvideo: " msg); \ 91 | } while (0) 92 | 93 | #define uvc_printk(level, msg...) \ 94 | printk(level "uvcvideo: " msg) 95 | 96 | /* ------------------------------------------------------------------------ 97 | * Driver specific constants 98 | */ 99 | 100 | #define UVC_NUM_REQUESTS 4 101 | #define UVC_MAX_REQUEST_SIZE 64 102 | #define UVC_MAX_EVENTS 4 103 | 104 | /* ------------------------------------------------------------------------ 105 | * Structures 106 | */ 107 | 108 | struct uvc_video 109 | { 110 | struct usb_ep *ep; 111 | 112 | /* Frame parameters */ 113 | u8 bpp; 114 | u32 fcc; 115 | unsigned int width; 116 | unsigned int height; 117 | unsigned int imagesize; 118 | 119 | /* Requests */ 120 | unsigned int req_size; 121 | struct usb_request *req[UVC_NUM_REQUESTS]; 122 | __u8 *req_buffer[UVC_NUM_REQUESTS]; 123 | struct list_head req_free; 124 | spinlock_t req_lock; 125 | 126 | void (*encode) (struct usb_request *req, struct uvc_video *video, 127 | struct uvc_buffer *buf); 128 | 129 | /* Context data used by the completion handler */ 130 | __u32 payload_size; 131 | __u32 max_payload_size; 132 | 133 | struct uvc_video_queue queue; 134 | unsigned int fid; 135 | }; 136 | 137 | enum uvc_state 138 | { 139 | UVC_STATE_DISCONNECTED, 140 | UVC_STATE_CONNECTED, 141 | UVC_STATE_STREAMING, 142 | }; 143 | 144 | struct uvc_device 145 | { 146 | struct video_device *vdev; 147 | struct v4l2_device v4l2_dev; 148 | enum uvc_state state; 149 | struct usb_function func; 150 | struct uvc_video video; 151 | 152 | /* Descriptors */ 153 | struct { 154 | const struct uvc_descriptor_header * const *fs_control; 155 | const struct uvc_descriptor_header * const *ss_control; 156 | const struct uvc_descriptor_header * const *fs_streaming; 157 | const struct uvc_descriptor_header * const *hs_streaming; 158 | const struct uvc_descriptor_header * const *ss_streaming; 159 | } desc; 160 | 161 | unsigned int control_intf; 162 | struct usb_ep *control_ep; 163 | struct usb_request *control_req; 164 | void *control_buf; 165 | 166 | unsigned int streaming_intf; 167 | 168 | /* Events */ 169 | unsigned int event_length; 170 | unsigned int event_setup_out : 1; 171 | }; 172 | 173 | static inline struct uvc_device *to_uvc(struct usb_function *f) 174 | { 175 | return container_of(f, struct uvc_device, func); 176 | } 177 | 178 | struct uvc_file_handle 179 | { 180 | struct v4l2_fh vfh; 181 | struct uvc_video *device; 182 | }; 183 | 184 | #define to_uvc_file_handle(handle) \ 185 | container_of(handle, struct uvc_file_handle, vfh) 186 | 187 | /* ------------------------------------------------------------------------ 188 | * Functions 189 | */ 190 | 191 | extern void uvc_function_setup_continue(struct uvc_device *uvc); 192 | extern void uvc_endpoint_stream(struct uvc_device *dev); 193 | 194 | extern void uvc_function_connect(struct uvc_device *uvc); 195 | extern void uvc_function_disconnect(struct uvc_device *uvc); 196 | 197 | #endif /* __KERNEL__ */ 198 | 199 | #endif /* _UVC_GADGET_H_ */ 200 | 201 | -------------------------------------------------------------------------------- /v4l2-mmal-uvc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * v4l2-mmal-uvc application 3 | * 4 | * Copyright (C) 2020 Kin Wei Lee 5 | * 6 | * based on uvc-gadget laurent.pinchart@ideasonboard.com 7 | * based on v4l2_mmal by 6by9 Raspberry Pi (Trading) Ltd. 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License along 20 | * with this program; if not, write to the Free Software Foundation, Inc., 21 | */ 22 | 23 | #define __STDC_FORMAT_MACROS 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | 47 | #include "uvc.h" 48 | 49 | #include "interface/mmal/mmal.h" 50 | #include "interface/mmal/mmal_buffer.h" 51 | #include "interface/mmal/util/mmal_connection.h" 52 | #include "interface/mmal/util/mmal_util.h" 53 | #include "interface/mmal/util/mmal_util_params.h" 54 | #include "bcm_host.h" 55 | #include "interface/vcsm/user-vcsm.h" 56 | 57 | #include "v4l2-mmal-uvc.h" 58 | 59 | #ifndef V4L2_BUF_FLAG_ERROR 60 | #define V4L2_BUF_FLAG_ERROR 0x0040 61 | #endif 62 | 63 | //output image size fixed to 1280x720 64 | /*#define WIDTH 1280 65 | #define HEIGHT 720*/ 66 | #define MAX_PLANES 8 67 | int debug = 0; 68 | #define print(...) do { if (debug) printf(__VA_ARGS__); } while (0) 69 | 70 | /* Enable debug prints. */ 71 | #undef ENABLE_BUFFER_DEBUG 72 | //#define ENABLE_BUFFER_DEBUG 73 | #undef ENABLE_USB_REQUEST_DEBUG 74 | 75 | #define CLEAR(x) memset (&(x), 0, sizeof (x)) 76 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 77 | 78 | #define clamp(val, min, max) ({ \ 79 | typeof(val) __val = (val); \ 80 | typeof(min) __min = (min); \ 81 | typeof(max) __max = (max); \ 82 | (void) (&__val == &__min); \ 83 | (void) (&__val == &__max); \ 84 | __val = __val < __min ? __min: __val; \ 85 | __val > __max ? __max: __val; }) 86 | 87 | //#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(a[0]))) 88 | #define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ 89 | ((x) >> 24) & 0xff 90 | 91 | /* 92 | * The UVC webcam gadget kernel driver (g_webcam.ko) supports changing 93 | * the Brightness attribute of the Processing Unit (PU). by default. If 94 | * the underlying video capture device supports changing the Brightness 95 | * attribute of the image being acquired (like the Virtual Video, VIVI 96 | * driver), then we should route this UVC request to the respective 97 | * video capture device. 98 | * 99 | * Incase, there is no actual video capture device associated with the 100 | * UVC gadget and we wish to use this application as the final 101 | * destination of the UVC specific requests then we should return 102 | * pre-cooked (static) responses to GET_CUR(BRIGHTNESS) and 103 | * SET_CUR(BRIGHTNESS) commands to keep command verifier test tools like 104 | * UVC class specific test suite of USBCV, happy. 105 | * 106 | * Note that the values taken below are in sync with the VIVI driver and 107 | * must be changed for your specific video capture device. These values 108 | * also work well in case there in no actual video capture device. 109 | */ 110 | #define PU_BRIGHTNESS_MIN_VAL 0 111 | #define PU_BRIGHTNESS_MAX_VAL 255 112 | #define PU_BRIGHTNESS_STEP_SIZE 1 113 | #define PU_BRIGHTNESS_DEFAULT_VAL 7 114 | 115 | /* declarations */ 116 | int default_format = 0; /* V4L2_PIX_FMT_YUYV */ 117 | static int video_queue_buffer (struct v4l2_device *dev, int index); 118 | /* --------------------------------------------------------------------------- 119 | * UVC specific stuff 120 | */ 121 | 122 | struct uvc_frame_info 123 | { 124 | unsigned int width; 125 | unsigned int height; 126 | unsigned int intervals[8]; 127 | }; 128 | 129 | struct uvc_format_info 130 | { 131 | unsigned int fcc; 132 | const struct uvc_frame_info *frames; 133 | }; 134 | static const struct uvc_frame_info uvc_frames_yuyv[] = { 135 | // {640, 360, {66666, 100000, 500000, 0},}, 136 | // {736, 480, {500000, 0},}, 137 | {1280, 720, {400000, 0},}, 138 | {0, 0, {0,},}, 139 | }; 140 | 141 | 142 | 143 | static const struct uvc_frame_info uvc_frames_mjpeg[] = { 144 | // {640, 360, {66666, 100000, 500000, 0},}, 145 | {1280, 720, {500000},}, 146 | {0, 0, {0,},}, 147 | }; 148 | 149 | 150 | static const struct uvc_format_info uvc_formats[] = { 151 | // {V4L2_PIX_FMT_YUYV, uvc_frames_yuyv}, 152 | {V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg}, 153 | }; 154 | 155 | /* --------------------------------------------------------------------------- 156 | * V4L2 and UVC device instances 157 | */ 158 | 159 | static void 160 | time_me (char *fn_name, struct v4l2_device *dev) 161 | { 162 | struct timespec stt; 163 | static int i = 0; 164 | static float ft = 0; 165 | clock_gettime (CLOCK_MONOTONIC, &stt); 166 | if (i) 167 | ft += 168 | ((float) (stt.tv_nsec - dev->last.tv_nsec) / 1e9 + 169 | (float) (stt.tv_sec - dev->last.tv_sec)); 170 | if (i % 100 == 0) 171 | printf ("%s %0.2ffps\n", fn_name, i / ft ); 172 | dev->last = stt; 173 | i++; 174 | } 175 | 176 | 177 | 178 | /* forward declarations */ 179 | static int uvc_video_stream (struct uvc_device *dev, int enable); 180 | 181 | 182 | // print out some structure info 183 | void 184 | v4l2_dump (char *txt, struct v4l2_device *dev) 185 | { 186 | print ("%s\n", txt); 187 | print ("dev->v4l2_fd %d\n", dev->v4l2_fd); 188 | print ("dev->type %d\n", dev->type); 189 | print ("dev->memtype %d\n", dev->memtype); 190 | } 191 | 192 | static bool 193 | video_is_mplane (struct v4l2_device *dev) 194 | { 195 | return dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 196 | } 197 | 198 | static bool 199 | video_is_capture (struct v4l2_device *dev) 200 | { 201 | return dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE; 202 | } 203 | 204 | static bool 205 | video_is_output (struct v4l2_device *dev) 206 | { 207 | return dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT; 208 | } 209 | 210 | 211 | static bool 212 | video_has_fd (struct v4l2_device *dev) 213 | { 214 | return dev->v4l2_fd != -1; 215 | } 216 | 217 | static int 218 | video_set_fd (struct v4l2_device *dev, int fd) 219 | { 220 | if (video_has_fd (dev)) { 221 | print ("Can't set fd (already open).\n"); 222 | return -1; 223 | } 224 | 225 | dev->v4l2_fd = fd; 226 | 227 | return 0; 228 | } 229 | 230 | static const struct v4l2_format_info * 231 | v4l2_format_by_fourcc (unsigned int fourcc) 232 | { 233 | unsigned int i; 234 | 235 | for (i = 0; i < ARRAY_SIZE (pixel_formats); ++i) { 236 | if (pixel_formats[i].fourcc == fourcc) 237 | return &pixel_formats[i]; 238 | } 239 | 240 | return NULL; 241 | } 242 | 243 | static const struct v4l2_format_info * 244 | v4l2_format_by_name (const char *name) 245 | { 246 | unsigned int i; 247 | 248 | for (i = 0; i < ARRAY_SIZE (pixel_formats); ++i) { 249 | if (strcasecmp (pixel_formats[i].name, name) == 0) 250 | return &pixel_formats[i]; 251 | } 252 | 253 | return NULL; 254 | } 255 | 256 | static const char * 257 | v4l2_format_name (unsigned int fourcc) 258 | { 259 | const struct v4l2_format_info *info; 260 | static char name[5]; 261 | unsigned int i; 262 | 263 | info = v4l2_format_by_fourcc (fourcc); 264 | if (info) 265 | return info->name; 266 | 267 | for (i = 0; i < 4; ++i) { 268 | name[i] = fourcc & 0xff; 269 | fourcc >>= 8; 270 | } 271 | 272 | name[4] = '\0'; 273 | return name; 274 | } 275 | 276 | static const struct 277 | { 278 | const char *name; 279 | enum v4l2_field field; 280 | } fields[] = { 281 | {"any", V4L2_FIELD_ANY}, 282 | {"none", V4L2_FIELD_NONE}, 283 | {"top", V4L2_FIELD_TOP}, 284 | {"bottom", V4L2_FIELD_BOTTOM}, 285 | {"interlaced", V4L2_FIELD_INTERLACED}, 286 | {"seq-tb", V4L2_FIELD_SEQ_TB}, 287 | {"seq-bt", V4L2_FIELD_SEQ_BT}, 288 | {"alternate", V4L2_FIELD_ALTERNATE}, 289 | {"interlaced-tb", V4L2_FIELD_INTERLACED_TB}, 290 | {"interlaced-bt", V4L2_FIELD_INTERLACED_BT}, 291 | }; 292 | 293 | static enum v4l2_field 294 | v4l2_field_from_string (const char *name) 295 | { 296 | unsigned int i; 297 | 298 | for (i = 0; i < ARRAY_SIZE (fields); ++i) { 299 | if (strcasecmp (fields[i].name, name) == 0) 300 | return fields[i].field; 301 | } 302 | 303 | return -1; 304 | } 305 | 306 | static const char * 307 | v4l2_field_name (enum v4l2_field field) 308 | { 309 | unsigned int i; 310 | 311 | for (i = 0; i < ARRAY_SIZE (fields); ++i) { 312 | if (fields[i].field == field) 313 | return fields[i].name; 314 | } 315 | 316 | return "unknown"; 317 | } 318 | 319 | static void 320 | video_set_buf_type (struct v4l2_device *dev, enum v4l2_buf_type type) 321 | { 322 | dev->type = type; 323 | } 324 | 325 | int 326 | video_querycap (struct v4l2_device *dev, unsigned int *capabilities) 327 | { 328 | struct v4l2_capability cap; 329 | unsigned int caps; 330 | int ret; 331 | 332 | memset (&cap, 0, sizeof cap); 333 | ret = ioctl (dev->v4l2_fd, VIDIOC_QUERYCAP, &cap); 334 | if (ret < 0) 335 | return 0; 336 | 337 | caps = cap.capabilities & V4L2_CAP_DEVICE_CAPS ? cap.device_caps : cap.capabilities; 338 | 339 | print 340 | ("Device `%s' on `%s' (driver '%s') is a video %s (%s mplanes) device.\n", 341 | cap.card, cap.bus_info, cap.driver, 342 | caps & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_CAPTURE) ? 343 | "capture" : "output", 344 | caps & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) ? 345 | "with" : "without"); 346 | 347 | *capabilities = caps; 348 | 349 | return 0; 350 | } 351 | 352 | 353 | static int 354 | cap_get_buf_type (unsigned int capabilities) 355 | { 356 | if (capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) { 357 | return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 358 | } 359 | else if (capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) { 360 | return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 361 | } 362 | else if (capabilities & V4L2_CAP_VIDEO_CAPTURE) { 363 | return V4L2_BUF_TYPE_VIDEO_CAPTURE; 364 | } 365 | else if (capabilities & V4L2_CAP_VIDEO_OUTPUT) { 366 | return V4L2_BUF_TYPE_VIDEO_OUTPUT; 367 | } 368 | else { 369 | print ("Device supports neither capture nor output.\n"); 370 | return -EINVAL; 371 | } 372 | 373 | return 0; 374 | } 375 | 376 | static int 377 | video_open (struct v4l2_device *dev, const char *devname) 378 | { 379 | if (video_has_fd (dev)) { 380 | print ("Can't open device (already open).\n"); 381 | return -1; 382 | } 383 | 384 | dev->v4l2_fd = open (devname, O_RDWR); 385 | if (dev->v4l2_fd < 0) { 386 | print ("Error opening device %s: %s (%d).\n", devname, 387 | strerror (errno), errno); 388 | return dev->v4l2_fd; 389 | } 390 | 391 | print ("Device %s opened.\n", devname); 392 | 393 | dev->opened = 1; 394 | 395 | return 0; 396 | } 397 | 398 | static void 399 | video_close (struct v4l2_device *dev) 400 | { 401 | unsigned int i; 402 | 403 | for (i = 0; i < dev->num_planes; i++) 404 | free (dev->pattern[i]); 405 | 406 | free (dev->buffers); 407 | if (dev->opened) 408 | close (dev->v4l2_fd); 409 | } 410 | 411 | 412 | static int 413 | video_get_format (struct v4l2_device *dev) 414 | { 415 | struct v4l2_format fmt; 416 | unsigned int i; 417 | int ret; 418 | 419 | memset (&fmt, 0, sizeof fmt); 420 | fmt.type = dev->type; 421 | 422 | ret = ioctl (dev->v4l2_fd, VIDIOC_G_FMT, &fmt); 423 | if (ret < 0) { 424 | print ("Unable to get format: %s (%d).\n", strerror (errno), errno); 425 | return ret; 426 | } 427 | 428 | if (video_is_mplane (dev)) { 429 | dev->width = fmt.fmt.pix_mp.width; 430 | dev->height = fmt.fmt.pix_mp.height; 431 | dev->num_planes = fmt.fmt.pix_mp.num_planes; 432 | 433 | print ("Video format: %s (%08x) %ux%u field %s, %u planes: \n", 434 | v4l2_format_name (fmt.fmt.pix_mp.pixelformat), 435 | fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.width, 436 | fmt.fmt.pix_mp.height, v4l2_field_name (fmt.fmt.pix_mp.field), 437 | fmt.fmt.pix_mp.num_planes); 438 | 439 | for (i = 0; i < fmt.fmt.pix_mp.num_planes; i++) { 440 | dev->plane_fmt[i].bytesperline = fmt.fmt.pix_mp.plane_fmt[i].bytesperline; 441 | dev->plane_fmt[i].sizeimage = fmt.fmt.pix_mp.plane_fmt[i].bytesperline ? 442 | fmt.fmt.pix_mp.plane_fmt[i].sizeimage : 0; 443 | 444 | print (" * Stride %u, buffer size %u\n", 445 | fmt.fmt.pix_mp.plane_fmt[i].bytesperline, 446 | fmt.fmt.pix_mp.plane_fmt[i].sizeimage); 447 | } 448 | } 449 | else { 450 | dev->width = fmt.fmt.pix.width; 451 | dev->height = fmt.fmt.pix.height; 452 | dev->num_planes = 1; 453 | 454 | dev->plane_fmt[0].bytesperline = fmt.fmt.pix.bytesperline; 455 | dev->plane_fmt[0].sizeimage = fmt.fmt.pix.bytesperline ? fmt.fmt.pix.sizeimage : 0; 456 | 457 | print 458 | ("Video format: %s (%08x) %ux%u (stride %u) field %s buffer size %u\n", 459 | v4l2_format_name (fmt.fmt.pix.pixelformat), fmt.fmt.pix.pixelformat, 460 | fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.bytesperline, 461 | v4l2_field_name (fmt.fmt.pix_mp.field), fmt.fmt.pix.sizeimage); 462 | } 463 | 464 | return 0; 465 | } 466 | 467 | static int 468 | format_bpp (__u32 pixfmt) 469 | { 470 | switch (pixfmt) { 471 | case V4L2_PIX_FMT_BGR24: 472 | case V4L2_PIX_FMT_RGB24: 473 | return 4; 474 | case V4L2_PIX_FMT_YUYV: 475 | case V4L2_PIX_FMT_YVYU: 476 | case V4L2_PIX_FMT_UYVY: 477 | case V4L2_PIX_FMT_VYUY: 478 | return 2; 479 | case V4L2_PIX_FMT_SRGGB8: 480 | case V4L2_PIX_FMT_SBGGR8: 481 | case V4L2_PIX_FMT_SGRBG8: 482 | case V4L2_PIX_FMT_SGBRG8: 483 | return 1; 484 | default: 485 | return 1; 486 | } 487 | } 488 | 489 | /*static int 490 | video_set_quality (struct v4l2_device *dev, unsigned int quality) 491 | { 492 | struct v4l2_jpegcompression jpeg; 493 | int ret; 494 | 495 | if (quality == (unsigned int) -1) 496 | return 0; 497 | 498 | memset (&jpeg, 0, sizeof jpeg); 499 | jpeg.quality = quality; 500 | 501 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_JPEGCOMP, &jpeg); 502 | if (ret < 0) { 503 | print ("Unable to set quality to %u: %s (%d).\n", quality, 504 | strerror (errno), errno); 505 | return ret; 506 | } 507 | 508 | ret = ioctl (dev->v4l2_fd, VIDIOC_G_JPEGCOMP, &jpeg); 509 | if (ret >= 0) 510 | print ("Quality set to %u\n", jpeg.quality); 511 | 512 | return 0; 513 | }*/ 514 | 515 | static int 516 | video_set_format (struct v4l2_device *dev, unsigned int w, unsigned int h, 517 | unsigned int format, unsigned int stride, 518 | unsigned int buffer_size, enum v4l2_field field, 519 | unsigned int flags) 520 | { 521 | struct v4l2_format fmt; 522 | unsigned int i; 523 | int ret; 524 | print ("video_set_format width %d, height %d\n", w, h); 525 | memset (&fmt, 0, sizeof fmt); 526 | fmt.type = dev->type; 527 | 528 | if (video_is_mplane (dev)) { 529 | const struct v4l2_format_info *info = v4l2_format_by_fourcc (format); 530 | 531 | fmt.fmt.pix_mp.width = w; 532 | fmt.fmt.pix_mp.height = h; 533 | fmt.fmt.pix_mp.pixelformat = format; 534 | fmt.fmt.pix_mp.field = field; 535 | fmt.fmt.pix_mp.num_planes = info->n_planes; 536 | fmt.fmt.pix_mp.flags = flags; 537 | 538 | for (i = 0; i < fmt.fmt.pix_mp.num_planes; i++) { 539 | fmt.fmt.pix_mp.plane_fmt[i].bytesperline = stride; 540 | fmt.fmt.pix_mp.plane_fmt[i].sizeimage = buffer_size; 541 | } 542 | } 543 | else { 544 | fmt.fmt.pix.width = w; 545 | fmt.fmt.pix.height = h; 546 | fmt.fmt.pix.pixelformat = format; 547 | fmt.fmt.pix.field = field; 548 | print ("stride is %d\n", stride); 549 | if (!stride) 550 | stride = ((w + 31) & ~31) * format_bpp (format); 551 | print ("stride is now %d\n", stride); 552 | fmt.fmt.pix.bytesperline = stride; 553 | fmt.fmt.pix.sizeimage = buffer_size; 554 | fmt.fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; 555 | fmt.fmt.pix.flags = flags; 556 | } 557 | 558 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_FMT, &fmt); 559 | if (ret < 0) { 560 | printf("Unable to set format: %s (%d).\n", strerror (errno), errno); 561 | return ret; 562 | } 563 | 564 | if (video_is_mplane (dev)) { 565 | printf("Video format set: %s (%08x) %ux%u field %s, %u planes: \n", 566 | v4l2_format_name (fmt.fmt.pix_mp.pixelformat), 567 | fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.width, 568 | fmt.fmt.pix_mp.height, v4l2_field_name (fmt.fmt.pix_mp.field), 569 | fmt.fmt.pix_mp.num_planes); 570 | 571 | for (i = 0; i < fmt.fmt.pix_mp.num_planes; i++) { 572 | printf(" * Stride %u, buffer size %u\n", 573 | fmt.fmt.pix_mp.plane_fmt[i].bytesperline, 574 | fmt.fmt.pix_mp.plane_fmt[i].sizeimage); 575 | } 576 | } 577 | else { 578 | printf 579 | ("Video format set: %s (%08x) %ux%u (stride %u) field %s buffer size %u\n", 580 | v4l2_format_name (fmt.fmt.pix.pixelformat), fmt.fmt.pix.pixelformat, 581 | fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.bytesperline, 582 | v4l2_field_name (fmt.fmt.pix.field), fmt.fmt.pix.sizeimage); 583 | } 584 | 585 | return 0; 586 | } 587 | 588 | // create dma buffer using vcm and link it to v4l2 buffer 589 | static int 590 | buffer_export (int v4l2fd, enum v4l2_buf_type bt, int index, int *dmafd, 591 | unsigned int *vcsm_hdl) 592 | { 593 | struct v4l2_exportbuffer expbuf; 594 | unsigned int vcsm_handle; 595 | 596 | memset (&expbuf, 0, sizeof (expbuf)); 597 | expbuf.type = bt; 598 | expbuf.index = index; 599 | if (ioctl (v4l2fd, VIDIOC_EXPBUF, &expbuf)) { 600 | print ("Failed to EXPBUF\n"); 601 | return -1; 602 | } 603 | *dmafd = expbuf.fd; 604 | 605 | print ("Importing DMABUF %d into VCSM...\n", expbuf.fd); 606 | vcsm_handle = vcsm_import_dmabuf (expbuf.fd, "V4L2 buf"); 607 | if (vcsm_handle) 608 | print ("...done. vcsm_handle %u\n", vcsm_handle); 609 | else 610 | print ("...done. Failed\n"); 611 | *vcsm_hdl = vcsm_handle; 612 | return vcsm_handle ? 0 : -1; 613 | } 614 | 615 | static int 616 | video_buffer_mmap (struct v4l2_device *dev, struct mmal_buffer *buffer, 617 | struct v4l2_buffer *v4l2buf) 618 | { 619 | unsigned int length; 620 | unsigned int offset; 621 | unsigned int i; 622 | 623 | for (i = 0; i < dev->num_planes; i++) { 624 | if (video_is_mplane (dev)) { 625 | length = v4l2buf->m.planes[i].length; 626 | offset = v4l2buf->m.planes[i].m.mem_offset; 627 | } 628 | else { 629 | length = v4l2buf->length; 630 | offset = v4l2buf->m.offset; 631 | } 632 | 633 | buffer->mem[i] = mmap (0, length, PROT_READ | PROT_WRITE, MAP_SHARED, 634 | dev->v4l2_fd, offset); 635 | 636 | if (buffer->mem[i] == MAP_FAILED) { 637 | print ("Unable to map buffer %u/%u: %s (%d)\n", buffer->idx, i, strerror (errno), errno); 638 | return -1; 639 | } 640 | 641 | buffer->size[i] = length; 642 | buffer->padding[i] = 0; 643 | 644 | print ("Buffer %u/%u mapped at address %p.\n", buffer->idx, i, buffer->mem[i]); 645 | } 646 | 647 | return 0; 648 | } 649 | 650 | static int 651 | video_buffer_munmap (struct v4l2_device *dev, struct mmal_buffer *buffer) 652 | { 653 | unsigned int i; 654 | int ret; 655 | 656 | for (i = 0; i < dev->num_planes; i++) { 657 | ret = munmap (buffer->mem[i], buffer->size[i]); 658 | if (ret < 0) { 659 | print ("Unable to unmap buffer %u/%u: %s (%d)\n", 660 | buffer->idx, i, strerror (errno), errno); 661 | } 662 | 663 | buffer->mem[i] = NULL; 664 | } 665 | 666 | return 0; 667 | } 668 | 669 | /* 670 | static int 671 | video_buffer_alloc_userptr (struct v4l2_device *dev, 672 | struct mmal_buffer *buffer, 673 | struct v4l2_buffer *v4l2buf, unsigned int offset, 674 | unsigned int padding) 675 | { 676 | int page_size = getpagesize (); 677 | unsigned int length; 678 | unsigned int i; 679 | int ret; 680 | 681 | for (i = 0; i < dev->num_planes; i++) { 682 | if (video_is_mplane (dev)) 683 | length = v4l2buf->m.planes[i].length; 684 | else 685 | length = v4l2buf->length; 686 | 687 | ret = posix_memalign (&buffer->mem[i], page_size, 688 | length + offset + padding); 689 | if (ret < 0) { 690 | print ("Unable to allocate buffer %u/%u (%d)\n", buffer->idx, i, ret); 691 | return -ENOMEM; 692 | } 693 | 694 | buffer->mem[i] += offset; 695 | buffer->size[i] = length; 696 | buffer->padding[i] = padding; 697 | 698 | print ("Buffer %u/%u allocated at address %p.\n", 699 | buffer->idx, i, buffer->mem[i]); 700 | } 701 | 702 | return 0; 703 | } 704 | */ 705 | 706 | static void 707 | video_buffer_free_userptr (struct v4l2_device *dev, 708 | struct mmal_buffer *buffer) 709 | { 710 | unsigned int i; 711 | 712 | for (i = 0; i < dev->num_planes; i++) { 713 | free (buffer->mem[i]); 714 | buffer->mem[i] = NULL; 715 | } 716 | } 717 | 718 | static void 719 | get_ts_flags (uint32_t flags, const char **ts_type, const char **ts_source) 720 | { 721 | switch (flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) { 722 | case V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN: 723 | *ts_type = "unk"; 724 | break; 725 | case V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC: 726 | *ts_type = "mono"; 727 | break; 728 | case V4L2_BUF_FLAG_TIMESTAMP_COPY: 729 | *ts_type = "copy"; 730 | break; 731 | default: 732 | *ts_type = "inv"; 733 | } 734 | switch (flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK) { 735 | case V4L2_BUF_FLAG_TSTAMP_SRC_EOF: 736 | *ts_source = "EoF"; 737 | break; 738 | case V4L2_BUF_FLAG_TSTAMP_SRC_SOE: 739 | *ts_source = "SoE"; 740 | break; 741 | default: 742 | *ts_source = "inv"; 743 | } 744 | } 745 | 746 | static int 747 | video_alloc_buffers (struct v4l2_device *dev, int nbufs) 748 | { 749 | struct v4l2_plane planes[MAX_PLANES]; 750 | struct v4l2_requestbuffers rb; 751 | struct v4l2_buffer buf; 752 | struct mmal_buffer *buffers; 753 | unsigned int i; 754 | int ret; 755 | 756 | memset (&rb, 0, sizeof rb); 757 | rb.count = nbufs; 758 | rb.type = dev->type; 759 | rb.memory = dev->memtype; 760 | ret = ioctl (dev->v4l2_fd, VIDIOC_REQBUFS, &rb); 761 | if (ret < 0) { 762 | print ("V4L2: Unable to request buffers: %s (%d).\n", strerror (errno), errno); 763 | return ret; 764 | } 765 | //printf ("video_alloc_buffers fd%d %d %d\n", dev->v4l2_fd, dev->type, ret); 766 | 767 | printf ("%u buffers requested, V4L2 returned %u bufs.\n", nbufs, rb.count); 768 | 769 | buffers = malloc (rb.count * sizeof buffers[0]); 770 | if (buffers == NULL) 771 | return -ENOMEM; 772 | 773 | /* Map the buffers. */ 774 | for (i = 0; i < rb.count; ++i) { 775 | const char *ts_type, *ts_source; 776 | 777 | memset (&buf, 0, sizeof buf); 778 | memset (planes, 0, sizeof planes); 779 | 780 | buf.index = i; 781 | buf.type = dev->type; 782 | buf.memory = dev->memtype; 783 | buf.length = MAX_PLANES; 784 | buf.m.planes = planes; 785 | 786 | ret = ioctl (dev->v4l2_fd, VIDIOC_QUERYBUF, &buf); 787 | if (ret < 0) { 788 | print ("Unable to query buffer %u: %s (%d).\n", i, strerror (errno), errno); 789 | return ret; 790 | } 791 | get_ts_flags (buf.flags, &ts_type, &ts_source); 792 | print ("length: %u offset: %u timestamp type/source: %s/%s\n", buf.length, buf.m.offset, ts_type, ts_source); 793 | 794 | buffers[i].idx = i; 795 | 796 | switch (dev->memtype) { 797 | case V4L2_MEMORY_MMAP: 798 | ret = video_buffer_mmap (dev, &buffers[i], &buf); 799 | break; 800 | 801 | /*case V4L2_MEMORY_USERPTR: 802 | ret = video_buffer_alloc_userptr (dev, &buffers[i], &buf, offset, padding); 803 | break;*/ 804 | 805 | default: 806 | ret = -1; 807 | break; 808 | } 809 | 810 | if (ret < 0) 811 | return ret; 812 | 813 | if (!buffer_export(dev->v4l2_fd, dev->type, i, &buffers[i].dma_fd, &buffers[i].vcsm_handle)) { 814 | dev->can_zero_copy = MMAL_TRUE; 815 | print ("Exported buffer %d to dmabuf %d, vcsm handle %u\n", i, 816 | buffers[i].dma_fd, buffers[i].vcsm_handle); 817 | } 818 | else { 819 | if (dev->can_zero_copy) { 820 | print ("Some buffer exported whilst others not. HELP!\n"); 821 | dev->can_zero_copy = MMAL_FALSE; 822 | } 823 | } 824 | if (dev->mmal_pool) { 825 | MMAL_BUFFER_HEADER_T *mmal_buf; 826 | mmal_buf = mmal_queue_get (dev->mmal_pool->queue); 827 | if (!mmal_buf) { 828 | print 829 | ("Failed to get a buffer from the pool. Queue length %d\n", mmal_queue_length (dev->mmal_pool->queue)); 830 | return -1; 831 | } 832 | mmal_buf->user_data = &buffers[i]; 833 | if (dev->can_zero_copy) 834 | mmal_buf->data = (uint8_t *) vcsm_vc_hdl_from_hdl (buffers[i].vcsm_handle); 835 | else 836 | mmal_buf->data = buffers[i].mem[0]; 837 | mmal_buf->alloc_size = buf.length; 838 | buffers[i].mmal = mmal_buf; 839 | print ("Linking V4L2 buffer index %d ptr %p to MMAL header %p. mmal->data 0x%X\n", 840 | i, &buffers[i], mmal_buf, (uint32_t) mmal_buf->data); 841 | /* Put buffer back in the pool */ 842 | mmal_buffer_header_release (mmal_buf); 843 | } 844 | } 845 | 846 | dev->timestamp_type = buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; 847 | dev->buffers = buffers; 848 | dev->nbufs = rb.count; 849 | return 0; 850 | } 851 | 852 | 853 | static int 854 | video_queue_all_buffers (struct v4l2_device *dev) 855 | { 856 | unsigned int i; 857 | int ret; 858 | print ("V4L2: video_queue_all_buffers try %d \n", dev->nbufs); 859 | /* Queue the buffers. */ 860 | for (i = 0; i < dev->nbufs; ++i) { 861 | ret = video_queue_buffer (dev, i);//, fill); 862 | if (ret < 0) 863 | return ret; 864 | } 865 | 866 | print ("V4L2: video_queue_all_buffers ok %d %lld\n", dev->nbufs, 867 | dev->qbuf_count); 868 | return 0; 869 | } 870 | 871 | static void 872 | video_verify_buffer (struct v4l2_device *dev, struct v4l2_buffer *buf) 873 | { 874 | struct mmal_buffer *buffer = &dev->buffers[buf->index]; 875 | unsigned int plane; 876 | unsigned int i; 877 | 878 | for (plane = 0; plane < dev->num_planes; ++plane) { 879 | const uint8_t *data = buffer->mem[plane] + buffer->size[plane]; 880 | unsigned int errors = 0; 881 | unsigned int dirty = 0; 882 | 883 | if (buffer->padding[plane] == 0) 884 | continue; 885 | 886 | for (i = 0; i < buffer->padding[plane]; ++i) { 887 | if (data[i] != 0x55) { 888 | errors++; 889 | dirty = i + 1; 890 | } 891 | } 892 | 893 | if (errors) { 894 | print 895 | ("Warning: %u bytes overwritten among %u first padding bytes for plane %u\n", 896 | errors, dirty, plane); 897 | 898 | dirty = (dirty + 15) & ~15; 899 | dirty = dirty > 32 ? 32 : dirty; 900 | 901 | for (i = 0; i < dirty; ++i) { 902 | print ("%02x ", data[i]); 903 | if (i % 16 == 15) 904 | print ("\n"); 905 | } 906 | } 907 | } 908 | } 909 | 910 | static int 911 | video_free_buffers (struct v4l2_device *dev) 912 | { 913 | struct v4l2_requestbuffers rb; 914 | unsigned int i; 915 | int ret; 916 | 917 | if (dev->nbufs == 0) 918 | return 0; 919 | 920 | for (i = 0; i < dev->nbufs; ++i) { 921 | switch (dev->memtype) { 922 | case V4L2_MEMORY_MMAP: 923 | if (dev->buffers[i].vcsm_handle) { 924 | print ("Releasing vcsm handle %u\n", dev->buffers[i].vcsm_handle); 925 | vcsm_free (dev->buffers[i].vcsm_handle); 926 | } 927 | if (dev->buffers[i].dma_fd) { 928 | print ("Closing dma_buf %d\n", dev->buffers[i].dma_fd); 929 | close (dev->buffers[i].dma_fd); 930 | } 931 | ret = video_buffer_munmap (dev, &dev->buffers[i]); 932 | if (ret < 0) 933 | return ret; 934 | break; 935 | case V4L2_MEMORY_USERPTR: 936 | video_buffer_free_userptr (dev, &dev->buffers[i]); 937 | break; 938 | default: 939 | break; 940 | } 941 | } 942 | 943 | memset (&rb, 0, sizeof rb); 944 | rb.count = 0; 945 | rb.type = dev->type; 946 | rb.memory = dev->memtype; 947 | 948 | ret = ioctl (dev->v4l2_fd, VIDIOC_REQBUFS, &rb); 949 | if (ret < 0) { 950 | print ("Unable to release buffers: %s (%d).\n", strerror (errno), errno); 951 | return ret; 952 | } 953 | 954 | print ("%u buffers released.\n", dev->nbufs); 955 | 956 | free (dev->buffers); 957 | dev->nbufs = 0; 958 | dev->buffers = NULL; 959 | 960 | return 0; 961 | } 962 | 963 | 964 | static int 965 | video_queue_buffer (struct v4l2_device *dev, int index) 966 | { 967 | struct v4l2_buffer buf; 968 | struct v4l2_plane planes[MAX_PLANES]; 969 | int ret; 970 | unsigned int i; 971 | 972 | memset (&buf, 0, sizeof buf); 973 | memset (&planes, 0, sizeof planes); 974 | 975 | buf.index = index; 976 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //dev->type; 977 | buf.memory = V4L2_MEMORY_MMAP; //dev->memtype; 978 | //print (" video_queue_buf \n"); 979 | /*if (video_is_output (dev)) 980 | { 981 | buf.flags = dev->buffer_output_flags; 982 | if (dev->timestamp_type == V4L2_BUF_FLAG_TIMESTAMP_COPY) 983 | { 984 | struct timespec ts; 985 | 986 | clock_gettime (CLOCK_MONOTONIC, &ts); 987 | buf.timestamp.tv_sec = ts.tv_sec; 988 | buf.timestamp.tv_usec = ts.tv_nsec / 1000; 989 | } 990 | } 991 | 992 | if (video_is_mplane (dev)) 993 | { 994 | buf.m.planes = planes; 995 | buf.length = dev->num_planes; 996 | } 997 | 998 | if (dev->memtype == V4L2_MEMORY_USERPTR) 999 | { 1000 | print ("v4l2 memtype is V4L2_MEMORY_USERPTR\n"); 1001 | if (video_is_mplane (dev)) 1002 | { 1003 | for (i = 0; i < dev->num_planes; i++) 1004 | { 1005 | buf.m.planes[i].m.userptr = (unsigned long) 1006 | dev->buffers[index].mem[i]; 1007 | buf.m.planes[i].length = dev->buffers[index].size[i]; 1008 | } 1009 | } 1010 | else 1011 | { 1012 | buf.m.userptr = (unsigned long) dev->buffers[index].mem[0]; 1013 | buf.length = dev->buffers[index].size[0]; 1014 | } 1015 | } 1016 | 1017 | for (i = 0; i < dev->num_planes; i++) 1018 | { 1019 | if (video_is_output (dev)) 1020 | { 1021 | if (video_is_mplane (dev)) 1022 | buf.m.planes[i].bytesused = dev->patternsize[i]; 1023 | else 1024 | buf.bytesused = dev->patternsize[i]; 1025 | 1026 | memcpy (dev->buffers[buf.index].mem[i], dev->pattern[i], 1027 | dev->patternsize[i]); 1028 | } 1029 | else 1030 | { 1031 | if (fill & BUFFER_FILL_FRAME) 1032 | memset (dev->buffers[buf.index].mem[i], 0x55, 1033 | dev->buffers[index].size[i]); 1034 | if (fill & BUFFER_FILL_PADDING) 1035 | memset (dev->buffers[buf.index].mem[i] + 1036 | dev->buffers[index].size[i], 1037 | 0x55, dev->buffers[index].padding[i]); 1038 | } 1039 | } */ 1040 | 1041 | ret = ioctl (dev->v4l2_fd, VIDIOC_QBUF, &buf); 1042 | if (ret < 0) 1043 | print ("MMAL: Unable to queue buffer: %s (%d).\n", strerror (errno), 1044 | errno); 1045 | dev->qbuf_count++; 1046 | return ret; 1047 | } 1048 | 1049 | static int 1050 | video_enable (struct v4l2_device *dev, int enable) 1051 | { 1052 | int type = dev->type; 1053 | int ret; 1054 | 1055 | ret = 1056 | ioctl (dev->v4l2_fd, enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type); 1057 | if (ret < 0) { 1058 | print ("Unable to %s streaming: %s (%d).\n", 1059 | enable ? "start" : "stop", strerror (errno), errno); 1060 | return ret; 1061 | } 1062 | printf ("V4L2: %s\n", enable ? "VIDIOC_STREAMON" : "VIDIOC_STREAMOFF"); 1063 | return 0; 1064 | } 1065 | 1066 | 1067 | static unsigned int 1068 | video_buffer_bytes_used (struct v4l2_device *dev, struct v4l2_buffer *buf) 1069 | { 1070 | unsigned int bytesused = 0; 1071 | unsigned int i; 1072 | 1073 | if (!video_is_mplane (dev)) 1074 | return buf->bytesused; 1075 | 1076 | for (i = 0; i < dev->num_planes; i++) 1077 | bytesused += buf->m.planes[i].bytesused; 1078 | 1079 | return bytesused; 1080 | } 1081 | 1082 | static int 1083 | video_set_dv_timings (struct v4l2_device *dev) 1084 | { 1085 | struct v4l2_dv_timings timings; 1086 | v4l2_std_id std; 1087 | int ret; 1088 | 1089 | memset (&timings, 0, sizeof timings); 1090 | ret = ioctl (dev->v4l2_fd, VIDIOC_QUERY_DV_TIMINGS, &timings); 1091 | if (ret >= 0) { 1092 | printf ("QUERY_DV_TIMINGS returned %ux%u pixclk %llu\n", timings.bt.width, timings.bt.height, timings.bt.pixelclock); 1093 | //Can read DV timings, so set them. 1094 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_DV_TIMINGS, &timings); 1095 | if (ret < 0) { 1096 | print ("Failed to set DV timings\n"); 1097 | return -1; 1098 | } 1099 | else { 1100 | double tot_height, tot_width; 1101 | const struct v4l2_bt_timings *bt = &timings.bt; 1102 | 1103 | tot_height = bt->height + 1104 | bt->vfrontporch + bt->vsync + bt->vbackporch + 1105 | bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch; 1106 | tot_width = bt->width + bt->hfrontporch + bt->hsync + bt->hbackporch; 1107 | dev->fps = 1108 | (unsigned int) ((double) bt->pixelclock / (tot_width * tot_height)); 1109 | printf ("Framerate is %u\n", dev->fps); 1110 | } 1111 | } 1112 | else { 1113 | memset (&std, 0, sizeof std); 1114 | ret = ioctl (dev->v4l2_fd, VIDIOC_QUERYSTD, &std); 1115 | if (ret >= 0) { 1116 | //Can read standard, so set it. 1117 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_STD, &std); 1118 | if (ret < 0) { 1119 | print ("Failed to set standard\n"); 1120 | return -1; 1121 | } 1122 | else { 1123 | // SD video - assume 50Hz / 25fps 1124 | dev->fps = 25; 1125 | } 1126 | } 1127 | } 1128 | return 0; 1129 | } 1130 | 1131 | //process data here 'p 1132 | static int 1133 | v4l2_process_data (struct v4l2_device *dev) 1134 | { 1135 | struct v4l2_buffer vbuf; 1136 | struct v4l2_buffer ubuf; 1137 | struct v4l2_plane planes[MAX_PLANES]; 1138 | int queue_buffer = 1; 1139 | struct timespec start; 1140 | struct timeval last; 1141 | struct timespec ts; 1142 | const char *ts_type, *ts_source; 1143 | unsigned int size; 1144 | static unsigned int i = 0; 1145 | double bps; 1146 | double fps; 1147 | int ret; 1148 | int dropped_frames = 0; 1149 | static int frames = 0; 1150 | 1151 | 1152 | /* Return immediately if V4l2 streaming has not yet started. */ 1153 | if (!dev->is_streaming) 1154 | return 0; 1155 | 1156 | if (dev->udev->first_buffer_queued) { 1157 | if (dev->dqbuf_count >= dev->qbuf_count) { 1158 | return 0; 1159 | } 1160 | } 1161 | 1162 | clock_gettime (CLOCK_MONOTONIC, &start); 1163 | last.tv_sec = start.tv_sec; 1164 | last.tv_usec = start.tv_nsec / 1000; 1165 | 1166 | /* Dequeue spent buffer rom V4L2 domain. */ 1167 | CLEAR (vbuf); 1168 | CLEAR (planes); 1169 | vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //dev->type; 1170 | vbuf.memory = V4L2_MEMORY_MMAP; // dev->memtype 1171 | vbuf.length = MAX_PLANES; 1172 | vbuf.m.planes = planes; 1173 | 1174 | ret = ioctl (dev->v4l2_fd, VIDIOC_DQBUF, &vbuf); 1175 | if (ret < 0) { 1176 | print ("V4L2: VIDIOC_DQBUF vbuf failed %s (%d)\n", strerror (errno), errno); 1177 | return ret; 1178 | } 1179 | dev->dqbuf_count++; 1180 | dev->dq_ubuf_ok = false; 1181 | 1182 | // video_verify_buffer (dev, &vbuf); 1183 | 1184 | fps = (vbuf.timestamp.tv_sec - last.tv_sec) * 1000000 1185 | + vbuf.timestamp.tv_usec - last.tv_usec; 1186 | fps = fps ? 1000000.0 / fps : 0.0; 1187 | 1188 | clock_gettime (CLOCK_MONOTONIC, &ts); 1189 | get_ts_flags (vbuf.flags, &ts_type, &ts_source); 1190 | print 1191 | ("%u (%u) [%c] %s %u %u B %ld.%06ld %ld.%06ld %.3f fps ts %s/%s \n", 1192 | i, vbuf.index, (vbuf.flags & V4L2_BUF_FLAG_ERROR) ? 'E' : '-', 1193 | v4l2_field_name (vbuf.field), vbuf.sequence, 1194 | video_buffer_bytes_used (dev, &vbuf), vbuf.timestamp.tv_sec, 1195 | vbuf.timestamp.tv_usec, ts.tv_sec, ts.tv_nsec / 1000, fps, 1196 | ts_type, ts_source); 1197 | 1198 | last = vbuf.timestamp; 1199 | 1200 | //go ize += buf.bytesused; buf send to MMAL input_isp 1201 | 1202 | print ("Dequeueing buffer at V4L2 side = %d\n", vbuf.index); 1203 | 1204 | //get data from mmal 1205 | if (dev->mmal_pool) { 1206 | MMAL_BUFFER_HEADER_T *mmal; 1207 | MMAL_STATUS_T status; 1208 | 1209 | while ((mmal = mmal_queue_get (dev->mmal_pool->queue)) && !mmal->user_data) { 1210 | printf ("Discarding MMAL buffer %p as not mapped\n", mmal); 1211 | } 1212 | //print ("v4l2_process_data\n"); 1213 | if (!mmal) { 1214 | printf ("Failed to get MMAL buffer\n"); 1215 | } 1216 | else { 1217 | // Need to wait for MMAL to be finished with the buffer before returning to V4L2 // 1218 | queue_buffer = 0; 1219 | if (((struct mmal_buffer *) mmal->user_data)->idx != vbuf.index) { 1220 | printf 1221 | ("Mismatch in expected buffers. V4L2 gave idx %d, MMAL expecting %d\n", 1222 | vbuf.index, ((struct mmal_buffer *) mmal->user_data)->idx); 1223 | } 1224 | 1225 | mmal->length = vbuf.length; //Deliberately use length as MMAL wants the padding 1226 | 1227 | print("mmal data %d length %d\n",((struct mmal_buffer *) mmal->user_data)->idx, mmal->length); 1228 | if (!dev->starttime.tv_sec) 1229 | dev->starttime = vbuf.timestamp; 1230 | 1231 | struct timeval pts; 1232 | timersub (&vbuf.timestamp, &dev->starttime, &pts); 1233 | //MMAL PTS is in usecs, so convert from struct timeval 1234 | mmal->pts = (pts.tv_sec * 1000000) + pts.tv_usec; 1235 | if (mmal->pts > (dev->lastpts + dev->frame_time_usec + 1000)) { 1236 | print ("DROPPED FRAME - %lld and %lld, delta %lld\n", 1237 | dev->lastpts, mmal->pts, mmal->pts - dev->lastpts); 1238 | dropped_frames++; 1239 | } 1240 | dev->lastpts = mmal->pts; 1241 | 1242 | mmal->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; 1243 | //print ("about to send isp input buffer to mmal\n"); //mmal->pts = buf.timestamp; 1244 | status = mmal_port_send_buffer (dev->isp->input[0], mmal); 1245 | if (status != MMAL_SUCCESS) 1246 | print ("mmal_port_send_buffer failed %d\n", status); 1247 | 1248 | } 1249 | } 1250 | i++; 1251 | //video_queue_buffer (dev, vbuf.index, BUFFER_FILL_NONE ); 1252 | return 0; 1253 | } 1254 | 1255 | /* --------------------------------------------------------------------------- 1256 | * V4L2 generic stuff 1257 | */ 1258 | 1259 | static int 1260 | v4l2_get_format (struct v4l2_device *dev) 1261 | { 1262 | struct v4l2_format fmt; 1263 | int ret; 1264 | CLEAR (fmt); 1265 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1266 | 1267 | ret = ioctl (dev->v4l2_fd, VIDIOC_G_FMT, &fmt); 1268 | if (ret < 0) { 1269 | printf ("V4L2: Unable to get format: %s (%d).\n", strerror (errno), errno); 1270 | return ret; 1271 | } 1272 | 1273 | printf ("V4L2: Getting current format: %c%c%c%c %ux%u\n", 1274 | pixfmtstr (fmt.fmt.pix.pixelformat), 1275 | fmt.fmt.pix.width, fmt.fmt.pix.height); 1276 | 1277 | return 0; 1278 | } 1279 | 1280 | static int 1281 | v4l2_set_format (struct v4l2_device *dev, struct v4l2_format *fmt) 1282 | { 1283 | int ret; 1284 | printf ("V4L2: Want setting format to: %c%c%c%c %ux%u\n", 1285 | pixfmtstr (fmt->fmt.pix.pixelformat), 1286 | fmt->fmt.pix.width, fmt->fmt.pix.height); 1287 | 1288 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_FMT, fmt); 1289 | if (ret < 0) { 1290 | printf ("V4L2_set_format: Unable to set format %s (%d).\n", strerror (errno), errno); 1291 | return ret; 1292 | } 1293 | 1294 | printf ("V4L2: Setting format to: %c%c%c%c %ux%u\n", 1295 | pixfmtstr (fmt->fmt.pix.pixelformat), 1296 | fmt->fmt.pix.width, fmt->fmt.pix.height); 1297 | 1298 | return 0; 1299 | } 1300 | 1301 | static int 1302 | v4l2_set_ctrl (struct v4l2_device *dev, int new_val, int ctrl) 1303 | { 1304 | struct v4l2_queryctrl queryctrl; 1305 | struct v4l2_control control; 1306 | int ret; 1307 | 1308 | CLEAR (queryctrl); 1309 | 1310 | switch (ctrl) { 1311 | case V4L2_CID_BRIGHTNESS: 1312 | queryctrl.id = V4L2_CID_BRIGHTNESS; 1313 | ret = ioctl (dev->v4l2_fd, VIDIOC_QUERYCTRL, &queryctrl); 1314 | //ret = 0; 1315 | if (-1 == ret) { 1316 | if (errno != EINVAL) 1317 | printf ("V4L2: VIDIOC_QUERYCTRL" 1318 | " failed: %s (%d).\n", strerror (errno), errno); 1319 | else 1320 | printf ("V4L2_CID_BRIGHTNESS is not" 1321 | " supported: %s (%d).\n", strerror (errno), errno); 1322 | 1323 | return ret; 1324 | } 1325 | else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { 1326 | printf ("V4L2_CID_BRIGHTNESS is not supported.\n"); 1327 | ret = -EINVAL; 1328 | return ret; 1329 | } 1330 | else { 1331 | CLEAR (control); 1332 | control.id = V4L2_CID_BRIGHTNESS; 1333 | control.value = new_val; 1334 | 1335 | ret = ioctl (dev->v4l2_fd, VIDIOC_S_CTRL, &control); 1336 | if (-1 == ret) { 1337 | printf ("V4L2: VIDIOC_S_CTRL failed: %s (%d).\n", 1338 | strerror (errno), errno); 1339 | return ret; 1340 | } 1341 | } 1342 | printf ("V4L2: Brightness control changed to value = 0x%x\n", new_val); 1343 | break; 1344 | 1345 | default: 1346 | printf ("V4L2: ctrl not supported %d\n", ctrl); 1347 | /* TODO: We don't support any other controls. */ 1348 | return -EINVAL; 1349 | } 1350 | 1351 | return 0; 1352 | } 1353 | 1354 | 1355 | static int 1356 | v4l2_open (struct v4l2_device **v4l2, char *devname, struct v4l2_format *s_fmt) 1357 | { 1358 | struct v4l2_device *dev; 1359 | struct v4l2_capability cap; 1360 | int fd; 1361 | int ret = -EINVAL; 1362 | 1363 | fd = open (devname, O_RDWR | O_NONBLOCK, 0); 1364 | if (fd == -1) { 1365 | printf ("V4L2: device open failed: %s (%d).\n", strerror (errno), errno); 1366 | return ret; 1367 | } 1368 | 1369 | ret = ioctl (fd, VIDIOC_QUERYCAP, &cap); 1370 | if (ret < 0) { 1371 | printf ("V4L2: VIDIOC_QUERYCAP failed: %s (%d).\n", 1372 | strerror (errno), errno); 1373 | goto err; 1374 | } 1375 | 1376 | if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 1377 | printf ("V4L2: %s is no video capture device\n", devname); 1378 | goto err; 1379 | } 1380 | 1381 | if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 1382 | printf ("V4L2: %s does not support streaming i/o\n", devname); 1383 | goto err; 1384 | } 1385 | 1386 | dev = calloc (1, sizeof *dev); 1387 | if (dev == NULL) { 1388 | ret = -ENOMEM; 1389 | goto err; 1390 | } 1391 | 1392 | printf ("V4L2 device is %s on bus %s\n", cap.card, cap.bus_info); 1393 | 1394 | dev->v4l2_fd = fd; 1395 | //dev->fd = fd; 1396 | //print ("opening %d\n",dev->v4l2_fd); 1397 | /* Get the default image format supported. */ 1398 | ret = v4l2_get_format (dev); 1399 | if (ret < 0) 1400 | goto err_free; 1401 | 1402 | /* 1403 | * Set the desired image format. 1404 | * Note: VIDIOC_S_FMT may change width and height. 1405 | */ 1406 | ret = v4l2_set_format (dev, s_fmt); 1407 | if (ret < 0) 1408 | goto err_free; 1409 | 1410 | /* Get the changed image format. */ 1411 | ret = v4l2_get_format (dev); 1412 | if (ret < 0) 1413 | goto err_free; 1414 | 1415 | printf ("v4l2 open succeeded, file descriptor = %d\n", fd); 1416 | 1417 | *v4l2 = dev; 1418 | 1419 | return 0; 1420 | 1421 | err_free: 1422 | free (dev); 1423 | err: 1424 | close (fd); 1425 | 1426 | return ret; 1427 | } 1428 | 1429 | static void 1430 | v4l2_close (struct v4l2_device *dev) 1431 | { 1432 | close (dev->v4l2_fd); 1433 | free (dev); 1434 | } 1435 | 1436 | 1437 | /* --------------------------------------------------------------------------- 1438 | * UVC generic stuff 1439 | */ 1440 | 1441 | static int 1442 | uvc_video_set_format (struct uvc_device *dev) 1443 | { 1444 | struct v4l2_format fmt; 1445 | int ret; 1446 | 1447 | CLEAR (fmt); 1448 | 1449 | fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1450 | fmt.fmt.pix.width = dev->width; 1451 | fmt.fmt.pix.height = dev->height; 1452 | fmt.fmt.pix.pixelformat = dev->fcc; 1453 | 1454 | fmt.fmt.pix.field = V4L2_FIELD_ANY; 1455 | if (dev->fcc == V4L2_PIX_FMT_MJPEG) 1456 | fmt.fmt.pix.sizeimage = dev->imgsize * 1; 1457 | 1458 | ret = ioctl (dev->uvc_fd, VIDIOC_S_FMT, &fmt); 1459 | if (ret < 0) { 1460 | printf ("UVC: Unable to set %c%c%c%c format %s (%d).\n", pixfmtstr (dev->fcc), strerror (errno), errno); 1461 | return ret; 1462 | } 1463 | printf ("UVC: Setting format to: %c%c%c%c %ux%u\n", pixfmtstr (dev->fcc), dev->width, dev->height); 1464 | 1465 | return 0; 1466 | } 1467 | 1468 | static int 1469 | uvc_video_stream (struct uvc_device *dev, int enable) 1470 | { 1471 | int type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1472 | int ret; 1473 | printf ("UVC: uvc_video_stream, enabled %d\n", enable); 1474 | if (!enable) { 1475 | ret = ioctl (dev->uvc_fd, VIDIOC_STREAMOFF, &type); 1476 | if (ret < 0) { 1477 | printf ("UVC: VIDIOC_STREAMOFF failed: %s (%d).\n", strerror (errno), errno); 1478 | return ret; 1479 | } 1480 | 1481 | printf ("UVC: Stopping video stream.\n"); 1482 | 1483 | return 0; 1484 | } 1485 | 1486 | ret = ioctl (dev->uvc_fd, VIDIOC_STREAMON, &type); 1487 | if (ret < 0) { 1488 | printf ("UVC: Unable to start streaming %s (%d).\n", strerror (errno), errno); 1489 | return ret; 1490 | } 1491 | 1492 | printf ("UVC: Starting video stream.\n"); 1493 | dev->uvc_streamon = 1; 1494 | dev->uvc_shutdown_requested = 0; 1495 | 1496 | return 0; 1497 | } 1498 | 1499 | /*static int 1500 | uvc_uninit_device (struct uvc_device *dev) 1501 | { 1502 | //unsigned int i; 1503 | //int ret; 1504 | 1505 | switch (dev->memtype) { 1506 | 1507 | case V4L2_MEMORY_USERPTR: 1508 | default: 1509 | break; 1510 | } 1511 | 1512 | return 0; 1513 | }redundant */ 1514 | 1515 | static int 1516 | uvc_video_open (struct uvc_device **uvc, char *devname) 1517 | { 1518 | struct uvc_device *dev; 1519 | struct v4l2_capability cap; 1520 | int fd; 1521 | int ret = -EINVAL; 1522 | 1523 | fd = open (devname, O_RDWR | O_NONBLOCK); 1524 | if (fd == -1) { 1525 | printf ("UVC: device open failed: %s (%d).\n", strerror (errno), errno); 1526 | return ret; 1527 | } 1528 | 1529 | ret = ioctl (fd, VIDIOC_QUERYCAP, &cap); 1530 | if (ret < 0) { 1531 | printf ("UVC: unable to query uvc device: %s (%d)\n", strerror (errno), errno); 1532 | goto err; 1533 | } 1534 | 1535 | if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { 1536 | printf ("UVC: %s is no video output device\n", devname); 1537 | goto err; 1538 | } 1539 | 1540 | dev = calloc (1, sizeof *dev); 1541 | if (dev == NULL) { 1542 | ret = -ENOMEM; 1543 | goto err; 1544 | } 1545 | 1546 | printf ("uvc device is %s on bus %s\n", cap.card, cap.bus_info); 1547 | printf ("uvc open succeeded, file descriptor = %d\n", fd); 1548 | 1549 | dev->uvc_fd = fd; 1550 | *uvc = dev; 1551 | 1552 | return 0; 1553 | 1554 | err: 1555 | close (fd); 1556 | return ret; 1557 | } 1558 | 1559 | static void 1560 | uvc_video_close (struct uvc_device *dev) 1561 | { 1562 | close (dev->uvc_fd); 1563 | free (dev->imgdata); 1564 | free (dev); 1565 | } 1566 | 1567 | /* --------------------------------------------------------------------------- 1568 | * UVC streaming related 'u 1569 | * I think this is called when the UVC buffer has been processed 1570 | */ 1571 | 1572 | static int 1573 | uvc_video_process (struct uvc_device *dev) 1574 | { 1575 | struct v4l2_buffer ubuf; 1576 | //struct v4l2_buffer vbuf; 1577 | static unsigned int i; 1578 | int ret; 1579 | /* 133ms per call 7.5fps!! 1580 | 1581 | */ 1582 | /* 1583 | * Return immediately if UVC video output device has not started 1584 | * streaming yet. 1585 | */ 1586 | if (!dev->is_streaming) { 1587 | return 0; 1588 | } 1589 | 1590 | /* Prepare a v4l2 buffer to be dequeued from UVC domain. */ 1591 | CLEAR (ubuf); 1592 | 1593 | ubuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1594 | ubuf.memory = V4L2_MEMORY_USERPTR; 1595 | 1596 | 1597 | /* UVC - V4L2 integrated path. */ 1598 | 1599 | /* 1600 | * Return immediately if V4L2 video capture device has not 1601 | * started streaming yet or if QBUF was not called even once on 1602 | * the UVC side. 1603 | */ 1604 | if (!dev->vdev->is_streaming || !dev->first_buffer_queued) { 1605 | print ("UVC: not streaming\n"); 1606 | return 0; 1607 | } 1608 | 1609 | //if(dev->vdev->dq_ubuf_ok==false){ return 0;} 1610 | /* 1611 | * Do not dequeue buffers from UVC side until there are atleast 1612 | * 2 buffers available at UVC domain. 1613 | */ 1614 | //print ("%d %d\n", dev->dqbuf_count,dev->qbuf_count); 1615 | /* if (!dev->uvc_shutdown_requested){ 1616 | if ((dev->dqbuf_count + 1 ) >= dev->qbuf_count){ 1617 | //print("UVC: not enough buffers ready\n"); 1618 | return 0; 1619 | } 1620 | } */ 1621 | if (!dev->uvc_streamon) 1622 | { 1623 | printf("uvc not streaming\n"); 1624 | return 0; 1625 | } 1626 | /* Dequeue the spent buffer from UVC domain */ 1627 | ret = ioctl (dev->uvc_fd, VIDIOC_DQBUF, &ubuf); 1628 | if (ret < 0) { 1629 | print ("UVC: unable to dequeue ubuf %s (%d)\n", strerror (errno), errno); 1630 | return ret; 1631 | } 1632 | else { 1633 | time_me("uvc_video_process", dev->vdev); 1634 | print ("UVC: dequeue ubuf okay\n"); 1635 | dev->vdev->dq_ubuf_ok = true; 1636 | } 1637 | 1638 | dev->dqbuf_count++; 1639 | 1640 | #ifdef ENABLE_BUFFER_DEBUG 1641 | printf ("DeQueued buffer at UVC side=%d\n", ubuf.index); 1642 | #endif 1643 | 1644 | /* 1645 | * If the dequeued buffer was marked with state ERROR by the 1646 | * underlying UVC driver gadget, do not queue the same to V4l2 1647 | * and wait for a STREAMOFF event on UVC side corresponding to 1648 | * set_alt(0). So, now all buffers pending at UVC end will be 1649 | * dequeued one-by-one and we will enter a state where we once 1650 | * again wait for a set_alt(1) command from the USB host side. 1651 | */ 1652 | 1653 | if (ubuf.flags & V4L2_BUF_FLAG_ERROR) { 1654 | dev->uvc_shutdown_requested = 1; 1655 | printf ("UVC: Possible USB shutdown requested from " 1656 | "Host, seen during VIDIOC_DQBUF %d\n", ubuf.flags); 1657 | return 0; 1658 | } 1659 | 1660 | /* Queue the buffer to V4L2 domain */ 1661 | //ret = video_queue_buffer (dev->vdev, ubuf.index, BUFFER_FILL_NONE); 1662 | /*CLEAR (vbuf); 1663 | 1664 | vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1665 | vbuf.memory = V4L2_MEMORY_MMAP; 1666 | vbuf.index = ubuf.index; 1667 | 1668 | ret = ioctl (dev->vdev->v4l2_fd, VIDIOC_QBUF, &vbuf); */ 1669 | /* if (ret < 0) { 1670 | printf ("V4L2: Unable to queue buffer: %s (%d).\n", 1671 | strerror (errno), errno); 1672 | return ret; 1673 | } 1674 | dev->vdev->qbuf_count++; 1675 | */ 1676 | /* 1677 | #ifdef ENABLE_BUFFER_DEBUG 1678 | printf ("ReQueueing buffer at V4L2 side = %d\n", vbuf.index); 1679 | #endif 1680 | */ 1681 | i++; 1682 | return 0; 1683 | } 1684 | 1685 | 1686 | 1687 | static int 1688 | uvc_video_reqbufs_userptr (struct uvc_device *dev, int nbufs) 1689 | { 1690 | struct v4l2_requestbuffers rb; 1691 | //unsigned int i, j, bpl, payload_size; 1692 | int ret; 1693 | 1694 | CLEAR (rb); 1695 | 1696 | rb.count = nbufs; 1697 | rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1698 | rb.memory = V4L2_MEMORY_USERPTR; 1699 | 1700 | ret = ioctl (dev->uvc_fd, VIDIOC_REQBUFS, &rb); 1701 | if (ret < 0) { 1702 | if (ret == -EINVAL) 1703 | printf ("UVC: does not support user pointer i/o\n"); 1704 | else 1705 | printf ("UVC: VIDIOC_REQBUFS error %s (%d).\n", 1706 | strerror (errno), errno); 1707 | goto err; 1708 | } 1709 | else { 1710 | printf ("UVC uvc_video_reqbufs_userptr okay %c%c%c%c\n", pixfmtstr(dev->fcc)); 1711 | } 1712 | 1713 | if (!rb.count) 1714 | return 0; 1715 | 1716 | dev->nbufs = rb.count; 1717 | printf ("UVC: %u buffers allocated.\n", rb.count); 1718 | 1719 | return 0; 1720 | 1721 | err: 1722 | printf ("UVC uvc_video_reqbufs_userptr failed\n"); 1723 | return ret; 1724 | 1725 | } 1726 | 1727 | 1728 | /* 1729 | * This function is called in response to either: 1730 | * - A SET_ALT(interface 1, alt setting 1) command from USB host, 1731 | * if the UVC gadget supports an ISOCHRONOUS video streaming endpoint 1732 | * or, 1733 | * 1734 | * - A UVC_VS_COMMIT_CONTROL command from USB host, if the UVC gadget 1735 | * supports a BULK type video streaming endpoint. 1736 | */ 1737 | static int 1738 | uvc_handle_streamon_event (struct uvc_device *dev) 1739 | { 1740 | int ret; 1741 | 1742 | printf ("uvc_handle_streamon_event\n"); 1743 | /*if( V4L2_MEMORY_USERPTR == dev->vdev->memtype){ 1744 | print("dev->vdev->io io method userptr\n"); 1745 | } */ 1746 | ret = uvc_video_reqbufs_userptr (dev, dev->nbufs); 1747 | if (ret < 0) 1748 | goto err; 1749 | 1750 | 1751 | printf("finished uvc_video_reqbuf\n"); 1752 | ret = video_queue_all_buffers (dev->vdev);//, BUFFER_FILL_NONE); 1753 | if (ret < 0) 1754 | goto err; 1755 | 1756 | 1757 | /* Start V4L2 capturing now. */ 1758 | //ret = v4l2_start_capturing (dev->vdev); 1759 | ret = video_enable (dev->vdev, 1); 1760 | if (ret < 0) 1761 | goto err; 1762 | 1763 | dev->vdev->is_streaming = 1; 1764 | 1765 | //start mmal 1766 | enable_mmal_input(dev->vdev); 1767 | return 0; 1768 | 1769 | err: 1770 | printf ("uvc_handle_streamon_eventi error\n"); 1771 | return ret; 1772 | } 1773 | 1774 | 1775 | 1776 | /* --------------------------------------------------------------------------- 1777 | * UVC Request processing 1778 | */ 1779 | 1780 | static void 1781 | uvc_fill_streaming_control (struct uvc_device *dev, 1782 | struct uvc_streaming_control *ctrl, 1783 | int iframe, int iformat) 1784 | { 1785 | const struct uvc_format_info *format; 1786 | const struct uvc_frame_info *frame; 1787 | unsigned int nframes; 1788 | 1789 | if (iformat < 0) 1790 | iformat = ARRAY_SIZE (uvc_formats) + iformat; 1791 | if (iformat < 0 || iformat >= (int) ARRAY_SIZE (uvc_formats)) 1792 | return; 1793 | format = &uvc_formats[iformat]; 1794 | 1795 | nframes = 0; 1796 | while (format->frames[nframes].width != 0) 1797 | ++nframes; 1798 | 1799 | if (iframe < 0) 1800 | iframe = nframes + iframe; 1801 | if (iframe < 0 || iframe >= (int) nframes) 1802 | return; 1803 | frame = &format->frames[iframe]; 1804 | 1805 | memset (ctrl, 0, sizeof *ctrl); 1806 | 1807 | ctrl->bmHint = 1; // 1 keep dwFrameInterval constant fps const 1808 | ctrl->bFormatIndex = iformat + 1; 1809 | ctrl->bFrameIndex = iframe + 1; 1810 | ctrl->dwFrameInterval = frame->intervals[0]; 1811 | //ctrl->wDelay = 10; 1812 | //ctrl->dwClockFrequency=1; 1813 | switch (format->fcc) { 1814 | case V4L2_PIX_FMT_YUYV: 1815 | ctrl->dwMaxVideoFrameSize = frame->width * frame->height * 2; 1816 | break; 1817 | case V4L2_PIX_FMT_MJPEG: 1818 | ctrl->dwMaxVideoFrameSize = frame->width * frame->height * 1; // dev->imgsize; 1819 | break; 1820 | } 1821 | printf 1822 | ("bmHint %d, bFormatIndex %d, bFrameIndex %d, dwFrameInterval %d, dwMaxVideoFramSize %d\n", 1823 | ctrl->bmHint, ctrl->bFormatIndex, ctrl->bFrameIndex, 1824 | ctrl->dwFrameInterval, ctrl->dwMaxVideoFrameSize); 1825 | printf("%c%c%c%c\n", pixfmtstr(format->fcc)); 1826 | 1827 | /* TODO: the UVC maxpayload transfer size should be filled 1828 | * by the driver. 1829 | */ 1830 | if (!dev->bulk) { 1831 | printf ("maxpkt %d, mult %d, burst %d\n", dev->maxpkt, dev->mult, dev->burst); 1832 | ctrl->dwMaxPayloadTransferSize = 1833 | (dev->maxpkt) * (dev->mult + 1) * (dev->burst + 1); 1834 | 1835 | } 1836 | else { 1837 | ctrl->dwMaxPayloadTransferSize = ctrl->dwMaxVideoFrameSize; 1838 | } 1839 | ctrl->bmFramingInfo = 3; 1840 | ctrl->bPreferedVersion = 1; 1841 | ctrl->bMaxVersion = 1; 1842 | } 1843 | 1844 | static void 1845 | uvc_events_process_standard (struct uvc_device *dev, 1846 | struct usb_ctrlrequest *ctrl, 1847 | struct uvc_request_data *resp) 1848 | { 1849 | printf ("standard request\n"); 1850 | (void) dev; 1851 | (void) ctrl; 1852 | (void) resp; 1853 | } 1854 | 1855 | static void 1856 | uvc_events_process_control (struct uvc_device *dev, uint8_t req, 1857 | uint8_t cs, uint8_t entity_id, 1858 | uint8_t len, struct uvc_request_data *resp) 1859 | { 1860 | switch (entity_id) { 1861 | case 0: 1862 | switch (cs) { 1863 | case UVC_VC_REQUEST_ERROR_CODE_CONTROL: 1864 | /* Send the request error code last prepared. */ 1865 | resp->data[0] = dev->request_error_code.data[0]; 1866 | resp->length = dev->request_error_code.length; 1867 | break; 1868 | 1869 | default: 1870 | /* 1871 | * If we were not supposed to handle this 1872 | * 'cs', prepare an error code response. 1873 | */ 1874 | dev->request_error_code.data[0] = 0x06; 1875 | dev->request_error_code.length = 1; 1876 | break; 1877 | } 1878 | break; 1879 | 1880 | /* Camera terminal unit 'UVC_VC_INPUT_TERMINAL'. */ 1881 | case 1: 1882 | switch (cs) { 1883 | /* 1884 | * We support only 'UVC_CT_AE_MODE_CONTROL' for CAMERA 1885 | * terminal, as our bmControls[0] = 2 for CT. Also we 1886 | * support only auto exposure. 1887 | */ 1888 | case UVC_CT_AE_MODE_CONTROL: 1889 | switch (req) { 1890 | case UVC_SET_CUR: 1891 | printf ("UVC_CT_AE_MODE_CONTROL\n"); 1892 | /* Incase of auto exposure, attempts to 1893 | * programmatically set the auto-adjusted 1894 | * controls are ignored. 1895 | */ 1896 | resp->data[0] = 0x01; 1897 | resp->length = 1; 1898 | /* 1899 | * For every successfully handled control 1900 | * request set the request error code to no 1901 | * error. 1902 | */ 1903 | dev->request_error_code.data[0] = 0x00; 1904 | dev->request_error_code.length = 1; 1905 | break; 1906 | 1907 | case UVC_GET_INFO: 1908 | /* 1909 | * TODO: We support Set and Get requests, but 1910 | * don't support async updates on an video 1911 | * status (interrupt) endpoint as of 1912 | * now. 1913 | */ 1914 | resp->data[0] = 0x03; 1915 | resp->length = 1; 1916 | /* 1917 | * For every successfully handled control 1918 | * request set the request error code to no 1919 | * error. 1920 | */ 1921 | dev->request_error_code.data[0] = 0x00; 1922 | dev->request_error_code.length = 1; 1923 | break; 1924 | 1925 | case UVC_GET_CUR: 1926 | case UVC_GET_DEF: 1927 | case UVC_GET_RES: 1928 | /* Auto Mode â?? auto Exposure Time, auto Iris. */ 1929 | resp->data[0] = 0x02; 1930 | resp->length = 1; 1931 | /* 1932 | * For every successfully handled control 1933 | * request set the request error code to no 1934 | * error. 1935 | */ 1936 | dev->request_error_code.data[0] = 0x00; 1937 | dev->request_error_code.length = 1; 1938 | break; 1939 | default: 1940 | /* 1941 | * We don't support this control, so STALL the 1942 | * control ep. 1943 | */ 1944 | resp->length = -EL2HLT; 1945 | /* 1946 | * For every unsupported control request 1947 | * set the request error code to appropriate 1948 | * value. 1949 | */ 1950 | dev->request_error_code.data[0] = 0x07; 1951 | dev->request_error_code.length = 1; 1952 | break; 1953 | } 1954 | break; 1955 | 1956 | default: 1957 | /* 1958 | * We don't support this control, so STALL the control 1959 | * ep. 1960 | */ 1961 | resp->length = -EL2HLT; 1962 | /* 1963 | * If we were not supposed to handle this 1964 | * 'cs', prepare a Request Error Code response. 1965 | */ 1966 | dev->request_error_code.data[0] = 0x06; 1967 | dev->request_error_code.length = 1; 1968 | break; 1969 | } 1970 | break; 1971 | 1972 | /* processing unit 'UVC_VC_PROCESSING_UNIT' */ 1973 | case 2: 1974 | switch (cs) { 1975 | /* 1976 | * We support only 'UVC_PU_BRIGHTNESS_CONTROL' for Processing 1977 | * Unit, as our bmControls[0] = 1 for PU. 1978 | */ 1979 | case UVC_PU_BRIGHTNESS_CONTROL: 1980 | switch (req) { 1981 | case UVC_SET_CUR: 1982 | printf ("UVC_SET_CUR BRIGHTNESS"); 1983 | resp->data[0] = 0x0; 1984 | resp->length = len; 1985 | /* 1986 | * For every successfully handled control 1987 | * request set the request error code to no 1988 | * error 1989 | */ 1990 | dev->request_error_code.data[0] = 0x00; 1991 | dev->request_error_code.length = 1; 1992 | break; 1993 | case UVC_GET_MIN: 1994 | printf ("UVC_GET_MIN BRIGHTNESS"); 1995 | resp->data[0] = PU_BRIGHTNESS_MIN_VAL; 1996 | resp->length = 2; 1997 | /* 1998 | * For every successfully handled control 1999 | * request set the request error code to no 2000 | * error 2001 | */ 2002 | dev->request_error_code.data[0] = 0x00; 2003 | dev->request_error_code.length = 1; 2004 | break; 2005 | case UVC_GET_MAX: 2006 | printf ("UVC_GET_MAX BR"); 2007 | resp->data[0] = PU_BRIGHTNESS_MAX_VAL; 2008 | resp->length = 2; 2009 | /* 2010 | * For every successfully handled control 2011 | * request set the request error code to no 2012 | * error 2013 | */ 2014 | dev->request_error_code.data[0] = 0x00; 2015 | dev->request_error_code.length = 1; 2016 | break; 2017 | case UVC_GET_CUR: 2018 | printf ("UVC_GET_CUR BR"); 2019 | resp->length = 2; 2020 | memcpy (&resp->data[0], &dev->brightness_val, resp->length); 2021 | /* 2022 | * For every successfully handled control 2023 | * request set the request error code to no 2024 | * error 2025 | */ 2026 | dev->request_error_code.data[0] = 0x00; 2027 | dev->request_error_code.length = 1; 2028 | break; 2029 | case UVC_GET_INFO: 2030 | printf ("UVC_GET_INFO BR"); 2031 | /* 2032 | * We support Set and Get requests and don't 2033 | * support async updates on an interrupt endpt 2034 | */ 2035 | resp->data[0] = 0x03; 2036 | resp->length = 1; 2037 | /* 2038 | * For every successfully handled control 2039 | * request, set the request error code to no 2040 | * error. 2041 | */ 2042 | dev->request_error_code.data[0] = 0x00; 2043 | dev->request_error_code.length = 1; 2044 | break; 2045 | case UVC_GET_DEF: 2046 | printf ("UVC_GET_DEF BR"); 2047 | resp->data[0] = PU_BRIGHTNESS_DEFAULT_VAL; 2048 | resp->length = 2; 2049 | /* 2050 | * For every successfully handled control 2051 | * request, set the request error code to no 2052 | * error. 2053 | */ 2054 | dev->request_error_code.data[0] = 0x00; 2055 | dev->request_error_code.length = 1; 2056 | break; 2057 | case UVC_GET_RES: 2058 | printf ("UVC_GET_RES BR"); 2059 | resp->data[0] = PU_BRIGHTNESS_STEP_SIZE; 2060 | resp->length = 2; 2061 | /* 2062 | * For every successfully handled control 2063 | * request, set the request error code to no 2064 | * error. 2065 | */ 2066 | dev->request_error_code.data[0] = 0x00; 2067 | dev->request_error_code.length = 1; 2068 | break; 2069 | default: 2070 | printf ("brightness unknown\n"); 2071 | /* 2072 | * We don't support this control, so STALL the 2073 | * default control ep. 2074 | */ 2075 | resp->length = -EL2HLT; 2076 | /* 2077 | * For every unsupported control request 2078 | * set the request error code to appropriate 2079 | * code. 2080 | */ 2081 | dev->request_error_code.data[0] = 0x07; 2082 | dev->request_error_code.length = 1; 2083 | break; 2084 | } 2085 | break; 2086 | 2087 | default: 2088 | /* 2089 | * We don't support this control, so STALL the control 2090 | * ep. 2091 | */ 2092 | resp->length = -EL2HLT; 2093 | /* 2094 | * If we were not supposed to handle this 2095 | * 'cs', prepare a Request Error Code response. 2096 | */ 2097 | dev->request_error_code.data[0] = 0x06; 2098 | dev->request_error_code.length = 1; 2099 | break; 2100 | } 2101 | 2102 | break; 2103 | 2104 | default: 2105 | /* 2106 | * If we were not supposed to handle this 2107 | * 'cs', prepare a Request Error Code response. 2108 | */ 2109 | dev->request_error_code.data[0] = 0x06; 2110 | dev->request_error_code.length = 1; 2111 | break; 2112 | 2113 | } 2114 | 2115 | printf ("control request (req %02x cs %02x)\n", req, cs); 2116 | } 2117 | 2118 | static void 2119 | uvc_events_process_streaming (struct uvc_device *dev, uint8_t req, uint8_t cs, 2120 | struct uvc_request_data *resp) 2121 | { 2122 | struct uvc_streaming_control *ctrl; 2123 | 2124 | printf ("streaming request (req %02x cs %02x)\n", req, cs); 2125 | 2126 | if (cs != UVC_VS_PROBE_CONTROL && cs != UVC_VS_COMMIT_CONTROL) { 2127 | printf ("cs not UVC_VS_PROBE_CONTROL or UVC_VS_COMMIT_CONTROL %d\n", cs); 2128 | return; 2129 | } 2130 | 2131 | ctrl = (struct uvc_streaming_control *) &resp->data; 2132 | resp->length = sizeof *ctrl; 2133 | 2134 | switch (req) { 2135 | case UVC_SET_CUR: 2136 | dev->control = cs; 2137 | resp->length = 34; 2138 | break; 2139 | 2140 | case UVC_GET_CUR: 2141 | if (cs == UVC_VS_PROBE_CONTROL) 2142 | memcpy (ctrl, &dev->probe, sizeof *ctrl); 2143 | else 2144 | memcpy (ctrl, &dev->commit, sizeof *ctrl); 2145 | break; 2146 | 2147 | case UVC_GET_MIN: 2148 | case UVC_GET_MAX: 2149 | case UVC_GET_DEF: 2150 | uvc_fill_streaming_control (dev, ctrl, req == UVC_GET_MAX ? -1 : 0, req == UVC_GET_MAX ? -1 : 0); 2151 | break; 2152 | 2153 | case UVC_GET_RES: 2154 | CLEAR (ctrl); 2155 | break; 2156 | 2157 | case UVC_GET_LEN: 2158 | resp->data[0] = 0x00; 2159 | resp->data[1] = 0x22; 2160 | resp->length = 2; 2161 | break; 2162 | 2163 | case UVC_GET_INFO: 2164 | resp->data[0] = 0x03; 2165 | resp->length = 1; 2166 | break; 2167 | default: 2168 | printf ("unhandled streaming request (req %02x cs %02x)\n", req, cs); 2169 | break; 2170 | } 2171 | } 2172 | 2173 | static void 2174 | uvc_events_process_class (struct uvc_device *dev, 2175 | struct usb_ctrlrequest *ctrl, 2176 | struct uvc_request_data *resp) 2177 | { 2178 | if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) 2179 | return; 2180 | 2181 | switch (ctrl->wIndex & 0xff) { 2182 | case UVC_INTF_CONTROL: 2183 | uvc_events_process_control (dev, ctrl->bRequest, 2184 | ctrl->wValue >> 8, 2185 | ctrl->wIndex >> 8, ctrl->wLength, resp); 2186 | break; 2187 | 2188 | case UVC_INTF_STREAMING: 2189 | uvc_events_process_streaming (dev, ctrl->bRequest, 2190 | ctrl->wValue >> 8, resp); 2191 | break; 2192 | 2193 | default: 2194 | break; 2195 | } 2196 | } 2197 | 2198 | static void 2199 | uvc_events_process_setup (struct uvc_device *dev, 2200 | struct usb_ctrlrequest *ctrl, 2201 | struct uvc_request_data *resp) 2202 | { 2203 | dev->control = 0; 2204 | 2205 | #ifdef ENABLE_USB_REQUEST_DEBUG 2206 | printf ("\nbRequestType %02x bRequest %02x wValue %04x wIndex %04x " 2207 | "wLength %04x\n", ctrl->bRequestType, ctrl->bRequest, 2208 | ctrl->wValue, ctrl->wIndex, ctrl->wLength); 2209 | #endif 2210 | 2211 | switch (ctrl->bRequestType & USB_TYPE_MASK) { 2212 | case USB_TYPE_STANDARD: 2213 | uvc_events_process_standard (dev, ctrl, resp); 2214 | break; 2215 | 2216 | case USB_TYPE_CLASS: 2217 | uvc_events_process_class (dev, ctrl, resp); 2218 | break; 2219 | 2220 | default: 2221 | break; 2222 | } 2223 | } 2224 | 2225 | static int 2226 | uvc_events_process_control_data (struct uvc_device *dev, 2227 | uint8_t cs, uint8_t entity_id, 2228 | struct uvc_request_data *data) 2229 | { 2230 | switch (entity_id) { 2231 | /* Processing unit 'UVC_VC_PROCESSING_UNIT'. */ 2232 | case 2: 2233 | switch (cs) { 2234 | /* 2235 | * We support only 'UVC_PU_BRIGHTNESS_CONTROL' for Processing Unit, as our bmControls[0] = 1 for PU. 2236 | */ 2237 | case UVC_PU_BRIGHTNESS_CONTROL: 2238 | memcpy (&dev->brightness_val, data->data, data->length); 2239 | /* UVC - V4L2 integrated path. */ 2240 | if (0) { 2241 | /* 2242 | * Try to change the Brightness attribute onVideo capture device. Note that this try may 2243 | * succeed or end up with some error on the video capture side. By default to keep tools 2244 | * like USBCV's UVC test suite happy, we are maintaining a local copy of the current 2245 | * brightness value in 'dev->brightness_val' variable and we return the same value to the 2246 | * Host on receiving a GET_CUR(BRIGHTNESS) control request. 2247 | * 2248 | * FIXME: Keeping in view the point discussed above, notice that we ignore the return value 2249 | * from the function call below. To be strictly compliant, we should return the same value 2250 | * accordingly. 2251 | */ 2252 | v4l2_set_ctrl (dev->vdev, dev->brightness_val, V4L2_CID_BRIGHTNESS); 2253 | } 2254 | break; 2255 | 2256 | default: 2257 | break; 2258 | } 2259 | break; 2260 | 2261 | default: 2262 | break; 2263 | } 2264 | 2265 | printf ("Control Request data phase (cs %02x entity %02x)\n", cs, entity_id); 2266 | 2267 | return 0; 2268 | } 2269 | 2270 | static int 2271 | uvc_events_process_data (struct uvc_device *dev, struct uvc_request_data *data) 2272 | { 2273 | struct uvc_streaming_control *target; 2274 | struct uvc_streaming_control *ctrl; 2275 | struct v4l2_format fmt; 2276 | const struct uvc_format_info *format; 2277 | const struct uvc_frame_info *frame; 2278 | const unsigned int *interval; 2279 | unsigned int iformat, iframe; 2280 | unsigned int nframes; 2281 | unsigned int *val = (unsigned int *) data->data; 2282 | int ret; 2283 | 2284 | switch (dev->control) { 2285 | case UVC_VS_PROBE_CONTROL: 2286 | printf ("setting probe control, length = %d\n", data->length); 2287 | target = &dev->probe; 2288 | break; 2289 | 2290 | case UVC_VS_COMMIT_CONTROL: 2291 | printf ("setting commit control, length = %d\n", data->length); 2292 | target = &dev->commit; 2293 | break; 2294 | 2295 | default: 2296 | printf ("setting other control, length = %d\n", data->length); 2297 | 2298 | /* 2299 | * As we support only BRIGHTNESS control, this request is 2300 | * for setting BRIGHTNESS control. 2301 | * Check for any invalid SET_CUR(BRIGHTNESS) requests 2302 | * from Host. Note that we support Brightness levels 2303 | * from 0x0 to 0x10 in a step of 0x1. So, any request 2304 | * with value greater than 0x10 is invalid. 2305 | */ 2306 | if (*val > PU_BRIGHTNESS_MAX_VAL) { 2307 | return -EINVAL; 2308 | } 2309 | else { 2310 | ret = uvc_events_process_control_data (dev, UVC_PU_BRIGHTNESS_CONTROL, 2, data); 2311 | if (ret < 0) 2312 | goto err; 2313 | 2314 | return 0; 2315 | } 2316 | } 2317 | 2318 | ctrl = (struct uvc_streaming_control *) &data->data; 2319 | iformat = clamp ((unsigned int) ctrl->bFormatIndex, 1U, 2320 | (unsigned int) ARRAY_SIZE (uvc_formats)); 2321 | format = &uvc_formats[iformat - 1]; 2322 | 2323 | nframes = 0; 2324 | while (format->frames[nframes].width != 0) 2325 | ++nframes; 2326 | 2327 | iframe = clamp ((unsigned int) ctrl->bFrameIndex, 1U, nframes); 2328 | frame = &format->frames[iframe - 1]; 2329 | interval = frame->intervals; 2330 | 2331 | while (interval[0] < ctrl->dwFrameInterval && interval[1]) 2332 | ++interval; 2333 | 2334 | target->bFormatIndex = iformat; 2335 | target->bFrameIndex = iframe; 2336 | 2337 | switch (format->fcc) { 2338 | case V4L2_PIX_FMT_YUYV: 2339 | target->dwMaxVideoFrameSize = frame->width * frame->height * 2; 2340 | // uses frame size and then dev->width & height get changed 2341 | break; 2342 | case V4L2_PIX_FMT_MJPEG: 2343 | target->dwMaxVideoFrameSize = frame->width * frame->height * 1; 2344 | break; 2345 | } 2346 | printf ("UVC: event proc %c%c%c%c %dx%d\n", pixfmtstr (format->fcc), frame->width, frame->height); 2347 | target->dwFrameInterval = *interval; 2348 | 2349 | if (dev->control == UVC_VS_COMMIT_CONTROL) { 2350 | dev->fcc = format->fcc; 2351 | dev->width = frame->width; 2352 | dev->height = frame->height; 2353 | printf("UVC: event set video format\n"); 2354 | 2355 | ret = uvc_video_set_format (dev); 2356 | if (ret < 0) 2357 | goto err; 2358 | 2359 | } 2360 | printf 2361 | ("bmHint %d, bFormatIndex %d, bFrameIndex %d, dwFrameInterval %d, dwMaxVideoFramSize %d\n", 2362 | ctrl->bmHint, ctrl->bFormatIndex, ctrl->bFrameIndex, 2363 | ctrl->dwFrameInterval, ctrl->dwMaxVideoFrameSize); 2364 | return 0; 2365 | 2366 | err: 2367 | return ret; 2368 | } 2369 | 2370 | static void 2371 | uvc_events_process (struct uvc_device *dev) 2372 | { 2373 | struct v4l2_event v4l2_event; 2374 | struct uvc_event *uvc_event = (void *) &v4l2_event.u.data; 2375 | struct uvc_request_data resp; 2376 | int ret; 2377 | 2378 | ret = ioctl (dev->uvc_fd, VIDIOC_DQEVENT, &v4l2_event); 2379 | if (ret < 0) { 2380 | printf ("VIDIOC_DQEVENT failed: %s (%d)\n", strerror (errno), errno); 2381 | return; 2382 | } 2383 | 2384 | memset (&resp, 0, sizeof resp); 2385 | resp.length = -EL2HLT; 2386 | 2387 | switch (v4l2_event.type) { 2388 | case UVC_EVENT_CONNECT: 2389 | printf ("UVC_EVENT_CONNECT\n"); 2390 | return; 2391 | 2392 | case UVC_EVENT_DISCONNECT: 2393 | dev->uvc_shutdown_requested = 1; 2394 | printf ("UVC: Possible USB shutdown requested from " 2395 | "Host, seen via UVC_EVENT_DISCONNECT\n"); 2396 | return; 2397 | 2398 | case UVC_EVENT_SETUP: 2399 | printf ("UVC_EVENT_SETUP\n"); 2400 | uvc_events_process_setup (dev, &uvc_event->req, &resp); 2401 | break; 2402 | 2403 | case UVC_EVENT_DATA: 2404 | printf ("UVC_EVENT_DATA\n"); 2405 | ret = uvc_events_process_data (dev, &uvc_event->data); 2406 | if (ret < 0) 2407 | break; 2408 | 2409 | return; 2410 | 2411 | case UVC_EVENT_STREAMON: 2412 | printf ("UVC_EVENT_STREAMON \n"); 2413 | uvc_handle_streamon_event (dev); 2414 | return; 2415 | 2416 | case UVC_EVENT_STREAMOFF: 2417 | printf ("UVC_EVENT_STREAMOFF\n"); 2418 | /* Stop V4L2 streaming... */ 2419 | if (dev->vdev->is_streaming) { 2420 | /* UVC - V4L2 integrated path. */ 2421 | //v4l2_stop_capturing (dev->vdev); 2422 | video_enable (dev->vdev, 0); 2423 | dev->vdev->is_streaming = 0; 2424 | } 2425 | 2426 | /* ... and now UVC streaming.. */ 2427 | if (dev->is_streaming) { 2428 | uvc_video_stream (dev, 0); 2429 | //uvc_uninit_device (dev); 2430 | uvc_video_reqbufs_userptr (dev, 0); 2431 | dev->is_streaming = 0; 2432 | dev->first_buffer_queued = 0; 2433 | } 2434 | 2435 | return; 2436 | 2437 | default: 2438 | printf ("hey unknown event %d\n", v4l2_event.type); 2439 | break; 2440 | } 2441 | 2442 | ret = ioctl (dev->uvc_fd, UVCIOC_SEND_RESPONSE, &resp); 2443 | if (ret < 0) { 2444 | printf ("UVCIOC_S_EVENT failed: %s (%d)\n", strerror (errno), errno); 2445 | return; 2446 | } 2447 | } 2448 | 2449 | static void 2450 | uvc_events_init (struct uvc_device *dev) 2451 | { 2452 | struct v4l2_event_subscription sub; 2453 | unsigned int payload_size; 2454 | 2455 | switch (dev->fcc) { 2456 | 2457 | case V4L2_PIX_FMT_YUYV: 2458 | payload_size = dev->width * dev->height * 2; 2459 | break; 2460 | case V4L2_PIX_FMT_MJPEG: 2461 | payload_size = dev->imgsize; 2462 | break; 2463 | } 2464 | 2465 | printf ("UVC: events init udev->width %d, udev->height %d %d \n", dev->width, dev->height, dev->probe.dwMaxPayloadTransferSize); 2466 | uvc_fill_streaming_control (dev, &dev->probe, 0, 0); 2467 | uvc_fill_streaming_control (dev, &dev->commit, 0, 0); 2468 | 2469 | /*if (dev->bulk) 2470 | FIXME Crude hack, must be negotiated with the driver. 2471 | dev->probe.dwMaxPayloadTransferSize = 2472 | dev->commit.dwMaxPayloadTransferSize = payload_size; */ 2473 | 2474 | memset (&sub, 0, sizeof sub); 2475 | sub.type = UVC_EVENT_SETUP; 2476 | 2477 | ioctl (dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 2478 | sub.type = UVC_EVENT_DATA; 2479 | ioctl (dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 2480 | sub.type = UVC_EVENT_STREAMON; 2481 | ioctl (dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 2482 | sub.type = UVC_EVENT_STREAMOFF; 2483 | ioctl (dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 2484 | } 2485 | 2486 | static void 2487 | image_load(struct uvc_device *dev, const char *img) 2488 | { 2489 | int fd = -1; 2490 | 2491 | if (img == NULL) 2492 | return; 2493 | 2494 | fd = open(img, O_RDONLY); 2495 | if (fd == -1) { 2496 | printf("Unable to open MJPEG image '%s'\n", img); 2497 | return; 2498 | } 2499 | 2500 | dev->imgsize = lseek(fd, 0, SEEK_END); 2501 | lseek(fd, 0, SEEK_SET); 2502 | dev->imgdata = malloc(dev->imgsize); 2503 | if (dev->imgdata == NULL) { 2504 | printf("Unable to allocate memory for MJPEG image\n"); 2505 | dev->imgsize = 0; 2506 | return; 2507 | } 2508 | 2509 | read(fd, dev->imgdata, dev->imgsize); 2510 | close(fd); 2511 | } 2512 | 2513 | 2514 | /* --------------------------------------------------------------------------- 2515 | * process thread 's 2516 | */ 2517 | static void * 2518 | save_thread (void *arg) 2519 | { 2520 | struct v4l2_device *dev = (struct v4l2_device *) arg; 2521 | MMAL_BUFFER_HEADER_T *buffer; 2522 | MMAL_STATUS_T status; 2523 | struct v4l2_buffer ubuf; 2524 | unsigned int bytes_written; 2525 | static int i = 0; 2526 | int ret = 0; 2527 | unsigned char *temp; 2528 | 2529 | //Being lazy and using a timed wait instead of setting up a 2530 | //mechanism for skipping this when destroying the thread 2531 | printf("save_thread started\n"); 2532 | /*char *filename; 2533 | 2534 | if(default_format){ 2535 | filename="singleframe.raw"; 2536 | } 2537 | else{ 2538 | filename="singleframe.mjpg"; 2539 | } 2540 | 2541 | int fd = open(filename, O_RDONLY); 2542 | bytes_written = lseek(fd, 0, SEEK_END); 2543 | temp = malloc(bytes_written); 2544 | read(fd, temp, bytes_written); 2545 | printf("file %s is %d bytes\n", filename, bytes_written); 2546 | close(fd);*/ 2547 | 2548 | CLEAR (ubuf); 2549 | 2550 | ubuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 2551 | ubuf.memory = V4L2_MEMORY_USERPTR; 2552 | 2553 | while (!dev->thread_quit) { 2554 | buffer = mmal_queue_timedwait (dev->save_queue, 500); 2555 | if (!buffer) 2556 | continue; 2557 | 2558 | print("save_thread %p, len %d, size %d \n", buffer, buffer->length, buffer->alloc_size); 2559 | 2560 | /* if (dev->mjpeg_fd) { 2561 | bytes_written = fwrite (buffer->data, 1, buffer->length, dev->mjpeg_fd); 2562 | fflush (dev->mjpeg_fd); 2563 | if (bytes_written != buffer->length) { 2564 | print ("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); 2565 | } 2566 | }*/ 2567 | 2568 | ubuf.m.userptr = (unsigned long) (buffer->data); 2569 | ubuf.length = dev->udev->imgsize;//buffer->length; // buffer->alloc_size;;bytes_written;// 2570 | ubuf.index = dev->vbuf.index;; //vbuf.index;L 2571 | ubuf.bytesused = buffer->length;// buffer->alloc_size; //;bytes_written;// 2572 | 2573 | ubuf.timestamp.tv_sec = dev->lastpts;//buffer->pts; 2574 | ubuf.flags = 0; 2575 | 2576 | /* ubuf.m.userptr = (unsigned long)temp; 2577 | ubuf.length = buffer->alloc_size; 2578 | ubuf.index = 0; 2579 | */ 2580 | // if(dev->dq_ubuf_ok==false){ 2581 | ret = ioctl (dev->udev->uvc_fd, VIDIOC_QBUF, &ubuf); 2582 | if (ret < 0) { 2583 | if(i%20==0) print ("UVC: Unable to queue buffer %d: %s (%d) %d %d.\n", dev->vbuf.index, strerror (errno), errno, i, dev->udev->uvc_fd); 2584 | // Check for a USB disconnect/shutdown event. 0 2585 | if (errno == ENODEV) { 2586 | //dev->udev->uvc_shutdown_requested = 1; 2587 | printf ("UVC: Possible USB shutdown requested from " 2588 | "Host, seen during VIDIOC_QBUF\n"); 2589 | return 0; 2590 | } 2591 | } 2592 | else { 2593 | dev->udev->qbuf_count++; 2594 | print 2595 | ("UVC: queueued buffer %d ok q %lld dq %lld, vbuf %lld %lld %d\n", 2596 | ubuf.index, dev->udev->qbuf_count, dev->udev->dqbuf_count, 2597 | dev->qbuf_count, dev->dqbuf_count, i); 2598 | //mmal_buffer_header_mem_unlock(buffer); 2599 | //#ifdef ENABLE_BUFFER_DEBUG 2600 | // printf ("Queueing buffer at UVC side = %d\n", ubuf.index); 2601 | //#endif 2602 | } 2603 | 2604 | if (!dev->udev->first_buffer_queued ) { 2605 | printf ("UVC: udev first buffers start\n"); 2606 | uvc_video_stream (dev->udev, 1); 2607 | dev->udev->first_buffer_queued = 1; 2608 | dev->udev->is_streaming = 1; 2609 | } 2610 | 2611 | buffer->length = 0; // what is this for? 2612 | if (default_format == 0) 2613 | status = mmal_port_send_buffer (dev->isp->output[0], buffer); 2614 | else 2615 | status = mmal_port_send_buffer (dev->encoder->output[0], buffer); 2616 | 2617 | if (status != MMAL_SUCCESS) { 2618 | print ("mmal_port_send_buffer failed on buffer %p, status %d", buffer, status); 2619 | } 2620 | i++; 2621 | // dev->dq_ubuf_ok = true; //dq_ubuf can be called since a buffer has been queued 2622 | } 2623 | return NULL; 2624 | } 2625 | 2626 | 2627 | static void 2628 | buffers_to_isp (struct v4l2_device *dev) 2629 | { 2630 | MMAL_BUFFER_HEADER_T *buffer; 2631 | print("buffers_to_isp\n"); 2632 | while ((buffer = mmal_queue_get (dev->isp_output_pool->queue)) != NULL) { 2633 | mmal_port_send_buffer (dev->isp->output[0], buffer); 2634 | } 2635 | 2636 | } 2637 | 2638 | 2639 | /* --------------------------------------------------------------------------- 2640 | * isp_input_callback 2641 | */ 2642 | static void 2643 | isp_input_callback (MMAL_PORT_T * port, MMAL_BUFFER_HEADER_T * buffer) 2644 | { 2645 | struct v4l2_device *dev = (struct v4l2_device *) port->userdata; 2646 | unsigned int i; 2647 | print("isp_input_callback %p from %s, len %d, size %d \n", buffer, port->name, buffer->length, buffer->alloc_size); 2648 | for (i = 0; i < dev->nbufs; i++) { 2649 | if (dev->buffers[i].mmal == buffer) { 2650 | //v4l2 buffer finished using so qback 2651 | print ("Matches V4L2 buffer index %d / %d\n", i, dev->buffers[i].idx); 2652 | video_queue_buffer (dev, dev->buffers[i].idx);//, BUFFER_FILL_NONE); 2653 | mmal_buffer_header_release (buffer); 2654 | buffer = NULL; 2655 | break; 2656 | } 2657 | } 2658 | if (buffer) { 2659 | print ("Failed to find matching V4L2 buffer for mmal buffer %p\n", buffer); 2660 | mmal_buffer_header_release (buffer); 2661 | } 2662 | 2663 | } 2664 | 2665 | 2666 | static void isp_output_callback (MMAL_PORT_T * port, MMAL_BUFFER_HEADER_T * buffer) 2667 | { 2668 | print("isp_output_callback %p from %s, len %d, size %d \n", buffer, port->name, buffer->length, buffer->alloc_size); 2669 | //vcos_log_error("File handle: %p", port->userdata); 2670 | struct v4l2_device *dev = (struct v4l2_device *) port->userdata; 2671 | 2672 | if (dev->render) { 2673 | MMAL_BUFFER_HEADER_T *out = mmal_queue_get (dev->render_pool->queue); 2674 | if (out) { 2675 | mmal_buffer_header_replicate (out, buffer); 2676 | mmal_port_send_buffer (dev->render->input[0], out); 2677 | } 2678 | } 2679 | if (dev->encoder) { 2680 | MMAL_BUFFER_HEADER_T *out = mmal_queue_get (dev->encode_pool->queue); 2681 | if (out) { 2682 | mmal_buffer_header_replicate (out, buffer); 2683 | mmal_port_send_buffer (dev->encoder->input[0], out); 2684 | } 2685 | } 2686 | mmal_buffer_header_release (buffer); 2687 | 2688 | buffers_to_isp (dev); 2689 | } 2690 | 2691 | 2692 | static void render_encoder_input_callback (MMAL_PORT_T * port, MMAL_BUFFER_HEADER_T * buffer) 2693 | { 2694 | print("render_encoder_input_callback %p from %s, len %d, size %d \n", buffer, port->name, buffer->length, buffer->alloc_size); 2695 | //vcos_log_error("File handle: %p", port->userdata); 2696 | struct v4l2_device *dev = (struct v4l2_device *) port->userdata; 2697 | mmal_buffer_header_release (buffer); 2698 | buffers_to_isp (dev); 2699 | } 2700 | 2701 | 2702 | /* --------------------------------------------------------------------------- 2703 | * isp_output_callback 'i 2704 | */ 2705 | 2706 | static void 2707 | encoder_buffer_cb (MMAL_PORT_T * port, MMAL_BUFFER_HEADER_T * buffer) 2708 | { 2709 | static int i = 0; 2710 | MMAL_STATUS_T status; 2711 | print("encoder_buffer_cb %p from %s, len %d, size %d \n", buffer, port->name, buffer->length, buffer->alloc_size); 2712 | //printf("%d\n",i); 2713 | //vcos_log_error("File handle: %p", port->userdata); 2714 | struct v4l2_device *dev = (struct v4l2_device *) port->userdata; 2715 | 2716 | if(1) 2717 | { 2718 | //printf ("encoder_buffer_cb %p, %p, %d %d %d %d\n", buffer, buffer->data, buffer->alloc_size, dev->vbuf.index, buffer->length, port->is_enabled); 2719 | //time_me("isp",dev); 2720 | if (port->is_enabled) { 2721 | dev->counter++; 2722 | mmal_queue_put (dev->save_queue, buffer); //if comment data won't be saved to disk 2723 | } 2724 | else { 2725 | print ("port disabled\n"); 2726 | mmal_buffer_header_release (buffer); 2727 | } 2728 | } 2729 | else { 2730 | //skipping buffer data so can just send to output isp 2731 | if (default_format == 0) 2732 | status = mmal_port_send_buffer (dev->isp->output[0], buffer); 2733 | else 2734 | status = mmal_port_send_buffer (dev->encoder->output[0], buffer); 2735 | 2736 | if (status != MMAL_SUCCESS) { 2737 | print ("Failed to send buffer isp\n"); 2738 | } 2739 | } 2740 | //mmal_buffer_header_mem_unlock(buffer); 2741 | i++; 2742 | } 2743 | 2744 | /* --------------------------------------------------------------------------- 2745 | * isp->input enable, wait until after buffers have been allocated 2746 | */ 2747 | int 2748 | enable_mmal_input (struct v4l2_device *dev) 2749 | { 2750 | MMAL_STATUS_T status; 2751 | MMAL_POOL_T *pool; 2752 | MMAL_PORT_T *port_output; 2753 | unsigned int i; 2754 | 2755 | printf("enable_mmal_input\n"); 2756 | status = mmal_port_parameter_set_boolean (dev->isp->input[0], MMAL_PARAMETER_ZERO_COPY, dev->can_zero_copy); 2757 | if (status != MMAL_SUCCESS) { 2758 | print ("Failed to set zero copy\n"); 2759 | return -1; 2760 | } 2761 | status = mmal_port_enable (dev->isp->input[0], isp_input_callback); 2762 | if (status != MMAL_SUCCESS) { 2763 | print ("ISP input enable failed\n"); 2764 | return -1; 2765 | } 2766 | // start encoding by passing empty buffers to ouput port of isp or encoder 2767 | if(default_format==0){ 2768 | pool = dev->isp_output_pool; 2769 | port_output = dev->isp->output[0]; 2770 | } 2771 | else 2772 | { 2773 | pool = dev->output_pool; 2774 | port_output = dev->encoder->output[0]; 2775 | } 2776 | 2777 | for (i = 0; i < port_output->buffer_num; i++) { 2778 | MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get (pool->queue); 2779 | if (!buffer) { 2780 | print ("buffer issue\n"); 2781 | return -1; 2782 | } 2783 | status = mmal_port_send_buffer ( port_output, buffer); 2784 | if (status != MMAL_SUCCESS) { 2785 | print ("mmal_port_send_buffer failed on buffer %p, status %d\n", buffer, status); 2786 | return -1; 2787 | } 2788 | } 2789 | return 0; 2790 | } 2791 | 2792 | 2793 | // setup_mmal mjpeg and yuyv 2794 | static int 2795 | setup_mmal_comp (struct v4l2_device *dev, int nbufs) 2796 | { 2797 | MMAL_STATUS_T status; 2798 | VCOS_STATUS_T vcos_status; 2799 | MMAL_PORT_T *port; 2800 | const struct v4l2_format_info *info; 2801 | struct v4l2_format fmt; 2802 | int ret; 2803 | MMAL_PORT_T *isp_output, *encoder_input = NULL, *encoder_output = NULL; 2804 | 2805 | status = mmal_component_create ("vc.ril.isp", &dev->isp); 2806 | if (status != MMAL_SUCCESS) { 2807 | print ("Failed to create isp\n"); 2808 | return -1; 2809 | } 2810 | 2811 | if (default_format) { 2812 | status = mmal_component_create ("vc.ril.video_encode", &dev->encoder); 2813 | if (status != MMAL_SUCCESS) { 2814 | print ("Failed to create encoder"); 2815 | return -1; 2816 | } 2817 | encoder_input = dev->encoder->input[0]; 2818 | encoder_output = dev->encoder->output[0]; 2819 | } 2820 | 2821 | if (0) { //render to HDMI disabled 2822 | status = mmal_component_create ("vc.ril.video_render", &dev->render); 2823 | if (status != MMAL_SUCCESS) { 2824 | print ("Failed to create render\n"); 2825 | return -1; 2826 | } 2827 | } 2828 | 2829 | port = dev->isp->input[0]; 2830 | 2831 | CLEAR(fmt); 2832 | fmt.type = dev->type; 2833 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2834 | dev->memtype = V4L2_MEMORY_MMAP; 2835 | 2836 | ret = ioctl (dev->v4l2_fd, VIDIOC_G_FMT, &fmt); 2837 | if (ret < 0) { 2838 | print ("Unable to get format: %s (%d).\n", strerror (errno), errno); 2839 | return ret; 2840 | } 2841 | 2842 | info = v4l2_format_by_fourcc (fmt.fmt.pix.pixelformat); 2843 | if (!info || info->mmal_encoding == MMAL_ENCODING_UNUSED) { 2844 | print ("Unsupported encoding\n"); 2845 | return -1; 2846 | } 2847 | 2848 | printf("input size %dx%d field %s, %c%c%c%c\n", 2849 | fmt.fmt.pix.width, fmt.fmt.pix.height, v4l2_field_name(fmt.fmt.pix.field), pixfmtstr(fmt.fmt.pix.pixelformat)); 2850 | port->format->encoding = info->mmal_encoding; 2851 | port->format->es->video.crop.width = fmt.fmt.pix.width; 2852 | port->format->es->video.crop.height = fmt.fmt.pix.height; 2853 | port->format->es->video.width = (port->format->es->video.crop.width + 31) & ~31; 2854 | //mmal_encoding_stride_to_width(port->format->encoding, fmt.fmt.pix.bytesperline); 2855 | /* FIXME - buffer may not be aligned vertically */ 2856 | port->format->es->video.height = (fmt.fmt.pix.height + 15) & ~15; 2857 | //Ignore for now, but will be wanted for video encode. 2858 | port->format->es->video.frame_rate.num = 10000; 2859 | port->format->es->video.frame_rate.den = 10000; 2860 | port->buffer_num = nbufs; 2861 | 2862 | //cropping 2863 | if (0) { 2864 | MMAL_PARAMETER_CROP_T crop ={ {MMAL_PARAMETER_CROP, sizeof (MMAL_PARAMETER_CROP_T)}, {0, 0, 0, 0}}; 2865 | crop.rect.x = 32; 2866 | crop.rect.y = 32; 2867 | crop.rect.width = 640; 2868 | crop.rect.height = 360; 2869 | mmal_port_parameter_set (port, &crop.hdr); 2870 | } 2871 | 2872 | if (dev->fps) { 2873 | dev->frame_time_usec = 1000000 / dev->fps; 2874 | } 2875 | 2876 | status = mmal_port_format_commit (port); 2877 | if (status != MMAL_SUCCESS) { 2878 | return -1; 2879 | } 2880 | mmal_log_dump_port (port); 2881 | 2882 | unsigned int mmal_stride = mmal_encoding_width_to_stride (info->mmal_encoding, port->format->es->video.width); 2883 | if (mmal_stride != fmt.fmt.pix.bytesperline) { 2884 | printf("stride adjustment\n"); 2885 | if (video_set_format 2886 | (dev, fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.pixelformat, 2887 | mmal_stride, fmt.fmt.pix.sizeimage, fmt.fmt.pix.field, fmt.fmt.pix.flags) < 0) 2888 | print ("Failed to adjust stride\n"); 2889 | else 2890 | // Retrieve settings again so local state is correct 2891 | video_get_format (dev); 2892 | } 2893 | 2894 | dev->mmal_pool = mmal_pool_create (nbufs, 0); 2895 | if (!dev->mmal_pool) { 2896 | print ("Failed to create pool\n"); 2897 | return -1; 2898 | } 2899 | print ("Created pool of length %d, size %d\n", nbufs, 0); 2900 | 2901 | port->userdata = (struct MMAL_PORT_USERDATA_T *) dev; 2902 | 2903 | mmal_format_copy (dev->isp->output[0]->format, port->format); 2904 | 2905 | isp_output = dev->isp->output[0]; 2906 | 2907 | if (default_format == 0){ 2908 | isp_output->format->encoding = MMAL_ENCODING_YUYV; // -f0 2909 | isp_output->buffer_num = 8; 2910 | } 2911 | else{ 2912 | isp_output->format->encoding = MMAL_ENCODING_I420; // -f1 intermediate format 2913 | isp_output->buffer_num = 3; 2914 | } 2915 | 2916 | isp_output->format->es->video.width = VCOS_ALIGN_UP (dev->udev->width, 32); 2917 | isp_output->format->es->video.height = VCOS_ALIGN_UP (dev->udev->height, 16); 2918 | 2919 | isp_output->format->es->video.crop.width = dev->udev->width; //WIDTH; //set output size 2920 | isp_output->format->es->video.crop.height = dev->udev->height; //HEIGHT; 2921 | 2922 | while (isp_output->format->es->video.crop.width > 1920) { 2923 | isp_output->format->es->video.crop.width >>= 1; 2924 | isp_output->format->es->video.crop.height >>= 1; 2925 | } 2926 | // port->format->es->video.crop.width = 640; //set output size 2927 | // port->format->es->video.crop.height = 360; 2928 | isp_output->userdata = (struct MMAL_PORT_USERDATA_T *) dev; 2929 | 2930 | status = mmal_port_format_commit (isp_output); 2931 | if (status != MMAL_SUCCESS) { 2932 | print ("ISP o/p commit failed\n"); 2933 | return -1; 2934 | } 2935 | 2936 | if (dev->render) { 2937 | status = mmal_format_full_copy (dev->render->input[0]->format, isp_output->format); 2938 | dev->render->input[0]->buffer_num = 3; 2939 | if (status == MMAL_SUCCESS) 2940 | status = mmal_port_format_commit (dev->render->input[0]); 2941 | } 2942 | 2943 | // Encoder setup 2944 | if (dev->encoder) { 2945 | status = mmal_format_full_copy (encoder_input->format, isp_output->format); 2946 | encoder_input->buffer_num = 1; 2947 | if (status == MMAL_SUCCESS) 2948 | status = mmal_port_format_commit (encoder_input); 2949 | 2950 | // Only supporting H264 at the moment 2951 | encoder_output->format->encoding = MMAL_ENCODING_MJPEG; 2952 | //encoder_output->format->encoding = MMAL_ENCODING_H264; 2953 | encoder_output->format->es->video.width = VCOS_ALIGN_UP (dev->udev->width, 32); 2954 | encoder_output->format->es->video.height = VCOS_ALIGN_UP (dev->udev->height, 16); 2955 | 2956 | encoder_output->format->es->video.crop.width = dev->udev->width;//WIDTH; //set output size 2957 | encoder_output->format->es->video.crop.height = dev->udev->height;//HEIGHT; 2958 | 2959 | encoder_output->format->bitrate = dev->bitrate;//1500000; //-b? 2960 | 2961 | encoder_output->buffer_size = 256 << 10;//encoder_output->buffer_size_recommended; buffer->alloc_size 2962 | 2963 | if (encoder_output->buffer_size < encoder_output->buffer_size_min) 2964 | encoder_output->buffer_size = encoder_output->buffer_size_min; 2965 | 2966 | encoder_output->buffer_num = 8; //encoder_output->buffer_num_recommended; 2967 | 2968 | if (encoder_output->buffer_num < encoder_output->buffer_num_min) 2969 | encoder_output->buffer_num = encoder_output->buffer_num_min; 2970 | 2971 | // We need to set the frame rate on output to 0, to ensure it gets 2972 | // updated correctly from the input framerate when port connected 2973 | encoder_output->format->es->video.frame_rate.num = 0; 2974 | encoder_output->format->es->video.frame_rate.den = 1; 2975 | 2976 | // Commit the port changes to the output port 2977 | status = mmal_port_format_commit (encoder_output); 2978 | 2979 | if (status != MMAL_SUCCESS) { 2980 | print ("Unable to set format on encoder output port %d\n", dev->dst_mmal_enc); 2981 | return -1; 2982 | } 2983 | {//bunch of H264 stuff that could be removed 2984 | if(0){ 2985 | MMAL_PARAMETER_VIDEO_PROFILE_T param; 2986 | param.hdr.id = MMAL_PARAMETER_PROFILE; 2987 | param.hdr.size = sizeof (param); 2988 | param.profile[0].profile = MMAL_VIDEO_PROFILE_H264_HIGH; //state->profile; 2989 | param.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; 2990 | status = mmal_port_parameter_set (encoder_output, ¶m.hdr); 2991 | if (status != MMAL_SUCCESS) { 2992 | print ("Unable to set H264 profile\n"); 2993 | } 2994 | } 2995 | 2996 | status =mmal_port_parameter_set_boolean (encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, 1); 2997 | if ( status != MMAL_SUCCESS) { 2998 | print ("Unable to set immutable input flag\n");// Continue rather than abort.. 2999 | } 3000 | 3001 | //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested 3002 | status =mmal_port_parameter_set_boolean (encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, 0); 3003 | if ( status != MMAL_SUCCESS) { 3004 | print ("failed to set INLINE HEADER FLAG parameters\n");// Continue rather than abort.. 3005 | } 3006 | 3007 | //set INLINE VECTORS flag to request motion vector estimates H264 3008 | status =mmal_port_parameter_set_boolean (encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, 0) ; 3009 | if ( status != MMAL_SUCCESS) { 3010 | print ("failed to set INLINE VECTORS parameters\n"); // Continue rather than abort.. 3011 | } 3012 | } 3013 | 3014 | if (status != MMAL_SUCCESS) { 3015 | print ("Unable to set format on video encoder input port\n"); 3016 | } 3017 | 3018 | print ("Enable encoder....\n"); 3019 | status = mmal_component_enable (dev->encoder); 3020 | if (status != MMAL_SUCCESS) { 3021 | print ("Failed to enable\n"); 3022 | return -1; 3023 | } 3024 | //status = mmal_port_parameter_set_boolean (encoder_output, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);//disabled 3025 | status = mmal_port_parameter_set_boolean (encoder_input, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); 3026 | if (status != MMAL_SUCCESS) { 3027 | print ("Failed to set zero copy\n"); 3028 | return -1; 3029 | } 3030 | encoder_input->userdata = (struct MMAL_PORT_USERDATA_T *) dev; 3031 | } 3032 | 3033 | if(default_format==1){ 3034 | //only for MJPEG 3035 | status = mmal_port_parameter_set_boolean (isp_output, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); 3036 | } 3037 | 3038 | 3039 | if (dev->render) { 3040 | status += mmal_port_parameter_set_boolean (dev->render->input[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); 3041 | if (status != MMAL_SUCCESS) { 3042 | return -1; 3043 | } 3044 | 3045 | dev->render->input[0]->userdata = (struct MMAL_PORT_USERDATA_T *) dev; 3046 | 3047 | status = mmal_port_enable (dev->render->input[0], render_encoder_input_callback); 3048 | if (status != MMAL_SUCCESS) 3049 | return -1; 3050 | 3051 | print ("Create pool of %d buffers of size %d for render\n", 3052 | dev->render->input[0]->buffer_num, dev->render->input[0]->buffer_size); 3053 | 3054 | dev->render_pool = mmal_port_pool_create (dev->render->input[0], 3055 | dev->render->input[0]->buffer_num, dev->render->input[0]->buffer_size); 3056 | 3057 | if (!dev->render_pool) { 3058 | print ("Failed to create render pool\n"); 3059 | return -1; 3060 | } 3061 | } 3062 | 3063 | if (dev->encoder){ 3064 | status = mmal_port_enable (encoder_input, render_encoder_input_callback); 3065 | if (status != MMAL_SUCCESS) 3066 | return -1; 3067 | 3068 | print ("Create pool of %d buffers of size %d for encode ip\n", encoder_input->buffer_num, isp_output->buffer_size); 3069 | dev->encode_pool = mmal_port_pool_create (encoder_input, isp_output->buffer_num, isp_output->buffer_size); 3070 | if (!dev->encode_pool) { 3071 | print ("Failed to create encode ip pool\n"); 3072 | return -1; 3073 | } 3074 | } 3075 | 3076 | //isp_output: call back setup 3077 | if (default_format == 0){ 3078 | status = mmal_port_enable (isp_output, encoder_buffer_cb); //-f0 3079 | } 3080 | else 3081 | { 3082 | status = mmal_port_enable (isp_output, isp_output_callback); //-f1 3083 | } 3084 | if (status != MMAL_SUCCESS) 3085 | return -1; 3086 | 3087 | //isp_ouput: buffer pool 3088 | print ("Create pool of %d buffers of size %d for encode/render\n", isp_output->buffer_num, isp_output->buffer_size); 3089 | dev->isp_output_pool = mmal_port_pool_create (isp_output, isp_output->buffer_num, isp_output->buffer_size); 3090 | if (!dev->isp_output_pool) { 3091 | print ("Failed to create pool\n"); 3092 | return -1; 3093 | } 3094 | 3095 | buffers_to_isp (dev); 3096 | 3097 | // open h264 file and put the file handle in userdata for the encoder output port 3098 | if (dev->encoder) { 3099 | /* 3100 | if (filename[0] == '-' && filename[1] == '\0') { 3101 | dev->h264_fd = stdout; 3102 | debug = 0; 3103 | }*/ 3104 | { 3105 | printf ("Writing data to %s\n", "/mnt/tmpfs/file.mjpg"); 3106 | dev->mjpeg_fd = fopen ("/mnt/tmpfs/file.mjpg", "wb"); 3107 | } 3108 | /* 3109 | dev->pts_fd = (void *) fopen ("file.pts", "wb"); 3110 | if (dev->pts_fd) // save header for mkvmerge 3111 | fprintf (dev->pts_fd, "# timecode format v2\n");*/ 3112 | 3113 | encoder_output->userdata = (void *) dev; 3114 | 3115 | //Create encoder output buffers 3116 | 3117 | print ("Create pool of %d buffers of size %d\n", encoder_output->buffer_num, encoder_output->buffer_size); 3118 | dev->output_pool = mmal_port_pool_create (encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); 3119 | if (!dev->output_pool) { 3120 | print ("Failed to create pool\n"); 3121 | return -1; 3122 | } 3123 | 3124 | status = mmal_port_enable (encoder_output, encoder_buffer_cb); 3125 | if (status != MMAL_SUCCESS) { 3126 | print ("Failed to enable port %s\n", encoder_output->name); 3127 | return -1; 3128 | } 3129 | } 3130 | 3131 | // queue always needed 3132 | dev->save_queue = mmal_queue_create (); 3133 | if (!dev->save_queue) { 3134 | print ("Failed to create queue\n"); 3135 | return -1; 3136 | } 3137 | 3138 | // thread alway needed 3139 | vcos_status = vcos_thread_create (&dev->save_thread, "save-thread", NULL, save_thread, dev); 3140 | if (vcos_status != VCOS_SUCCESS) { 3141 | print ("Failed to create save thread\n"); 3142 | return -1; 3143 | } 3144 | 3145 | return 0; 3146 | } 3147 | 3148 | 3149 | 3150 | 3151 | /* --------------------------------------------------------------------------- 3152 | * main 3153 | */ 3154 | 3155 | static void 3156 | usage (const char *argv0) 3157 | { 3158 | fprintf (stderr, "Usage: %s [options]\n", argv0); 3159 | fprintf (stderr, "Available options are\n"); 3160 | fprintf (stderr, " -b bitrate (1500000)\n"); 3161 | // fprintf (stderr, " -d Do not use any real V4L2 capture device\n"); 3162 | fprintf (stderr, " -f Select frame format\n\t" 3163 | "0 = V4L2_PIX_FMT_YUYV\n\t" "1 = V4L2_PIX_FMT_MJPEG\n\t"); 3164 | fprintf (stderr, " -g 1280x720 input device frame size\n"); 3165 | fprintf (stderr, " -h Print this help screen and exit\n"); 3166 | // fprintf (stderr, " -i image MJPEG image\n"); 3167 | // fprintf (stderr, " -m Streaming mult for ISOC (b/w 0 and 2)\n"); 3168 | // fprintf (stderr, " -n Number of Video buffers (b/w 2 and 32)\n"); 3169 | // fprintf (stderr, " -o Select UVC IO method:\n\t" 3170 | // "0 = MMAP\n\t" "1 = USER_PTR\n"); 3171 | fprintf (stderr, " -r Select output frame resolution:\n\t" 3172 | "0 = 480p, (720x480)\n\t" "1 = 720p, WXGA (1280x720)\n"); 3173 | fprintf (stderr, " -s Select USB bus speed (b/w 0 and 2)\n\t" 3174 | "0 = Full Speed (FS)\n\t" 3175 | "1 = High Speed (HS)\n\t" "2 = Super Speed (SS)\n"); 3176 | fprintf (stderr, " -t Streaming burst (b/w 0 and 15)\n"); 3177 | fprintf (stderr, " -u device UVC Video Output device\n"); 3178 | fprintf (stderr, " -v device V4L2 Video Capture device\n"); 3179 | 3180 | } 3181 | 3182 | 3183 | int 3184 | main (int argc, char *argv[]) 3185 | { 3186 | struct uvc_device *udev; 3187 | struct v4l2_device *vdev; 3188 | struct timeval tv; 3189 | struct v4l2_format fmt; 3190 | char *uvc_devname = "/dev/video1"; 3191 | char *v4l2_devname = "/dev/video0"; 3192 | //nchar *mjpeg_image = NULL; 3193 | fd_set fdsv, fdsu; 3194 | int ret, opt, nfds; 3195 | int bulk_mode = 0; 3196 | int bitrate= 1500000; 3197 | //int dummy_data_gen_mode = 0; 3198 | /* Frame format/resolution related params. */ 3199 | //int default_format = 0; /* V4L2_PIX_FMT_YUYV */ 3200 | int default_resolution = 1; /* 720p */ 3201 | int nbufs = 2; /* was 2 Ping-Pong buffers */ 3202 | int vnbufs = 3; //isp input and output buffers 3203 | /* USB speed related params */ 3204 | int mult = 0; 3205 | int burst = 0; 3206 | enum usb_device_speed speed = USB_SPEED_HIGH; /* High-Speed */ 3207 | enum v4l2_memory uvc_io_method = V4L2_MEMORY_USERPTR; 3208 | int frames = 0; 3209 | struct timespec start; 3210 | struct timespec last;; 3211 | //v4l2 setup parameters 3212 | const struct v4l2_format_info *info; 3213 | unsigned int pixelformat = V4L2_PIX_FMT_UYVY; 3214 | 3215 | int v4l2_width = 0; 3216 | int v4l2_height = 0; 3217 | 3218 | char *endptr; 3219 | 3220 | while ((opt = getopt (argc, argv, "b:df:g:hi:m:n:o:r:s:t:u:v:z:")) != -1) { 3221 | switch (opt) { 3222 | case 'b': 3223 | bitrate = atoi (optarg); 3224 | break; 3225 | 3226 | case 'f': 3227 | if (atoi (optarg) < 0 && atoi (optarg) > 1) { 3228 | usage (argv[0]); 3229 | return 1; 3230 | } 3231 | default_format = atoi (optarg); 3232 | break; 3233 | 3234 | case 'g': 3235 | //set v4l2 frame size 3236 | //do_set_format = 1; 3237 | v4l2_width = strtol(optarg, &endptr, 10); 3238 | if (*endptr != 'x' || endptr == optarg) { 3239 | print("Invalid size '%s'\n", optarg); 3240 | return 1; 3241 | } 3242 | v4l2_height = strtol(endptr + 1, &endptr, 10); 3243 | if (*endptr != 0) { 3244 | print("Invalid size '%s'\n", optarg); 3245 | return 1; 3246 | } 3247 | 3248 | break; 3249 | 3250 | case 'h': 3251 | usage (argv[0]); 3252 | return 1; 3253 | 3254 | case 'm': 3255 | if (atoi (optarg) < 0 && atoi (optarg) > 2) { 3256 | usage (argv[0]); 3257 | return 1; 3258 | } 3259 | 3260 | mult = atoi (optarg); 3261 | printf ("Requested Mult value = %d\n", mult); 3262 | break; 3263 | 3264 | case 'n': 3265 | if (atoi (optarg) < 2 && atoi (optarg) > 32) { 3266 | usage (argv[0]); 3267 | return 1; 3268 | } 3269 | 3270 | nbufs = atoi (optarg); 3271 | vnbufs = 3; // number of input v4l2 buffers? 3272 | //nbufs = 2; 3273 | printf ("Number of buffers requested = %d\n", nbufs); 3274 | break; 3275 | 3276 | case 'r': 3277 | if (atoi (optarg) < 0 && atoi (optarg) > 1) { 3278 | usage (argv[0]); 3279 | return 1; 3280 | } 3281 | 3282 | default_resolution = atoi (optarg); 3283 | break; 3284 | 3285 | case 's': 3286 | if (atoi (optarg) < 0 && atoi (optarg) > 2) { 3287 | usage (argv[0]); 3288 | return 1; 3289 | } 3290 | 3291 | speed = atoi (optarg); 3292 | break; 3293 | 3294 | case 't': 3295 | if (atoi (optarg) < 0 && atoi (optarg) > 15) { 3296 | usage (argv[0]); 3297 | return 1; 3298 | } 3299 | 3300 | burst = atoi (optarg); 3301 | printf ("Requested Burst value = %d\n", burst); 3302 | break; 3303 | 3304 | case 'u': 3305 | uvc_devname = optarg; 3306 | break; 3307 | 3308 | case 'v': 3309 | v4l2_devname = optarg; 3310 | printf ("v4l2_devname %s\n", v4l2_devname); 3311 | break; 3312 | 3313 | case 'z': 3314 | info = v4l2_format_by_name (optarg); 3315 | if (info == NULL) { 3316 | printf("Unsupported video format for V4L2 requested %s\n", optarg); 3317 | return 1; 3318 | } 3319 | pixelformat = info->fourcc; 3320 | break; 3321 | 3322 | default: 3323 | printf ("Invalid option '-%c'\n", opt); 3324 | usage (argv[0]); 3325 | return 1; 3326 | } 3327 | } 3328 | 3329 | { 3330 | // Try to set the default format at the V4L2 video capture device as //requested by the user. actually not setup here, use video_set_format// 3331 | CLEAR (fmt); 3332 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3333 | fmt.fmt.pix.width = v4l2_width;//(default_resolution == 0) ? 640 : 1280; 3334 | fmt.fmt.pix.height = v4l2_height;//(default_resolution == 0) ? 360 : 720; 3335 | printf ("width %d, height %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height); 3336 | switch (0) { 3337 | case 1: 3338 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; 3339 | fmt.fmt.pix.sizeimage = fmt.fmt.pix.width * fmt.fmt.pix.height * 1; 3340 | break; 3341 | case 0: 3342 | default: 3343 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 3344 | fmt.fmt.pix.sizeimage = fmt.fmt.pix.width * fmt.fmt.pix.height * 2; 3345 | break; 3346 | } 3347 | fmt.fmt.pix.field = V4L2_FIELD_ANY; 3348 | } 3349 | 3350 | /* video_init */ 3351 | vdev = calloc (1, sizeof *vdev); 3352 | vdev->v4l2_fd = -1; 3353 | vdev->memtype = V4L2_MEMORY_MMAP; 3354 | vdev->buffers = NULL; 3355 | 3356 | vdev->v4l2_devname = v4l2_devname; 3357 | vdev->bitrate = bitrate; 3358 | vdev->height = v4l2_height; 3359 | vdev->width = v4l2_width; 3360 | /* Open the V4L2 device. but if useing loopback this could fail */ 3361 | ret = video_open (vdev, v4l2_devname); 3362 | //ret = v4l2_open (&vdev, v4l2_devname, &fmt); 3363 | if (vdev == NULL || ret < 0) 3364 | return 1; 3365 | else 3366 | print ("v4l2_open %s\n", v4l2_devname); 3367 | vdev->v4l2_devname = v4l2_devname; 3368 | 3369 | /* Open the UVC device. */ 3370 | ret = uvc_video_open (&udev, uvc_devname); 3371 | if (udev == NULL || ret < 0) 3372 | return 1; 3373 | print ("uvc fd %d\n", udev->uvc_fd); 3374 | 3375 | udev->uvc_streamon = 0; 3376 | udev->uvc_devname = uvc_devname; 3377 | 3378 | //vdev->nbufs = 4; 3379 | { 3380 | /* Bind UVC and V4L2 devices. */ 3381 | udev->vdev = vdev; 3382 | vdev->udev = udev; 3383 | } 3384 | 3385 | /* Set parameters as passed by user udev width height uses default_resolution 3386 | * settting, (vdev uses -g setting) */ 3387 | switch (default_resolution) { 3388 | case 0: 3389 | udev->width = 640 ; 3390 | udev->height = 360 ; 3391 | break; 3392 | case 1: 3393 | udev->width = 1280; 3394 | udev->height = 720; 3395 | break; 3396 | case 2: 3397 | udev->width = 1920; 3398 | udev->height = 1080; 3399 | break; 3400 | } 3401 | 3402 | switch (default_format) { 3403 | case 1: 3404 | udev->fcc = V4L2_PIX_FMT_MJPEG; 3405 | udev->imgsize = udev->width * udev->height * 1; 3406 | break; 3407 | case 0: 3408 | default: 3409 | udev->fcc = V4L2_PIX_FMT_YUYV; 3410 | udev->imgsize = udev->width * udev->height * 2; 3411 | break; 3412 | } 3413 | 3414 | udev->memtype = uvc_io_method; 3415 | udev->bulk = bulk_mode; 3416 | udev->nbufs = nbufs; 3417 | udev->mult = mult; 3418 | udev->burst = burst; 3419 | udev->speed = speed; 3420 | printf ("getting udev format\n"); 3421 | 3422 | { 3423 | /* UVC - V4L2 integrated path */ 3424 | /*IO methods used at UVC and V4L2 domains must be complementary to avoid any memcpy from the CPU.*/ 3425 | switch (uvc_io_method) { 3426 | case V4L2_MEMORY_MMAP: 3427 | vdev->memtype = V4L2_MEMORY_USERPTR; 3428 | break; 3429 | case V4L2_MEMORY_USERPTR: 3430 | print ("set vdev to V4L2_MEMORY_MMAP\n"); 3431 | vdev->memtype = V4L2_MEMORY_MMAP; 3432 | default: 3433 | break; 3434 | } 3435 | } 3436 | 3437 | switch (speed) { 3438 | case USB_SPEED_FULL: 3439 | udev->maxpkt = 1023; 3440 | break; 3441 | case USB_SPEED_HIGH: 3442 | udev->maxpkt = 2048; 3443 | break; 3444 | case USB_SPEED_SUPER: 3445 | udev->maxpkt = 1024; 3446 | break; 3447 | default: 3448 | udev->maxpkt = 1024; 3449 | break; 3450 | } 3451 | 3452 | uvc_events_init (udev); 3453 | 3454 | unsigned int fmt_flags = 0; 3455 | //fmt_flags |= V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; 3456 | unsigned int capabilities = V4L2_CAP_VIDEO_CAPTURE; 3457 | unsigned int buftype = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3458 | enum v4l2_memory memtype = V4L2_MEMORY_MMAP; 3459 | int vret; 3460 | //enum v4l2_field field = V4L2_FIELD_ANY; 3461 | enum v4l2_field field = V4L2_FIELD_INTERLACED; // for eos m 3462 | //field = v4l2_field_from_string 3463 | bcm_host_init (); 3464 | vdev->nbufs = vnbufs; 3465 | 3466 | //info = v4l2_format_by_name ("UYVY"); 3467 | //pixelformat = info->fourcc; 3468 | vret = video_querycap (vdev, &capabilities); 3469 | vret = cap_get_buf_type (capabilities); 3470 | 3471 | video_set_buf_type (vdev, vret); 3472 | 3473 | vdev->memtype = memtype; 3474 | //make user selectable dimensions and field type 3475 | ret = video_set_format (vdev, v4l2_width, v4l2_height, pixelformat, 0, 0, field, fmt_flags); // generic setup 3476 | //ret = video_set_format (vdev, 1280, 720, V4L2_PIX_FMT_UYVY, 0, 0, field, fmt_flags); // eos m5 ok 3477 | //ret = video_set_format (vdev, 720 , 480, V4L2_PIX_FMT_RGB24, 0, 0, V4L2_FIELD_INTERLACED, fmt_flags); // eos m 3478 | // rGB24 is need for interlaced 3479 | if ( ret < 0 ) 3480 | printf("Cannot set %s to %c%c%c%c\n", vdev->v4l2_devname, pixfmtstr(pixelformat)); 3481 | //video_set_format (vdev, 1280, 720, V4L2_PIX_FMT_RGB24, 0, 0, field, fmt_flags);//colours wrong 3482 | 3483 | video_set_dv_timings (vdev); 3484 | video_get_format (vdev); 3485 | setup_mmal_comp (vdev, vnbufs); 3486 | // video_set_quality (&dev, -1) 3487 | //videhhh_prepare_capture (vdev, vnbufs);//, 0, "", BUFFER_FILL_NONE); 3488 | video_alloc_buffers (vdev, vnbufs ); 3489 | //enable_mmal_input (vdev); 3490 | //print ("before video q all buf\n"); 3491 | //video_queue_all_buffers(vdev, BUFFER_FILL_NONE); 3492 | //printf("uvc; udev->width %d, udev->height %d\n", udev->width, udev->height); 3493 | v4l2_dump ("process", vdev); 3494 | vdev->dq_ubuf_ok = true; 3495 | /* Init UVC events. */ 3496 | last = start; 3497 | while (1) { 3498 | FD_ZERO (&fdsv); 3499 | FD_ZERO (&fdsu); 3500 | /* We want both setup and data events on UVC interface.. */ 3501 | FD_SET (udev->uvc_fd, &fdsu); 3502 | 3503 | fd_set efds = fdsu; //events 3504 | fd_set dfds = fdsu; //write 3505 | /* Timeout. */ 3506 | tv.tv_sec = 2; 3507 | tv.tv_usec = 0; 3508 | /* ..but only data events on V4L2 interface */ 3509 | FD_SET (vdev->v4l2_fd, &fdsv); 3510 | 3511 | //if (1) { 3512 | nfds = max (vdev->v4l2_fd, udev->uvc_fd); 3513 | ret = select (nfds + 1, &fdsv, &dfds, &efds, &tv); 3514 | 3515 | if (ret == -1) { 3516 | printf ("select error %d, %s\n", errno, strerror (errno)); 3517 | continue; 3518 | if (errno == EINTR) 3519 | continue; 3520 | break; 3521 | } 3522 | 3523 | if (ret == 0) { 3524 | printf ("select timeout\n"); 3525 | break; 3526 | } 3527 | 3528 | if (FD_ISSET (udev->uvc_fd, &efds)) 3529 | uvc_events_process (udev); 3530 | 3531 | if (FD_ISSET (udev->uvc_fd, &dfds)) //write 3532 | uvc_video_process (udev); 3533 | 3534 | if (FD_ISSET (vdev->v4l2_fd, &fdsv)) { //reading 3535 | ret = v4l2_process_data (vdev); 3536 | if (ret == 1) { 3537 | print ("v4l2_process-data failed\n"); 3538 | break; 3539 | 3540 | } 3541 | } 3542 | // add some delay when not streaming to reduce cpu in while loop 3543 | if (!vdev->is_streaming && !udev->is_streaming) 3544 | nanosleep ((const struct timespec[]) { {0, 5000000L} }, NULL); 3545 | } 3546 | printf ("closing"); 3547 | if (vdev->is_streaming) { 3548 | /* Stop V4L2 streaming... */ 3549 | video_enable (vdev, 0); //v4l2_stop_capturing (vdev); 3550 | video_free_buffers (vdev); 3551 | 3552 | for (unsigned int i = 0; i < vdev->nbufs; ++i) { 3553 | video_buffer_munmap (vdev, &vdev->buffers[i]); 3554 | } 3555 | vdev->is_streaming = 0; 3556 | } 3557 | 3558 | if (udev->is_streaming) { 3559 | /* ... and now UVC streaming.. */ 3560 | uvc_video_stream (udev, 0); 3561 | //uvc_uninit_device (udev); 3562 | uvc_video_reqbufs_userptr (udev, 0); 3563 | udev->is_streaming = 0; 3564 | } 3565 | 3566 | video_close (vdev); 3567 | 3568 | uvc_video_close (udev); 3569 | 3570 | return 0; 3571 | } 3572 | 3573 | -------------------------------------------------------------------------------- /v4l2-mmal-uvc.h: -------------------------------------------------------------------------------- 1 | //#ifndef YAVTA_RAW_H_INCLUDED 2 | //#define YAVTA_RAW_H_INCLUDED 3 | #define ARRAY_SIZE(a) ((sizeof(a) / sizeof(a[0]))) 4 | #include 5 | extern int debug; 6 | //#define print(...) do { if (debug) printf(__VA_ARGS__); } while (0) 7 | 8 | #define MMAL_ENCODING_UNUSED 0 9 | struct v4l2_format_info 10 | { 11 | const char *name; 12 | unsigned int fourcc; 13 | unsigned char n_planes; 14 | MMAL_FOURCC_T mmal_encoding; 15 | }; 16 | 17 | const struct v4l2_format_info pixel_formats[] = { 18 | {"RGB332", V4L2_PIX_FMT_RGB332, 1, MMAL_ENCODING_UNUSED}, 19 | {"RGB444", V4L2_PIX_FMT_RGB444, 1, MMAL_ENCODING_UNUSED}, 20 | {"ARGB444", V4L2_PIX_FMT_ARGB444, 1, MMAL_ENCODING_UNUSED}, 21 | {"XRGB444", V4L2_PIX_FMT_XRGB444, 1, MMAL_ENCODING_UNUSED}, 22 | {"RGB555", V4L2_PIX_FMT_RGB555, 1, MMAL_ENCODING_UNUSED}, 23 | {"ARGB555", V4L2_PIX_FMT_ARGB555, 1, MMAL_ENCODING_UNUSED}, 24 | {"XRGB555", V4L2_PIX_FMT_XRGB555, 1, MMAL_ENCODING_UNUSED}, 25 | {"RGB565", V4L2_PIX_FMT_RGB565, 1, MMAL_ENCODING_UNUSED}, 26 | {"RGB555X", V4L2_PIX_FMT_RGB555X, 1, MMAL_ENCODING_UNUSED}, 27 | {"RGB565X", V4L2_PIX_FMT_RGB565X, 1, MMAL_ENCODING_RGB16}, 28 | {"BGR666", V4L2_PIX_FMT_BGR666, 1, MMAL_ENCODING_UNUSED}, 29 | {"BGR24", V4L2_PIX_FMT_BGR24, 1, MMAL_ENCODING_RGB24}, 30 | {"RGB24", V4L2_PIX_FMT_RGB24, 1, MMAL_ENCODING_BGR24}, 31 | {"BGR32", V4L2_PIX_FMT_BGR32, 1, MMAL_ENCODING_BGR32}, 32 | {"ABGR32", V4L2_PIX_FMT_ABGR32, 1, MMAL_ENCODING_BGRA}, 33 | {"XBGR32", V4L2_PIX_FMT_XBGR32, 1, MMAL_ENCODING_BGR32}, 34 | {"RGB32", V4L2_PIX_FMT_RGB32, 1, MMAL_ENCODING_RGB32}, 35 | {"ARGB32", V4L2_PIX_FMT_ARGB32, 1, MMAL_ENCODING_ARGB}, 36 | {"XRGB32", V4L2_PIX_FMT_XRGB32, 1, MMAL_ENCODING_UNUSED}, 37 | {"HSV24", V4L2_PIX_FMT_HSV24, 1, MMAL_ENCODING_UNUSED}, 38 | {"HSV32", V4L2_PIX_FMT_HSV32, 1, MMAL_ENCODING_UNUSED}, 39 | {"Y8", V4L2_PIX_FMT_GREY, 1, MMAL_ENCODING_UNUSED}, 40 | {"Y10", V4L2_PIX_FMT_Y10, 1, MMAL_ENCODING_UNUSED}, 41 | {"Y12", V4L2_PIX_FMT_Y12, 1, MMAL_ENCODING_UNUSED}, 42 | {"Y16", V4L2_PIX_FMT_Y16, 1, MMAL_ENCODING_UNUSED}, 43 | {"UYVY", V4L2_PIX_FMT_UYVY, 1, MMAL_ENCODING_UYVY}, 44 | {"VYUY", V4L2_PIX_FMT_VYUY, 1, MMAL_ENCODING_VYUY}, 45 | {"YUYV", V4L2_PIX_FMT_YUYV, 1, MMAL_ENCODING_YUYV}, 46 | {"YVYU", V4L2_PIX_FMT_YVYU, 1, MMAL_ENCODING_YVYU}, 47 | {"NV12", V4L2_PIX_FMT_NV12, 1, MMAL_ENCODING_NV12}, 48 | {"NV12M", V4L2_PIX_FMT_NV12M, 2, MMAL_ENCODING_UNUSED}, 49 | {"NV21", V4L2_PIX_FMT_NV21, 1, MMAL_ENCODING_NV21}, 50 | {"NV21M", V4L2_PIX_FMT_NV21M, 2, MMAL_ENCODING_UNUSED}, 51 | {"NV16", V4L2_PIX_FMT_NV16, 1, MMAL_ENCODING_UNUSED}, 52 | {"NV16M", V4L2_PIX_FMT_NV16M, 2, MMAL_ENCODING_UNUSED}, 53 | {"NV61", V4L2_PIX_FMT_NV61, 1, MMAL_ENCODING_UNUSED}, 54 | {"NV61M", V4L2_PIX_FMT_NV61M, 2, MMAL_ENCODING_UNUSED}, 55 | {"NV24", V4L2_PIX_FMT_NV24, 1, MMAL_ENCODING_UNUSED}, 56 | {"NV42", V4L2_PIX_FMT_NV42, 1, MMAL_ENCODING_UNUSED}, 57 | {"YUV420M", V4L2_PIX_FMT_YUV420M, 3, MMAL_ENCODING_UNUSED}, 58 | {"YUV422M", V4L2_PIX_FMT_YUV422M, 3, MMAL_ENCODING_UNUSED}, 59 | {"YUV444M", V4L2_PIX_FMT_YUV444M, 3, MMAL_ENCODING_UNUSED}, 60 | {"YVU420M", V4L2_PIX_FMT_YVU420M, 3, MMAL_ENCODING_UNUSED}, 61 | {"YVU422M", V4L2_PIX_FMT_YVU422M, 3, MMAL_ENCODING_UNUSED}, 62 | {"YVU444M", V4L2_PIX_FMT_YVU444M, 3, MMAL_ENCODING_UNUSED}, 63 | {"SBGGR8", V4L2_PIX_FMT_SBGGR8, 1, MMAL_ENCODING_BAYER_SBGGR8}, 64 | {"SGBRG8", V4L2_PIX_FMT_SGBRG8, 1, MMAL_ENCODING_BAYER_SGBRG8}, 65 | {"SGRBG8", V4L2_PIX_FMT_SGRBG8, 1, MMAL_ENCODING_BAYER_SGRBG8}, 66 | {"SRGGB8", V4L2_PIX_FMT_SRGGB8, 1, MMAL_ENCODING_BAYER_SRGGB8}, 67 | {"SBGGR10_DPCM8", V4L2_PIX_FMT_SBGGR10DPCM8, 1, MMAL_ENCODING_UNUSED}, 68 | {"SGBRG10_DPCM8", V4L2_PIX_FMT_SGBRG10DPCM8, 1, MMAL_ENCODING_UNUSED}, 69 | {"SGRBG10_DPCM8", V4L2_PIX_FMT_SGRBG10DPCM8, 1, MMAL_ENCODING_UNUSED}, 70 | {"SRGGB10_DPCM8", V4L2_PIX_FMT_SRGGB10DPCM8, 1, MMAL_ENCODING_UNUSED}, 71 | {"SBGGR10", V4L2_PIX_FMT_SBGGR10, 1, MMAL_ENCODING_UNUSED}, 72 | {"SGBRG10", V4L2_PIX_FMT_SGBRG10, 1, MMAL_ENCODING_UNUSED}, 73 | {"SGRBG10", V4L2_PIX_FMT_SGRBG10, 1, MMAL_ENCODING_UNUSED}, 74 | {"SRGGB10", V4L2_PIX_FMT_SRGGB10, 1, MMAL_ENCODING_UNUSED}, 75 | {"SBGGR10P", V4L2_PIX_FMT_SBGGR10P, 1, MMAL_ENCODING_BAYER_SBGGR10P}, 76 | {"SGBRG10P", V4L2_PIX_FMT_SGBRG10P, 1, MMAL_ENCODING_BAYER_SGBRG10P}, 77 | {"SGRBG10P", V4L2_PIX_FMT_SGRBG10P, 1, MMAL_ENCODING_BAYER_SGRBG10P}, 78 | {"SRGGB10P", V4L2_PIX_FMT_SRGGB10P, 1, MMAL_ENCODING_BAYER_SRGGB10P}, 79 | {"SBGGR12", V4L2_PIX_FMT_SBGGR12, 1, MMAL_ENCODING_UNUSED}, 80 | {"SGBRG12", V4L2_PIX_FMT_SGBRG12, 1, MMAL_ENCODING_UNUSED}, 81 | {"SGRBG12", V4L2_PIX_FMT_SGRBG12, 1, MMAL_ENCODING_UNUSED}, 82 | {"SRGGB12", V4L2_PIX_FMT_SRGGB12, 1, MMAL_ENCODING_UNUSED}, 83 | {"DV", V4L2_PIX_FMT_DV, 1, MMAL_ENCODING_UNUSED}, 84 | {"MJPEG", V4L2_PIX_FMT_MJPEG, 1, MMAL_ENCODING_UNUSED}, 85 | {"MPEG", V4L2_PIX_FMT_MPEG, 1, MMAL_ENCODING_UNUSED}, 86 | }; 87 | 88 | enum buffer_fill_mode 89 | { 90 | BUFFER_FILL_NONE = 0, 91 | BUFFER_FILL_FRAME = 1 << 0, 92 | BUFFER_FILL_PADDING = 1 << 1, 93 | }; 94 | 95 | 96 | 97 | /* Buffer representing one video frame */ 98 | 99 | struct buffer 100 | { 101 | struct v4l2_buffer buf; 102 | void *start; 103 | size_t length; 104 | }; 105 | 106 | struct mmal_buffer 107 | { 108 | unsigned int idx; 109 | unsigned int padding[VIDEO_MAX_PLANES]; 110 | unsigned int size[VIDEO_MAX_PLANES]; 111 | void *mem[VIDEO_MAX_PLANES]; 112 | MMAL_BUFFER_HEADER_T *mmal; 113 | int dma_fd; 114 | unsigned int vcsm_handle; 115 | }; 116 | 117 | /* Represents a UVC based video output device */ 118 | struct uvc_device 119 | { 120 | // uvc device specific / 121 | int uvc_fd; 122 | int is_streaming; 123 | //nint run_standalone; 124 | char *uvc_devname; 125 | 126 | // uvc control request specific // 127 | struct uvc_streaming_control probe; 128 | struct uvc_streaming_control commit; 129 | int control; 130 | struct uvc_request_data request_error_code; 131 | unsigned int brightness_val; 132 | 133 | // uvc buffer specific // 134 | //enum io_method io; 135 | enum v4l2_memory memtype; 136 | struct buffer *mem; 137 | 138 | //struct buffer *dummy_buf; 139 | unsigned int nbufs; 140 | unsigned int fcc; 141 | unsigned int width; 142 | unsigned int height; 143 | 144 | unsigned int bulk; 145 | uint8_t color; 146 | unsigned int imgsize; 147 | void *imgdata; 148 | 149 | // USB speed specific // 150 | int mult; 151 | int burst; 152 | int maxpkt; 153 | enum usb_device_speed speed; 154 | 155 | // uvc specific flags // 156 | int first_buffer_queued; 157 | int uvc_shutdown_requested; 158 | int uvc_streamon; 159 | // uvc buffer queue and dequeue counters // 160 | unsigned long long int qbuf_count; 161 | unsigned long long int dqbuf_count; 162 | 163 | // v4l2 device hook // 164 | struct v4l2_device *vdev; 165 | }; 166 | 167 | /* Represents a V4L2 based video capture device */ 168 | struct v4l2_device 169 | { 170 | /* v4l2 device specific */ 171 | int v4l2_fd; 172 | int opened; 173 | int fd2; 174 | int opened2; 175 | int is_streaming; 176 | char *v4l2_devname; 177 | int mjpeg_fd; 178 | int counter; 179 | /* v4l2 buffer specific */ 180 | struct buffer *mem; //store /dev/video0 buffer info 181 | unsigned int nbufs; 182 | struct mmal_buffer *buffers; 183 | 184 | enum v4l2_buf_type type; 185 | enum v4l2_memory memtype; 186 | //enum io_method io; 187 | /// 188 | unsigned int width; 189 | unsigned int height; 190 | unsigned int fps; 191 | unsigned int frame_time_usec; 192 | uint32_t buffer_output_flags; 193 | uint32_t timestamp_type; 194 | struct timeval starttime; 195 | int64_t lastpts; 196 | struct timespec last ; //general timer tracking 197 | /* v4l2 buffer queue and dequeue counters */ 198 | unsigned long long int qbuf_count; 199 | unsigned long long int dqbuf_count; 200 | int bitrate; 201 | 202 | /* uvc device hook */ 203 | struct uvc_device *udev; 204 | 205 | MMAL_FOURCC_T dst_mmal_enc; 206 | //mmal setup 207 | 208 | VCOS_THREAD_T save_thread; 209 | MMAL_QUEUE_T *save_queue; 210 | int thread_quit; 211 | 212 | MMAL_COMPONENT_T *isp; 213 | MMAL_COMPONENT_T *render; 214 | MMAL_COMPONENT_T *encoder; 215 | MMAL_POOL_T *isp_output_pool; 216 | MMAL_POOL_T *render_pool; 217 | MMAL_POOL_T *encode_pool; 218 | 219 | MMAL_BOOL_T can_zero_copy; 220 | 221 | /* V4L2 to MMAL interface */ 222 | MMAL_QUEUE_T *isp_queue; 223 | MMAL_POOL_T *mmal_pool; 224 | /* Encoded data */ 225 | MMAL_POOL_T *output_pool; 226 | 227 | 228 | unsigned char num_planes; 229 | struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; 230 | struct v4l2_buffer vbuf; 231 | void *pattern[VIDEO_MAX_PLANES]; 232 | unsigned int patternsize[VIDEO_MAX_PLANES]; 233 | bool write_data_prefix; 234 | 235 | bool dq_ubuf_ok; 236 | }; 237 | //extern const char * v4l2_format_name (unsigned int fourcc); 238 | //extern const struct v4l2_format_info * v4l2_format_by_name (const char *name); 239 | //extern const struct v4l2_format_info * v4l2_format_by_fourcc (unsigned int ); 240 | 241 | --------------------------------------------------------------------------------