├── .github ├── FUNDING.yml └── workflows │ └── linux.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── COPYING ├── ChangeLog ├── README.md ├── akvcam.kdev4 ├── package_info.conf.in ├── ports ├── ci │ └── linux │ │ ├── build.sh │ │ ├── deploy.sh │ │ └── install_deps.sh └── deploy │ ├── installscript.posix.qs │ └── setup.sh ├── share ├── config_example.ini └── examples │ └── output.c └── src ├── Makefile ├── attributes.c ├── attributes.h ├── buffers.c ├── buffers.h ├── buffers_types.h ├── controls.c ├── controls.h ├── controls_types.h ├── device.c ├── device.h ├── device_types.h ├── dkms.conf ├── driver.c ├── driver.h ├── file_read.c ├── file_read.h ├── format.c ├── format.h ├── format_types.h ├── frame.c ├── frame.h ├── frame_filter.c ├── frame_filter.h ├── frame_filter_types.h ├── frame_types.h ├── ioctl.c ├── ioctl.h ├── list.c ├── list.h ├── list_types.h ├── log.c ├── log.h ├── map.c ├── map.h ├── module.c ├── rbuffer.c ├── rbuffer.h ├── settings.c ├── settings.h ├── utils.c └── utils.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: hipersayanX 2 | patreon: hipersayanx 3 | ko_fi: hipersayanx 4 | liberapay: hipersayanx 5 | custom: ["https://www.paypal.me/WebcamoidDonations", "https://buymeacoffee.com/hipersayanx", "https://webcamoid.github.io/donations#crypto"] 6 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux Ubuntu 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | QTIFWVER: 4.8.0 7 | 8 | jobs: 9 | build: 10 | name: Ubuntu x86_64 11 | runs-on: ubuntu-latest 12 | strategy: 13 | max-parallel: 10 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - dockerimg: amd64/ubuntu:focal 18 | repository: v4.19.316 19 | kernel_version: 4.19.316-0419316 20 | kernel_version_c: 202406161134 21 | upload: 1 22 | - dockerimg: amd64/ubuntu:focal 23 | repository: v5.4.278 24 | kernel_version: 5.4.278-0504278 25 | kernel_version_c: 202406161242 26 | upload: 0 27 | # Do not update 28 | - dockerimg: amd64/ubuntu:jammy 29 | repository: v5.10.130 30 | kernel_version: 5.10.130-0510130 31 | kernel_version_c: 202207121545 32 | upload: 0 33 | - dockerimg: amd64/ubuntu:jammy 34 | repository: v5.15.161 35 | kernel_version: 5.15.161-0515161 36 | kernel_version_c: 202406161242 37 | upload: 0 38 | - dockerimg: amd64/ubuntu:mantic 39 | repository: v6.1.95 40 | kernel_version: 6.1.95-060195 41 | kernel_version_c: 202406211343 42 | upload: 0 43 | - dockerimg: amd64/ubuntu:oracular 44 | repository: v6.6.35 45 | kernel_version: 6.6.35-060635 46 | kernel_version_c: 202406210941 47 | upload: 0 48 | - dockerimg: amd64/ubuntu:noble 49 | repository: v6.8.12 50 | kernel_version: 6.8.12-060812 51 | kernel_version_c: 202405300722 52 | upload: 0 53 | - dockerimg: amd64/ubuntu:noble 54 | repository: v6.9.3 55 | kernel_version: 6.9.3-060903 56 | kernel_version_c: 202405300957 57 | upload: 0 58 | steps: 59 | - uses: actions/checkout@v2 60 | - name: Release build 61 | uses: addnab/docker-run-action@v3 62 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 63 | with: 64 | username: ${{ secrets.DOCKER_USERNAME }} 65 | password: ${{ secrets.DOCKER_PASSWORD }} 66 | registry: gcr.io 67 | image: ${{ matrix.dockerimg }} 68 | options: >- 69 | --privileged 70 | -v ${{ github.workspace }}:/sources 71 | -e GITHUB_REF=${{ env.GITHUB_REF }} 72 | -e GITHUB_SERVER_URL=${{ env.GITHUB_SERVER_URL }} 73 | -e GITHUB_REPOSITORY=${{ env.GITHUB_REPOSITORY }} 74 | -e GITHUB_RUN_ID=${{ env.GITHUB_RUN_ID }} 75 | -e DOCKERIMG=${{ matrix.dockerimg }} 76 | -e REPOSITORY=${{ matrix.repository }} 77 | -e KERNEL_VERSION=${{ matrix.kernel_version }} 78 | -e KERNEL_VERSION_C=${{ matrix.kernel_version_c }} 79 | -e ARCHITECTURE=amd64 80 | -e QTIFWVER=${{ env.QTIFWVER }} 81 | -e UPLOAD=${{ matrix.upload }} 82 | run: | 83 | cd /sources 84 | echo 85 | echo Install dependencies 86 | echo 87 | chmod +x ports/ci/linux/install_deps.sh 88 | ./ports/ci/linux/install_deps.sh 89 | echo 90 | echo Release Build 91 | echo 92 | chmod +x ports/ci/linux/build.sh 93 | ./ports/ci/linux/build.sh 94 | echo 95 | echo Release Deploy 96 | echo 97 | chmod +x ports/ci/linux/deploy.sh 98 | ./ports/ci/linux/deploy.sh 99 | - name: Daily build 100 | uses: addnab/docker-run-action@v3 101 | if: ${{ !startsWith(github.ref, 'refs/tags/') }} 102 | with: 103 | username: ${{ secrets.DOCKER_USERNAME }} 104 | password: ${{ secrets.DOCKER_PASSWORD }} 105 | registry: gcr.io 106 | image: ${{ matrix.dockerimg }} 107 | options: >- 108 | --privileged 109 | -v ${{ github.workspace }}:/sources 110 | -e GITHUB_REF=${{ env.GITHUB_REF }} 111 | -e GITHUB_SERVER_URL=${{ env.GITHUB_SERVER_URL }} 112 | -e GITHUB_REPOSITORY=${{ env.GITHUB_REPOSITORY }} 113 | -e GITHUB_RUN_ID=${{ env.GITHUB_RUN_ID }} 114 | -e DOCKERIMG=${{ matrix.dockerimg }} 115 | -e REPOSITORY=${{ matrix.repository }} 116 | -e KERNEL_VERSION=${{ matrix.kernel_version }} 117 | -e KERNEL_VERSION_C=${{ matrix.kernel_version_c }} 118 | -e ARCHITECTURE=amd64 119 | -e QTIFWVER=${{ env.QTIFWVER }} 120 | -e UPLOAD=${{ matrix.upload }} 121 | -e DAILY_BUILD=1 122 | run: | 123 | cd /sources 124 | echo 125 | echo Install dependencies 126 | echo 127 | chmod +x ports/ci/linux/install_deps.sh 128 | ./ports/ci/linux/install_deps.sh 129 | echo 130 | echo Daily Build 131 | echo 132 | chmod +x ports/ci/linux/build.sh 133 | ./ports/ci/linux/build.sh 134 | echo 135 | echo Daily Deploy 136 | echo 137 | chmod +x ports/ci/linux/deploy.sh 138 | ./ports/ci/linux/deploy.sh 139 | - name: Release Upload 140 | uses: softprops/action-gh-release@v1 141 | if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.upload }} 142 | with: 143 | files: packages-v4.19/* 144 | env: 145 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 146 | - name: Daily Build Upload 147 | uses: softprops/action-gh-release@v1 148 | if: ${{ !startsWith(github.ref, 'refs/tags/') && matrix.upload }} 149 | with: 150 | body: "${{ github.event.head_commit.message }} (commit: ${{ github.sha }})
**Note**: Ignore the commit information of the tag, the files in the release keep updating with every new build, these packages were built from ${{ github.sha }} commit." 151 | prerelease: true 152 | files: packages-v4.19/* 153 | name: Daily Build 154 | tag_name: daily-build 155 | env: 156 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 157 | # multiarch: 158 | # name: Ubuntu Multiarch 159 | # runs-on: ubuntu-latest 160 | # strategy: 161 | # max-parallel: 10 162 | # fail-fast: false 163 | # matrix: 164 | # include: 165 | # - distro: ubuntu_latest 166 | # architecture: aarch64 167 | # repository: v6.8.12 168 | # kernel_version: 6.8.12-060812 169 | # kernel_version_c: 202405300722 170 | # upload: 1 171 | # - distro: ubuntu_latest 172 | # architecture: aarch64 173 | # repository: v6.9.3 174 | # kernel_version: 6.9.3-060903 175 | # kernel_version_c: 202405300957 176 | # upload: 0 177 | # steps: 178 | # - uses: actions/checkout@v2 179 | # - name: Release build 180 | # uses: uraimo/run-on-arch-action@v2 181 | # if: ${{ startsWith(github.ref, 'refs/tags/') }} 182 | # with: 183 | # arch: ${{ matrix.architecture }} 184 | # distro: ${{ matrix.distro }} 185 | # githubToken: ${{ github.token }} 186 | # dockerRunArgs: | 187 | # --privileged 188 | # -v "${{ github.workspace }}:/sources" 189 | # env: | 190 | # GITHUB_REF: "${{ env.GITHUB_REF }}" 191 | # GITHUB_SERVER_URL: "${{ env.GITHUB_SERVER_URL }}" 192 | # GITHUB_REPOSITORY: "${{ env.GITHUB_REPOSITORY }}" 193 | # GITHUB_RUN_ID: "${{ env.GITHUB_RUN_ID }}" 194 | # REPOSITORY: ${{ matrix.repository }} 195 | # ARCHITECTURE: ${{ matrix.architecture }} 196 | # KERNEL_VERSION: ${{ matrix.kernel_version }} 197 | # KERNEL_VERSION_C: ${{ matrix.kernel_version_c }} 198 | # QTIFWVER: ${{ env.QTIFWVER }} 199 | # UPLOAD: ${{ matrix.upload }} 200 | # run: | 201 | # cd /sources 202 | # echo 203 | # echo Install dependencies 204 | # echo 205 | # chmod +x ports/ci/linux/install_deps.sh 206 | # ./ports/ci/linux/install_deps.sh 207 | # echo 208 | # echo Daily Build 209 | # echo 210 | # chmod +x ports/ci/linux/build.sh 211 | # ./ports/ci/linux/build.sh 212 | # echo 213 | # echo Daily Deploy 214 | # echo 215 | # chmod +x ports/ci/linux/deploy.sh 216 | # ./ports/ci/linux/deploy.sh 217 | # - name: Daily build 218 | # uses: uraimo/run-on-arch-action@v2 219 | # if: ${{ !startsWith(github.ref, 'refs/tags/') }} 220 | # with: 221 | # arch: ${{ matrix.architecture }} 222 | # distro: ${{ matrix.distro }} 223 | # githubToken: ${{ github.token }} 224 | # dockerRunArgs: | 225 | # --privileged 226 | # -v "${{ github.workspace }}:/sources" 227 | # env: | 228 | # GITHUB_REF: "${{ env.GITHUB_REF }}" 229 | # GITHUB_SERVER_URL: "${{ env.GITHUB_SERVER_URL }}" 230 | # GITHUB_REPOSITORY: "${{ env.GITHUB_REPOSITORY }}" 231 | # GITHUB_RUN_ID: "${{ env.GITHUB_RUN_ID }}" 232 | # REPOSITORY: ${{ matrix.repository }} 233 | # ARCHITECTURE: ${{ matrix.architecture }} 234 | # KERNEL_VERSION: ${{ matrix.kernel_version }} 235 | # KERNEL_VERSION_C: ${{ matrix.kernel_version_c }} 236 | # QTIFWVER: ${{ env.QTIFWVER }} 237 | # DAILY_BUILD: 1 238 | # run: | 239 | # cd /sources 240 | # echo 241 | # echo Install dependencies 242 | # echo 243 | # chmod +x ports/ci/linux/install_deps.sh 244 | # ./ports/ci/linux/install_deps.sh 245 | # echo 246 | # echo Daily Build 247 | # echo 248 | # chmod +x ports/ci/linux/build.sh 249 | # ./ports/ci/linux/build.sh 250 | # echo 251 | # echo Daily Deploy 252 | # echo 253 | # chmod +x ports/ci/linux/deploy.sh 254 | # ./ports/ci/linux/deploy.sh 255 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | !src/Makefile 3 | *.user 4 | *.pro.user.* 5 | *.cmd 6 | *.mk 7 | *.mod 8 | *.mod.c 9 | *.mod.o 10 | *.o 11 | *.ko 12 | *.so 13 | *.so.* 14 | Module.symvers 15 | modules.order 16 | .qmake.stash 17 | *.kate-swp 18 | *.tmp 19 | *.o.d 20 | *.ur-safe 21 | __pycache__ 22 | packages_auto 23 | .kdev4 24 | CMakeLists.txt.user.* 25 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # akvcam, virtual camera for Linux. 2 | # Copyright (C) 2021 Gonzalo Exequiel Pedone 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | cmake_minimum_required(VERSION 3.14) 19 | 20 | project(akvcam LANGUAGES C) 21 | 22 | set(CMAKE_VERBOSE_MAKEFILE ON) 23 | execute_process(COMMAND uname -r 24 | OUTPUT_VARIABLE KERNEL_RELEASE 25 | OUTPUT_STRIP_TRAILING_WHITESPACE) 26 | set(KERNEL_DIR /lib/modules/${KERNEL_RELEASE}/build CACHE PATH "Kernel sources") 27 | set(USE_SPARSE OFF CACHE BOOL "Use sparse for bug catching") 28 | set(SPARSE_MODE 2 CACHE STRING "Sparse mode") 29 | set(DAILY_BUILD OFF CACHE BOOL "Mark this as a daily build") 30 | 31 | if (USE_SPARSE) 32 | set(MAKE_CMD make KERNEL_DIR=${KERNEL_DIR} USE_SPARSE=1 SPARSE_MODE=${SPARSE_MODE}) 33 | else () 34 | set(MAKE_CMD make KERNEL_DIR=${KERNEL_DIR}) 35 | endif () 36 | 37 | file(GLOB_RECURSE MODULE_SOURCES 38 | RELATIVE ${CMAKE_SOURCE_DIR} 39 | CONFIGURE_DEPENDS 40 | ${CMAKE_SOURCE_DIR}/src/*.h 41 | ${CMAKE_SOURCE_DIR}/src/*.c) 42 | 43 | foreach(SOURCE_FILE ${MODULE_SOURCES}) 44 | configure_file(${SOURCE_FILE} ${SOURCE_FILE} COPYONLY) 45 | endforeach() 46 | 47 | configure_file(src/dkms.conf src/dkms.conf COPYONLY) 48 | configure_file(src/Makefile src/Makefile COPYONLY) 49 | add_custom_target(akvcam ALL ${MAKE_CMD} 50 | BYPRODUCTS src/akvcam.ko 51 | WORKING_DIRECTORY src 52 | VERBATIM 53 | SOURCES src/dkms.conf 54 | src/Makefile 55 | share/config_example.ini) 56 | 57 | # This is a hack for making possible for the IDE to detect the kernel include 58 | # directories. Don't try to build it, because it won't. 59 | add_library(akvcam-sources STATIC EXCLUDE_FROM_ALL ${MODULE_SOURCES}) 60 | include_directories(src 61 | ${KERNEL_DIR}/include 62 | ${KERNEL_DIR}/include/linux 63 | ${KERNEL_DIR}/include/uapi 64 | ${KERNEL_DIR}/arch/x86/include 65 | ${KERNEL_DIR}/arch/x86/include/generated) 66 | add_definitions(-D__KERNEL__ 67 | -DCONFIG_COMPAT 68 | -DCONFIG_HZ=300 69 | -DCONFIG_PAGE_OFFSET=0 70 | -DCONFIG_PCI 71 | -DKBUILD_MODNAME="") 72 | 73 | file(READ src/Makefile SRC_MAKEFILE) 74 | string(REGEX MATCH "MODULE_VERSION *= *([0-9]+.[0-9]+.[0-9]+)" _ ${SRC_MAKEFILE}) 75 | set(VERSION ${CMAKE_MATCH_1}) 76 | set(QTIFW_TARGET_DIR "\@ApplicationsDir\@/akvcam") 77 | configure_file(package_info.conf.in package_info.conf) 78 | 79 | install(FILES ${MODULE_SOURCES} 80 | src/dkms.conf 81 | src/Makefile 82 | COPYING 83 | DESTINATION .) 84 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # akvcam Individual Contributor License Agreement # 2 | 3 | Thank you for your interest in contributing to akvcam ("We" or "Us"). 4 | 5 | This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please sign it and send it to Us by email, following the instructions at CONTRIBUTING.md. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us. 6 | 7 | You are accepting this agreement by making a pull request to the repository. 8 | 9 | ## 1. Definitions ## 10 | 11 | "You" means the individual who Submits a Contribution to Us. 12 | 13 | "Contribution" means any work of authorship that is Submitted by You to Us in which You own or assert ownership of the Copyright. If You do not own the Copyright in the entire work of authorship, please follow the instructions in CONTRIBUTING.md. 14 | 15 | "Copyright" means all rights protecting works of authorship owned or controlled by You, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence including any extensions by You. 16 | 17 | "Material" means the work of authorship which is made available by Us to third parties. When this Agreement covers more than one software project, the Material means the work of authorship to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material. 18 | 19 | "Submit" means any form of electronic, verbal, or written communication sent to Us or our representatives, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us for the purpose of discussing and improving the Material, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 20 | 21 | "Submission Date" means the date on which You Submit a Contribution to Us. 22 | 23 | "Effective Date" means the date You execute this Agreement or the date You first Submit a Contribution to Us, whichever is earlier. 24 | 25 | "Media" means any portion of a Contribution which is not software. 26 | 27 | ## 2. Grant of Rights ## 28 | 29 | ### 2.1 Copyright License ### 30 | 31 | (a) You retain ownership of the Copyright in Your Contribution and have the same rights to use or license the Contribution which You would have had without entering into the Agreement. 32 | 33 | (b) To the maximum extent permitted by the relevant law, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable license under the Copyright covering the Contribution, with the right to sublicense such rights through multiple tiers of sublicensees, to reproduce, modify, display, perform and distribute the Contribution as part of the Material; provided that this license is conditioned upon compliance with Section 2.3. 34 | 35 | ### 2.2 Patent License ### 36 | 37 | For patent claims including, without limitation, method, process, and apparatus claims which You own, control or have the right to grant, now or in the future, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable patent license, with the right to sublicense these rights to multiple tiers of sublicensees, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with the Material (and portions of such combination). This license is granted only to the extent that the exercise of the licensed rights infringes such patent claims; and provided that this license is conditioned upon compliance with Section 2.3. 38 | 39 | ### 2.3 Outbound License ### 40 | 41 | As a condition on the grant of rights in Sections 2.1 and 2.2, We agree to license the Contribution only under the terms of the license or licenses which We are using on the Submission Date for the Material or any licenses on the Free Software Foundation's list of "Recommended copyleft licenses" on or after the Effective Date, whether or not such licenses are subsequently disapproved (including any right to adopt any future version of a license if permitted). 42 | 43 | In addition, We may use the following licenses for Media in the Contribution: Creative Commons Attribution Share Alike 3.0 (including any right to adopt any future version of a license if permitted). 44 | 45 | **2.4 Moral Rights.** If moral rights apply to the Contribution, to the maximum extent permitted by law, You waive and agree not to assert such moral rights against Us or our successors in interest, or any of our licensees, either direct or indirect. 46 | 47 | **2.5 Our Rights.** You acknowledge that We are not obligated to use Your Contribution as part of the Material and may decide to include any Contribution We consider appropriate. 48 | 49 | **2.6 Reservation of Rights.** Any rights not expressly licensed under this section are expressly reserved by You. 50 | 51 | ## 3. Agreement ## 52 | 53 | You confirm that: 54 | 55 | (a) You have the legal authority to enter into this Agreement. 56 | 57 | (b) You own the Copyright and patent claims covering the Contribution which are required to grant the rights under Section 2. 58 | 59 | (c) The grant of rights under Section 2 does not violate any grant of rights which You have made to third parties, including Your employer. If You are an employee, You have had Your employer approve this Agreement or sign the Entity version of this document. If You are less than eighteen years old, please have Your parents or guardian sign the Agreement. 60 | 61 | (d) You have followed the instructions in CONTRIBUTING.md, if You do not own the Copyright in the entire work of authorship Submitted. 62 | 63 | ## 4. Disclaimer ## 64 | 65 | EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW. 66 | 67 | ## 5. Consequential Damage Waiver ## 68 | 69 | TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED. 70 | 71 | ## 6. Miscellaneous ## 72 | 73 | 6.1 This Agreement will be governed by and construed in accordance with the laws of Argentina excluding its conflicts of law provisions. Under certain circumstances, the governing law in this section might be superseded by the United Nations Convention on Contracts for the International Sale of Goods ("UN Convention") and the parties intend to avoid the application of the UN Convention to this Agreement and, thus, exclude the application of the UN Convention in its entirety to this Agreement. 74 | 75 | 6.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings. 76 | 77 | 6.3 If You or We assign the rights or obligations received through this Agreement to a third party, as a condition of the assignment, that third party must agree in writing to abide by all the rights and obligations in the Agreement. 78 | 79 | 6.4 The failure of either party to require performance by the other party of any provision of this Agreement in one situation shall not affect the right of a party to require such performance at any time in the future. A waiver of performance under a provision in one situation shall not be considered a waiver of the performance of the provision in the future or a waiver of the provision in its entirety. 80 | 81 | 6.5 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and which is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law. 82 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | akvcam 1.2.6: 2 | 3 | - Minimum supported kernel version: 4.19. 4 | - Fix v6.8 renamed symbols (Thanks to @iam-TJ). 5 | 6 | akvcam 1.2.5: 7 | 8 | - Fixed missing V4L2_DEVICE_NAME_SIZE variable in Linux version > 6.7. 9 | 10 | akvcam 1.2.4: 11 | 12 | - Minimum supported kernel version: 4.14. 13 | - Remove deprecated REMAKE_INITRD (Thanks to dkadioglu). 14 | - Minimum number of buffers decreased to 2 (issue #20). 15 | - Added makeself as a new install option. 16 | 17 | akvcam 1.2.3: 18 | 19 | - Set v4l2_buffer.type after memset() (Thanks to Hirosam1). 20 | 21 | akvcam 1.2.2: 22 | 23 | - Properly mark installer as a GNU/Linux only installer. 24 | 25 | akvcam 1.2.1: 26 | 27 | - Fixed 'Swap Read and Blue' control. 28 | - Switched top build system from Qmake to Cmake, you can still use make 29 | command to build the module. 30 | - Removed the global_deleter, and all global objects, objects now have a well 31 | defined lifetime. 32 | - Removed dangerous static non-constant variables, this should make the module 33 | a bit more stable. 34 | 35 | akvcam 1.2.0: 36 | 37 | - Added support for linux 5.10. 38 | - Use V4L2 kernel APIs for dealing with almost anything, v4l2 protocol is too 39 | much complicated to be handled manually. 40 | - Added DMABUF support. 41 | - RW devices can also have controls too. 42 | - Removed a bunch of useless code. 43 | 44 | akvcam 1.1.1: 45 | 46 | - Minimum supported kernel version: 4.4 47 | - Maximum tested kernel version: 5.9 48 | - Install the module to /usr/src when running make install. 49 | - Added USE_DKMS for make install to install the module using DKMS. 50 | - Replaced spin_lock with mutex_lock_interruptible. 51 | - Don't stop streaming if the released node is not the node that started the 52 | streaming. 53 | - Print ioctl error messages. 54 | - Print the device that's calling the node and ioctl functions. 55 | - Added installer and daily build. 56 | 57 | akvcam 1.1.0: 58 | 59 | - Make it work with linux 5.7. 60 | - Allow setting the device number. 61 | - Added virtual camera usage example. 62 | 63 | akvcam 1.0.4: 64 | 65 | - Update to support 5.6 kernel. 66 | 67 | akvcam 1.0.3: 68 | 69 | - Set video_device.device_caps when creating akvcam_device. 70 | 71 | akvcam 1.0.2: 72 | 73 | - Fixed nearest video format calculation. 74 | 75 | akvcam 1.0.1: 76 | 77 | - Added support for linux 5.0. 78 | 79 | akvcam 1.0.0: 80 | 81 | - First release. 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # akvcam, Virtual camera driver for Linux # 2 | 3 | akvcam is a fully compliant V4L2 virtual camera driver for Linux. 4 | 5 | ## Features ## 6 | 7 | * Flexible configuration with a simple INI file like. 8 | * Support for map, user pointer, and read/write modes. 9 | * Can cat and echo to the device. 10 | * Supports emulated camera controls in capture devices (brightness, contrast, saturation, etc.). 11 | * Configurable default picture in case no input signal available. 12 | * The devices can't be rejected by programs that rejects M2M devices. 13 | * Fully compliant with V4L2 standard. 14 | * Support for LTS kernels. 15 | 16 | ## Build and Install ## 17 | 18 | Visit the [wiki](https://github.com/webcamoid/akvcam/wiki) for a comprehensive compile and install instructions. 19 | 20 | ## Downloads ## 21 | 22 | [![Download](https://img.shields.io/badge/Download-Releases-3f2a7e.svg)](https://github.com/webcamoid/akvcam/releases) 23 | [![Daily Build](https://img.shields.io/badge/Download-Daily%20Build-3f2a7e.svg)](https://github.com/webcamoid/akvcam/releases/tag/daily-build) 24 | [![Total Downloads](https://img.shields.io/github/downloads/webcamoid/akvcam/total.svg?label=Total%20Downloads&color=3f2a7e)](https://tooomm.github.io/github-release-stats/?username=webcamoid&repository=akvcam) 25 | 26 | ## Donations ## 27 | 28 | If you are interested in donating to the project you can look at all available methods in the [donations page](https://webcamoid.github.io/donations). 29 | 30 | ## Status ## 31 | 32 | [![Linux](https://github.com/webcamoid/akvcam/actions/workflows/linux.yml/badge.svg)](https://github.com/webcamoid/akvcam/actions/workflows/linux.yml) 33 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/eaeeaacb491c498bbffbe2087bc2d4dd)](https://www.codacy.com/gh/webcamoid/akvcam/dashboard?utm_source=github.com&utm_medium=referral&utm_content=webcamoid/akvcam&utm_campaign=Badge_Grade) 34 | [![Project Stats](https://www.openhub.net/p/akvcam/widgets/project_thin_badge.gif)](https://www.openhub.net/p/akvcam) 35 | 36 | ## Reporting Bugs ## 37 | 38 | Report all issues in the [issues tracker](http://github.com/webcamoid/akvcam/issues). 39 | -------------------------------------------------------------------------------- /akvcam.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | CreatedFrom=CMakeLists.txt 3 | Manager=KDevCMakeManager 4 | Name=akvcam 5 | -------------------------------------------------------------------------------- /package_info.conf.in: -------------------------------------------------------------------------------- 1 | [Package] 2 | name = akvcam 3 | version = @VERSION@ 4 | sourcesDir = @CMAKE_SOURCE_DIR@ 5 | targetPlatform = posix 6 | buildInfoFile = share/build-info.txt 7 | targetArch = any 8 | outputFormats = QtIFW, Makeself 9 | hideArch = true 10 | dailyBuild = @DAILY_BUILD@ 11 | 12 | [Makeself] 13 | name = akvcam-installer-cli 14 | appName = akvcam 15 | label = Install akvcam 16 | targetDir = /opt/akvcam 17 | license = COPYING 18 | installScript = ports/deploy/setup.sh 19 | installScriptArgs = akvcam @VERSION@ 20 | pkgTargetPlatform = linux 21 | 22 | [QtIFW] 23 | organization = org.webcamoidprj 24 | name = akvcam-installer-gui 25 | appName = akvcam 26 | title = akvcam, fully compliant V4L2 virtual camera driver for Linux. 27 | description = Install akvcam 28 | url = https://github.com/webcamoid/akvcam 29 | targetDir = @QTIFW_TARGET_DIR@ 30 | license = COPYING 31 | licenseName = GNU General Public License v2.0 or later 32 | script = ports/deploy/installscript.posix.qs 33 | changeLog = ChangeLog 34 | pkgTargetPlatform = linux 35 | -------------------------------------------------------------------------------- /ports/ci/linux/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # akvcam, virtual camera for Linux. 4 | # Copyright (C) 2018 Gonzalo Exequiel Pedone 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | echo "Available kernel headers:" 21 | echo 22 | ls /usr/src | grep linux-headers- | sort 23 | echo 24 | 25 | # Build the driver and show it's info. 26 | 27 | export INSTALL_PREFIX="${PWD}/package-data-${REPOSITORY%.*}" 28 | cp -vf package_info.conf.in package_info.conf 29 | version=$(grep '^MODULE_VERSION' src/Makefile | awk -F= '{print $2}' | tr -d ' ') 30 | sed -i "s|@VERSION@|${version}|g" package_info.conf 31 | sed -i "s|@CMAKE_SOURCE_DIR@|${PWD}|g" package_info.conf 32 | sed -i "s|@QTIFW_TARGET_DIR@|@ApplicationsDir@/akvcam|g" package_info.conf 33 | sed -i "s|@DAILY_BUILD@|${DAILY_BUILD}|g" package_info.conf 34 | buildDir=src-${REPOSITORY%.*} 35 | cp -rvf src "${buildDir}" 36 | cd "${buildDir}" 37 | export CFLAGS="-I/usr/src/linux-headers-${KERNEL_VERSION}" 38 | make KERNEL_DIR="/usr/src/linux-headers-${KERNEL_VERSION}-generic" USE_SPARSE=1 39 | make install INSTALLDIR="${INSTALL_PREFIX}/src" 40 | echo 41 | echo "Driver info:" 42 | echo 43 | modinfo akvcam.ko 44 | -------------------------------------------------------------------------------- /ports/ci/linux/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # akvcam, virtual camera for Linux. 4 | # Copyright (C) 2020 Gonzalo Exequiel Pedone 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | git clone https://github.com/webcamoid/DeployTools.git 21 | git config --global --add safe.directory /sources 22 | 23 | export PATH="${PWD}/.local/bin:${PATH}" 24 | export INSTALL_PREFIX="${PWD}/package-data-${REPOSITORY%.*}" 25 | export PACKAGES_DIR="${PWD}/packages-${REPOSITORY%.*}" 26 | export PYTHONPATH="${PWD}/DeployTools" 27 | 28 | xvfb-run --auto-servernum python3 \ 29 | ./DeployTools/deploy.py \ 30 | -d "${INSTALL_PREFIX}" \ 31 | -c ./package_info.conf \ 32 | -o "${PACKAGES_DIR}" 33 | -------------------------------------------------------------------------------- /ports/ci/linux/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # akvcam, virtual camera for Linux. 4 | # Copyright (C) 2018 Gonzalo Exequiel Pedone 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | if [ ! -z "${USE_WGET}" ]; then 21 | export DOWNLOAD_CMD="wget -nv -c" 22 | else 23 | export DOWNLOAD_CMD="curl --retry 10 -sS -kLOC -" 24 | fi 25 | 26 | # Fix keyboard layout bug when running apt 27 | 28 | cat << EOF > keyboard_config 29 | XKBMODEL="pc105" 30 | XKBLAYOUT="us" 31 | XKBVARIANT="" 32 | XKBOPTIONS="" 33 | BACKSPACE="guess" 34 | EOF 35 | 36 | export LC_ALL=C 37 | export DEBIAN_FRONTEND=noninteractive 38 | 39 | apt-get -qq -y update 40 | apt-get install -qq -y keyboard-configuration 41 | cp -vf keyboard_config /etc/default/keyboard 42 | dpkg-reconfigure --frontend noninteractive keyboard-configuration 43 | 44 | # Install missing dependenies 45 | 46 | apt-get -qq -y upgrade 47 | apt-get -qq -y install \ 48 | curl \ 49 | libdbus-1-3 \ 50 | libfontconfig1 \ 51 | libgl1 \ 52 | libx11-xcb1 \ 53 | libxcb-glx0 \ 54 | libxcb-icccm4 \ 55 | libxcb-image0 \ 56 | libxcb-keysyms1 \ 57 | libxcb-randr0 \ 58 | libxcb-render-util0 \ 59 | libxcb-shape0 \ 60 | libxcb-xfixes0 \ 61 | libxcb-xinerama0 \ 62 | libxext6 \ 63 | libxkbcommon-x11-0 \ 64 | libxrender1 \ 65 | wget 66 | 67 | mkdir -p .local/bin 68 | 69 | if [ -z "${ARCHITECTURE}" ]; then 70 | architecture="${DOCKERIMG%%/*}" 71 | else 72 | case "${ARCHITECTURE}" in 73 | aarch64) 74 | architecture=arm64v8 75 | ;; 76 | armv7) 77 | architecture=arm32v7 78 | ;; 79 | *) 80 | architecture=${ARCHITECTURE} 81 | ;; 82 | esac 83 | fi 84 | 85 | if [[ ( "${architecture}" = amd64 || "${architecture}" = arm64v8 ) && ! -z "${QTIFWVER}" ]]; then 86 | # Install Qt Installer Framework 87 | 88 | case "${architecture}" in 89 | arm64v8) 90 | qtArch=arm64 91 | ;; 92 | *) 93 | qtArch=x64 94 | ;; 95 | esac 96 | 97 | qtIFW=QtInstallerFramework-linux-${qtArch}-${QTIFWVER}.run 98 | ${DOWNLOAD_CMD} "http://download.qt.io/official_releases/qt-installer-framework/${QTIFWVER}/${qtIFW}" || true 99 | 100 | if [ -e "${qtIFW}" ]; then 101 | if [ "${architecture}" = arm64v8 ]; then 102 | ln -svf libtiff.so.6 /usr/lib/aarch64-linux-gnu/libtiff.so.5 103 | ln -svf libwebp.so.7 /usr/lib/aarch64-linux-gnu/libwebp.so.6 104 | fi 105 | 106 | chmod +x "${qtIFW}" 107 | QT_QPA_PLATFORM=minimal \ 108 | ./"${qtIFW}" \ 109 | --verbose \ 110 | --root ~/QtIFW \ 111 | --accept-licenses \ 112 | --accept-messages \ 113 | --confirm-command \ 114 | install 115 | cd .local 116 | cp -rvf ~/QtIFW/* . 117 | cd .. 118 | fi 119 | fi 120 | 121 | # Install dev tools 122 | 123 | apt-get -qq -y install \ 124 | g++ \ 125 | git \ 126 | kmod \ 127 | libelf-dev \ 128 | make \ 129 | makeself \ 130 | python3 \ 131 | sparse \ 132 | wget \ 133 | xvfb \ 134 | xz-utils 135 | 136 | case "${architecture}" in 137 | arm64v8) 138 | systemArch=arm64 139 | ;; 140 | arm32v7) 141 | systemArch=armhf 142 | ;; 143 | *) 144 | systemArch=amd64 145 | ;; 146 | esac 147 | 148 | url=https://kernel.ubuntu.com/mainline/${REPOSITORY} 149 | headers=amd64/linux-headers-${KERNEL_VERSION}_${KERNEL_VERSION}.${KERNEL_VERSION_C}_all.deb 150 | headers_generic=${systemArch}/linux-headers-${KERNEL_VERSION}-generic_${KERNEL_VERSION}.${KERNEL_VERSION_C}_${systemArch}.deb 151 | 152 | for package in ${headers} ${headers_generic}; do 153 | ${DOWNLOAD_CMD} "${url}/${package}" 154 | dpkg -i "${package#*/}" 155 | done 156 | -------------------------------------------------------------------------------- /ports/deploy/installscript.posix.qs: -------------------------------------------------------------------------------- 1 | function Component() 2 | { 3 | let paths = installer.environmentVariable("PATH").split(":"); 4 | let kernelVersion = installer.execute("uname", ["-r"])[0].trim(); 5 | let linuxSources = "/lib/modules/" + kernelVersion + "/build"; 6 | let cmds = ["dkms", "gcc", "kmod", "make"] 7 | 8 | for (;;) { 9 | let missing_dependencies = []; 10 | 11 | for (let i in cmds) 12 | if (installer.findPath(cmds[i], paths).length < 1) 13 | missing_dependencies.push(cmds[i]); 14 | 15 | if (!installer.fileExists(linuxSources + "/include/generated/uapi/linux/version.h")) 16 | missing_dependencies.push("linux-headers"); 17 | 18 | if (missing_dependencies.length < 1) 19 | break; 20 | 21 | let result = QMessageBox.information("missing_dependencies", 22 | "Missing dependencies", 23 | "The following dependencies are missing: " 24 | + missing_dependencies.join(", ") 25 | + ". Install them and try again", 26 | QMessageBox.Retry | QMessageBox.Close); 27 | 28 | if (result == QMessageBox.Close) { 29 | QMessageBox.critical("missing_dependencies.dialog_close", 30 | "Error", 31 | "Aborting driver installation due to unmeet dependencies.", 32 | QMessageBox.Ok); 33 | installer.setCanceled(); 34 | 35 | break; 36 | } 37 | } 38 | } 39 | 40 | Component.prototype.beginInstallation = function() 41 | { 42 | component.beginInstallation(); 43 | } 44 | 45 | Component.prototype.createOperations = function() 46 | { 47 | component.createOperations(); 48 | 49 | // Create a symlink to the sources. 50 | component.addElevatedOperation("Execute", 51 | "ln", 52 | "-s", 53 | "@TargetDir@/src", 54 | "/usr/src/@Name@-@Version@", 55 | "UNDOEXECUTE", 56 | "rm", 57 | "-f", 58 | "/usr/src/@Name@-@Version@"); 59 | 60 | // Run DKMS. 61 | component.addElevatedOperation("Execute", 62 | "dkms", 63 | "install", 64 | "@Name@/@Version@", 65 | "UNDOEXECUTE", 66 | "dkms", 67 | "remove", 68 | "@Name@/@Version@", 69 | "--all"); 70 | } 71 | -------------------------------------------------------------------------------- /ports/deploy/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | scriptPath=$(readlink -f "$0") 4 | installDataFile=install.data 5 | NAME=$1 6 | 7 | if [ -z "$NAME" ]; then 8 | NAME=$(grep 'NAME=' "${installDataFile}" 2>/dev/null) 9 | NAME=${NAME#*=} 10 | fi 11 | 12 | VERSION=$2 13 | 14 | if [ -z "$VERSION" ]; then 15 | VERSION=$(grep 'VERSION=' "${installDataFile}" 2>/dev/null) 16 | VERSION=${VERSION#*=} 17 | fi 18 | 19 | TARGET_DIR=$(dirname "${scriptPath}") 20 | 21 | if [ ! -z "$3" ]; then 22 | TARGET_DIR=$3 23 | fi 24 | 25 | SCRIPT_BASENAME=$(basename "$0") 26 | SUDO_CMD= 27 | 28 | if [ ! -w "${TARGET_DIR}" ]; then 29 | SUDO_CMD=sudo 30 | fi 31 | 32 | if [ -z "${NAME}" ]; then 33 | echo "Installation started" 34 | else 35 | echo "${NAME} installation started" 36 | fi 37 | 38 | echo 39 | ${SUDO_CMD} chmod 755 "${TARGET_DIR}" 40 | 41 | echo "Writting install data" 42 | installData=${TARGET_DIR}/${installDataFile} 43 | 44 | ${SUDO_CMD} tee "${installData}" > /dev/null < /dev/null < /dev/null </dev/null 120 | 121 | # https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html 122 | sensible_directories=" 123 | / 124 | /bin 125 | /boot 126 | /dev 127 | /etc 128 | /home 129 | \${HOME} 130 | /lib 131 | /media 132 | /mnt 133 | /opt 134 | /proc 135 | /root 136 | /run 137 | /sbin 138 | /svr 139 | /sys 140 | /tmp 141 | /usr 142 | /usr/bin 143 | /usr/include 144 | /usr/lib 145 | /usr/libexec 146 | /usr/local 147 | /usr/local/share 148 | /usr/sbin 149 | /usr/share 150 | /usr/share/color 151 | /usr/share/dict 152 | /usr/share/man 153 | /usr/share/misc 154 | /usr/share/ppd 155 | /usr/share/sgml 156 | /usr/share/xml 157 | /usr/src 158 | /var 159 | /var/account 160 | /var/cache 161 | /var/cache/fonts 162 | /var/cache/man 163 | /var/crash 164 | /var/games 165 | /var/lib 166 | /var/lib/color 167 | /var/lib/hwclock 168 | /var/lib/misc 169 | /var/lock 170 | /var/log 171 | /var/mail 172 | /var/opt 173 | /var/run 174 | /var/spool 175 | /var/spool/cron 176 | /var/spool/lpd 177 | /var/spool/rwho 178 | /var/tmp 179 | /var/yp 180 | \${XDG_DESKTOP_DIR} 181 | \${XDG_DOCUMENTS_DIR} 182 | \${XDG_DOWNLOAD_DIR} 183 | \${XDG_MUSIC_DIR} 184 | \${XDG_PICTURES_DIR} 185 | \${XDG_PUBLICSHARE_DIR} 186 | \${XDG_TEMPLATES_DIR} 187 | \${XDG_VIDEOS_DIR}" 188 | 189 | for sdir in \$sensible_directories; do 190 | if [ "\$sdir" = "\$directory" ]; then 191 | echo true 192 | 193 | return 1 194 | fi 195 | done 196 | 197 | echo false 198 | 199 | return 0 200 | } 201 | 202 | sensibleDir=\$(is_sensible_directory "\${TARGET_DIR}") 203 | 204 | if [ "\${sensibleDir}" = true ]; then 205 | echo "'\${TARGET_DIR}' can't be deleted" 206 | else 207 | echo "Deleting '\${TARGET_DIR}'" 208 | \${SUDO_CMD} rm -rf "\${TARGET_DIR}" 2>/dev/null 209 | fi 210 | 211 | echo 212 | 213 | if [ -z "\${NAME}" ]; then 214 | echo "Uninstallation finished" 215 | else 216 | echo "\${NAME} uninstallation finished" 217 | fi 218 | EOF 219 | 220 | ${SUDO_CMD} chmod 755 "${uninstallScript}" 221 | 222 | detect_missing_dependencies() { 223 | linuxSources=/lib/modules/$(uname -r)/build 224 | cmds="dkms gcc kmod make" 225 | missing_dependencies=""; 226 | 227 | for cmd in $cmds; do 228 | cmdPath=$(which "${cmd}" 2>/dev/null) 229 | 230 | if [ "$?" != 0 ]; then 231 | if [ -z "${missing_dependencies}" ]; then 232 | missing_dependencies="${cmd}" 233 | else 234 | missing_dependencies="${missing_dependencies} ${cmd}" 235 | fi 236 | fi 237 | done 238 | 239 | if [ ! -e "${linuxSources}/include/generated/uapi/linux/version.h" ]; then 240 | if [ -z "${missing_dependencies}" ]; then 241 | missing_dependencies=linux-headers 242 | else 243 | missing_dependencies="${missing_dependencies} linux-headers" 244 | fi 245 | fi 246 | 247 | echo "${missing_dependencies}" 248 | } 249 | 250 | distro() { 251 | distroId=$(grep -h ^ID_LIKE= /etc/*-release | tr -d '"') 252 | 253 | if [ -z "${distroId}" ]; then 254 | distroId=$(grep -h ^ID= /etc/*-release | tr -d '"') 255 | fi 256 | 257 | distroId=${distroId#*=} 258 | 259 | case "${distroId}" in 260 | *suse*) 261 | distroId=opensuse 262 | ;; 263 | *) 264 | ;; 265 | esac 266 | 267 | echo "${distroId}" 268 | } 269 | 270 | distro_package() { 271 | distroId=$1 272 | package=$2 273 | depsMap="" 274 | 275 | case "${distro}Id" in 276 | arch) 277 | depsMap=";dkms:dkms" 278 | depsMap="${depsMap};gcc:gcc" 279 | depsMap="${depsMap};kmod:kmod" 280 | depsMap="${depsMap};make:make" 281 | depsMap="${depsMap};linux-headers:linux-headers" 282 | ;; 283 | debian) 284 | depsMap=";dkms:dkms" 285 | depsMap="${depsMap};gcc:gcc" 286 | depsMap="${depsMap};kmod:kmod" 287 | depsMap="${depsMap};make:make" 288 | depsMap="${depsMap};linux-headers:linux-headers-$(uname -r)" 289 | ;; 290 | fedora) 291 | depsMap=";dkms:dkms" 292 | depsMap="${depsMap};gcc:gcc" 293 | depsMap="${depsMap};kmod:kmod" 294 | depsMap="${depsMap};make:make" 295 | depsMap="${depsMap};linux-headers:kernel-devel kernel-headers" 296 | ;; 297 | mageia) 298 | depsMap=";dkms:dkms" 299 | depsMap="${depsMap};gcc:gcc" 300 | depsMap="${depsMap};kmod:kmod" 301 | depsMap="${depsMap};make:make" 302 | depsMap="${depsMap};linux-headers:kernel-linus-devel" 303 | ;; 304 | opensuse) 305 | depsMap=";dkms:dkms" 306 | depsMap="${depsMap};gcc:gcc" 307 | depsMap="${depsMap};kmod:kmod" 308 | depsMap="${depsMap};make:make" 309 | depsMap="${depsMap};linux-headers:kernel-devel" 310 | ;; 311 | *) 312 | depsMap=";dkms:dkms" 313 | depsMap="${depsMap};gcc:gcc" 314 | depsMap="${depsMap};kmod:kmod" 315 | depsMap="${depsMap};make:make" 316 | depsMap="${depsMap};linux-headers:linux-headers" 317 | ;; 318 | esac 319 | 320 | packages="${depsMap#*;${package}:}" 321 | packages="${packages%%;*}" 322 | 323 | if [ -z "${packages}" ]; then 324 | echo "${package}" 325 | else 326 | echo "${packages}" 327 | fi 328 | } 329 | 330 | distro_packages() { 331 | distroId=$1 332 | shift 333 | packages="" 334 | 335 | for package in $@; do 336 | distroPackage=$(distro_package "${distroId}" "${package}") 337 | 338 | if [ -z "${packages}" ]; then 339 | packages="${distroPackage}" 340 | else 341 | packages="${packages} ${distroPackage}" 342 | fi 343 | done 344 | 345 | echo "${packages}" 346 | } 347 | 348 | install_packages() { 349 | distroId=$1 350 | shift 351 | missing_dependencies=$@ 352 | SUDO_CMD= 353 | 354 | if [ "$EUID" != 0 ]; then 355 | SUDO_CMD=sudo 356 | fi 357 | 358 | case "${distroId}" in 359 | arch) 360 | ${SUDO_CMD} pacman -Syu --noconfirm --ignore linux,linux-api-headers,linux-docs,linux-firmware,linux-headers,pacman 361 | ${SUDO_CMD} pacman --noconfirm --needed -S ${missing_dependencies} 362 | ;; 363 | debian) 364 | ${SUDO_CMD} apt-get -y update 365 | ${SUDO_CMD} apt-get -y upgrade 366 | ${SUDO_CMD} apt-get -y install ${missing_dependencies} 367 | ;; 368 | fedora) 369 | ${SUDO_CMD} dnf -y upgrade-minimal --exclude=systemd,systemd-libs 370 | ${SUDO_CMD} dnf -y --skip-broken install ${missing_dependencies} 371 | ;; 372 | mageia) 373 | ${SUDO_CMD} dnf -y update 374 | ${SUDO_CMD} dnf -y install ${missing_dependencies} 375 | ;; 376 | opensuse) 377 | ${SUDO_CMD} zypper -n dup 378 | ${SUDO_CMD} zypper -n in ${missing_dependencies} 379 | ;; 380 | *) 381 | ;; 382 | esac 383 | } 384 | 385 | is_distro_supported() { 386 | distroId=$1 387 | 388 | case "${distroId}" in 389 | arch) 390 | ;; 391 | debian) 392 | ;; 393 | fedora) 394 | ;; 395 | mageia) 396 | ;; 397 | opensuse) 398 | ;; 399 | *) 400 | echo false 401 | 402 | return 0 403 | ;; 404 | esac 405 | 406 | echo true 407 | 408 | return 1 409 | } 410 | 411 | distroId=$(distro) 412 | missing_dependencies=$(detect_missing_dependencies) 413 | missing_dependencies=$(distro_packages "${distroId}" ${missing_dependencies}) 414 | 415 | if [ ! -z "${missing_dependencies}" ]; then 416 | if [ "$(is_distro_supported "${distroId}")" = true ]; then 417 | echo "Installing missing dependencies:" $(echo "${missing_dependencies}" | sed 's/ /, /g') 418 | echo 419 | install_packages "${distroId}" ${missing_dependencies} 420 | 421 | if [ "$?" != 0 ]; then 422 | exit $? 423 | fi 424 | else 425 | echo "The following dependencies are missing:" $(echo "${missing_dependencies}" | sed 's/ /, /g') >&2 426 | echo >&2 427 | echo "Install them and then run '${TARGET_DIR}/${SCRIPT_BASENAME}' script." >&2 428 | 429 | exit -1 430 | fi 431 | fi 432 | 433 | echo "Creating a symlink to the sources" 434 | 435 | write_link() { 436 | SUDO_CMD= 437 | 438 | if [ "$EUID" != 0 ]; then 439 | SUDO_CMD=sudo 440 | fi 441 | 442 | ${SUDO_CMD} ln -sf "${TARGET_DIR}/src" "/usr/src/${NAME}-${VERSION}" 443 | 444 | if [ "$?" != 0 ]; then 445 | exit $? 446 | fi 447 | } 448 | 449 | write_link 450 | 451 | echo "Runnig DKMS" 452 | 453 | run_dkms() { 454 | SUDO_CMD= 455 | 456 | if [ "$EUID" != 0 ]; then 457 | SUDO_CMD=sudo 458 | fi 459 | 460 | ${SUDO_CMD} dkms install "${NAME}/${VERSION}" 461 | 462 | if [ "$?" != 0 ]; then 463 | exit $? 464 | fi 465 | } 466 | 467 | run_dkms 468 | echo 469 | 470 | if [ -z "${NAME}" ]; then 471 | echo "Installation finished" 472 | else 473 | echo "${NAME} installation finished" 474 | fi 475 | -------------------------------------------------------------------------------- /share/config_example.ini: -------------------------------------------------------------------------------- 1 | # Virtual camera configuration file example. 2 | # 3 | # Please, read the instructions to the end. 4 | 5 | [Cameras] 6 | # First at all you must define how many virtual cameras will be created. 7 | cameras/size = 2 8 | 9 | # Then, define it's properties. 10 | # 11 | # A virtual camera can be of 2 types: 'capture' and 'output'. 12 | # A 'capture' device will be seen as a normal webcam by any webcam capture 13 | # program. 14 | # A 'output' device will receive frames from a producer program and send it to 15 | # one or many 'capture' devices. 16 | # 17 | # A camera can have also 3 capture/output modes: 'mmap', 'userptr' and 'rw'. 18 | # 'mmap' is the most widely supported mode by far, enabling this is more than 19 | # enough in most cases. 'rw' allow you to "echo" or "cat" frames as raw data 20 | # directly to the device using the default frame format. Enabling 'rw' mode will 21 | # disable emulated camera controls in the 'capture' device (brightness, 22 | # contrast, saturation, etc.). 23 | # A device can support all 3 modes at same time. 24 | # 25 | # 'formats' is a comma separated list of index in the format list bellow. 26 | # 27 | # It's also possible to set the device number by setting the 'videonr' property, 28 | # if for example videonr=7 the the device will be created as "/dev/video7". 29 | # If 'videonr' is already taken, negative or not set, the driver will assign the 30 | # first free device number. 31 | cameras/1/type = output 32 | cameras/1/mode = mmap, userptr, rw 33 | cameras/1/description = Virtual Camera (output device) 34 | cameras/1/formats = 2 35 | cameras/1/videonr = 7 36 | 37 | cameras/2/type = capture 38 | cameras/2/mode = mmap, rw 39 | cameras/2/description = Virtual Camera 40 | cameras/2/formats = 1, 2 41 | 42 | [Formats] 43 | # Define how many formats will be supported by the camera. 44 | formats/size = 2 45 | 46 | # Now define the frame pixel formats, resolutions and frame rates supported by 47 | # the camera. 48 | # 49 | # Supported capture formats: 50 | # 51 | # RGB32 52 | # RGB24 53 | # RGB16 54 | # RGB15 55 | # BGR32 56 | # BGR24 57 | # UYVY 58 | # YUY2 59 | # 60 | # Supported output formats: 61 | # 62 | # RGB24 63 | # BGR24 64 | # 65 | # YUY2 640x480 is one of the most widely supported formats in webcam capture 66 | # programs. First format defined is the default frame format for 67 | # 'capture'/'output'. 68 | # 'width', 'height' and 'fps' are unsigned integers. 69 | formats/1/format = YUY2 70 | formats/1/width = 640 71 | formats/1/height = 480 72 | formats/1/fps = 30 73 | 74 | # The parameters can also be specified as a comma separated list, so it's 75 | # possible to combine the parameters to define several formats in one group. 76 | # 'fps' can also be defined as a fraction. 77 | # The following lines will define 4 formats: 78 | # 79 | # RGB24 640x480 20 FPS 80 | # RGB24 640x480 7.5 FPS 81 | # YUY2 640x480 20 FPS 82 | # YUY2 640x480 7.5 FPS 83 | formats/2/format = RGB24, YUY2 84 | formats/2/width = 640 85 | formats/2/height = 480 86 | formats/2/fps = 20/1, 15/2 87 | 88 | # Finally, to create a fully working virtual camera, you must connect one 89 | # 'output' to one or many 'capture' devices. 90 | # Connections are made by index, separated by a colon. The first index is the 91 | # 'output' device, the following index are 'capture' devices. 92 | [Connections] 93 | connections/size = 1 94 | connections/1/connection = 1:2 95 | 96 | # You can also define a default frame when a 'capture' device is not receiving 97 | # any input. Only 24 bpp and 32 bpp BMP files are supported. 98 | [General] 99 | default_frame = /etc/akvcam/default_frame.bmp 100 | 101 | # This config will take effect on modprobe/insmod. 102 | -------------------------------------------------------------------------------- /share/examples/output.c: -------------------------------------------------------------------------------- 1 | /* Virtual camera output example. 2 | * Copyright (C) 2020 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | * 18 | * Alternatively you can redistribute this file under the terms of the 19 | * BSD license as stated below: 20 | * 21 | * Redistribution and use in source and binary forms, with or without 22 | * modification, are permitted provided that the following conditions 23 | * are met: 24 | * 25 | * 1. Redistributions of source code must retain the above copyright 26 | * notice, this list of conditions and the following disclaimer. 27 | * 2. Redistributions in binary form must reproduce the above copyright 28 | * notice, this list of conditions and the following disclaimer in 29 | * the documentation and/or other materials provided with the 30 | * distribution. 31 | * 3. The names of its contributors may not be used to endorse or promote 32 | * products derived from this software without specific prior written 33 | * permission. 34 | * 35 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 41 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 42 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 43 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 44 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 45 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | */ 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | /* For the sake of simplicity, the program won't print anything to the terminal, 58 | * or do any validation check, you are adviced the check every single value 59 | * returned by ioctl() and other functions. 60 | * This program shows the very minimum code required to write frames to the 61 | * driver in every supported mode: rw, mmap and userptr. 62 | */ 63 | 64 | // We'll assume this is a valid akvcam output device. 65 | #define VIDEO_OUTPUT "/dev/video7" 66 | 67 | /* Choose the desired capture method, possible values: 68 | * 69 | * V4L2_CAP_READWRITE for rw 70 | * V4L2_CAP_STREAMING for mmap and userptr 71 | */ 72 | //#define CAPTURE_METHOD V4L2_CAP_READWRITE 73 | #define CAPTURE_METHOD V4L2_CAP_STREAMING 74 | 75 | /* Choose the desired memory mapping method, possible values: 76 | * 77 | * V4L2_MEMORY_MMAP 78 | * V4L2_MEMORY_USERPTR 79 | */ 80 | #define MEMORY_TYPE V4L2_MEMORY_MMAP 81 | //#define MEMORY_TYPE V4L2_MEMORY_USERPTR 82 | 83 | // Choose the number of buffers to use in mmap and userptr. 84 | #define N_BUFFERS 4 85 | 86 | // Send frames for about 30 seconds in a 30 FPS stream. 87 | #define FPS 30 88 | #define DURATION_SECONDS 30 89 | #define N_FRAMES (FPS * DURATION_SECONDS) 90 | 91 | // This structure will store the frames data. 92 | struct DataBuffer 93 | { 94 | char *start; 95 | size_t length; 96 | }; 97 | 98 | int main() 99 | { 100 | // Open the output device 101 | int fd = open(VIDEO_OUTPUT, O_RDWR | O_NONBLOCK, 0); 102 | 103 | /* Check that this is an actual output device and read the default frame 104 | * format. 105 | */ 106 | struct v4l2_format fmt; 107 | memset(&fmt, 0, sizeof(struct v4l2_format)); 108 | fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 109 | ioctl(fd, VIDIOC_G_FMT, &fmt); 110 | 111 | /* This step is not necessary, but you can also set a different output 112 | * format from the supported ones. Supported pixel formats are: 113 | * 114 | * V4L2_PIX_FMT_RGB24; 115 | * V4L2_PIX_FMT_BGR24; 116 | */ 117 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; 118 | fmt.fmt.pix.width = 640; 119 | fmt.fmt.pix.height = 480; 120 | ioctl(fd, VIDIOC_S_FMT, &fmt); 121 | 122 | // Query which methods are supported by the driver. 123 | struct v4l2_capability capabilities; 124 | memset(&capabilities, 0, sizeof(struct v4l2_capability)); 125 | ioctl(fd, VIDIOC_QUERYCAP, &capabilities); 126 | 127 | struct DataBuffer *buffers = NULL; 128 | 129 | if (CAPTURE_METHOD == V4L2_CAP_READWRITE 130 | && capabilities.capabilities & V4L2_CAP_READWRITE) { 131 | // In 'rw' mode just reserve one single buffer. 132 | buffers = calloc(1, sizeof(struct DataBuffer)); 133 | buffers->length = fmt.fmt.pix.sizeimage; 134 | buffers->start = calloc(1, fmt.fmt.pix.sizeimage); 135 | } else if (CAPTURE_METHOD == V4L2_CAP_STREAMING 136 | && capabilities.capabilities & V4L2_CAP_STREAMING) { 137 | // Request N_BUFFERS. 138 | struct v4l2_requestbuffers requestBuffers; 139 | memset(&requestBuffers, 0, sizeof(struct v4l2_requestbuffers)); 140 | requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 141 | requestBuffers.memory = MEMORY_TYPE; 142 | requestBuffers.count = N_BUFFERS; 143 | ioctl(fd, VIDIOC_REQBUFS, &requestBuffers); 144 | buffers = calloc(requestBuffers.count, 145 | sizeof(struct DataBuffer)); 146 | 147 | // Initialize the buffers. 148 | for (__u32 i = 0; i < requestBuffers.count; i++) { 149 | if (MEMORY_TYPE == V4L2_MEMORY_MMAP) { 150 | struct v4l2_buffer buffer; 151 | memset(&buffer, 0, sizeof(struct v4l2_buffer)); 152 | buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 153 | buffer.memory = V4L2_MEMORY_MMAP; 154 | buffer.index = i; 155 | ioctl(fd, VIDIOC_QUERYBUF, &buffer); 156 | buffers[i].length = buffer.length; 157 | buffers[i].start = mmap(NULL, 158 | buffer.length, 159 | PROT_READ | PROT_WRITE, 160 | MAP_SHARED, 161 | fd, 162 | buffer.m.offset); 163 | } else { 164 | buffers[i].length = fmt.fmt.pix.sizeimage; 165 | buffers[i].start = calloc(1, fmt.fmt.pix.sizeimage); 166 | } 167 | } 168 | 169 | // Queue the buffers to the driver. 170 | for (__u32 i = 0; i < requestBuffers.count; i++) { 171 | struct v4l2_buffer buffer; 172 | memset(&buffer, 0, sizeof(struct v4l2_buffer)); 173 | 174 | if (MEMORY_TYPE == V4L2_MEMORY_MMAP) { 175 | buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 176 | buffer.memory = V4L2_MEMORY_MMAP; 177 | buffer.index = i; 178 | } else { 179 | buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 180 | buffer.memory = V4L2_MEMORY_USERPTR; 181 | buffer.index = i; 182 | buffer.m.userptr = (unsigned long) buffers[i].start; 183 | buffer.length = buffers[i].length; 184 | } 185 | 186 | ioctl(fd, VIDIOC_QBUF, &buffer); 187 | } 188 | 189 | // Start the stream. 190 | enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 191 | ioctl(fd, VIDIOC_STREAMON, &type); 192 | } 193 | 194 | // Generate some random noise frames. 195 | srand(time(0)); 196 | 197 | for (int i = 0; i < N_FRAMES; i++) { 198 | if (CAPTURE_METHOD == V4L2_CAP_READWRITE 199 | && capabilities.capabilities & V4L2_CAP_READWRITE) { 200 | // Write the frame data to the buffer. 201 | for (size_t byte = 0; byte < buffers->length; byte++) 202 | buffers->start[byte] = rand() & 0xff; 203 | 204 | write(fd, buffers->start, buffers->length); 205 | } else if (CAPTURE_METHOD == V4L2_CAP_STREAMING 206 | && capabilities.capabilities & V4L2_CAP_STREAMING) { 207 | // Dequeue one buffer. 208 | struct v4l2_buffer buffer; 209 | memset(&buffer, 0, sizeof(struct v4l2_buffer)); 210 | buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 211 | buffer.memory = MEMORY_TYPE; 212 | ioctl(fd, VIDIOC_DQBUF, &buffer); 213 | 214 | // Write the frame data to the buffer. 215 | for (size_t byte = 0; byte < buffer.bytesused; byte++) 216 | buffers[buffer.index].start[byte] = rand() & 0xff; 217 | 218 | // Queue the buffer with the frame data. 219 | ioctl(fd, VIDIOC_QBUF, &buffer); 220 | } 221 | 222 | struct timespec ts; 223 | ts.tv_sec = 0; 224 | ts.tv_nsec = 1e9 / FPS; 225 | nanosleep(&ts, &ts); 226 | } 227 | 228 | // Free the buffers. 229 | if (CAPTURE_METHOD == V4L2_CAP_READWRITE 230 | && capabilities.capabilities & V4L2_CAP_READWRITE) { 231 | free(buffers->start); 232 | } else if (CAPTURE_METHOD == V4L2_CAP_STREAMING 233 | && capabilities.capabilities & V4L2_CAP_STREAMING) { 234 | // Stop the stream. 235 | enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 236 | ioctl(fd, VIDIOC_STREAMOFF, &type); 237 | 238 | for (__u32 i = 0; i < N_BUFFERS; i++) { 239 | if (MEMORY_TYPE == V4L2_MEMORY_MMAP) 240 | munmap(buffers[i].start, buffers[i].length); 241 | else 242 | free(buffers[i].start); 243 | } 244 | } 245 | 246 | free(buffers); 247 | 248 | // Close the output device. 249 | close(fd); 250 | 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | MODULE_NAME = akvcam 2 | MODULE_VERSION = 1.2.6 3 | KERNEL_ROOT ?= $(shell realpath /lib/modules/$(shell uname -r)) 4 | KERNEL_DIR ?= $(KERNEL_ROOT)/build 5 | COPY = cp -f 6 | MKDIR = mkdir -p 7 | RMDIR = rm -rvf 8 | PWD := $(shell pwd) 9 | DKMS := dkms 10 | DESTDIR := 11 | PREFIX := $(DESTDIR)/usr/src 12 | INSTALLDIR := $(PREFIX)/$(MODULE_NAME)-$(MODULE_VERSION) 13 | 14 | HAVE_SPARSE := $(shell which sparse 2>/dev/null) 15 | SPARSE_MODE ?= 2 16 | 17 | ifdef USE_SPARSE 18 | ifdef HAVE_SPARSE 19 | SPARSE_VAR = C=$(SPARSE_MODE) 20 | endif 21 | endif 22 | 23 | obj-m += $(MODULE_NAME).o 24 | akvcam-objs := \ 25 | module.o \ 26 | attributes.o \ 27 | buffers.o \ 28 | controls.o \ 29 | device.o \ 30 | driver.o \ 31 | file_read.o \ 32 | format.o \ 33 | frame.o \ 34 | frame_filter.o \ 35 | ioctl.o \ 36 | list.o \ 37 | log.o \ 38 | map.o \ 39 | rbuffer.o \ 40 | settings.o \ 41 | utils.o 42 | 43 | all: 44 | $(MAKE) -C $(KERNEL_DIR) M=$(PWD) $(SPARSE_VAR) modules 45 | 46 | clean: 47 | $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean 48 | 49 | install: uninstall 50 | $(MKDIR) $(INSTALLDIR) 51 | $(COPY) Makefile $(INSTALLDIR) 52 | $(COPY) dkms.conf $(INSTALLDIR) 53 | $(COPY) *.h $(INSTALLDIR) 54 | $(COPY) *.c $(INSTALLDIR) 55 | 56 | dkms_install: | dkms_uninstall install 57 | $(DKMS) install $(MODULE_NAME)/$(MODULE_VERSION) 58 | 59 | uninstall: 60 | $(RMDIR) $(INSTALLDIR) 61 | 62 | dkms_uninstall: 63 | - $(DKMS) remove $(MODULE_NAME)/$(MODULE_VERSION) --all 64 | $(RMDIR) $(INSTALLDIR) 65 | -------------------------------------------------------------------------------- /src/attributes.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "attributes.h" 23 | #include "controls.h" 24 | #include "device.h" 25 | #include "list.h" 26 | 27 | static const struct attribute_group *akvcam_attributes_capture_groups[2]; 28 | static const struct attribute_group *akvcam_attributes_output_groups[2]; 29 | 30 | typedef struct 31 | { 32 | AKVCAM_RW_MODE rw_mode; 33 | char str[AKVCAM_MAX_STRING_SIZE]; 34 | } akvcam_attributes_rw_mode_strings, *akvcam_attributes_rw_mode_strings_t; 35 | 36 | typedef struct 37 | { 38 | const char *name; 39 | __u32 id; 40 | } akvcam_attributes_controls_map, *akvcam_attributes_controls_map_t; 41 | 42 | static akvcam_attributes_controls_map akvcam_attributes_controls[] = { 43 | {"brightness" , V4L2_CID_BRIGHTNESS }, 44 | {"contrast" , V4L2_CID_CONTRAST }, 45 | {"saturation" , V4L2_CID_SATURATION }, 46 | {"hue" , V4L2_CID_HUE }, 47 | {"gamma" , V4L2_CID_GAMMA }, 48 | {"hflip" , V4L2_CID_HFLIP }, 49 | {"vflip" , V4L2_CID_VFLIP }, 50 | {"scaling" , AKVCAM_CID_SCALING }, 51 | {"aspect_ratio", AKVCAM_CID_ASPECT_RATIO}, 52 | {"swap_rgb" , AKVCAM_CID_SWAP_RGB }, 53 | {"colorfx" , V4L2_CID_COLORFX }, 54 | {NULL , 0 }, 55 | }; 56 | 57 | __u32 akvcam_attributes_controls_id_by_name(const char *name); 58 | 59 | const struct attribute_group **akvcam_attributes_groups(AKVCAM_DEVICE_TYPE device_type) 60 | { 61 | return device_type == AKVCAM_DEVICE_TYPE_OUTPUT? 62 | akvcam_attributes_output_groups: 63 | akvcam_attributes_capture_groups; 64 | } 65 | 66 | __u32 akvcam_attributes_controls_id_by_name(const char *name) 67 | { 68 | size_t i; 69 | 70 | for (i = 0; akvcam_attributes_controls[i].name; i++) 71 | if (strcmp(akvcam_attributes_controls[i].name, name) == 0) 72 | return akvcam_attributes_controls[i].id; 73 | 74 | return 0; 75 | } 76 | 77 | static ssize_t akvcam_attributes_connected_devices_show(struct device *dev, 78 | struct device_attribute *attribute, 79 | char *buffer) 80 | { 81 | struct video_device *vdev = to_video_device(dev); 82 | akvcam_device_t device = video_get_drvdata(vdev); 83 | akvcam_devices_list_t devices; 84 | akvcam_list_element_t it = NULL; 85 | size_t n = 0; 86 | size_t i; 87 | 88 | UNUSED(attribute); 89 | devices = akvcam_device_connected_devices_nr(device); 90 | memset(buffer, 0, PAGE_SIZE); 91 | 92 | for (i = 0; i < 64 && PAGE_SIZE > n; i++) { 93 | device = akvcam_list_next(devices, &it); 94 | 95 | if (!it) 96 | break; 97 | 98 | n = snprintf(buffer + n, 99 | PAGE_SIZE - n, 100 | "/dev/video%d\n", 101 | akvcam_device_num(device)); 102 | } 103 | 104 | return n; 105 | } 106 | 107 | static ssize_t akvcam_attributes_streaming_devices_show(struct device *dev, 108 | struct device_attribute *attribute, 109 | char *buffer) 110 | { 111 | struct video_device *vdev = to_video_device(dev); 112 | akvcam_device_t device = video_get_drvdata(vdev); 113 | akvcam_devices_list_t devices; 114 | akvcam_list_element_t it = NULL; 115 | size_t n = 0; 116 | size_t i; 117 | 118 | UNUSED(attribute); 119 | devices = akvcam_device_connected_devices_nr(device); 120 | memset(buffer, 0, PAGE_SIZE); 121 | 122 | for (i = 0; i < 64;) { 123 | device = akvcam_list_next(devices, &it); 124 | 125 | if (!it) 126 | break; 127 | 128 | if (akvcam_device_streaming(device)) { 129 | n = snprintf(buffer + n, 130 | PAGE_SIZE - n, 131 | "/dev/video%d\n", 132 | akvcam_device_num(device)); 133 | i++; 134 | } 135 | } 136 | 137 | return n; 138 | } 139 | 140 | static ssize_t akvcam_attributes_device_modes_show(struct device *dev, 141 | struct device_attribute *attribute, 142 | char *buffer) 143 | { 144 | struct video_device *vdev = to_video_device(dev); 145 | akvcam_device_t device = video_get_drvdata(vdev); 146 | AKVCAM_RW_MODE rw_mode = akvcam_device_rw_mode(device); 147 | char *data = buffer; 148 | size_t i; 149 | size_t n = 0; 150 | static const akvcam_attributes_rw_mode_strings rw_mode_strings[] = { 151 | {AKVCAM_RW_MODE_READWRITE, "rw" }, 152 | {AKVCAM_RW_MODE_MMAP , "mmap" }, 153 | {AKVCAM_RW_MODE_USERPTR , "userptr"}, 154 | {AKVCAM_RW_MODE_DMABUF , "dmabuf" }, 155 | {0 , "" }, 156 | }; 157 | 158 | UNUSED(attribute); 159 | memset(data, 0, PAGE_SIZE); 160 | 161 | for (i = 0; akvcam_strlen(rw_mode_strings[i].str) > 0; i++) 162 | if (rw_mode_strings[i].rw_mode & rw_mode) 163 | n += snprintf(data + n, PAGE_SIZE - n, "%s\n", rw_mode_strings[i].str); 164 | 165 | return (ssize_t) n; 166 | } 167 | 168 | 169 | static ssize_t akvcam_attributes_int_show(struct device *dev, 170 | struct device_attribute *attribute, 171 | char *buffer) 172 | { 173 | struct video_device *vdev = to_video_device(dev); 174 | akvcam_device_t device = video_get_drvdata(vdev); 175 | akvcam_controls_t controls; 176 | __u32 id = akvcam_attributes_controls_id_by_name(attribute->attr.name); 177 | __s32 value; 178 | 179 | controls = akvcam_device_controls_nr(device); 180 | value = akvcam_controls_value(controls, id); 181 | memset(buffer, 0, PAGE_SIZE); 182 | 183 | return snprintf(buffer, PAGE_SIZE, "%d\n", value); 184 | } 185 | 186 | static ssize_t akvcam_attributes_int_store(struct device *dev, 187 | struct device_attribute *attribute, 188 | const char *buffer, 189 | size_t size) 190 | { 191 | struct video_device *vdev = to_video_device(dev); 192 | akvcam_device_t device = video_get_drvdata(vdev); 193 | akvcam_controls_t controls; 194 | __u32 id = akvcam_attributes_controls_id_by_name(attribute->attr.name); 195 | __s32 value = 0; 196 | int result; 197 | 198 | if (kstrtos32(buffer, 10, (__s32 *) &value) != 0) 199 | return -EINVAL; 200 | 201 | controls = akvcam_device_controls_nr(device); 202 | result = akvcam_controls_set_value(controls, id, value); 203 | 204 | if (result) 205 | return result; 206 | 207 | return (ssize_t) size; 208 | } 209 | 210 | static ssize_t akvcam_attributes_menu_show(struct device *dev, 211 | struct device_attribute *attribute, 212 | char *buffer) 213 | { 214 | struct video_device *vdev = to_video_device(dev); 215 | akvcam_device_t device = video_get_drvdata(vdev); 216 | akvcam_controls_t controls; 217 | __u32 id = akvcam_attributes_controls_id_by_name(attribute->attr.name); 218 | const char *value; 219 | 220 | controls = akvcam_device_controls_nr(device); 221 | value = akvcam_controls_string_value(controls, id); 222 | memset(buffer, 0, PAGE_SIZE); 223 | 224 | return snprintf(buffer, PAGE_SIZE, "%s\n", value); 225 | } 226 | 227 | static ssize_t akvcam_attributes_menu_store(struct device *dev, 228 | struct device_attribute *attribute, 229 | const char *buffer, 230 | size_t size) 231 | { 232 | struct video_device *vdev = to_video_device(dev); 233 | akvcam_device_t device = video_get_drvdata(vdev); 234 | akvcam_controls_t controls; 235 | char *buffer_stripped; 236 | __u32 id = akvcam_attributes_controls_id_by_name(attribute->attr.name); 237 | ssize_t result; 238 | 239 | controls = akvcam_device_controls_nr(device); 240 | buffer_stripped = akvcam_strip_str(buffer, AKVCAM_MEMORY_TYPE_KMALLOC); 241 | result = akvcam_controls_set_string_value(controls, id, buffer_stripped); 242 | kfree(buffer_stripped); 243 | 244 | if (result) 245 | return result; 246 | 247 | return size; 248 | } 249 | 250 | static DEVICE_ATTR(connected_devices, 251 | S_IRUGO, 252 | akvcam_attributes_connected_devices_show, 253 | NULL); 254 | static DEVICE_ATTR(listeners, 255 | S_IRUGO, 256 | akvcam_attributes_streaming_devices_show, 257 | NULL); 258 | static DEVICE_ATTR(broadcasters, 259 | S_IRUGO, 260 | akvcam_attributes_streaming_devices_show, 261 | NULL); 262 | static DEVICE_ATTR(modes, 263 | S_IRUGO, 264 | akvcam_attributes_device_modes_show, 265 | NULL); 266 | static DEVICE_ATTR(brightness, 267 | S_IRUGO | S_IWUSR, 268 | akvcam_attributes_int_show, 269 | akvcam_attributes_int_store); 270 | static DEVICE_ATTR(contrast, 271 | S_IRUGO | S_IWUSR, 272 | akvcam_attributes_int_show, 273 | akvcam_attributes_int_store); 274 | static DEVICE_ATTR(saturation, 275 | S_IRUGO | S_IWUSR, 276 | akvcam_attributes_int_show, 277 | akvcam_attributes_int_store); 278 | static DEVICE_ATTR(hue, 279 | S_IRUGO | S_IWUSR, 280 | akvcam_attributes_int_show, 281 | akvcam_attributes_int_store); 282 | static DEVICE_ATTR(gamma, 283 | S_IRUGO | S_IWUSR, 284 | akvcam_attributes_int_show, 285 | akvcam_attributes_int_store); 286 | static DEVICE_ATTR(hflip, 287 | S_IRUGO | S_IWUSR, 288 | akvcam_attributes_int_show, 289 | akvcam_attributes_int_store); 290 | static DEVICE_ATTR(colorfx, 291 | S_IRUGO | S_IWUSR, 292 | akvcam_attributes_menu_show, 293 | akvcam_attributes_menu_store); 294 | static DEVICE_ATTR(vflip, 295 | S_IRUGO | S_IWUSR, 296 | akvcam_attributes_int_show, 297 | akvcam_attributes_int_store); 298 | static DEVICE_ATTR(aspect_ratio, 299 | S_IRUGO | S_IWUSR, 300 | akvcam_attributes_menu_show, 301 | akvcam_attributes_menu_store); 302 | static DEVICE_ATTR(scaling, 303 | S_IRUGO | S_IWUSR, 304 | akvcam_attributes_menu_show, 305 | akvcam_attributes_menu_store); 306 | static DEVICE_ATTR(swap_rgb, 307 | S_IRUGO | S_IWUSR, 308 | akvcam_attributes_int_show, 309 | akvcam_attributes_int_store); 310 | 311 | // Define capture groups. 312 | 313 | static struct attribute *akvcam_attributes_capture[] = { 314 | &dev_attr_connected_devices.attr, 315 | &dev_attr_broadcasters.attr, 316 | &dev_attr_modes.attr, 317 | &dev_attr_brightness.attr, 318 | &dev_attr_contrast.attr, 319 | &dev_attr_saturation.attr, 320 | &dev_attr_hue.attr, 321 | &dev_attr_gamma.attr, 322 | &dev_attr_hflip.attr, 323 | &dev_attr_vflip.attr, 324 | &dev_attr_colorfx.attr, 325 | NULL 326 | }; 327 | 328 | static struct attribute_group akvcam_attributes_capture_group = { 329 | .name = "controls", 330 | .attrs = akvcam_attributes_capture 331 | }; 332 | 333 | static const struct attribute_group *akvcam_attributes_capture_groups[] = { 334 | &akvcam_attributes_capture_group, 335 | NULL 336 | }; 337 | 338 | // Define output groups. 339 | 340 | static struct attribute *akvcam_attributes_output[] = { 341 | &dev_attr_connected_devices.attr, 342 | &dev_attr_listeners.attr, 343 | &dev_attr_modes.attr, 344 | &dev_attr_hflip.attr, 345 | &dev_attr_vflip.attr, 346 | &dev_attr_aspect_ratio.attr, 347 | &dev_attr_scaling.attr, 348 | &dev_attr_swap_rgb.attr, 349 | NULL 350 | }; 351 | 352 | static struct attribute_group akvcam_attributes_output_group = { 353 | .name = "controls", 354 | .attrs = akvcam_attributes_output 355 | }; 356 | 357 | static const struct attribute_group *akvcam_attributes_output_groups[] = { 358 | &akvcam_attributes_output_group, 359 | NULL 360 | }; 361 | -------------------------------------------------------------------------------- /src/attributes.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_ATTRIBUTES_H 20 | #define AKVCAM_ATTRIBUTES_H 21 | 22 | #include "device_types.h" 23 | 24 | const struct attribute_group **akvcam_attributes_groups(AKVCAM_DEVICE_TYPE device_type); 25 | 26 | #endif // AKVCAM_ATTRIBUTES_H 27 | -------------------------------------------------------------------------------- /src/buffers.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "buffers.h" 26 | #include "device.h" 27 | #include "format.h" 28 | #include "frame.h" 29 | #include "log.h" 30 | 31 | #define AKVCAM_BUFFERS_MIN 2 32 | 33 | typedef struct { 34 | struct vb2_v4l2_buffer vb; 35 | struct list_head list; 36 | } akvcam_buffers_buffer, *akvcam_buffers_buffer_t; 37 | 38 | static const struct vb2_ops akvcam_akvcam_buffers_queue_ops; 39 | 40 | struct akvcam_buffers 41 | { 42 | struct kref ref; 43 | struct list_head buffers; 44 | struct vb2_queue queue; 45 | struct mutex buffers_mutex; 46 | struct mutex frames_mutex; 47 | akvcam_format_t format; 48 | akvcam_signal_callback(buffers, streaming_started); 49 | akvcam_signal_callback(buffers, streaming_stopped); 50 | enum v4l2_buf_type type; 51 | AKVCAM_RW_MODE rw_mode; 52 | __u32 sequence; 53 | }; 54 | 55 | akvcam_signal_define(buffers, streaming_started) 56 | akvcam_signal_define(buffers, streaming_stopped) 57 | 58 | enum vb2_io_modes akvcam_buffers_io_modes_from_device_type(enum v4l2_buf_type type, 59 | AKVCAM_RW_MODE rw_mode); 60 | int akvcam_buffers_queue_setup(struct vb2_queue *queue, 61 | unsigned int *num_buffers, 62 | unsigned int *num_planes, 63 | unsigned int sizes[], 64 | struct device *alloc_devs[]); 65 | int akvcam_buffers_buffer_prepare(struct vb2_buffer *buffer); 66 | void akvcam_buffers_buffer_queue(struct vb2_buffer *buffer); 67 | int akvcam_buffers_start_streaming(struct vb2_queue *queue, unsigned int count); 68 | void akvcam_buffers_stop_streaming(struct vb2_queue *queue); 69 | 70 | akvcam_buffers_t akvcam_buffers_new(AKVCAM_RW_MODE rw_mode, 71 | enum v4l2_buf_type type) 72 | { 73 | akvcam_buffers_t self = kzalloc(sizeof(struct akvcam_buffers), GFP_KERNEL); 74 | 75 | kref_init(&self->ref); 76 | INIT_LIST_HEAD(&self->buffers); 77 | mutex_init(&self->buffers_mutex); 78 | mutex_init(&self->frames_mutex); 79 | self->rw_mode = rw_mode; 80 | self->type = type; 81 | self->format = akvcam_format_new(0, 0, 0, NULL); 82 | self->queue.type = type; 83 | self->queue.io_modes = 84 | akvcam_buffers_io_modes_from_device_type(self->type, 85 | self->rw_mode); 86 | self->queue.drv_priv = self; 87 | self->queue.lock = &self->buffers_mutex; 88 | self->queue.buf_struct_size = sizeof(akvcam_buffers_buffer); 89 | self->queue.mem_ops = &vb2_vmalloc_memops; 90 | self->queue.ops = &akvcam_akvcam_buffers_queue_ops; 91 | self->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 92 | 93 | akvcam_buffers_set_count(self, AKVCAM_BUFFERS_MIN); 94 | 95 | return self; 96 | } 97 | 98 | static void akvcam_buffers_free(struct kref *ref) 99 | { 100 | akvcam_buffers_t self = container_of(ref, struct akvcam_buffers, ref); 101 | akvcam_format_delete(self->format); 102 | kfree(self); 103 | } 104 | 105 | void akvcam_buffers_delete(akvcam_buffers_t self) 106 | { 107 | if (self) 108 | kref_put(&self->ref, akvcam_buffers_free); 109 | } 110 | 111 | akvcam_buffers_t akvcam_buffers_ref(akvcam_buffers_t self) 112 | { 113 | if (self) 114 | kref_get(&self->ref); 115 | 116 | return self; 117 | } 118 | 119 | akvcam_format_t akvcam_buffers_format(akvcam_buffers_ct self) 120 | { 121 | return akvcam_format_new_copy(self->format); 122 | } 123 | 124 | void akvcam_buffers_set_format(akvcam_buffers_t self, akvcam_format_ct format) 125 | { 126 | akvcam_format_copy(self->format, format); 127 | } 128 | 129 | size_t akvcam_buffers_count(akvcam_buffers_ct self) 130 | { 131 | #if LINUX_VERSION_CODE < KERNEL_VERSION( 6, 8, 0) 132 | return self->queue.min_buffers_needed; 133 | #else 134 | return self->queue.min_queued_buffers; 135 | #endif 136 | } 137 | 138 | void akvcam_buffers_set_count(akvcam_buffers_t self, size_t nbuffers) 139 | { 140 | #if LINUX_VERSION_CODE < KERNEL_VERSION( 6, 8, 0) 141 | self->queue.min_buffers_needed = nbuffers; 142 | #else 143 | self->queue.min_queued_buffers = nbuffers; 144 | #endif 145 | } 146 | 147 | akvcam_frame_t akvcam_buffers_read_frame(akvcam_buffers_t self) 148 | { 149 | akvcam_frame_t frame; 150 | akvcam_buffers_buffer_t buf; 151 | size_t i; 152 | 153 | akpr_function(); 154 | 155 | if (mutex_lock_interruptible(&self->frames_mutex)) 156 | return NULL; 157 | 158 | if (list_empty(&self->buffers)) { 159 | mutex_unlock(&self->frames_mutex); 160 | 161 | return NULL; 162 | } 163 | 164 | buf = list_entry(self->buffers.next, akvcam_buffers_buffer, list); 165 | list_del(&buf->list); 166 | buf->vb.vb2_buf.timestamp = ktime_get_ns(); 167 | buf->vb.field = V4L2_FIELD_NONE; 168 | buf->vb.sequence = self->sequence++; 169 | mutex_unlock(&self->frames_mutex); 170 | 171 | frame = akvcam_frame_new(self->format, NULL, 0); 172 | 173 | for (i = 0; i < buf->vb.vb2_buf.num_planes; i++) { 174 | memcpy(akvcam_frame_plane_data(frame, i), 175 | vb2_plane_vaddr(&buf->vb.vb2_buf, i), 176 | akvcam_format_plane_size(self->format, i)); 177 | } 178 | 179 | vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 180 | 181 | return frame; 182 | } 183 | 184 | int akvcam_buffers_write_frame(akvcam_buffers_t self, akvcam_frame_t frame) 185 | { 186 | akvcam_buffers_buffer_t buf; 187 | size_t i; 188 | int result; 189 | 190 | akpr_function(); 191 | result = mutex_lock_interruptible(&self->frames_mutex); 192 | 193 | if (result) 194 | return result; 195 | 196 | if (list_empty(&self->buffers)) { 197 | mutex_unlock(&self->frames_mutex); 198 | 199 | return -EAGAIN; 200 | } 201 | 202 | buf = list_entry(self->buffers.next, akvcam_buffers_buffer, list); 203 | list_del(&buf->list); 204 | buf->vb.vb2_buf.timestamp = ktime_get_ns(); 205 | buf->vb.field = V4L2_FIELD_NONE; 206 | buf->vb.sequence = self->sequence++; 207 | mutex_unlock(&self->frames_mutex); 208 | 209 | for (i = 0; i < buf->vb.vb2_buf.num_planes; i++) { 210 | memcpy(vb2_plane_vaddr(&buf->vb.vb2_buf, i), 211 | akvcam_frame_plane_data(frame, i), 212 | vb2_plane_size(&buf->vb.vb2_buf, i)); 213 | } 214 | 215 | vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 216 | 217 | return 0; 218 | } 219 | 220 | struct vb2_queue *akvcam_buffers_vb2_queue(akvcam_buffers_t self) 221 | { 222 | return &self->queue; 223 | } 224 | 225 | enum vb2_io_modes akvcam_buffers_io_modes_from_device_type(enum v4l2_buf_type type, 226 | AKVCAM_RW_MODE rw_mode) 227 | { 228 | enum vb2_io_modes io_modes = 0; 229 | 230 | if (rw_mode & AKVCAM_RW_MODE_READWRITE) { 231 | if (akvcam_device_type_from_v4l2(type) == AKVCAM_DEVICE_TYPE_CAPTURE) 232 | io_modes |= VB2_READ; 233 | else 234 | io_modes |= VB2_WRITE; 235 | } 236 | 237 | if (rw_mode & AKVCAM_RW_MODE_MMAP) 238 | io_modes |= VB2_MMAP; 239 | 240 | if (rw_mode & AKVCAM_RW_MODE_USERPTR) 241 | io_modes |= VB2_USERPTR; 242 | 243 | if (rw_mode & AKVCAM_RW_MODE_DMABUF) 244 | io_modes |= VB2_DMABUF; 245 | 246 | return io_modes; 247 | } 248 | 249 | int akvcam_buffers_queue_setup(struct vb2_queue *queue, 250 | unsigned int *num_buffers, 251 | unsigned int *num_planes, 252 | unsigned int sizes[], 253 | struct device *alloc_devs[]) 254 | { 255 | akvcam_buffers_t self = vb2_get_drv_priv(queue); 256 | size_t i; 257 | UNUSED(alloc_devs); 258 | akpr_function(); 259 | 260 | if (*num_buffers < 1) 261 | *num_buffers = 1; 262 | 263 | if (*num_planes > 0) { 264 | if (*num_planes < akvcam_format_planes(self->format)) 265 | return -EINVAL; 266 | 267 | for (i = 0; i < *num_planes; i++) 268 | if (sizes[i] < akvcam_format_plane_size(self->format, i)) 269 | return -EINVAL; 270 | 271 | return 0; 272 | } 273 | 274 | *num_planes = akvcam_format_planes(self->format); 275 | 276 | for (i = 0; i < *num_planes; i++) 277 | sizes[i] = akvcam_format_plane_size(self->format, i); 278 | 279 | return 0; 280 | } 281 | 282 | int akvcam_buffers_buffer_prepare(struct vb2_buffer *buffer) 283 | { 284 | akvcam_buffers_t self = vb2_get_drv_priv(buffer->vb2_queue); 285 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buffer); 286 | size_t i; 287 | 288 | akpr_function(); 289 | 290 | for (i = 0; i < buffer->num_planes; i++) { 291 | size_t plane_size = akvcam_format_plane_size(self->format, i); 292 | 293 | if (vb2_plane_size(buffer, i) < plane_size) 294 | return -EINVAL; 295 | else 296 | vb2_set_plane_payload(buffer, i, plane_size); 297 | } 298 | 299 | if (vbuf->field == V4L2_FIELD_ANY) 300 | vbuf->field = V4L2_FIELD_NONE; 301 | 302 | return 0; 303 | } 304 | 305 | void akvcam_buffers_buffer_queue(struct vb2_buffer *buffer) 306 | { 307 | akvcam_buffers_t self = vb2_get_drv_priv(buffer->vb2_queue); 308 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buffer); 309 | akvcam_buffers_buffer_t buf = container_of(vbuf, akvcam_buffers_buffer, vb); 310 | 311 | akpr_function(); 312 | 313 | if (!mutex_lock_interruptible(&self->frames_mutex)) { 314 | list_add_tail(&buf->list, &self->buffers); 315 | mutex_unlock(&self->frames_mutex); 316 | } 317 | } 318 | 319 | int akvcam_buffers_start_streaming(struct vb2_queue *queue, unsigned int count) 320 | { 321 | akvcam_buffers_t self = vb2_get_drv_priv(queue); 322 | UNUSED(count); 323 | 324 | akpr_function(); 325 | self->sequence = 0; 326 | 327 | return akvcam_call_no_args(self, streaming_started); 328 | } 329 | 330 | void akvcam_buffers_stop_streaming(struct vb2_queue *queue) 331 | { 332 | akvcam_buffers_t self = vb2_get_drv_priv(queue); 333 | 334 | akpr_function(); 335 | akvcam_emit_no_args(self, streaming_stopped); 336 | 337 | if (!mutex_lock_interruptible(&self->frames_mutex)) { 338 | akvcam_buffers_buffer_t buf; 339 | akvcam_buffers_buffer_t node; 340 | 341 | list_for_each_entry_safe(buf, node, &self->buffers, list) { 342 | vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 343 | list_del(&buf->list); 344 | } 345 | 346 | mutex_unlock(&self->frames_mutex); 347 | } 348 | } 349 | 350 | static const struct vb2_ops akvcam_akvcam_buffers_queue_ops = { 351 | .queue_setup = akvcam_buffers_queue_setup, 352 | .buf_prepare = akvcam_buffers_buffer_prepare, 353 | .buf_queue = akvcam_buffers_buffer_queue, 354 | .start_streaming = akvcam_buffers_start_streaming, 355 | .stop_streaming = akvcam_buffers_stop_streaming, 356 | .wait_prepare = vb2_ops_wait_prepare, 357 | .wait_finish = vb2_ops_wait_finish, 358 | }; 359 | -------------------------------------------------------------------------------- /src/buffers.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_BUFFERS_H 20 | #define AKVCAM_BUFFERS_H 21 | 22 | #include 23 | 24 | #include "buffers_types.h" 25 | #include "device_types.h" 26 | #include "format_types.h" 27 | #include "frame_types.h" 28 | #include "utils.h" 29 | 30 | enum v4l2_buf_type; 31 | struct vb2_queue; 32 | 33 | akvcam_buffers_t akvcam_buffers_new(AKVCAM_RW_MODE rw_mode, 34 | enum v4l2_buf_type type); 35 | void akvcam_buffers_delete(akvcam_buffers_t self); 36 | akvcam_buffers_t akvcam_buffers_ref(akvcam_buffers_t self); 37 | 38 | akvcam_format_t akvcam_buffers_format(akvcam_buffers_ct self); 39 | void akvcam_buffers_set_format(akvcam_buffers_t self, akvcam_format_ct format); 40 | size_t akvcam_buffers_count(akvcam_buffers_ct self); 41 | void akvcam_buffers_set_count(akvcam_buffers_t self, size_t nbuffers); 42 | akvcam_frame_t akvcam_buffers_read_frame(akvcam_buffers_t self); 43 | int akvcam_buffers_write_frame(akvcam_buffers_t self, akvcam_frame_t frame); 44 | struct vb2_queue *akvcam_buffers_vb2_queue(akvcam_buffers_t self); 45 | 46 | // signals 47 | akvcam_signal_no_args(buffers, streaming_started); 48 | akvcam_signal_no_args(buffers, streaming_stopped); 49 | 50 | #endif // AKVCAM_BUFFERS_H 51 | -------------------------------------------------------------------------------- /src/buffers_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_BUFFERS_TYPES_H 20 | #define AKVCAM_BUFFERS_TYPES_H 21 | 22 | struct akvcam_buffers; 23 | typedef struct akvcam_buffers *akvcam_buffers_t; 24 | typedef const struct akvcam_buffers *akvcam_buffers_ct; 25 | 26 | #endif // AKVCAM_BUFFERS_TYPES_H 27 | -------------------------------------------------------------------------------- /src/controls.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "controls.h" 24 | #include "frame_types.h" 25 | 26 | struct akvcam_controls 27 | { 28 | struct kref ref; 29 | struct v4l2_ctrl_handler handler; 30 | akvcam_signal_callback(controls, updated); 31 | }; 32 | 33 | akvcam_signal_define(controls, updated) 34 | 35 | static const struct v4l2_ctrl_ops akvcam_controls_ops; 36 | 37 | static const struct v4l2_ctrl_config akvcam_controls_capture[] = { 38 | {.id = V4L2_CID_BRIGHTNESS, .min = -255 , .max = 255 , .step = 1}, 39 | {.id = V4L2_CID_CONTRAST , .min = -255 , .max = 255 , .step = 1}, 40 | {.id = V4L2_CID_SATURATION, .min = -255 , .max = 255 , .step = 1}, 41 | {.id = V4L2_CID_HUE , .min = -359 , .max = 359 , .step = 1}, 42 | {.id = V4L2_CID_GAMMA , .min = -255 , .max = 255 , .step = 1}, 43 | {.id = V4L2_CID_HFLIP , .min = 0 , .max = 1 , .step = 1}, 44 | {.id = V4L2_CID_VFLIP , .min = 0 , .max = 1 , .step = 1}, 45 | {.id = V4L2_CID_COLORFX , .min = V4L2_COLORFX_NONE, .max = V4L2_COLORFX_BW, .step = 1}, 46 | {.id = 0 }, 47 | }; 48 | 49 | static const char * const akvcam_controls_scaling_menu[] = { 50 | [AKVCAM_SCALING_FAST ] = "Fast" , 51 | [AKVCAM_SCALING_LINEAR] = "Linear", 52 | }; 53 | 54 | static const char * const akvcam_controls_aspect_menu[] = { 55 | [AKVCAM_ASPECT_RATIO_IGNORE ] = "Ignore" , 56 | [AKVCAM_ASPECT_RATIO_KEEP ] = "Keep" , 57 | [AKVCAM_ASPECT_RATIO_EXPANDING] = "Expanding", 58 | }; 59 | 60 | static const struct v4l2_ctrl_config akvcam_controls_output[] = { 61 | {.id = V4L2_CID_HFLIP, .max = 1, .step = 1}, 62 | {.id = V4L2_CID_VFLIP, .max = 1, .step = 1}, 63 | { 64 | .id = AKVCAM_CID_SCALING, 65 | .type = V4L2_CTRL_TYPE_MENU, 66 | .name = "Scaling Mode", 67 | .max = ARRAY_SIZE(akvcam_controls_scaling_menu) - 1, 68 | .step = 0, 69 | .qmenu = akvcam_controls_scaling_menu, 70 | .ops = &akvcam_controls_ops 71 | }, 72 | { 73 | .id = AKVCAM_CID_ASPECT_RATIO, 74 | .type = V4L2_CTRL_TYPE_MENU, 75 | .name = "Aspect Ratio Mode" , 76 | .max = ARRAY_SIZE(akvcam_controls_aspect_menu) - 1, 77 | .step = 0, 78 | .qmenu = akvcam_controls_aspect_menu, 79 | .ops = &akvcam_controls_ops 80 | }, 81 | { 82 | .id = AKVCAM_CID_SWAP_RGB, 83 | .type = V4L2_CTRL_TYPE_BOOLEAN, 84 | .name = "Swap Read and Blue", 85 | .max = 1, 86 | .step = 1, 87 | .ops = &akvcam_controls_ops 88 | }, 89 | {.id = 0}, 90 | }; 91 | 92 | int akvcam_controls_cotrol_changed(struct v4l2_ctrl *control); 93 | 94 | akvcam_controls_t akvcam_controls_new(AKVCAM_DEVICE_TYPE device_type) 95 | { 96 | akvcam_controls_t self = kzalloc(sizeof(struct akvcam_controls), GFP_KERNEL); 97 | const struct v4l2_ctrl_config *control_params; 98 | size_t n_controls = 0; 99 | size_t i; 100 | 101 | kref_init(&self->ref); 102 | 103 | // Initialize controls with default values. 104 | if (device_type == AKVCAM_DEVICE_TYPE_OUTPUT) 105 | control_params = akvcam_controls_output; 106 | else 107 | control_params = akvcam_controls_capture; 108 | 109 | for (i = 0; control_params[i].id; i++) 110 | n_controls++; 111 | 112 | v4l2_ctrl_handler_init(&self->handler, n_controls); 113 | 114 | for (i = 0; control_params[i].id; i++) { 115 | const struct v4l2_ctrl_config *params = control_params + i; 116 | 117 | if (params->id < AKVCAM_CID_BASE) { 118 | const char *name; 119 | enum v4l2_ctrl_type type; 120 | s64 min; 121 | s64 max; 122 | u64 step; 123 | s64 def; 124 | u32 flags; 125 | v4l2_ctrl_fill(params->id, 126 | &name, 127 | &type, 128 | &min, 129 | &max, 130 | &step, 131 | &def, 132 | &flags); 133 | 134 | if (type == V4L2_CTRL_TYPE_MENU) { 135 | v4l2_ctrl_new_std_menu(&self->handler, 136 | &akvcam_controls_ops, 137 | params->id, 138 | params->max, 139 | params->menu_skip_mask, 140 | params->def); 141 | } else { 142 | v4l2_ctrl_new_std(&self->handler, 143 | &akvcam_controls_ops, 144 | params->id, 145 | params->min, 146 | params->max, 147 | params->step, 148 | params->def); 149 | } 150 | } else { 151 | v4l2_ctrl_new_custom(&self->handler, params, self); 152 | } 153 | } 154 | 155 | return self; 156 | } 157 | 158 | static void akvcam_controls_free(struct kref *ref) 159 | { 160 | akvcam_controls_t self = container_of(ref, struct akvcam_controls, ref); 161 | v4l2_ctrl_handler_free(&self->handler);; 162 | kfree(self); 163 | } 164 | 165 | void akvcam_controls_delete(akvcam_controls_t self) 166 | { 167 | if (self) 168 | kref_put(&self->ref, akvcam_controls_free); 169 | } 170 | 171 | akvcam_controls_t akvcam_controls_ref(akvcam_controls_t self) 172 | { 173 | if (self) 174 | kref_get(&self->ref); 175 | 176 | return self; 177 | } 178 | __s32 akvcam_controls_value(akvcam_controls_t self, __u32 id) 179 | { 180 | struct v4l2_ctrl *control = v4l2_ctrl_find(&self->handler, id); 181 | 182 | if (!control) 183 | return 0; 184 | 185 | return v4l2_ctrl_g_ctrl(control); 186 | } 187 | 188 | const char *akvcam_controls_string_value(akvcam_controls_t self, __u32 id) 189 | { 190 | struct v4l2_ctrl *control = v4l2_ctrl_find(&self->handler, id); 191 | 192 | if (!control || !control->qmenu) 193 | return NULL; 194 | 195 | return control->qmenu[v4l2_ctrl_g_ctrl(control)]; 196 | } 197 | 198 | int akvcam_controls_set_value(akvcam_controls_t self, __u32 id, __s32 value) 199 | { 200 | struct v4l2_ctrl *control = v4l2_ctrl_find(&self->handler, id); 201 | int result; 202 | 203 | if (!control) 204 | return -EINVAL; 205 | 206 | result = v4l2_ctrl_s_ctrl(control, value); 207 | 208 | if (result) 209 | return result; 210 | 211 | akvcam_emit(self, updated, control->id, control->val); 212 | 213 | return 0; 214 | } 215 | 216 | int akvcam_controls_set_string_value(akvcam_controls_t self, __u32 id, const char *value) 217 | { 218 | struct v4l2_ctrl *control = v4l2_ctrl_find(&self->handler, id); 219 | s64 i; 220 | int result = -EINVAL; 221 | 222 | if (!control || !control->qmenu) 223 | return -EINVAL; 224 | 225 | for (i = 0; i <= control->maximum; i++) 226 | if (strncmp(control->qmenu[i], value, AKVCAM_MAX_STRING_SIZE) == 0) { 227 | result = 0; 228 | 229 | break; 230 | } 231 | 232 | if (result) 233 | return result; 234 | 235 | result = v4l2_ctrl_s_ctrl(control, i); 236 | 237 | if (result) 238 | return result; 239 | 240 | akvcam_emit(self, updated, control->id, control->val); 241 | 242 | return 0; 243 | } 244 | 245 | struct v4l2_ctrl_handler *akvcam_controls_handler(akvcam_controls_t self) 246 | { 247 | return self->handler.error? NULL: &self->handler; 248 | } 249 | 250 | int akvcam_controls_cotrol_changed(struct v4l2_ctrl *control) 251 | { 252 | akvcam_controls_t self = 253 | container_of(control->handler, struct akvcam_controls, handler); 254 | 255 | akvcam_emit(self, updated, control->id, control->val); 256 | 257 | return 0; 258 | } 259 | 260 | static const struct v4l2_ctrl_ops akvcam_controls_ops = { 261 | .s_ctrl = akvcam_controls_cotrol_changed, 262 | }; 263 | -------------------------------------------------------------------------------- /src/controls.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_CONTROLS_H 20 | #define AKVCAM_CONTROLS_H 21 | 22 | #include 23 | 24 | #include "controls_types.h" 25 | #include "device_types.h" 26 | #include "utils.h" 27 | 28 | // public 29 | akvcam_controls_t akvcam_controls_new(AKVCAM_DEVICE_TYPE device_type); 30 | void akvcam_controls_delete(akvcam_controls_t self); 31 | akvcam_controls_t akvcam_controls_ref(akvcam_controls_t self); 32 | 33 | __s32 akvcam_controls_value(akvcam_controls_t self, __u32 id); 34 | const char *akvcam_controls_string_value(akvcam_controls_t self, __u32 id); 35 | int akvcam_controls_set_value(akvcam_controls_t self, __u32 id, __s32 value); 36 | int akvcam_controls_set_string_value(akvcam_controls_t self, __u32 id, const char *value); 37 | struct v4l2_ctrl_handler *akvcam_controls_handler(akvcam_controls_t self); 38 | 39 | // signals 40 | akvcam_signal(controls, updated, __u32 id, __s32 value); 41 | 42 | #endif // AKVCAM_CONTROLS_H 43 | -------------------------------------------------------------------------------- /src/controls_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_CONTROLS_TYPES_H 20 | #define AKVCAM_CONTROLS_TYPES_H 21 | 22 | #define AKVCAM_CID_BASE (V4L2_CID_USER_BASE | 0xe000) 23 | #define AKVCAM_CID_SCALING (AKVCAM_CID_BASE + 0) 24 | #define AKVCAM_CID_ASPECT_RATIO (AKVCAM_CID_BASE + 1) 25 | #define AKVCAM_CID_SWAP_RGB (AKVCAM_CID_BASE + 2) 26 | 27 | struct akvcam_controls; 28 | typedef struct akvcam_controls *akvcam_controls_t; 29 | typedef const struct akvcam_controls *akvcam_controls_ct; 30 | 31 | #endif // AKVCAM_CONTROLS_TYPES_H 32 | -------------------------------------------------------------------------------- /src/device.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_DEVICE_H 20 | #define AKVCAM_DEVICE_H 21 | 22 | #include "device_types.h" 23 | #include "buffers_types.h" 24 | #include "controls_types.h" 25 | #include "format_types.h" 26 | #include "frame_filter_types.h" 27 | #include "frame_types.h" 28 | 29 | struct file; 30 | 31 | // public 32 | akvcam_device_t akvcam_device_new(const char *name, 33 | const char *description, 34 | AKVCAM_DEVICE_TYPE type, 35 | AKVCAM_RW_MODE rw_mode, 36 | akvcam_formats_list_t formats, 37 | akvcam_frame_ct default_frame, 38 | akvcam_frame_filter_ct frame_filter); 39 | void akvcam_device_delete(akvcam_device_t self); 40 | akvcam_device_t akvcam_device_ref(akvcam_device_t self); 41 | 42 | bool akvcam_device_register(akvcam_device_t self); 43 | void akvcam_device_unregister(akvcam_device_t self); 44 | int32_t akvcam_device_num(akvcam_device_ct self); 45 | void akvcam_device_set_num(akvcam_device_t self, int32_t num); 46 | bool akvcam_device_is_registered(akvcam_device_ct self); 47 | const char *akvcam_device_description(akvcam_device_t self); 48 | AKVCAM_DEVICE_TYPE akvcam_device_type(akvcam_device_ct self); 49 | enum v4l2_buf_type akvcam_device_v4l2_type(akvcam_device_ct self); 50 | AKVCAM_RW_MODE akvcam_device_rw_mode(akvcam_device_ct self); 51 | akvcam_formats_list_t akvcam_device_formats(akvcam_device_ct self); 52 | akvcam_format_t akvcam_device_format(akvcam_device_ct self); 53 | void akvcam_device_set_format(akvcam_device_t self, 54 | akvcam_format_t format); 55 | akvcam_controls_t akvcam_device_controls_nr(akvcam_device_ct self); 56 | akvcam_controls_t akvcam_device_controls(akvcam_device_ct self); 57 | akvcam_buffers_t akvcam_device_buffers_nr(akvcam_device_ct self); 58 | akvcam_buffers_t akvcam_device_buffers(akvcam_device_ct self); 59 | bool akvcam_device_streaming(akvcam_device_ct self); 60 | akvcam_devices_list_t akvcam_device_connected_devices_nr(akvcam_device_ct self); 61 | akvcam_devices_list_t akvcam_device_connected_devices(akvcam_device_ct self); 62 | __u32 akvcam_device_caps(akvcam_device_ct self); 63 | 64 | // public static 65 | AKVCAM_DEVICE_TYPE akvcam_device_type_from_v4l2(enum v4l2_buf_type type); 66 | 67 | #endif //AKVCAM_ DEVICE_H 68 | -------------------------------------------------------------------------------- /src/device_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_DEVICE_TYPES_H 20 | #define AKVCAM_DEVICE_TYPES_H 21 | 22 | #include 23 | 24 | #include "list_types.h" 25 | 26 | #define AKVCAM_RW_MODE_READWRITE BIT(0) 27 | #define AKVCAM_RW_MODE_MMAP BIT(1) 28 | #define AKVCAM_RW_MODE_USERPTR BIT(2) 29 | #define AKVCAM_RW_MODE_DMABUF BIT(3) 30 | 31 | struct akvcam_device; 32 | typedef struct akvcam_device *akvcam_device_t; 33 | typedef const struct akvcam_device *akvcam_device_ct; 34 | typedef akvcam_list_tt(akvcam_device_t) akvcam_devices_list_t; 35 | typedef akvcam_list_ctt(akvcam_device_t) akvcam_devices_list_ct; 36 | typedef __u32 AKVCAM_RW_MODE; 37 | 38 | typedef enum 39 | { 40 | AKVCAM_DEVICE_TYPE_CAPTURE, 41 | AKVCAM_DEVICE_TYPE_OUTPUT, 42 | } AKVCAM_DEVICE_TYPE; 43 | 44 | #endif // AKVCAM_DEVICE_TYPES_H 45 | -------------------------------------------------------------------------------- /src/dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="akvcam" 2 | PACKAGE_VERSION="1.2.6" 3 | 4 | MAKE[0]="make KERNEL_DIR=${kernel_source_dir} all" 5 | CLEAN="make clean" 6 | 7 | BUILT_MODULE_NAME[0]="$PACKAGE_NAME" 8 | DEST_MODULE_LOCATION[0]="/extra" 9 | 10 | AUTOINSTALL="yes" 11 | -------------------------------------------------------------------------------- /src/driver.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_DRIVER_H 20 | #define AKVCAM_DRIVER_H 21 | 22 | #include 23 | 24 | // public static 25 | int akvcam_driver_init(const char *name, const char *description); 26 | void akvcam_driver_uninit(void); 27 | 28 | const char *akvcam_driver_name(void); 29 | const char *akvcam_driver_description(void); 30 | uint akvcam_driver_version(void); 31 | 32 | #endif // AKVCAM_DRIVER_H 33 | -------------------------------------------------------------------------------- /src/file_read.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "file_read.h" 26 | #include "log.h" 27 | #include "rbuffer.h" 28 | #include "utils.h" 29 | 30 | #define AKVCAM_READ_BLOCK 512 31 | 32 | struct akvcam_file 33 | { 34 | struct kref ref; 35 | char *file_name; 36 | struct file *filp; 37 | akvcam_rbuffer_tt(char *) buffer; 38 | size_t size; 39 | size_t bytes_read; 40 | size_t file_bytes_read; 41 | bool is_open; 42 | }; 43 | 44 | akvcam_file_t akvcam_file_new(const char *file_name) 45 | { 46 | akvcam_file_t self = kzalloc(sizeof(struct akvcam_file), GFP_KERNEL); 47 | kref_init(&self->ref); 48 | self->file_name = akvcam_strdup(file_name, AKVCAM_MEMORY_TYPE_KMALLOC); 49 | self->buffer = akvcam_rbuffer_new(); 50 | 51 | return self; 52 | } 53 | 54 | static void akvcam_file_free(struct kref *ref) 55 | { 56 | akvcam_file_t self = container_of(ref, struct akvcam_file, ref); 57 | akvcam_file_close(self); 58 | 59 | if (self->file_name) 60 | kfree(self->file_name); 61 | 62 | akvcam_rbuffer_delete(self->buffer); 63 | kfree(self); 64 | } 65 | 66 | void akvcam_file_delete(akvcam_file_t self) 67 | { 68 | if (self) 69 | kref_put(&self->ref, akvcam_file_free); 70 | } 71 | 72 | akvcam_file_t akvcam_file_ref(akvcam_file_t self) 73 | { 74 | if (self) 75 | kref_get(&self->ref); 76 | 77 | return self; 78 | } 79 | 80 | const char *akvcam_file_file_name(akvcam_file_ct self) 81 | { 82 | return self->file_name; 83 | } 84 | 85 | void akvcam_file_set_file_name(akvcam_file_t self, const char *file_name) 86 | { 87 | if (self->file_name) 88 | kfree(self->file_name); 89 | 90 | self->file_name = akvcam_strdup(file_name, AKVCAM_MEMORY_TYPE_KMALLOC); 91 | } 92 | 93 | bool akvcam_file_open(akvcam_file_t self) 94 | { 95 | akvcam_file_close(self); 96 | 97 | if (akvcam_strlen(self->file_name) < 1) 98 | return false; 99 | 100 | self->filp = filp_open(self->file_name, O_RDONLY, 0); 101 | 102 | if (IS_ERR(self->filp)) { 103 | int error = PTR_ERR(self->filp); 104 | char *error_str = kzalloc(AKVCAM_MAX_STRING_SIZE, GFP_KERNEL); 105 | 106 | akvcam_string_from_error(error, error_str, AKVCAM_MAX_STRING_SIZE); 107 | akpr_err("%s\n", error_str); 108 | kfree(error_str); 109 | 110 | return false; 111 | } 112 | 113 | self->size = i_size_read(self->filp->f_inode); 114 | akvcam_rbuffer_resize(self->buffer, 115 | self->size, 116 | sizeof(char), 117 | AKVCAM_MEMORY_TYPE_VMALLOC); 118 | akvcam_rbuffer_clear(self->buffer); 119 | self->bytes_read = 0; 120 | self->file_bytes_read = 0; 121 | self->is_open = true; 122 | 123 | return true; 124 | } 125 | 126 | void akvcam_file_close(akvcam_file_t self) 127 | { 128 | if (!self->is_open) 129 | return; 130 | 131 | filp_close(self->filp, NULL); 132 | self->filp = NULL; 133 | akvcam_rbuffer_clear(self->buffer); 134 | akvcam_rbuffer_resize(self->buffer, 135 | 0, 136 | sizeof(char), 137 | AKVCAM_MEMORY_TYPE_VMALLOC); 138 | self->size = 0; 139 | self->bytes_read = 0; 140 | self->file_bytes_read = 0; 141 | self->is_open = false; 142 | } 143 | 144 | bool akvcam_file_is_open(akvcam_file_ct self) 145 | { 146 | return self->is_open; 147 | } 148 | 149 | bool akvcam_file_eof(akvcam_file_ct self) 150 | { 151 | return self->bytes_read >= self->size; 152 | } 153 | 154 | bool akvcam_file_seek(akvcam_file_t self, ssize_t offset, AKVCAM_FILE_SEEK pos) 155 | { 156 | if (!self->is_open) 157 | return false; 158 | 159 | akvcam_rbuffer_clear(self->buffer); 160 | 161 | switch (pos) { 162 | case AKVCAM_FILE_SEEK_BEG: 163 | self->bytes_read = 164 | self->file_bytes_read = 165 | (size_t) akvcam_bound(0, offset, (ssize_t) self->size); 166 | 167 | break; 168 | 169 | case AKVCAM_FILE_SEEK_CUR: 170 | self->bytes_read = 171 | self->file_bytes_read = 172 | (size_t) akvcam_bound(0, 173 | (ssize_t) self->bytes_read + offset, 174 | (ssize_t) self->size); 175 | 176 | break; 177 | 178 | case AKVCAM_FILE_SEEK_END: 179 | self->bytes_read = 180 | self->file_bytes_read = 181 | (size_t) akvcam_bound(0, 182 | (ssize_t) self->size + offset, 183 | (ssize_t) self->size); 184 | 185 | break; 186 | } 187 | 188 | vfs_setpos(self->filp, (loff_t) self->bytes_read, (loff_t) self->size); 189 | 190 | return true; 191 | } 192 | 193 | size_t akvcam_file_read(akvcam_file_t self, void *data, size_t size) 194 | { 195 | loff_t offset; 196 | char read_block[AKVCAM_READ_BLOCK]; 197 | 198 | if (!self->is_open || size < 1) 199 | return 0; 200 | 201 | while (self->file_bytes_read < self->size 202 | && akvcam_rbuffer_data_size(self->buffer) < size) { 203 | ssize_t bytes_read; 204 | 205 | offset = (loff_t) self->file_bytes_read; 206 | bytes_read = kernel_read(self->filp, 207 | read_block, 208 | AKVCAM_READ_BLOCK, 209 | &offset); 210 | 211 | if (bytes_read < 1) 212 | break; 213 | 214 | akvcam_rbuffer_queue_bytes(self->buffer, read_block, (size_t) bytes_read); 215 | self->file_bytes_read += (size_t) bytes_read; 216 | } 217 | 218 | size = akvcam_min(akvcam_rbuffer_size(self->buffer), size); 219 | 220 | if (size < 1) 221 | return 0; 222 | 223 | akvcam_rbuffer_dequeue_bytes(self->buffer, data, &size, false); 224 | self->bytes_read += size; 225 | 226 | return size; 227 | } 228 | 229 | static bool akvcam_file_find_new_line(const char *element, 230 | const char *new_line) 231 | { 232 | return strncmp(element, new_line, 1) == 0; 233 | } 234 | 235 | char *akvcam_file_read_line(akvcam_file_t self) 236 | { 237 | loff_t offset; 238 | ssize_t bytes_read; 239 | ssize_t new_line = -1; 240 | char read_block[AKVCAM_READ_BLOCK]; 241 | char *line; 242 | 243 | if (!self->is_open) 244 | return vzalloc(1); 245 | 246 | for (;;) { 247 | new_line = 0; 248 | akvcam_rbuffer_find(self->buffer, 249 | "\n", 250 | (akvcam_are_equals_t) akvcam_file_find_new_line, 251 | &new_line); 252 | 253 | if (new_line >= 0) 254 | break; 255 | 256 | if (self->file_bytes_read >= self->size) 257 | break; 258 | 259 | bytes_read = (ssize_t) akvcam_min(AKVCAM_READ_BLOCK, 260 | self->size - self->file_bytes_read); 261 | 262 | if (bytes_read < 1) 263 | break; 264 | 265 | offset = (loff_t) self->file_bytes_read; 266 | bytes_read = kernel_read(self->filp, 267 | read_block, 268 | (size_t) bytes_read, 269 | &offset); 270 | akvcam_rbuffer_queue_bytes(self->buffer, read_block, (size_t) bytes_read); 271 | self->file_bytes_read += (size_t) bytes_read; 272 | } 273 | 274 | if (new_line >= 0) { 275 | line = vzalloc((size_t) new_line + 2); 276 | new_line++; 277 | akvcam_rbuffer_dequeue_bytes(self->buffer, 278 | line, 279 | (size_t *) &new_line, 280 | false); 281 | line[(size_t) new_line - 1] = 0; 282 | self->bytes_read += (size_t) new_line; 283 | } else if (self->bytes_read < self->size) { 284 | new_line = (ssize_t) akvcam_rbuffer_data_size(self->buffer); 285 | line = vzalloc((size_t) new_line + 1); 286 | akvcam_rbuffer_dequeue_bytes(self->buffer, 287 | line, 288 | (size_t *) &new_line, 289 | false); 290 | self->bytes_read += (size_t) new_line; 291 | } else { 292 | line = vzalloc(1); 293 | } 294 | 295 | return line; 296 | } 297 | -------------------------------------------------------------------------------- /src/file_read.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FILE_H 20 | #define AKVCAM_FILE_H 21 | 22 | #include 23 | 24 | struct akvcam_file; 25 | typedef struct akvcam_file *akvcam_file_t; 26 | typedef const struct akvcam_file *akvcam_file_ct; 27 | 28 | typedef enum 29 | { 30 | AKVCAM_FILE_SEEK_BEG, 31 | AKVCAM_FILE_SEEK_CUR, 32 | AKVCAM_FILE_SEEK_END 33 | } AKVCAM_FILE_SEEK; 34 | 35 | // public 36 | akvcam_file_t akvcam_file_new(const char *file_name); 37 | void akvcam_file_delete(akvcam_file_t self); 38 | akvcam_file_t akvcam_file_ref(akvcam_file_t self); 39 | 40 | const char *akvcam_file_file_name(akvcam_file_ct self); 41 | void akvcam_file_set_file_name(akvcam_file_t self, const char *file_name); 42 | bool akvcam_file_open(akvcam_file_t self); 43 | void akvcam_file_close(akvcam_file_t self); 44 | bool akvcam_file_is_open(akvcam_file_ct self); 45 | bool akvcam_file_eof(akvcam_file_ct self); 46 | bool akvcam_file_seek(akvcam_file_t self, ssize_t offset, AKVCAM_FILE_SEEK pos); 47 | size_t akvcam_file_read(akvcam_file_t self, void *data, size_t size); 48 | char *akvcam_file_read_line(akvcam_file_t self); 49 | 50 | #endif // AKVCAM_FILE_H 51 | -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FORMAT_H 20 | #define AKVCAM_FORMAT_H 21 | 22 | #include 23 | 24 | #include "format_types.h" 25 | 26 | struct v4l2_fract; 27 | struct v4l2_format; 28 | 29 | // public 30 | akvcam_format_t akvcam_format_new(__u32 fourcc, 31 | size_t width, 32 | size_t height, 33 | const struct v4l2_fract *frame_rate); 34 | akvcam_format_t akvcam_format_new_copy(akvcam_format_ct other); 35 | void akvcam_format_delete(akvcam_format_t self); 36 | akvcam_format_t akvcam_format_ref(akvcam_format_t self); 37 | 38 | void akvcam_format_copy(akvcam_format_t self, akvcam_format_ct other); 39 | __u32 akvcam_format_fourcc(akvcam_format_ct self); 40 | void akvcam_format_set_fourcc(akvcam_format_t self, __u32 fourcc); 41 | const char *akvcam_format_fourcc_str(akvcam_format_ct self); 42 | void akvcam_format_set_fourcc_str(akvcam_format_t self, const char *fourcc); 43 | size_t akvcam_format_width(akvcam_format_ct self); 44 | void akvcam_format_set_width(akvcam_format_t self, size_t width); 45 | size_t akvcam_format_height(akvcam_format_ct self); 46 | void akvcam_format_set_height(akvcam_format_t self, size_t height); 47 | struct v4l2_fract akvcam_format_frame_rate(akvcam_format_ct self); 48 | void akvcam_format_set_frame_rate(akvcam_format_t self, const struct v4l2_fract frame_rate); 49 | size_t akvcam_format_bpp(akvcam_format_ct self); 50 | size_t akvcam_format_bypl(akvcam_format_ct self, size_t plane); 51 | size_t akvcam_format_size(akvcam_format_ct self); 52 | size_t akvcam_format_planes(akvcam_format_ct self); 53 | size_t akvcam_format_offset(akvcam_format_ct self, size_t plane); 54 | size_t akvcam_format_plane_size(akvcam_format_ct self, size_t plane); 55 | bool akvcam_format_is_valid(akvcam_format_ct self); 56 | void akvcam_format_clear(akvcam_format_t self); 57 | const char *akvcam_format_to_string(akvcam_format_t self); 58 | 59 | // public static 60 | void akvcam_format_round_nearest(int width, int height, 61 | int *owidth, int *oheight, 62 | int align); 63 | __u32 akvcam_format_fourcc_from_string(const char *fourcc_str); 64 | const char *akvcam_format_string_from_fourcc(__u32 fourcc); 65 | akvcam_format_t akvcam_format_nearest(akvcam_formats_list_ct formats, 66 | akvcam_format_ct format); 67 | akvcam_pixel_formats_list_t akvcam_format_pixel_formats(akvcam_formats_list_ct formats); 68 | akvcam_resolutions_list_t akvcam_format_resolutions(akvcam_formats_list_t formats, 69 | __u32 fourcc); 70 | akvcam_fps_list_t akvcam_format_frame_rates(akvcam_formats_list_ct formats, 71 | __u32 fourcc, 72 | size_t width, 73 | size_t height); 74 | akvcam_format_ct akvcam_format_from_v4l2_nr(akvcam_formats_list_ct formats, 75 | const struct v4l2_format *format); 76 | akvcam_format_t akvcam_format_from_v4l2(akvcam_formats_list_ct formats, 77 | const struct v4l2_format *format); 78 | bool akvcam_format_have_multiplanar(akvcam_formats_list_ct formats); 79 | 80 | #endif // AKVCAM_FORMAT_H 81 | -------------------------------------------------------------------------------- /src/format_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FORMAT_TYPES_H 20 | #define AKVCAM_FORMAT_TYPES_H 21 | 22 | #include "list_types.h" 23 | 24 | typedef akvcam_list_tt(struct v4l2_frmsize_discrete) akvcam_resolutions_list_t; 25 | typedef akvcam_list_ctt(struct v4l2_frmsize_discrete) akvcam_resolutions_list_ct; 26 | typedef akvcam_list_tt(struct v4l2_fract) akvcam_fps_list_t; 27 | typedef akvcam_list_ctt(struct v4l2_fract) akvcam_fps_list_ct; 28 | typedef akvcam_list_tt(__u32) akvcam_pixel_formats_list_t; 29 | typedef akvcam_list_ctt(__u32) akvcam_pixel_formats_list_ct; 30 | 31 | struct akvcam_format; 32 | typedef struct akvcam_format *akvcam_format_t; 33 | typedef const struct akvcam_format *akvcam_format_ct; 34 | typedef akvcam_list_tt(akvcam_formats_t) akvcam_formats_list_t; 35 | typedef akvcam_list_ctt(akvcam_formats_t) akvcam_formats_list_ct; 36 | 37 | #endif // AKVCAM_FORMAT_TYPES_H 38 | -------------------------------------------------------------------------------- /src/frame.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FRAME_H 20 | #define AKVCAM_FRAME_H 21 | 22 | #include 23 | 24 | #include "frame_types.h" 25 | #include "format_types.h" 26 | 27 | // public 28 | akvcam_frame_t akvcam_frame_new(akvcam_format_t format, 29 | const void *data, 30 | size_t size); 31 | akvcam_frame_t akvcam_frame_new_copy(akvcam_frame_ct other); 32 | void akvcam_frame_delete(akvcam_frame_t self); 33 | akvcam_frame_t akvcam_frame_ref(akvcam_frame_t self); 34 | 35 | void akvcam_frame_copy(akvcam_frame_t self, akvcam_frame_ct other); 36 | akvcam_format_t akvcam_frame_format(akvcam_frame_ct self); 37 | void *akvcam_frame_data(akvcam_frame_ct self); 38 | void *akvcam_frame_line(akvcam_frame_ct self, size_t plane, size_t y); 39 | const void *akvcam_frame_const_line(akvcam_frame_ct self, 40 | size_t plane, 41 | size_t y); 42 | void *akvcam_frame_plane_data(akvcam_frame_ct self, size_t plane); 43 | const void *akvcam_frame_plane_const_data(akvcam_frame_ct self, size_t plane); 44 | size_t akvcam_frame_size(akvcam_frame_ct self); 45 | void akvcam_frame_resize(akvcam_frame_t self, size_t size); 46 | void akvcam_frame_clear(akvcam_frame_t self); 47 | bool akvcam_frame_load(akvcam_frame_t self, const char *file_name); 48 | void akvcam_frame_mirror(akvcam_frame_t self, 49 | bool horizontalMirror, 50 | bool verticalMirror); 51 | bool akvcam_frame_scaled(akvcam_frame_t self, 52 | size_t width, 53 | size_t height, 54 | AKVCAM_SCALING mode, 55 | AKVCAM_ASPECT_RATIO aspectRatio); 56 | bool akvcam_frame_convert(akvcam_frame_t self, __u32 fourcc); 57 | 58 | // public static 59 | const char *akvcam_frame_scaling_to_string(AKVCAM_SCALING scaling); 60 | const char *akvcam_frame_aspect_ratio_to_string(AKVCAM_ASPECT_RATIO aspect_ratio); 61 | bool akvcam_frame_can_convert(__u32 in_fourcc, __u32 out_fourcc); 62 | 63 | #endif // AKVCAM_FRAME_H 64 | -------------------------------------------------------------------------------- /src/frame_filter.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2021 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "frame_filter.h" 25 | #include "frame.h" 26 | #include "format.h" 27 | #include "log.h" 28 | #include "utils.h" 29 | 30 | typedef struct 31 | { 32 | uint8_t b; 33 | uint8_t g; 34 | uint8_t r; 35 | } akvcam_RGB24, *akvcam_RGB24_t; 36 | 37 | struct akvcam_frame_filter 38 | { 39 | struct kref ref; 40 | uint8_t *contrast_table; 41 | uint8_t *gamma_table; 42 | }; 43 | 44 | void akvcam_rgb_to_hsl(int r, int g, int b, int *h, int *s, int *l); 45 | void akvcam_hsl_to_rgb(int h, int s, int l, int *r, int *g, int *b); 46 | void akvcam_init_contrast_table(akvcam_frame_filter_t self); 47 | void akvcam_init_gamma_table(akvcam_frame_filter_t self); 48 | int akvcam_grayval(int r, int g, int b); 49 | bool akvcam_filter_format_supported(__u32 fourcc); 50 | 51 | akvcam_frame_filter_t akvcam_frame_filter_new(void) 52 | { 53 | akvcam_frame_filter_t self = 54 | kzalloc(sizeof(struct akvcam_frame_filter), GFP_KERNEL); 55 | kref_init(&self->ref); 56 | akvcam_init_contrast_table(self); 57 | akvcam_init_gamma_table(self); 58 | 59 | return self; 60 | } 61 | 62 | static void akvcam_frame_filter_free(struct kref *ref) 63 | { 64 | akvcam_frame_filter_t self = 65 | container_of(ref, struct akvcam_frame_filter, ref); 66 | 67 | if (self->gamma_table) 68 | vfree(self->gamma_table); 69 | 70 | if (self->contrast_table) 71 | vfree(self->contrast_table); 72 | 73 | kfree(self); 74 | } 75 | 76 | void akvcam_frame_filter_delete(akvcam_frame_filter_t self) 77 | { 78 | if (self) 79 | kref_put(&self->ref, akvcam_frame_filter_free); 80 | } 81 | 82 | akvcam_frame_filter_t akvcam_frame_filter_ref(akvcam_frame_filter_t self) 83 | { 84 | if (self) 85 | kref_get(&self->ref); 86 | 87 | return self; 88 | } 89 | 90 | void akvcam_frame_filter_swap_rgb(akvcam_frame_filter_ct self, 91 | akvcam_frame_t frame) 92 | { 93 | __u32 fourcc; 94 | size_t width; 95 | size_t height; 96 | size_t x; 97 | size_t y; 98 | akvcam_format_t format; 99 | UNUSED(self); 100 | 101 | akpr_function(); 102 | 103 | format = akvcam_frame_format(frame); 104 | fourcc = akvcam_format_fourcc(format); 105 | 106 | if (!akvcam_filter_format_supported(fourcc)) { 107 | akvcam_format_delete(format); 108 | 109 | return; 110 | } 111 | 112 | width = akvcam_format_width(format); 113 | height = akvcam_format_height(format); 114 | akvcam_format_delete(format); 115 | 116 | for (y = 0; y < height; y++) { 117 | akvcam_RGB24_t line = akvcam_frame_line(frame, 0, y); 118 | 119 | for (x = 0; x < width; x++) { 120 | uint8_t tmp = line[x].r; 121 | line[x].r = line[x].b; 122 | line[x].b = tmp; 123 | } 124 | } 125 | } 126 | 127 | void akvcam_frame_filter_hsl(akvcam_frame_filter_ct self, 128 | akvcam_frame_t frame, 129 | int hue, 130 | int saturation, 131 | int luminance) 132 | { 133 | __u32 fourcc; 134 | size_t width; 135 | size_t height; 136 | size_t x; 137 | size_t y; 138 | int h; 139 | int s; 140 | int l; 141 | int r; 142 | int g; 143 | int b; 144 | akvcam_format_t format; 145 | UNUSED(self); 146 | 147 | akpr_function(); 148 | 149 | if (hue == 0 && saturation == 0 && luminance == 0) 150 | return; 151 | 152 | format = akvcam_frame_format(frame); 153 | fourcc = akvcam_format_fourcc(format); 154 | 155 | if (!akvcam_filter_format_supported(fourcc)) { 156 | akvcam_format_delete(format); 157 | 158 | return; 159 | } 160 | 161 | width = akvcam_format_width(format); 162 | height = akvcam_format_height(format); 163 | akvcam_format_delete(format); 164 | 165 | for (y = 0; y < height; y++) { 166 | akvcam_RGB24_t line = akvcam_frame_line(frame, 0, y); 167 | 168 | for (x = 0; x < width; x++) { 169 | akvcam_rgb_to_hsl(line[x].r, line[x].g, line[x].b, &h, &s, &l); 170 | 171 | h = akvcam_mod(h + hue, 360); 172 | s = akvcam_bound(0, s + saturation, 255); 173 | l = akvcam_bound(0, l + luminance, 255); 174 | 175 | akvcam_hsl_to_rgb(h, s, l, &r, &g, &b); 176 | 177 | line[x].r = (uint8_t) r; 178 | line[x].g = (uint8_t) g; 179 | line[x].b = (uint8_t) b; 180 | } 181 | } 182 | } 183 | 184 | void akvcam_frame_filter_contrast(akvcam_frame_filter_ct self, 185 | akvcam_frame_t frame, 186 | int contrast) 187 | { 188 | __u32 fourcc; 189 | size_t width; 190 | size_t height; 191 | size_t x; 192 | size_t y; 193 | size_t contrast_offset; 194 | akvcam_format_t format; 195 | 196 | akpr_function(); 197 | 198 | if (!self->contrast_table || contrast == 0) 199 | return; 200 | 201 | format = akvcam_frame_format(frame); 202 | fourcc = akvcam_format_fourcc(format); 203 | 204 | if (!akvcam_filter_format_supported(fourcc)) { 205 | akvcam_format_delete(format); 206 | 207 | return; 208 | } 209 | 210 | width = akvcam_format_width(format); 211 | height = akvcam_format_height(format); 212 | akvcam_format_delete(format); 213 | contrast = akvcam_bound(-255, contrast, 255); 214 | contrast_offset = (size_t) (contrast + 255) << 8; 215 | 216 | for (y = 0; y < height; y++) { 217 | akvcam_RGB24_t line = akvcam_frame_line(frame, 0, y); 218 | 219 | for (x = 0; x < width; x++) { 220 | line[x].r = self->contrast_table[contrast_offset | line[x].r]; 221 | line[x].g = self->contrast_table[contrast_offset | line[x].g]; 222 | line[x].b = self->contrast_table[contrast_offset | line[x].b]; 223 | } 224 | } 225 | } 226 | 227 | void akvcam_frame_filter_gamma(akvcam_frame_filter_ct self, 228 | akvcam_frame_t frame, 229 | int gamma) 230 | { 231 | __u32 fourcc; 232 | size_t width; 233 | size_t height; 234 | size_t x; 235 | size_t y; 236 | size_t gamma_offset; 237 | akvcam_format_t format; 238 | 239 | akpr_function(); 240 | 241 | if (!self->gamma_table || gamma == 0) 242 | return; 243 | 244 | format = akvcam_frame_format(frame); 245 | fourcc = akvcam_format_fourcc(format); 246 | 247 | if (!akvcam_filter_format_supported(fourcc)) { 248 | akvcam_format_delete(format); 249 | 250 | return; 251 | } 252 | 253 | width = akvcam_format_width(format); 254 | height = akvcam_format_height(format); 255 | akvcam_format_delete(format); 256 | gamma = akvcam_bound(-255, gamma, 255); 257 | gamma_offset = (size_t) (gamma + 255) << 8; 258 | 259 | for (y = 0; y < height; y++) { 260 | akvcam_RGB24_t line = akvcam_frame_line(frame, 0, y); 261 | 262 | for (x = 0; x < width; x++) { 263 | line[x].r = self->gamma_table[gamma_offset | line[x].r]; 264 | line[x].g = self->gamma_table[gamma_offset | line[x].g]; 265 | line[x].b = self->gamma_table[gamma_offset | line[x].b]; 266 | } 267 | } 268 | } 269 | 270 | void akvcam_frame_filter_gray(akvcam_frame_filter_ct self, 271 | akvcam_frame_t frame) 272 | { 273 | __u32 fourcc; 274 | size_t width; 275 | size_t height; 276 | size_t x; 277 | size_t y; 278 | akvcam_format_t format; 279 | UNUSED(self); 280 | 281 | akpr_function(); 282 | format = akvcam_frame_format(frame); 283 | fourcc = akvcam_format_fourcc(format); 284 | 285 | if (!akvcam_filter_format_supported(fourcc)) { 286 | akvcam_format_delete(format); 287 | 288 | return; 289 | } 290 | 291 | width = akvcam_format_width(format); 292 | height = akvcam_format_height(format); 293 | akvcam_format_delete(format); 294 | 295 | for (y = 0; y < height; y++) { 296 | akvcam_RGB24_t line = akvcam_frame_line(frame, 0, y); 297 | 298 | for (x = 0; x < width; x++) { 299 | int luma = akvcam_grayval(line[x].r, line[x].g, line[x].b); 300 | 301 | line[x].r = (uint8_t) luma; 302 | line[x].g = (uint8_t) luma; 303 | line[x].b = (uint8_t) luma; 304 | } 305 | } 306 | } 307 | 308 | void akvcam_frame_filter_apply(akvcam_frame_filter_ct self, 309 | akvcam_frame_t frame, 310 | int hue, 311 | int saturation, 312 | int luminance, 313 | int contrast, 314 | int gamma, 315 | bool gray, 316 | bool swap_rgb) 317 | { 318 | akpr_function(); 319 | 320 | if (swap_rgb) 321 | akvcam_frame_filter_swap_rgb(self, frame); 322 | 323 | akvcam_frame_filter_hsl(self, frame, hue, saturation, luminance); 324 | akvcam_frame_filter_gamma(self, frame, gamma); 325 | akvcam_frame_filter_contrast(self, frame, contrast); 326 | 327 | if (gray) 328 | akvcam_frame_filter_gray(self, frame); 329 | } 330 | 331 | void akvcam_rgb_to_hsl(int r, int g, int b, int *h, int *s, int *l) 332 | { 333 | int max = akvcam_max(r, akvcam_max(g, b)); 334 | int min = akvcam_min(r, akvcam_min(g, b)); 335 | int c = max - min; 336 | 337 | *l = (max + min) / 2; 338 | 339 | if (!c) { 340 | *h = 0; 341 | *s = 0; 342 | } else { 343 | if (max == r) 344 | *h = akvcam_mod(g - b, 6 * c); 345 | else if (max == g) 346 | *h = b - r + 2 * c; 347 | else 348 | *h = r - g + 4 * c; 349 | 350 | *h = 60 * (*h) / c; 351 | *s = 255 * c / (255 - akvcam_abs(max + min - 255)); 352 | } 353 | } 354 | 355 | void akvcam_hsl_to_rgb(int h, int s, int l, int *r, int *g, int *b) 356 | { 357 | int c = s * (255 - akvcam_abs(2 * l - 255)) / 255; 358 | int x = c * (60 - akvcam_abs((h % 120) - 60)) / 60; 359 | int m; 360 | 361 | if (h >= 0 && h < 60) { 362 | *r = c; 363 | *g = x; 364 | *b = 0; 365 | } else if (h >= 60 && h < 120) { 366 | *r = x; 367 | *g = c; 368 | *b = 0; 369 | } else if (h >= 120 && h < 180) { 370 | *r = 0; 371 | *g = c; 372 | *b = x; 373 | } else if (h >= 180 && h < 240) { 374 | *r = 0; 375 | *g = x; 376 | *b = c; 377 | } else if (h >= 240 && h < 300) { 378 | *r = x; 379 | *g = 0; 380 | *b = c; 381 | } else if (h >= 300 && h < 360) { 382 | *r = c; 383 | *g = 0; 384 | *b = x; 385 | } else { 386 | *r = 0; 387 | *g = 0; 388 | *b = 0; 389 | } 390 | 391 | m = 2 * l - c; 392 | 393 | *r = (2 * (*r) + m) / 2; 394 | *g = (2 * (*g) + m) / 2; 395 | *b = (2 * (*b) + m) / 2; 396 | } 397 | 398 | void akvcam_init_contrast_table(akvcam_frame_filter_t self) 399 | { 400 | static const int64_t min_contrast = -255; 401 | static const int64_t max_contrast = 255; 402 | static const int64_t max_color = 255; 403 | int64_t contrast; 404 | 405 | self->contrast_table = vmalloc((max_color + 1) * (max_contrast - min_contrast + 1)); 406 | 407 | for (contrast = min_contrast; contrast <= max_contrast; contrast++) { 408 | uint64_t i; 409 | int64_t f_num = 259 * (255 + contrast); 410 | int64_t f_den = 255 * (259 - contrast); 411 | size_t offset = (size_t) (contrast + 255) << 8; 412 | 413 | for (i = 0; i <= max_color; i++) { 414 | int64_t ic = (f_num * ((ssize_t) i - 128) + 128 * f_den) / f_den; 415 | self->contrast_table[offset | i] = (uint8_t) akvcam_bound(0, ic, 255); 416 | } 417 | } 418 | } 419 | 420 | /* Gamma correction is traditionally computed with the following formula: 421 | * 422 | * c = N * (c / N) ^ gamma 423 | * 424 | * Where 'c' is the color component and 'N' is the maximum value of the color 425 | * component, 255 in this case. The formula will define a curve between 0 and 426 | * N. When 'gamma' is 1 it will draw a rect, returning the identity image at the 427 | * output. when 'gamma' is near to 0 it will draw a decreasing curve (mountain), 428 | * Giving more light to darker colors. When 'gamma' is higher than 1 it will 429 | * draw a increasing curve (valley), making bright colors darker. 430 | * 431 | * Explained in a simple way, gamma correction will modify image brightness 432 | * preserving the contrast. 433 | * 434 | * The problem with the original formula is that it requires floating point 435 | * computing which is not possible in the kernel because not all target 436 | * architectures have a FPU. 437 | * 438 | * So instead, we will use a quadric function, that even if it does not returns 439 | * the same values, it will cause the same effect and is good enough for our 440 | * purpose. We use the formula: 441 | * 442 | * y = a * x ^ 2 + b * x 443 | * 444 | * and because we have the point (0, N) already defined, then we can calculate 445 | * b as : 446 | * 447 | * b = 1 - a * N 448 | * 449 | * and replacing 450 | * 451 | * y = a * x ^ 2 + (1 - a * N) * x 452 | * 453 | * we are missing a third point (x', y') to fully define the value of 'a', so 454 | * the value of 'a' will be given by: 455 | * 456 | * a = (y' - x') / (x' ^ 2 - N * x') 457 | * 458 | * we will take the point (x', y') from the segment orthogonal to the curve's 459 | * segment, that is: 460 | * 461 | * y' = N - x' 462 | * 463 | * Here x' will be our fake 'gamma' value. 464 | * Then the value of 'a' becomes: 465 | * 466 | * a = (N - 2 * x') / (x' ^ 2 - N * x') 467 | * 468 | * finally we clamp/bound the resulting value between 0 and N and that's what 469 | * this code does. 470 | */ 471 | void akvcam_init_gamma_table(akvcam_frame_filter_t self) 472 | { 473 | static const int64_t min_gamma = -255; 474 | static const int64_t max_gamma = 255; 475 | static const int64_t max_color = 255; 476 | int64_t gamma; 477 | 478 | self->gamma_table = vmalloc((max_color + 1) * (max_gamma - min_gamma + 1)); 479 | 480 | for (gamma = min_gamma; gamma <= max_gamma; gamma++) { 481 | int64_t i; 482 | int64_t g = (255 + gamma) >> 1; 483 | int64_t f_num = 2 * g - 255; 484 | int64_t f_den = g * (g - 255); 485 | size_t offset = (size_t) (gamma + 255) << 8; 486 | 487 | for (i = 0; i <= max_color; i++) { 488 | int64_t ig; 489 | 490 | if (g > 0 && g != 255) { 491 | ig = (f_num * i * i + (f_den - f_num * 255) * i) / f_den; 492 | ig = akvcam_bound(0, ig, 255); 493 | } else if (g != 255) { 494 | ig = 0; 495 | } else { 496 | ig = 255; 497 | } 498 | 499 | self->gamma_table[offset | i] = (uint8_t) ig; 500 | } 501 | } 502 | } 503 | 504 | int akvcam_grayval(int r, int g, int b) 505 | { 506 | return (11 * r + 16 * g + 5 * b) >> 5; 507 | } 508 | 509 | bool akvcam_filter_format_supported(__u32 fourcc) 510 | { 511 | size_t i; 512 | static const __u32 filter_contrast_formats[] = { 513 | V4L2_PIX_FMT_BGR24, 514 | V4L2_PIX_FMT_RGB24, 515 | 0 516 | }; 517 | 518 | for (i = 0; filter_contrast_formats[i]; i++) 519 | if (filter_contrast_formats[i] == fourcc) 520 | return true; 521 | 522 | return false; 523 | } 524 | -------------------------------------------------------------------------------- /src/frame_filter.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2021 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FRAME_FILTER_H 20 | #define AKVCAM_FRAME_FILTER_H 21 | 22 | #include 23 | 24 | #include "frame_filter_types.h" 25 | #include "frame_types.h" 26 | 27 | // public 28 | akvcam_frame_filter_t akvcam_frame_filter_new(void); 29 | void akvcam_frame_filter_delete(akvcam_frame_filter_t self); 30 | akvcam_frame_filter_t akvcam_frame_filter_ref(akvcam_frame_filter_t self); 31 | 32 | void akvcam_frame_filter_swap_rgb(akvcam_frame_filter_ct self, 33 | akvcam_frame_t frame); 34 | void akvcam_frame_filter_hsl(akvcam_frame_filter_ct self, 35 | akvcam_frame_t frame, 36 | int hue, 37 | int saturation, 38 | int luminance); 39 | void akvcam_frame_filter_contrast(akvcam_frame_filter_ct self, 40 | akvcam_frame_t frame, 41 | int contrast); 42 | void akvcam_frame_filter_gamma(akvcam_frame_filter_ct self, 43 | akvcam_frame_t frame, 44 | int gamma); 45 | void akvcam_frame_filter_gray(akvcam_frame_filter_ct self, 46 | akvcam_frame_t frame); 47 | void akvcam_frame_filter_apply(akvcam_frame_filter_ct self, 48 | akvcam_frame_t frame, 49 | int hue, 50 | int saturation, 51 | int luminance, 52 | int contrast, 53 | int gamma, 54 | bool gray, 55 | bool swap_rgb); 56 | 57 | #endif // AKVCAM_FRAME_FILTER_H 58 | -------------------------------------------------------------------------------- /src/frame_filter_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2021 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FRAME_FILTER_TYPES_H 20 | #define AKVCAM_FRAME_FILTER_TYPES_H 21 | 22 | struct akvcam_frame_filter; 23 | typedef struct akvcam_frame_filter *akvcam_frame_filter_t; 24 | typedef const struct akvcam_frame_filter *akvcam_frame_filter_ct; 25 | 26 | #endif // AKVCAM_FRAME_FILTER_TYPES_H 27 | -------------------------------------------------------------------------------- /src/frame_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_FRAME_TYPES_H 20 | #define AKVCAM_FRAME_TYPES_H 21 | 22 | struct akvcam_frame; 23 | typedef struct akvcam_frame *akvcam_frame_t; 24 | typedef const struct akvcam_frame *akvcam_frame_ct; 25 | 26 | typedef enum 27 | { 28 | AKVCAM_SCALING_FAST, 29 | AKVCAM_SCALING_LINEAR 30 | } AKVCAM_SCALING; 31 | 32 | typedef enum 33 | { 34 | AKVCAM_ASPECT_RATIO_IGNORE, 35 | AKVCAM_ASPECT_RATIO_KEEP, 36 | AKVCAM_ASPECT_RATIO_EXPANDING 37 | } AKVCAM_ASPECT_RATIO; 38 | 39 | #endif // AKVCAM_FRAME_TYPES_H 40 | -------------------------------------------------------------------------------- /src/ioctl.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_IOCTL_H 20 | #define AKVCAM_IOCTL_H 21 | 22 | const struct v4l2_ioctl_ops *akvcam_ioctl_ops(void); 23 | 24 | #endif // AKVCAM_IOCTL_H 25 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "list.h" 23 | 24 | typedef struct akvcam_list_element 25 | { 26 | void *data; 27 | akvcam_copy_t copier; 28 | akvcam_delete_t deleter; 29 | struct akvcam_list_element *prev; 30 | struct akvcam_list_element *next; 31 | } akvcam_list_element, *akvcam_list_element_t; 32 | 33 | struct akvcam_list 34 | { 35 | struct kref ref; 36 | size_t size; 37 | akvcam_list_element_t head; 38 | akvcam_list_element_t tail; 39 | }; 40 | 41 | void akvcam_matrix_combine_p(akvcam_matrix_ct matrix, 42 | size_t index, 43 | akvcam_list_t combined, 44 | akvcam_matrix_t combinations); 45 | 46 | akvcam_list_t akvcam_list_new(void) 47 | { 48 | akvcam_list_t self = kzalloc(sizeof(struct akvcam_list), GFP_KERNEL); 49 | kref_init(&self->ref); 50 | 51 | return self; 52 | } 53 | 54 | akvcam_list_t akvcam_list_new_copy(akvcam_list_ct other) 55 | { 56 | akvcam_list_t self = kzalloc(sizeof(struct akvcam_list), GFP_KERNEL); 57 | kref_init(&self->ref); 58 | akvcam_list_append(self, other); 59 | 60 | return self; 61 | } 62 | 63 | static void akvcam_list_free(struct kref *ref) 64 | { 65 | akvcam_list_t self = container_of(ref, struct akvcam_list, ref); 66 | akvcam_list_clear(self); 67 | kfree(self); 68 | } 69 | 70 | void akvcam_list_delete(akvcam_list_t self) 71 | { 72 | if (self) 73 | kref_put(&self->ref, akvcam_list_free); 74 | } 75 | 76 | akvcam_list_t akvcam_list_ref(akvcam_list_t self) 77 | { 78 | if (self) 79 | kref_get(&self->ref); 80 | 81 | return self; 82 | } 83 | 84 | void akvcam_list_copy(akvcam_list_t self, akvcam_list_ct other) 85 | { 86 | akvcam_list_clear(self); 87 | akvcam_list_append(self, other); 88 | } 89 | 90 | void akvcam_list_append(akvcam_list_t self, akvcam_list_ct other) 91 | { 92 | akvcam_list_element_t it = NULL; 93 | 94 | for (;;) { 95 | void *data = akvcam_list_next(other, &it); 96 | 97 | if (!it) 98 | break; 99 | 100 | akvcam_list_push_back(self, 101 | data, 102 | it->copier, 103 | it->deleter); 104 | } 105 | } 106 | 107 | size_t akvcam_list_size(akvcam_list_ct self) 108 | { 109 | if (!self) 110 | return 0; 111 | 112 | return self->size; 113 | } 114 | 115 | bool akvcam_list_empty(akvcam_list_ct self) 116 | { 117 | if (!self) 118 | return true; 119 | 120 | return self->size < 1; 121 | } 122 | 123 | void *akvcam_list_at(akvcam_list_ct self, size_t i) 124 | { 125 | akvcam_list_element_t it = NULL; 126 | size_t j; 127 | 128 | for (j = 0;; j++) { 129 | void *element_data = akvcam_list_next(self, &it); 130 | 131 | if (!it) 132 | break; 133 | 134 | if (i == j) 135 | return element_data; 136 | } 137 | 138 | return NULL; 139 | } 140 | 141 | void *akvcam_list_front(akvcam_list_ct self) 142 | { 143 | if (!self || self->size < 1) 144 | return NULL; 145 | 146 | return self->head->data; 147 | } 148 | 149 | void *akvcam_list_back(akvcam_list_ct self) 150 | { 151 | if (!self || self->size < 1) 152 | return NULL; 153 | 154 | return self->tail->data; 155 | } 156 | 157 | akvcam_list_element_t akvcam_list_push_back(akvcam_list_t self, 158 | void *data, 159 | akvcam_copy_t copier, 160 | akvcam_delete_t deleter) 161 | { 162 | akvcam_list_element_t element; 163 | 164 | if (!self) 165 | return NULL; 166 | 167 | element = kzalloc(sizeof(struct akvcam_list_element), GFP_KERNEL); 168 | 169 | if (!element) { 170 | akvcam_set_last_error(-ENOMEM); 171 | 172 | return NULL; 173 | } 174 | 175 | element->data = copier? copier(data): data; 176 | element->copier = copier; 177 | element->deleter = deleter; 178 | element->prev = self->tail; 179 | self->size++; 180 | 181 | if (self->tail) { 182 | self->tail->next = element; 183 | self->tail = element; 184 | } else { 185 | self->head = element; 186 | self->tail = element; 187 | } 188 | 189 | akvcam_set_last_error(0); 190 | 191 | return element; 192 | } 193 | 194 | akvcam_list_element_t akvcam_list_it(akvcam_list_ct self, size_t i) 195 | { 196 | akvcam_list_element_t it = NULL; 197 | size_t j; 198 | 199 | for (j = 0;; j++) { 200 | akvcam_list_next(self, &it); 201 | 202 | if (!it) 203 | break; 204 | 205 | if (i == j) 206 | return it; 207 | } 208 | 209 | return NULL; 210 | } 211 | 212 | void akvcam_list_erase(akvcam_list_t self, akvcam_list_element_ct element) 213 | { 214 | akvcam_list_element_t it; 215 | 216 | if (!self) 217 | return; 218 | 219 | for (it = self->head; it != NULL; it = it->next) 220 | if (it == element) { 221 | if (it->data && it->deleter) 222 | it->deleter(it->data); 223 | 224 | if (it->prev) 225 | it->prev->next = it->next; 226 | else 227 | self->head = it->next; 228 | 229 | if (it->next) 230 | it->next->prev = it->prev; 231 | else 232 | self->tail = it->prev; 233 | 234 | kfree(it); 235 | self->size--; 236 | 237 | break; 238 | } 239 | } 240 | 241 | void akvcam_list_clear(akvcam_list_t self) 242 | { 243 | akvcam_list_element_t element; 244 | akvcam_list_element_t next; 245 | 246 | if (!self) 247 | return; 248 | 249 | element = self->head; 250 | 251 | while (element) { 252 | if (element->data && element->deleter) 253 | element->deleter(element->data); 254 | 255 | next = element->next; 256 | kfree(element); 257 | element = next; 258 | } 259 | 260 | self->size = 0; 261 | self->head = NULL; 262 | self->tail = NULL; 263 | } 264 | 265 | akvcam_list_element_t akvcam_list_find(akvcam_list_ct self, 266 | const void *data, 267 | akvcam_are_equals_t equals) 268 | { 269 | akvcam_list_element_t it = NULL; 270 | 271 | if (!equals) 272 | return NULL; 273 | 274 | for (;;) { 275 | void *element_data = akvcam_list_next(self, &it); 276 | 277 | if (!it) 278 | break; 279 | 280 | if (equals(element_data, data)) 281 | return it; 282 | } 283 | 284 | return NULL; 285 | } 286 | 287 | ssize_t akvcam_list_index_of(akvcam_list_ct self, 288 | const void *data, 289 | akvcam_are_equals_t equals) 290 | { 291 | akvcam_list_element_t it = NULL; 292 | ssize_t i; 293 | 294 | if (!equals) 295 | return -1; 296 | 297 | for (i = 0;; i++) { 298 | void *element_data = akvcam_list_next(self, &it); 299 | 300 | if (!it) 301 | break; 302 | 303 | if (equals(element_data, data)) 304 | return i; 305 | } 306 | 307 | return -1; 308 | } 309 | 310 | bool akvcam_list_contains(akvcam_list_ct self, 311 | const void *data, 312 | akvcam_are_equals_t equals) 313 | { 314 | return akvcam_list_find(self, data, equals) != NULL; 315 | } 316 | 317 | void *akvcam_list_next(akvcam_list_ct self, 318 | akvcam_list_element_t *element) 319 | { 320 | if (!element) 321 | return NULL; 322 | 323 | if (!self) { 324 | *element = NULL; 325 | 326 | return NULL; 327 | } 328 | 329 | if (*element) { 330 | *element = (*element)->next; 331 | 332 | return *element? (*element)->data: NULL; 333 | } 334 | 335 | *element = self->head; 336 | 337 | return self->head? self->head->data: NULL; 338 | } 339 | 340 | void *akvcam_list_element_data(akvcam_list_element_ct element) 341 | { 342 | if (!element) 343 | return NULL; 344 | 345 | return element->data; 346 | } 347 | 348 | akvcam_copy_t akvcam_list_element_copier(akvcam_list_element_ct element) 349 | { 350 | if (!element) 351 | return NULL; 352 | 353 | return element->copier; 354 | } 355 | 356 | akvcam_delete_t akvcam_list_element_deleter(akvcam_list_element_ct element) 357 | { 358 | if (!element) 359 | return NULL; 360 | 361 | return element->deleter; 362 | } 363 | 364 | akvcam_matrix_t akvcam_matrix_combine(akvcam_matrix_ct matrix) 365 | { 366 | akvcam_list_t combined = akvcam_list_new(); 367 | akvcam_matrix_t combinations = akvcam_list_new(); 368 | akvcam_matrix_combine_p(matrix, 0, combined, combinations); 369 | akvcam_list_delete(combined); 370 | 371 | return combinations; 372 | } 373 | 374 | /* A matrix is a list of lists where each element in the main list is a row, 375 | * and each element in a row is a column. We combine each element in a row with 376 | * each element in the next rows. 377 | */ 378 | void akvcam_matrix_combine_p(akvcam_matrix_ct matrix, 379 | size_t index, 380 | akvcam_list_t combined, 381 | akvcam_matrix_t combinations) 382 | { 383 | akvcam_list_t row; 384 | akvcam_list_element_t it = NULL; 385 | 386 | if (index >= akvcam_list_size(matrix)) { 387 | akvcam_list_push_back(combinations, 388 | combined, 389 | (akvcam_copy_t) akvcam_list_ref, 390 | (akvcam_delete_t) akvcam_list_delete); 391 | 392 | return; 393 | } 394 | 395 | row = akvcam_list_at(matrix, index); 396 | 397 | for (;;) { 398 | akvcam_list_t combined_p1; 399 | void *data = akvcam_list_next(row, &it); 400 | 401 | if (!it) 402 | break; 403 | 404 | combined_p1 = akvcam_list_new_copy(combined); 405 | akvcam_list_push_back(combined_p1, 406 | data, 407 | it->copier, 408 | it->deleter); 409 | akvcam_matrix_combine_p(matrix, 410 | index + 1, 411 | combined_p1, 412 | combinations); 413 | akvcam_list_delete(combined_p1); 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_LIST_H 20 | #define AKVCAM_LIST_H 21 | 22 | #include 23 | 24 | #include "list_types.h" 25 | #include "utils.h" 26 | 27 | // public 28 | akvcam_list_t akvcam_list_new(void); 29 | akvcam_list_t akvcam_list_new_copy(akvcam_list_ct other); 30 | void akvcam_list_delete(akvcam_list_t self); 31 | akvcam_list_t akvcam_list_ref(akvcam_list_t self); 32 | 33 | void akvcam_list_copy(akvcam_list_t self, akvcam_list_ct other); 34 | void akvcam_list_append(akvcam_list_t self, akvcam_list_ct other); 35 | size_t akvcam_list_size(akvcam_list_ct self); 36 | bool akvcam_list_empty(akvcam_list_ct self); 37 | void *akvcam_list_at(akvcam_list_ct self, size_t i); 38 | void *akvcam_list_front(akvcam_list_ct self); 39 | void *akvcam_list_back(akvcam_list_ct self); 40 | akvcam_list_element_t akvcam_list_push_back(akvcam_list_t self, 41 | void *data, 42 | akvcam_copy_t copier, 43 | akvcam_delete_t deleter); 44 | akvcam_list_element_t akvcam_list_it(akvcam_list_ct self, size_t i); 45 | void akvcam_list_erase(akvcam_list_t self, akvcam_list_element_ct element); 46 | void akvcam_list_clear(akvcam_list_t self); 47 | akvcam_list_element_t akvcam_list_find(akvcam_list_ct self, 48 | const void *data, 49 | akvcam_are_equals_t equals); 50 | ssize_t akvcam_list_index_of(akvcam_list_ct self, 51 | const void *data, 52 | akvcam_are_equals_t equals); 53 | bool akvcam_list_contains(akvcam_list_ct self, 54 | const void *data, 55 | akvcam_are_equals_t equals); 56 | void *akvcam_list_next(akvcam_list_ct self, 57 | akvcam_list_element_t *element); 58 | void *akvcam_list_element_data(akvcam_list_element_ct element); 59 | akvcam_copy_t akvcam_list_element_copier(akvcam_list_element_ct element); 60 | akvcam_delete_t akvcam_list_element_deleter(akvcam_list_element_ct element); 61 | akvcam_matrix_t akvcam_matrix_combine(akvcam_matrix_ct matrix); 62 | 63 | #endif // AKVCAM_LIST_H 64 | -------------------------------------------------------------------------------- /src/list_types.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_LIST_TYPES_H 20 | #define AKVCAM_LIST_TYPES_H 21 | 22 | #define akvcam_list_tt(type) akvcam_list_t 23 | #define akvcam_list_ctt(type) akvcam_list_ct 24 | 25 | struct akvcam_list; 26 | typedef struct akvcam_list *akvcam_list_t; 27 | typedef const struct akvcam_list *akvcam_list_ct; 28 | struct akvcam_list_element; 29 | typedef struct akvcam_list_element *akvcam_list_element_t; 30 | typedef const struct akvcam_list_element *akvcam_list_element_ct; 31 | typedef akvcam_list_tt(char *) akvcam_string_list_t; 32 | typedef akvcam_list_ctt(char *) akvcam_string_list_ct; 33 | typedef akvcam_list_tt(akvcam_list_t) akvcam_matrix_t; 34 | typedef akvcam_list_ctt(akvcam_list_t) akvcam_matrix_ct; 35 | typedef akvcam_list_tt(akvcam_string_list_t) akvcam_string_matrix_t; 36 | typedef akvcam_list_ctt(akvcam_string_list_t) akvcam_string_matrix_ct; 37 | 38 | #endif // AKVCAM_LIST_TYPES_H 39 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "log.h" 20 | 21 | static struct akvcam_log 22 | { 23 | int level; 24 | } akvcam_log_private; 25 | 26 | int akvcam_log_level(void) 27 | { 28 | return akvcam_log_private.level; 29 | } 30 | 31 | void akvcam_log_set_level(int level) 32 | { 33 | akvcam_log_private.level = level; 34 | } 35 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_LOG_H 20 | #define AKVCAM_LOG_H 21 | 22 | #include 23 | 24 | #define akpr_file_name (strrchr(__FILE__, '/') + 1) 25 | #define akpr_log_format "[akvcam] %s(%d): " 26 | 27 | #define akpr_err(fmt, ...) \ 28 | do { \ 29 | if (akvcam_log_level() >= LOGLEVEL_ERR) { \ 30 | printk(KERN_ERR akpr_log_format fmt, \ 31 | akpr_file_name, __LINE__, ##__VA_ARGS__); \ 32 | } \ 33 | } while (false) 34 | 35 | #define akpr_warning(fmt, ...) \ 36 | do { \ 37 | if (akvcam_log_level() >= LOGLEVEL_WARNING) { \ 38 | printk(KERN_WARNING akpr_log_format fmt, \ 39 | akpr_file_name, __LINE__, ##__VA_ARGS__); \ 40 | } \ 41 | } while (false) 42 | 43 | #define akpr_info(fmt, ...) \ 44 | do { \ 45 | if (akvcam_log_level() >= LOGLEVEL_INFO) { \ 46 | printk(KERN_INFO akpr_log_format fmt, \ 47 | akpr_file_name, __LINE__, ##__VA_ARGS__); \ 48 | } \ 49 | } while (false) 50 | 51 | #define akpr_debug(fmt, ...) \ 52 | do { \ 53 | if (akvcam_log_level() >= LOGLEVEL_DEBUG) { \ 54 | printk(KERN_DEBUG akpr_log_format fmt, \ 55 | akpr_file_name, __LINE__, ##__VA_ARGS__); \ 56 | } \ 57 | } while (false) 58 | 59 | #define akpr_function() \ 60 | akpr_debug("%s()\n", __FUNCTION__) 61 | 62 | int akvcam_log_level(void); 63 | void akvcam_log_set_level(int level); 64 | 65 | #endif // AKVCAM_LOG_H 66 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "map.h" 23 | #include "list.h" 24 | 25 | typedef struct akvcam_map_element 26 | { 27 | char *key; 28 | void *value; 29 | akvcam_copy_t copier; 30 | akvcam_delete_t deleter; 31 | akvcam_list_element_t it; 32 | } akvcam_map_element, *akvcam_map_element_t; 33 | typedef const akvcam_map_element *akvcam_map_element_ct; 34 | 35 | struct akvcam_map 36 | { 37 | struct kref ref; 38 | akvcam_list_tt(akvcam_map_element_t) elements; 39 | }; 40 | 41 | akvcam_map_t akvcam_map_new(void) 42 | { 43 | akvcam_map_t self = kzalloc(sizeof(struct akvcam_map), GFP_KERNEL); 44 | kref_init(&self->ref); 45 | self->elements = akvcam_list_new(); 46 | 47 | return self; 48 | } 49 | 50 | akvcam_map_t akvcam_map_new_copy(akvcam_map_ct other) 51 | { 52 | akvcam_map_t self = kzalloc(sizeof(struct akvcam_map), GFP_KERNEL); 53 | kref_init(&self->ref); 54 | self->elements = akvcam_list_new(); 55 | akvcam_map_update(self, other); 56 | 57 | return self; 58 | } 59 | 60 | static void akvcam_map_free(struct kref *ref) 61 | { 62 | akvcam_map_t self = container_of(ref, struct akvcam_map, ref); 63 | akvcam_map_clear(self); 64 | akvcam_list_delete(self->elements); 65 | kfree(self); 66 | } 67 | 68 | void akvcam_map_delete(akvcam_map_t self) 69 | { 70 | if (self) 71 | kref_put(&self->ref, akvcam_map_free); 72 | } 73 | 74 | akvcam_map_t akvcam_map_ref(akvcam_map_t self) 75 | { 76 | if (self) 77 | kref_get(&self->ref); 78 | 79 | return self; 80 | } 81 | 82 | void akvcam_map_copy(akvcam_map_t self, const akvcam_map_t other) 83 | { 84 | akvcam_map_clear(self); 85 | akvcam_map_update(self, other); 86 | } 87 | 88 | void akvcam_map_update(akvcam_map_t self, akvcam_map_ct other) 89 | { 90 | akvcam_map_element_t it = NULL; 91 | 92 | while (akvcam_map_next(other, &it)) { 93 | akvcam_map_set_value(self, 94 | it->key, 95 | it->value, 96 | it->copier, 97 | it->deleter); 98 | } 99 | } 100 | 101 | size_t akvcam_map_size(akvcam_map_ct self) 102 | { 103 | return akvcam_list_size(self->elements); 104 | } 105 | 106 | bool akvcam_map_empty(akvcam_map_ct self) 107 | { 108 | return akvcam_list_empty(self->elements); 109 | } 110 | 111 | void *akvcam_map_value(akvcam_map_ct self, const char *key) 112 | { 113 | akvcam_map_element_t element = akvcam_map_it(self, key); 114 | 115 | if (!element) 116 | return NULL; 117 | 118 | return element->value; 119 | } 120 | 121 | static bool akvcam_map_equals_keys(akvcam_map_element_ct element, 122 | const char *key) 123 | { 124 | return strcmp(element->key, key) == 0; 125 | } 126 | 127 | static akvcam_map_element_t akvcam_map_element_copy(akvcam_map_element_ct element) 128 | { 129 | return kmemdup(element, sizeof(akvcam_map_element), GFP_KERNEL); 130 | } 131 | 132 | akvcam_map_element_t akvcam_map_set_value(akvcam_map_t self, 133 | const char *key, 134 | void *value, 135 | akvcam_copy_t copier, 136 | akvcam_delete_t deleter) 137 | { 138 | akvcam_map_element element; 139 | akvcam_list_element_t it = 140 | akvcam_list_find(self->elements, 141 | key, 142 | (akvcam_are_equals_t) akvcam_map_equals_keys); 143 | 144 | if (it) 145 | akvcam_list_erase(self->elements, it); 146 | 147 | element.key = akvcam_strdup(key, AKVCAM_MEMORY_TYPE_KMALLOC); 148 | element.value = copier? copier(value): value; 149 | element.copier = copier; 150 | element.deleter = deleter; 151 | element.it = akvcam_list_push_back(self->elements, 152 | &element, 153 | (akvcam_copy_t) akvcam_map_element_copy, 154 | (akvcam_delete_t) kfree); 155 | 156 | return akvcam_list_back(self->elements); 157 | } 158 | 159 | bool akvcam_map_contains(akvcam_map_ct self, const char *key) 160 | { 161 | return akvcam_map_value(self, key) != NULL; 162 | } 163 | 164 | static char *akvcam_map_key_copy(const char *str) 165 | { 166 | return kstrdup(str, GFP_KERNEL); 167 | } 168 | 169 | akvcam_list_t akvcam_map_keys(akvcam_map_ct self) 170 | { 171 | akvcam_list_element_t element = NULL; 172 | akvcam_list_t keys = akvcam_list_new(); 173 | 174 | for (;;) { 175 | akvcam_map_element_t map_element = 176 | akvcam_list_next(self->elements, &element); 177 | 178 | if (!element) 179 | break; 180 | 181 | akvcam_list_push_back(keys, 182 | map_element->key, 183 | (akvcam_copy_t) akvcam_map_key_copy, 184 | (akvcam_delete_t) kfree); 185 | } 186 | 187 | return keys; 188 | } 189 | 190 | akvcam_list_t akvcam_map_values(akvcam_map_ct self) 191 | { 192 | akvcam_list_element_t element = NULL; 193 | akvcam_list_t values = akvcam_list_new(); 194 | 195 | for (;;) { 196 | akvcam_map_element_t map_element = 197 | akvcam_list_next(self->elements, &element); 198 | 199 | if (!element) 200 | break; 201 | 202 | akvcam_list_push_back(values, 203 | map_element->value, 204 | map_element->copier, 205 | map_element->deleter); 206 | } 207 | 208 | return values; 209 | } 210 | 211 | akvcam_map_element_t akvcam_map_it(akvcam_map_ct self, const char *key) 212 | { 213 | akvcam_list_element_t element = NULL; 214 | 215 | for (;;) { 216 | akvcam_map_element_t map_element = 217 | akvcam_list_next(self->elements, &element); 218 | 219 | if (!element) 220 | break; 221 | 222 | if (strcmp(map_element->key, key) == 0) 223 | return map_element; 224 | } 225 | 226 | return NULL; 227 | } 228 | 229 | void akvcam_map_erase(akvcam_map_t self, akvcam_map_element_ct element) 230 | { 231 | if (element->key) 232 | kfree(element->key); 233 | 234 | if (element->value && element->deleter) 235 | element->deleter(element->value); 236 | 237 | akvcam_list_erase(self->elements, element->it); 238 | } 239 | 240 | void akvcam_map_clear(akvcam_map_t self) 241 | { 242 | akvcam_list_element_t it = NULL; 243 | 244 | for (;;) { 245 | akvcam_map_element_t element = akvcam_list_next(self->elements, &it); 246 | 247 | if (!it) 248 | break; 249 | 250 | if (element->key) 251 | kfree(element->key); 252 | 253 | if (element->value && element->deleter) 254 | element->deleter(element->value); 255 | } 256 | 257 | akvcam_list_clear(self->elements); 258 | } 259 | 260 | bool akvcam_map_next(akvcam_map_ct self, akvcam_map_element_t *element) 261 | { 262 | akvcam_list_element_t it = *element? (*element)->it: NULL; 263 | akvcam_map_element_t next = akvcam_list_next(self->elements, &it); 264 | 265 | if (!it) 266 | return false; 267 | 268 | *element = next; 269 | 270 | return true; 271 | } 272 | 273 | char *akvcam_map_element_key(akvcam_map_element_ct element) 274 | { 275 | return element->key; 276 | } 277 | 278 | void *akvcam_map_element_value(akvcam_map_element_ct element) 279 | { 280 | return element->value; 281 | } 282 | 283 | akvcam_copy_t akvcam_map_element_copier(akvcam_map_element_ct element) 284 | { 285 | return element->copier; 286 | } 287 | 288 | akvcam_delete_t akvcam_map_element_deleter(akvcam_map_element_ct element) 289 | { 290 | return element->deleter; 291 | } 292 | -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_MAP_H 20 | #define AKVCAM_MAP_H 21 | 22 | #include 23 | 24 | #include "list_types.h" 25 | #include "utils.h" 26 | 27 | #define akvcam_map_tt(value_type) akvcam_map_t 28 | #define akvcam_map_ctt(value_type) akvcam_map_ct 29 | 30 | struct akvcam_map; 31 | typedef struct akvcam_map *akvcam_map_t; 32 | typedef const struct akvcam_map *akvcam_map_ct; 33 | struct akvcam_map_element; 34 | typedef struct akvcam_map_element *akvcam_map_element_t; 35 | typedef const struct akvcam_map_element *akvcam_map_element_ct; 36 | typedef akvcam_map_tt(char *) akvcam_string_map_t; 37 | typedef akvcam_map_ctt(char *) akvcam_string_map_ct; 38 | 39 | // public 40 | akvcam_map_t akvcam_map_new(void); 41 | akvcam_map_t akvcam_map_new_copy(akvcam_map_ct other); 42 | void akvcam_map_delete(akvcam_map_t self); 43 | akvcam_map_t akvcam_map_ref(akvcam_map_t self); 44 | 45 | void akvcam_map_copy(akvcam_map_t self, const akvcam_map_t other); 46 | void akvcam_map_update(akvcam_map_t self, akvcam_map_ct other); 47 | size_t akvcam_map_size(akvcam_map_ct self); 48 | bool akvcam_map_empty(akvcam_map_ct self); 49 | void *akvcam_map_value(akvcam_map_ct self, const char *key); 50 | akvcam_map_element_t akvcam_map_set_value(akvcam_map_t self, 51 | const char *key, 52 | void *value, 53 | akvcam_copy_t copier, 54 | akvcam_delete_t deleter); 55 | bool akvcam_map_contains(akvcam_map_ct self, const char *key); 56 | akvcam_list_t akvcam_map_keys(akvcam_map_ct self); 57 | akvcam_list_t akvcam_map_values(akvcam_map_ct self); 58 | akvcam_map_element_t akvcam_map_it(akvcam_map_ct self, const char *key); 59 | void akvcam_map_erase(akvcam_map_t self, akvcam_map_element_ct element); 60 | void akvcam_map_clear(akvcam_map_t self); 61 | bool akvcam_map_next(akvcam_map_ct self, akvcam_map_element_t *element); 62 | char *akvcam_map_element_key(akvcam_map_element_ct element); 63 | void *akvcam_map_element_value(akvcam_map_element_ct element); 64 | akvcam_copy_t akvcam_map_element_copier(akvcam_map_element_ct element); 65 | akvcam_delete_t akvcam_map_element_deleter(akvcam_map_element_ct element); 66 | 67 | #endif // AKVCAM_MAP_H 68 | -------------------------------------------------------------------------------- /src/module.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "driver.h" 24 | #include "log.h" 25 | #include "settings.h" 26 | 27 | #define AKVCAM_DRIVER_NAME "akvcam" 28 | #define AKVCAM_DRIVER_DESCRIPTION "AkVCam Virtual Camera" 29 | 30 | static int loglevel = 0; 31 | module_param(loglevel, int, S_IRUGO | S_IWUSR); 32 | MODULE_PARM_DESC(loglevel, "Debug verbosity (-2 to 7)"); 33 | 34 | static char config_file[4096] = "/etc/akvcam/config.ini"; 35 | module_param_string(config_file, config_file, 4096, S_IRUGO | S_IWUSR); 36 | MODULE_PARM_DESC(config_file, "Full path to virtual cameras config file"); 37 | 38 | static int __init akvcam_init(void) 39 | { 40 | akvcam_log_set_level(loglevel); 41 | akvcam_settings_set_file(config_file); 42 | 43 | return akvcam_driver_init(AKVCAM_DRIVER_NAME, AKVCAM_DRIVER_DESCRIPTION); 44 | } 45 | 46 | static void __exit akvcam_uninit(void) 47 | { 48 | akvcam_driver_uninit(); 49 | } 50 | 51 | module_init(akvcam_init) 52 | module_exit(akvcam_uninit) 53 | 54 | MODULE_LICENSE("GPL"); 55 | MODULE_AUTHOR("Gonzalo Exequiel Pedone"); 56 | MODULE_DESCRIPTION(AKVCAM_DRIVER_DESCRIPTION); 57 | MODULE_VERSION("1.2.6"); 58 | -------------------------------------------------------------------------------- /src/rbuffer.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "rbuffer.h" 25 | #include "utils.h" 26 | 27 | struct akvcam_rbuffer 28 | { 29 | struct kref ref; 30 | char *data; 31 | size_t size; 32 | size_t data_size; 33 | size_t read; 34 | size_t write; 35 | size_t step; 36 | AKVCAM_MEMORY_TYPE memory_type; 37 | }; 38 | 39 | void *akvcam_rbuffer_alloc_data(AKVCAM_MEMORY_TYPE memory_type, size_t size); 40 | void akvcam_rbuffer_free_data(AKVCAM_MEMORY_TYPE memory_type, void *data); 41 | 42 | akvcam_rbuffer_t akvcam_rbuffer_new(void) 43 | { 44 | akvcam_rbuffer_t self = kzalloc(sizeof(struct akvcam_rbuffer), GFP_KERNEL); 45 | kref_init(&self->ref); 46 | self->memory_type = AKVCAM_MEMORY_TYPE_KMALLOC; 47 | 48 | return self; 49 | } 50 | 51 | akvcam_rbuffer_t akvcam_rbuffer_new_copy(akvcam_rbuffer_ct other) 52 | { 53 | akvcam_rbuffer_t self = kzalloc(sizeof(struct akvcam_rbuffer), GFP_KERNEL); 54 | kref_init(&self->ref); 55 | self->data = kzalloc(other->size, GFP_KERNEL); 56 | memcpy(self->data, other->data, other->size); 57 | self->size = other->size; 58 | self->data_size = other->data_size; 59 | self->read = other->read; 60 | self->write = other->write; 61 | self->step = other->step; 62 | self->memory_type = other->memory_type; 63 | 64 | return self; 65 | } 66 | 67 | static void akvcam_rbuffer_free(struct kref *ref) 68 | { 69 | akvcam_rbuffer_t self = container_of(ref, struct akvcam_rbuffer, ref); 70 | 71 | if (self->data) 72 | akvcam_rbuffer_free_data(self->memory_type, self->data); 73 | 74 | kfree(self); 75 | } 76 | 77 | void akvcam_rbuffer_delete(akvcam_rbuffer_t self) 78 | { 79 | if (self) 80 | kref_put(&self->ref, akvcam_rbuffer_free); 81 | } 82 | 83 | akvcam_rbuffer_t akvcam_rbuffer_ref(akvcam_rbuffer_t self) 84 | { 85 | if (self) 86 | kref_get(&self->ref); 87 | 88 | return self; 89 | } 90 | 91 | void akvcam_rbuffer_copy(akvcam_rbuffer_t self, akvcam_rbuffer_ct other) 92 | { 93 | if (self->data) 94 | akvcam_rbuffer_free_data(self->memory_type, self->data); 95 | 96 | self->data = kzalloc(other->size, GFP_KERNEL); 97 | memcpy(self->data, other->data, other->size); 98 | self->size = other->size; 99 | self->data_size = other->data_size; 100 | self->read = other->read; 101 | self->write = other->write; 102 | self->step = other->step; 103 | } 104 | 105 | void akvcam_rbuffer_resize(akvcam_rbuffer_t self, 106 | size_t n_elements, 107 | size_t element_size, 108 | AKVCAM_MEMORY_TYPE memory_type) 109 | { 110 | char *new_data; 111 | size_t new_size = n_elements * element_size; 112 | size_t data_size = akvcam_min(self->data_size, new_size); 113 | 114 | if (new_size == self->size) 115 | return; 116 | 117 | if (new_size < 1) { 118 | if (self->data) { 119 | akvcam_rbuffer_free_data(self->memory_type, self->data); 120 | self->data = NULL; 121 | } 122 | 123 | self->size = 0; 124 | self->read = 0; 125 | self->write = 0; 126 | self->step = 0; 127 | self->memory_type = memory_type; 128 | 129 | return; 130 | } 131 | 132 | new_data = akvcam_rbuffer_alloc_data(memory_type, new_size); 133 | 134 | if (self->data) { 135 | size_t left_size = akvcam_min(self->size - self->read, data_size); 136 | 137 | if (left_size > 0) 138 | memcpy(new_data, self->data + self->read, left_size); 139 | 140 | if (data_size > left_size) 141 | memcpy(new_data + left_size, self->data, data_size - left_size); 142 | 143 | akvcam_rbuffer_free_data(self->memory_type, self->data); 144 | } 145 | 146 | self->data = new_data; 147 | self->size = new_size; 148 | self->data_size = data_size; 149 | self->step = element_size; 150 | self->read = 0; 151 | self->write = data_size % new_size; 152 | self->memory_type = memory_type; 153 | } 154 | 155 | size_t akvcam_rbuffer_size(akvcam_rbuffer_ct self) 156 | { 157 | return self->size; 158 | } 159 | 160 | size_t akvcam_rbuffer_data_size(akvcam_rbuffer_ct self) 161 | { 162 | return self->data_size; 163 | } 164 | 165 | size_t akvcam_rbuffer_n_elements(akvcam_rbuffer_ct self) 166 | { 167 | if (self->step < 1) 168 | return self->size; 169 | 170 | return self->size / self->step; 171 | } 172 | 173 | size_t akvcam_rbuffer_element_size(akvcam_rbuffer_ct self) 174 | { 175 | return self->step; 176 | } 177 | 178 | size_t akvcam_rbuffer_n_data(akvcam_rbuffer_ct self) 179 | { 180 | if (self->step < 1) 181 | return self->data_size; 182 | 183 | return self->data_size / self->step; 184 | } 185 | 186 | ssize_t akvcam_rbuffer_available_data_size(akvcam_rbuffer_ct self) 187 | { 188 | return self->size - self->data_size; 189 | } 190 | 191 | bool akvcam_rbuffer_data_empty(akvcam_rbuffer_ct self) 192 | { 193 | return self->data_size < 1; 194 | } 195 | 196 | bool akvcam_rbuffer_elements_empty(akvcam_rbuffer_ct self) 197 | { 198 | return self->data_size < self->step; 199 | } 200 | 201 | bool akvcam_rbuffer_data_full(akvcam_rbuffer_ct self) 202 | { 203 | return self->data_size >= self->size; 204 | } 205 | 206 | bool akvcam_rbuffer_elements_full(akvcam_rbuffer_ct self) 207 | { 208 | return (self->step + self->data_size) > self->size; 209 | } 210 | 211 | void *akvcam_rbuffer_queue(akvcam_rbuffer_t self, const void *data) 212 | { 213 | return akvcam_rbuffer_queue_bytes(self, data, self->step); 214 | } 215 | 216 | void *akvcam_rbuffer_queue_bytes(akvcam_rbuffer_t self, 217 | const void *data, 218 | size_t size) 219 | { 220 | void *output_data; 221 | bool move_read; 222 | 223 | if (self->size < 1) 224 | return NULL; 225 | 226 | size = akvcam_min(size, self->size); 227 | output_data = self->data + self->write; 228 | 229 | if (data) { 230 | size_t right_size = akvcam_min(self->size - self->write, size); 231 | 232 | if (right_size > 0) 233 | memcpy(output_data, data, right_size); 234 | 235 | if (size > right_size) 236 | memcpy(self->data, 237 | (const char *) data + right_size, 238 | size - right_size); 239 | } 240 | 241 | move_read = 242 | akvcam_between(self->write, self->read, self->write + size - 1) 243 | && self->data_size > 0; 244 | self->write = (self->write + size) % self->size; 245 | self->data_size = akvcam_min(self->data_size + size, self->size); 246 | 247 | if (move_read) 248 | self->read = self->write; 249 | 250 | return output_data; 251 | } 252 | 253 | void *akvcam_rbuffer_dequeue(akvcam_rbuffer_t self, 254 | void *data, 255 | bool keep) 256 | { 257 | size_t size = self->step; 258 | 259 | return akvcam_rbuffer_dequeue_bytes(self, data, &size, keep); 260 | } 261 | 262 | void *akvcam_rbuffer_dequeue_bytes(akvcam_rbuffer_t self, 263 | void *data, 264 | size_t *size, 265 | bool keep) 266 | { 267 | void *input_data; 268 | 269 | if (self->data_size < 1) 270 | return NULL; 271 | 272 | *size = akvcam_min(*size, self->data_size); 273 | input_data = self->data + self->read; 274 | 275 | if (data) { 276 | size_t left_size = akvcam_min(self->size - self->read, *size); 277 | 278 | if (left_size > 0) 279 | memcpy(data, input_data, left_size); 280 | 281 | if (*size > left_size) 282 | memcpy((char *) data + left_size, 283 | self->data, 284 | *size - left_size); 285 | } 286 | 287 | self->read = (self->read + *size) % self->size; 288 | 289 | if (!keep) 290 | self->data_size -= *size; 291 | 292 | return input_data; 293 | } 294 | 295 | void akvcam_rbuffer_clear(akvcam_rbuffer_t self) 296 | { 297 | self->data_size = 0; 298 | self->read = 0; 299 | self->write = 0; 300 | } 301 | 302 | void *akvcam_rbuffer_ptr_at(akvcam_rbuffer_ct self, size_t i) 303 | { 304 | size_t offset = i * self->step; 305 | 306 | if (!self->data || offset >= self->size) 307 | return NULL; 308 | 309 | offset = (self->read + offset) % self->size; 310 | 311 | return self->data + offset; 312 | } 313 | 314 | void *akvcam_rbuffer_ptr_front(akvcam_rbuffer_ct self) 315 | { 316 | if (!self->data || self->data_size < 1) 317 | return NULL; 318 | 319 | return self->data + self->read; 320 | } 321 | 322 | void *akvcam_rbuffer_ptr_back(akvcam_rbuffer_ct self) 323 | { 324 | size_t offset; 325 | 326 | if (!self->data || self->size < 1) 327 | return NULL; 328 | 329 | offset = (self->read + self->size - self->step) % self->size; 330 | 331 | return self->data + offset; 332 | } 333 | 334 | void *akvcam_rbuffer_find(akvcam_rbuffer_ct self, 335 | const void *data, 336 | akvcam_are_equals_t equals, 337 | ssize_t *offset) 338 | { 339 | size_t i; 340 | 341 | if (offset) 342 | *offset = 0; 343 | 344 | for (i = 0; i < self->data_size; i += self->step) { 345 | size_t ioffset = (self->read + i) % self->size; 346 | 347 | if (equals) { 348 | if (equals(self->data + ioffset, data)) 349 | return self->data + ioffset; 350 | } else { 351 | if (self->data + ioffset == data) 352 | return self->data + ioffset; 353 | } 354 | 355 | if (offset) 356 | (*offset)++; 357 | } 358 | 359 | if (offset) 360 | *offset = -1; 361 | 362 | return NULL; 363 | } 364 | 365 | void *akvcam_rbuffer_alloc_data(AKVCAM_MEMORY_TYPE memory_type, size_t size) 366 | { 367 | if (memory_type == AKVCAM_MEMORY_TYPE_KMALLOC) 368 | return kzalloc(size, GFP_KERNEL); 369 | 370 | return vzalloc(size); 371 | } 372 | 373 | void akvcam_rbuffer_free_data(AKVCAM_MEMORY_TYPE memory_type, void *data) 374 | { 375 | if (memory_type == AKVCAM_MEMORY_TYPE_KMALLOC) 376 | kfree(data); 377 | else 378 | vfree(data); 379 | } 380 | -------------------------------------------------------------------------------- /src/rbuffer.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_RBUFFER_H 20 | #define AKVCAM_RBUFFER_H 21 | 22 | #include 23 | 24 | #include "utils.h" 25 | 26 | #define akvcam_rbuffer_tt(type) akvcam_rbuffer_t 27 | #define akvcam_rbuffer_ctt(type) akvcam_rbuffer_ct 28 | 29 | struct akvcam_rbuffer; 30 | typedef struct akvcam_rbuffer *akvcam_rbuffer_t; 31 | typedef const struct akvcam_rbuffer *akvcam_rbuffer_ct; 32 | 33 | akvcam_rbuffer_t akvcam_rbuffer_new(void); 34 | akvcam_rbuffer_t akvcam_rbuffer_new_copy(akvcam_rbuffer_ct other); 35 | void akvcam_rbuffer_delete(akvcam_rbuffer_t self); 36 | akvcam_rbuffer_t akvcam_rbuffer_ref(akvcam_rbuffer_t self); 37 | 38 | void akvcam_rbuffer_copy(akvcam_rbuffer_t self, akvcam_rbuffer_ct other); 39 | void akvcam_rbuffer_resize(akvcam_rbuffer_t self, 40 | size_t n_elements, 41 | size_t element_size, 42 | AKVCAM_MEMORY_TYPE memory_type); 43 | size_t akvcam_rbuffer_size(akvcam_rbuffer_ct self); 44 | size_t akvcam_rbuffer_data_size(akvcam_rbuffer_ct self); 45 | size_t akvcam_rbuffer_n_elements(akvcam_rbuffer_ct self); 46 | size_t akvcam_rbuffer_element_size(akvcam_rbuffer_ct self); 47 | size_t akvcam_rbuffer_n_data(akvcam_rbuffer_ct self); 48 | ssize_t akvcam_rbuffer_available_data_size(akvcam_rbuffer_ct self); 49 | bool akvcam_rbuffer_data_empty(akvcam_rbuffer_ct self); 50 | bool akvcam_rbuffer_elements_empty(akvcam_rbuffer_ct self); 51 | bool akvcam_rbuffer_data_full(akvcam_rbuffer_ct self); 52 | bool akvcam_rbuffer_elements_full(akvcam_rbuffer_ct self); 53 | void *akvcam_rbuffer_queue(akvcam_rbuffer_t self, const void *data); 54 | void *akvcam_rbuffer_queue_bytes(akvcam_rbuffer_t self, 55 | const void *data, 56 | size_t size); 57 | void *akvcam_rbuffer_dequeue(akvcam_rbuffer_t self, 58 | void *data, 59 | bool keep); 60 | void *akvcam_rbuffer_dequeue_bytes(akvcam_rbuffer_t self, 61 | void *data, 62 | size_t *size, 63 | bool keep); 64 | void akvcam_rbuffer_clear(akvcam_rbuffer_t self); 65 | void *akvcam_rbuffer_ptr_at(akvcam_rbuffer_ct self, size_t i); 66 | void *akvcam_rbuffer_ptr_front(akvcam_rbuffer_ct self); 67 | void *akvcam_rbuffer_ptr_back(akvcam_rbuffer_ct self); 68 | void *akvcam_rbuffer_find(akvcam_rbuffer_ct self, 69 | const void *data, 70 | const akvcam_are_equals_t equals, 71 | ssize_t *offset); 72 | 73 | #endif // AKVCAM_RBUFFER_H 74 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_SETTINGS_H 20 | #define AKVCAM_SETTINGS_H 21 | 22 | #include 23 | 24 | #include "list_types.h" 25 | 26 | struct akvcam_settings; 27 | typedef struct akvcam_settings *akvcam_settings_t; 28 | typedef const struct akvcam_settings *akvcam_settings_ct; 29 | struct v4l2_fract; 30 | 31 | // public 32 | akvcam_settings_t akvcam_settings_new(void); 33 | void akvcam_settings_delete(akvcam_settings_t self); 34 | akvcam_settings_t akvcam_settings_ref(akvcam_settings_t self); 35 | 36 | bool akvcam_settings_load(akvcam_settings_t self, const char *file_name); 37 | void akvcam_settings_begin_group(akvcam_settings_t self, const char *prefix); 38 | void akvcam_settings_end_group(akvcam_settings_t self); 39 | size_t akvcam_settings_begin_array(akvcam_settings_t self, const char *prefix); 40 | void akvcam_settings_set_array_index(akvcam_settings_t self, size_t i); 41 | void akvcam_settings_end_array(akvcam_settings_t self); 42 | akvcam_string_list_t akvcam_settings_groups(akvcam_settings_ct self); 43 | akvcam_string_list_t akvcam_settings_keys(akvcam_settings_ct self); 44 | void akvcam_settings_clear(akvcam_settings_t self); 45 | bool akvcam_settings_contains(akvcam_settings_ct self, const char *key); 46 | char *akvcam_settings_value(akvcam_settings_ct self, const char *key); 47 | bool akvcam_settings_value_bool(akvcam_settings_ct self, const char *key); 48 | int32_t akvcam_settings_value_int32(akvcam_settings_ct self, 49 | const char *key); 50 | uint32_t akvcam_settings_value_uint32(akvcam_settings_ct self, 51 | const char *key); 52 | akvcam_string_list_t akvcam_settings_value_list(akvcam_settings_ct self, 53 | const char *key, 54 | const char *separators); 55 | struct v4l2_fract akvcam_settings_value_frac(akvcam_settings_ct self, 56 | const char *key); 57 | 58 | // public static 59 | bool akvcam_settings_to_bool(const char *value); 60 | int32_t akvcam_settings_to_int32(const char *value); 61 | uint32_t akvcam_settings_to_uint32(const char *value); 62 | akvcam_string_list_t akvcam_settings_to_list(const char *value, 63 | const char *separators); 64 | struct v4l2_fract akvcam_settings_to_frac(const char *value); 65 | const char *akvcam_settings_file(void); 66 | void akvcam_settings_set_file(const char *file_name); 67 | 68 | #endif // AKVCAM_SETTINGS_H 69 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "utils.h" 26 | 27 | static struct akvcam_utils 28 | { 29 | uint64_t id; 30 | int last_error; 31 | } akvcam_utils_private; 32 | 33 | typedef struct 34 | { 35 | __u32 cmd; 36 | char str[32]; 37 | } akvcam_utils_ioctl_strings, *akvcam_utils_ioctl_strings_t; 38 | 39 | typedef struct 40 | { 41 | int error; 42 | char str[32]; 43 | char description[AKVCAM_MAX_STRING_SIZE]; 44 | } akvcam_utils_error_strings, *akvcam_utils_error_strings_t; 45 | 46 | typedef struct 47 | { 48 | enum v4l2_buf_type type; 49 | char str[AKVCAM_MAX_STRING_SIZE]; 50 | } akvcam_utils_buf_type_strings, *akvcam_utils_buf_type_strings_t; 51 | 52 | typedef struct 53 | { 54 | enum v4l2_field field; 55 | char str[AKVCAM_MAX_STRING_SIZE]; 56 | } akvcam_utils_field_strings, *akvcam_utils_field_strings_t; 57 | 58 | typedef struct 59 | { 60 | enum v4l2_frmsizetypes type; 61 | char str[AKVCAM_MAX_STRING_SIZE]; 62 | } akvcam_utils_frmsize_type_strings, *akvcam_utils_frmsize_type_strings_t; 63 | 64 | typedef struct 65 | { 66 | __u32 pixelformat; 67 | char str[AKVCAM_MAX_STRING_SIZE]; 68 | } akvcam_utils_pixelformat_strings, *akvcam_utils_pixelformat_strings_t; 69 | 70 | typedef struct 71 | { 72 | enum v4l2_memory memory; 73 | char str[AKVCAM_MAX_STRING_SIZE]; 74 | } akvcam_utils_v4l2_memory_strings, *akvcam_utils_v4l2_memory_strings_t; 75 | 76 | typedef struct 77 | { 78 | enum v4l2_colorspace colorspace; 79 | char str[AKVCAM_MAX_STRING_SIZE]; 80 | } akvcam_utils_v4l2_colorspace_strings, *akvcam_utils_v4l2_colorspace_strings_t; 81 | 82 | typedef struct 83 | { 84 | AKVCAM_RW_MODE rw_mode; 85 | char str[AKVCAM_MAX_STRING_SIZE]; 86 | } akvcam_utils_rw_mode_strings, *akvcam_utils_rw_mode_strings_t; 87 | 88 | typedef struct 89 | { 90 | __u32 flag; 91 | char str[AKVCAM_MAX_STRING_SIZE]; 92 | } akvcam_utils_buffer_flags_strings, *akvcam_utils_buffer_flags_strings_t; 93 | 94 | typedef struct 95 | { 96 | __u32 flag; 97 | char str[AKVCAM_MAX_STRING_SIZE]; 98 | } akvcam_utils_buffer_capabilities_strings, *akvcam_utils_buffer_capabilities_strings_t; 99 | 100 | typedef struct 101 | { 102 | __s32 ctrl_which; 103 | char str[AKVCAM_MAX_STRING_SIZE]; 104 | } akvcam_utils_ctrl_which_class_strings, *akvcam_utils_ctrl_which_class_strings_t; 105 | 106 | uint64_t akvcam_id(void) 107 | { 108 | return akvcam_utils_private.id++; 109 | } 110 | 111 | int akvcam_get_last_error(void) 112 | { 113 | return akvcam_utils_private.last_error; 114 | } 115 | 116 | int akvcam_set_last_error(int error) 117 | { 118 | akvcam_utils_private.last_error = error; 119 | 120 | return error; 121 | } 122 | 123 | void akvcam_string_from_error(int error, char *str, size_t len) 124 | { 125 | size_t i; 126 | static const akvcam_utils_error_strings error_strings[] = { 127 | {EPERM , "EPERM" , "Operation not permitted" }, 128 | {ENOENT , "ENOENT" , "No such file or directory" }, 129 | {ESRCH , "ESRCH" , "No such process" }, 130 | {EINTR , "EINTR" , "Interrupted system call" }, 131 | {EIO , "EIO" , "I/O error" }, 132 | {ENXIO , "ENXIO" , "No such device or address" }, 133 | {E2BIG , "E2BIG" , "Argument list too long" }, 134 | {ENOEXEC, "ENOEXEC", "Exec format error" }, 135 | {EBADF , "EBADF" , "Bad file number" }, 136 | {ECHILD , "ECHILD" , "No child processes" }, 137 | {EAGAIN , "EAGAIN" , "Try again" }, 138 | {ENOMEM , "ENOMEM" , "Out of memory" }, 139 | {EACCES , "EACCES" , "Permission denied" }, 140 | {EFAULT , "EFAULT" , "Bad address" }, 141 | {ENOTBLK, "ENOTBLK", "Block device required" }, 142 | {EBUSY , "EBUSY" , "Device or resource busy" }, 143 | {EEXIST , "EEXIST" , "File exists" }, 144 | {EXDEV , "EXDEV" , "Cross-device link" }, 145 | {ENODEV , "ENODEV" , "No such device" }, 146 | {ENOTDIR, "ENOTDIR", "Not a directory" }, 147 | {EISDIR , "EISDIR" , "Is a directory" }, 148 | {EINVAL , "EINVAL" , "Invalid argument" }, 149 | {ENFILE , "ENFILE" , "File table overflow" }, 150 | {EMFILE , "EMFILE" , "Too many open files" }, 151 | {ENOTTY , "ENOTTY" , "Not a typewriter" }, 152 | {ETXTBSY, "ETXTBSY", "Text file busy" }, 153 | {EFBIG , "EFBIG" , "File too large" }, 154 | {ENOSPC , "ENOSPC" , "No space left on device" }, 155 | {ESPIPE , "ESPIPE" , "Illegal seek" }, 156 | {EROFS , "EROFS" , "Read-only file system " }, 157 | {EMLINK , "EMLINK" , "Too many links" }, 158 | {EPIPE , "EPIPE" , "Broken pipe" }, 159 | {EDOM , "EDOM" , "Math argument out of domain of func"}, 160 | {ERANGE , "ERANGE" , "Math result not representable" }, 161 | {0 , "" , "" }, 162 | }; 163 | 164 | memset(str, 0, len); 165 | 166 | for (i = 0; akvcam_strlen(error_strings[i].str) > 0; i++) 167 | if (error_strings[i].error == -error) { 168 | snprintf(str, 169 | len, 170 | "%s (%s)", 171 | error_strings[i].description, 172 | error_strings[i].str); 173 | 174 | return; 175 | } 176 | 177 | snprintf(str, len, "Unknown error (%d)", error); 178 | } 179 | 180 | void akvcam_string_from_rw_mode(AKVCAM_RW_MODE rw_mode, char *str, size_t len) 181 | { 182 | size_t i = 0; 183 | size_t j = 0; 184 | size_t n; 185 | static const akvcam_utils_rw_mode_strings rw_mode_strings[] = { 186 | {AKVCAM_RW_MODE_READWRITE, "rw" }, 187 | {AKVCAM_RW_MODE_MMAP , "mmap" }, 188 | {AKVCAM_RW_MODE_USERPTR , "userptr"}, 189 | {AKVCAM_RW_MODE_DMABUF , "dmabuf" }, 190 | {0 , "" }, 191 | }; 192 | 193 | memset(str, 0, len); 194 | n = snprintf(str, len, "AKVCAM_RW_MODE("); 195 | 196 | for (i = 0; akvcam_strlen(rw_mode_strings[i].str) > 0; i++) 197 | if (rw_mode_strings[i].rw_mode & rw_mode) { 198 | if (j > 0) 199 | n += snprintf(str + n, len - n, ", "); 200 | 201 | n += snprintf(str + n, len - n, "%s", rw_mode_strings[i].str); 202 | j++; 203 | } 204 | 205 | snprintf(str + n, len - n, ")"); 206 | } 207 | 208 | char *akvcam_strdup(const char *str, AKVCAM_MEMORY_TYPE type) 209 | { 210 | char *str_dup; 211 | size_t len = akvcam_strlen(str); 212 | 213 | if (type == AKVCAM_MEMORY_TYPE_KMALLOC) 214 | str_dup = kmalloc(len + 1, GFP_KERNEL); 215 | else 216 | str_dup = vmalloc(len + 1); 217 | 218 | str_dup[len] = 0; 219 | 220 | if (str) 221 | memcpy(str_dup, str, len + 1); 222 | 223 | return str_dup; 224 | } 225 | 226 | char *akvcam_strip_str(const char *str, AKVCAM_MEMORY_TYPE type) 227 | { 228 | if (!str) 229 | return NULL; 230 | 231 | return akvcam_strip_str_sub(str, 232 | 0, 233 | akvcam_strlen(str), 234 | type); 235 | } 236 | 237 | char *akvcam_strip_str_sub(const char *str, 238 | size_t from, 239 | size_t size, 240 | AKVCAM_MEMORY_TYPE type) 241 | { 242 | char *stripped_str; 243 | ssize_t i; 244 | size_t len; 245 | size_t left; 246 | size_t stripped_len; 247 | 248 | len = akvcam_min(akvcam_strlen(str), from + size); 249 | 250 | for (i = (ssize_t) from; i < (ssize_t) len; i++) 251 | if (!isspace(str[i])) 252 | break; 253 | 254 | left = (size_t) i; 255 | 256 | if (left == len) { 257 | stripped_len = 0; 258 | } else { 259 | size_t right; 260 | 261 | for (i = (ssize_t) len - 1; i >= (ssize_t) from; i--) 262 | if (!isspace(str[i])) 263 | break; 264 | 265 | right = (size_t) i; 266 | stripped_len = right - left + 1; 267 | } 268 | 269 | if (type == AKVCAM_MEMORY_TYPE_KMALLOC) 270 | stripped_str = kmalloc(stripped_len + 1, GFP_KERNEL); 271 | else 272 | stripped_str = vmalloc(stripped_len + 1); 273 | 274 | stripped_str[stripped_len] = 0; 275 | 276 | if (stripped_len > 0) 277 | memcpy(stripped_str, str + left, stripped_len); 278 | 279 | return stripped_str; 280 | } 281 | 282 | void akvcam_replace(char *str, char from, char to) 283 | { 284 | if (!str) 285 | return; 286 | 287 | for (; *str; str++) 288 | if (*str == from) 289 | *str = to; 290 | } 291 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* akvcam, virtual camera for Linux. 2 | * Copyright (C) 2018 Gonzalo Exequiel Pedone 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef AKVCAM_UTILS_H 20 | #define AKVCAM_UTILS_H 21 | 22 | #include 23 | #include 24 | 25 | #include "device_types.h" 26 | 27 | #define UNUSED(x) (void)(x) 28 | #define AKVCAM_MAX_STRING_SIZE 1024 29 | 30 | #define akvcam_min(value1, value2) \ 31 | ((value1) < (value2)? (value1): (value2)) 32 | 33 | #define akvcam_max(value1, value2) \ 34 | ((value1) > (value2)? (value1): (value2)) 35 | 36 | #define akvcam_abs(value) \ 37 | ((value) < 0? -(value): (value)) 38 | 39 | #define akvcam_between(min, value, max) \ 40 | ((value) >= (min) && (value) <= (max)) 41 | 42 | #define akvcam_bound(min, value, max) \ 43 | ((value) < (min)? (min): (value) > (max)? (max): (value)) 44 | 45 | #define akvcam_align_up(value, align) \ 46 | (((value) + (align) - 1) & ~((align) - 1)) 47 | 48 | #define akvcam_align32(value) akvcam_align_up(value, 32) 49 | 50 | #define akvcam_mod(value, mod) \ 51 | (((value) % (mod) + (mod)) % (mod)) 52 | 53 | #define akvcam_signal(class, signal, ...) \ 54 | typedef int (*akvcam_##class##_##signal##_proc)(void *user_data, __VA_ARGS__); \ 55 | \ 56 | typedef struct \ 57 | { \ 58 | void *user_data; \ 59 | akvcam_##class##_##signal##_proc callback; \ 60 | } akvcam_##class##_##signal##_callback, *akvcam_##class##_##signal##_callback_t; \ 61 | \ 62 | void akvcam_##class##_set_##signal##_callback(akvcam_##class##_t self, \ 63 | const akvcam_##class##_##signal##_callback callback) 64 | 65 | #define akvcam_signal_no_args(class, signal) \ 66 | typedef int (*akvcam_##class##_##signal##_proc)(void *user_data); \ 67 | \ 68 | typedef struct \ 69 | { \ 70 | void *user_data; \ 71 | akvcam_##class##_##signal##_proc callback; \ 72 | } akvcam_##class##_##signal##_callback, *akvcam_##class##_##signal##_callback_t; \ 73 | \ 74 | void akvcam_##class##_set_##signal##_callback(akvcam_##class##_t self, \ 75 | const akvcam_##class##_##signal##_callback callback) 76 | 77 | #define akvcam_signal_callback(class, signal) \ 78 | akvcam_##class##_##signal##_callback signal##_callback 79 | 80 | #define akvcam_signal_define(class, signal) \ 81 | void akvcam_##class##_set_##signal##_callback(akvcam_##class##_t self, \ 82 | const akvcam_##class##_##signal##_callback callback) \ 83 | { \ 84 | self->signal##_callback = callback; \ 85 | } 86 | 87 | #define akvcam_connect(class, sender, signal, receiver, method) \ 88 | do { \ 89 | akvcam_##class##_##signal##_callback signal_callback; \ 90 | signal_callback.user_data = receiver; \ 91 | signal_callback.callback = (akvcam_##class##_##signal##_proc) method; \ 92 | akvcam_##class##_set_##signal##_callback(sender, signal_callback); \ 93 | } while (false) 94 | 95 | #define akvcam_emit(self, signal, ...) \ 96 | do { \ 97 | if ((self)->signal##_callback.callback) \ 98 | (self)->signal##_callback.callback(self->signal##_callback.user_data, \ 99 | __VA_ARGS__); \ 100 | } while (false) 101 | 102 | #define akvcam_emit_no_args(self, signal) \ 103 | do { \ 104 | if ((self)->signal##_callback.callback) \ 105 | (self)->signal##_callback.callback(self->signal##_callback.user_data); \ 106 | } while (false) 107 | 108 | #define akvcam_call(self, signal, ...) \ 109 | ({ \ 110 | int result = 0; \ 111 | \ 112 | if ((self)->signal##_callback.callback) \ 113 | result = (self)->signal##_callback.callback(self->signal##_callback.user_data, \ 114 | __VA_ARGS__); \ 115 | \ 116 | result; \ 117 | }) 118 | 119 | #define akvcam_call_no_args(self, signal) \ 120 | ({ \ 121 | int result = 0; \ 122 | \ 123 | if ((self)->signal##_callback.callback) \ 124 | result = (self)->signal##_callback.callback(self->signal##_callback.user_data); \ 125 | \ 126 | result; \ 127 | }) 128 | 129 | #define akvcam_init_field(v4l2_struct, field) \ 130 | memset((v4l2_struct)->field, 0, sizeof((v4l2_struct)->field)) 131 | 132 | #define akvcam_init_reserved(v4l2_struct) \ 133 | akvcam_init_field(v4l2_struct, reserved) 134 | 135 | #define akvcam_wait_condition(wait_queue, condition, mtx, msecs) \ 136 | ({ \ 137 | int result; \ 138 | int mutex_result; \ 139 | \ 140 | mutex_unlock(mtx); \ 141 | result = wait_event_interruptible_timeout(wait_queue, \ 142 | condition, \ 143 | msecs_to_jiffies(msecs)); \ 144 | mutex_result = mutex_lock_interruptible(mtx); \ 145 | \ 146 | if (mutex_result) \ 147 | result = mutex_result; \ 148 | \ 149 | result; \ 150 | }) 151 | 152 | #define akvcam_strlen(str) \ 153 | ({ \ 154 | size_t len = 0; \ 155 | \ 156 | if (str) \ 157 | len = strnlen(str, AKVCAM_MAX_STRING_SIZE); \ 158 | \ 159 | len; \ 160 | }) 161 | 162 | typedef enum 163 | { 164 | AKVCAM_MEMORY_TYPE_KMALLOC, 165 | AKVCAM_MEMORY_TYPE_VMALLOC, 166 | } AKVCAM_MEMORY_TYPE; 167 | 168 | typedef bool (*akvcam_are_equals_t)(const void *element_data, const void *data); 169 | typedef void *(*akvcam_copy_t)(void *data); 170 | typedef void (*akvcam_delete_t)(void *data); 171 | 172 | uint64_t akvcam_id(void); 173 | int akvcam_get_last_error(void); 174 | int akvcam_set_last_error(int error); 175 | void akvcam_string_from_error(int error, char *str, size_t len); 176 | void akvcam_string_from_rw_mode(AKVCAM_RW_MODE rw_mode, char *str, size_t len); 177 | char *akvcam_strdup(const char *str, AKVCAM_MEMORY_TYPE type); 178 | char *akvcam_strip_str(const char *str, AKVCAM_MEMORY_TYPE type); 179 | char *akvcam_strip_str_sub(const char *str, 180 | size_t from, 181 | size_t size, 182 | AKVCAM_MEMORY_TYPE type); 183 | void akvcam_replace(char *str, char from, char to); 184 | 185 | #endif // AKVCAM_UTILS_H 186 | --------------------------------------------------------------------------------