pax_global_header00006660000000000000000000000064147543145120014520gustar00rootroot0000000000000052 comment=eda4bd4910e6cadae94f3dba04472af5ba701362 aravis-0.8.34/000077500000000000000000000000001475431451200131015ustar00rootroot00000000000000aravis-0.8.34/.clang-format000066400000000000000000000013621475431451200154560ustar00rootroot00000000000000# https://clang.llvm.org/docs/ClangFormatStyleOptions.html # Glib has formatting at https://wiki.apertis.org/Guidelines/Coding_conventions#Code_formatting # https://gitlab.gnome.org/GNOME/glib/-/blob/main/.clang-format # But we do not follow it fully BasedOnStyle: GNU AllowShortIfStatementsOnASingleLine: AllIfsAndElse AllowShortLoopsOnASingleLine: true BinPackParameters: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Linux BreakBeforeTernaryOperators: false ColumnLimit: 0 ContinuationIndentWidth: 8 Cpp11BracedListStyle: false FixNamespaceComments: false IndentCaseLabels: true IndentGotoLabels: false IndentWidth: 8 SeparateDefinitionBlocks: Always SortIncludes: Never SpaceAfterCStyleCast: true SpaceBeforeParens: Always UseTab: Always aravis-0.8.34/.editorconfig000066400000000000000000000002451475431451200155570ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf [*.[ch]] indent_size = 8 indent_style = space max_line_length = 120 tab_width = 8 trim_trailing_whitespace = true aravis-0.8.34/.gitattributes000066400000000000000000000000001475431451200157620ustar00rootroot00000000000000aravis-0.8.34/.github/000077500000000000000000000000001475431451200144415ustar00rootroot00000000000000aravis-0.8.34/.github/FUNDING.yml000066400000000000000000000000431475431451200162530ustar00rootroot00000000000000open_collective: aravis-project aravis-0.8.34/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475431451200166245ustar00rootroot00000000000000aravis-0.8.34/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000010621475431451200213150ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve Aravis title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Camera description:** - Manufacturer - Model - Interface [e.g. USB3] **Platform description:** - Aravis version - OS: [e.g. Fedora 20] - Hardware [e.g. x86_64] **Additional context** Add any other context about the problem here. aravis-0.8.34/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010311475431451200223440ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for Aravis title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context about the feature request here. aravis-0.8.34/.github/workflows/000077500000000000000000000000001475431451200164765ustar00rootroot00000000000000aravis-0.8.34/.github/workflows/aravis-linux.yml000066400000000000000000000032431475431451200216450ustar00rootroot00000000000000name: Aravis-Linux on: push: branches: [ main, aravis-0-8 ] pull_request: branches: [ main, aravis-0-8 ] release: workflow_dispatch: jobs: linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: | pip3 install meson ninja Markdown toml typogrify sudo apt update sudo apt install python3 libusb-1.0-0-dev gobject-introspection valgrind libgstreamer-plugins-bad1.0-dev libgtk-3-dev libgirepository1.0-dev python3-gi libunwind-dev gettext - name: Build run: | meson --buildtype=plain -Ddocumentation=enabled -Dgst-plugin=enabled -Dintrospection=enabled -Dusb=disabled -Dviewer=enabled . ./build ninja -C ./build meson configure -Dusb=enabled build ninja -C ./build env: CC: gcc - name: Tests run: meson test -C build/ -v - name: Valgrind run: meson test -C build/ -v --setup=valgrind - uses: actions/upload-artifact@v4 if: failure() with: name: Linux_Meson_Testlog path: build/meson-logs/testlog*.txt - name: Development Documentation uses: peaceiris/actions-gh-pages@v3 if: ${{ github.ref == 'refs/heads/main' }} with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./build/docs/reference/aravis/aravis-0.10 destination_dir: aravis-main - name: Stable Documentation uses: peaceiris/actions-gh-pages@v3 if: ${{ github.ref == 'refs/heads/aravis-0-8' }} with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./build/docs/reference/aravis/aravis-0.8 destination_dir: aravis-stable aravis-0.8.34/.github/workflows/aravis-macos.yml000066400000000000000000000023371475431451200216130ustar00rootroot00000000000000name: Aravis-macOS on: push: branches: [ main, aravis-0-8 ] pull_request: branches: [ main, aravis-0-8 ] release: workflow_dispatch: jobs: macos: runs-on: macos-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: '3.x' - name: Unbreak Python in Github Actions run: | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete sudo rm -rf /Library/Frameworks/Python.framework/ brew install --force python3 && brew unlink python3 && brew unlink python3 && brew link --overwrite python3 - name: Install dependencies run: | brew update brew install meson ninja gcc gettext intltool libxml2 libusb gstreamer gnome-icon-theme gobject-introspection glib - name: Build run: | meson --buildtype=plain -Ddocumentation=disabled -Dgst-plugin=enabled -Dintrospection=disabled -Dusb=enabled -Dviewer=enabled . ./build ninja -C ./build env: CC: gcc - name: Tests run: meson test -C build/ -v - uses: actions/upload-artifact@v4 if: failure() with: name: MacOS_Meson_Testlog path: build/meson-logs/testlog.txt aravis-0.8.34/.github/workflows/aravis-mingw.yml000066400000000000000000000040611475431451200216260ustar00rootroot00000000000000# MSYS2 setup with actions is described here: # https://github.com/marketplace/actions/setup-msys2 name: Aravis-MinGW on: push: branches: [ main, aravis-0-8 ] pull_request: branches: [ main, aravis-0-8 ] workflow_dispatch: jobs: mingw: runs-on: windows-latest strategy: fail-fast: false # see all failures, not just the first one matrix: include: - { sys: mingw64, env: x86_64 } # - { sys: mingw32, env: i686 } # mingw32 is missing gst-plugins-good package, disabled for now - { sys: ucrt64, env: ucrt-x86_64 } # - { sys: clang64, env: clang-x86_64 } # clang64 is missing several packages, disabled for now defaults: run: shell: msys2 {0} steps: - uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.sys}} release: true path-type: minimal update: false install: >- mingw-w64-${{matrix.env}}-toolchain mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-gobject-introspection mingw-w64-${{matrix.env}}-meson mingw-w64-${{matrix.env}}-gst-plugins-good mingw-w64-${{matrix.env}}-gst-plugins-bad mingw-w64-${{matrix.env}}-gstreamer mingw-w64-${{matrix.env}}-gtk3 mingw-w64-${{matrix.env}}-libxml2 mingw-w64-${{matrix.env}}-zlib mingw-w64-${{matrix.env}}-libusb mingw-w64-${{matrix.env}}-gobject-introspection-runtime mingw-w64-${{matrix.env}}-python-gobject - uses: actions/checkout@v3 - name: meson run: | mkdir build meson --buildtype=plain -Ddocumentation=disabled -Dgst-plugin=enabled -Dintrospection=enabled -Dusb=enabled -Dviewer=enabled -Dgv-n-buffers=1 . ./build - name: ninja install run: | ninja -C ./build --verbose install - name: meson test run: | meson test -C ./build - uses: actions/upload-artifact@v4 if: failure() with: name: ${{matrix.sys}}_Meson_Testlog path: build/meson-logs/testlog.txt aravis-0.8.34/.github/workflows/aravis-msvc.yml000066400000000000000000000050741475431451200214620ustar00rootroot00000000000000name: Aravis-MSVC on: push: branches: [ main, aravis-0-8 ] pull_request: branches: [ main, aravis-0-8 ] workflow_dispatch: jobs: msvc: runs-on: windows-2019 strategy: fail-fast: false matrix: include: - version: 16 arch: x86_64 build_type_conan: Release build_type_meson: release steps: - name: pip run: | pip install "conan<2.0.0" - name: disable-perl run: | rm -r C:\Strawberry\perl - name: checkout uses: actions/checkout@v3 - name: conan env: INPUT_CONANFILE: | [requires] libiconv/1.17 #gobject-introspection/1.69.0 gstreamer/1.22.3 #gst-plugins-base/1.22.3 Disable gstreamer for now, latest gst-plugins-base is 1.19.2 #gtk/4.4.0 libxml2/2.10.3 zlib/1.2.13 libusb/1.0.26 [build_requires] meson/0.59.2 pkgconf/1.9.3 [generators] pkg_config virtualenv virtualbuildenv virtualrunenv [options] glib:shared=True gstreamer:shared=True gst-plugins-base:shared=True run: | $Env:INPUT_CONANFILE | Out-File -FilePath ${{ github.workspace }}\conanfile.txt -Encoding utf8 conan install -s os="Windows" -s compiler="Visual Studio" -s compiler.version=${{ matrix.version }} -s arch=${{ matrix.arch }} -s build_type=${{ matrix.build_type_conan }} -b pcre -b missing -b cascade -if build . - name: meson run: | .\build\activate.ps1 .\build\activate_build.ps1 .\build\activate_run.ps1 echo "::group::configure" meson --prefix ${{ github.workspace }}\install --buildtype ${{ matrix.build_type_meson }} --pkg-config-path ${{ github.workspace }}\build -Ddocumentation=disabled -Dgst-plugin=disabled -Dintrospection=disabled -Dusb=enabled -Dviewer=disabled -Dgv-n-buffers=1 . .\build echo "::endgroup::" echo "::group::compile" meson compile -C .\build -v echo "::endgroup::" echo "::group::install" meson install -C .\build echo "::endgroup::" - name: test run: | .\build\activate.ps1 .\build\activate_build.ps1 .\build\activate_run.ps1 meson test -C .\build - name: logs uses: actions/upload-artifact@v4 if: failure() with: name: msvc_${{ matrix.version }}_${{ matrix.arch }}_${{ matrix.build_type_meson }}_Meson_Testlog path: build/meson-logs/testlog.txt aravis-0.8.34/.gitignore000066400000000000000000000000531475431451200150670ustar00rootroot00000000000000build/ .*.swp *~ build-*/ .cache/ .vscode/ aravis-0.8.34/.lgtm.yml000066400000000000000000000004111475431451200146410ustar00rootroot00000000000000extraction: cpp: prepare: packages: - python3-pip - python3-setuptools - python3-wheel after_prepare: - pip3 install meson==0.56 - export PATH="$HOME/.local/bin:$PATH" python: python_setup: version: 3 aravis-0.8.34/AUTHORS000066400000000000000000000000451475431451200141500ustar00rootroot00000000000000Emmanuel Pacaud aravis-0.8.34/CITATION.cff000066400000000000000000000011111475431451200147650ustar00rootroot00000000000000# This CITATION.cff file was generated with cffinit. # Visit https://bit.ly/cffinit to generate yours today! cff-version: 1.2.0 title: Aravis message: >- If you use this software, please cite it using the metadata from this file. type: software authors: - name: Aravis Project website: 'https://github.com/AravisProject' repository-code: 'https://github.com/AravisProject/aravis' url: 'https://aravisproject.github.io/aravis/' abstract: >- Aravis is a GObject based library for the control and the video stream acquisition of digital cameras. license: LGPL-2.1-or-later aravis-0.8.34/COPYING000066400000000000000000000636421475431451200141470ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! aravis-0.8.34/NEWS.md000066400000000000000000000764741475431451200142210ustar00rootroot00000000000000Stable release 0.8.34 ===================== * gigevision: allow to select discovery interface (Yuto) * genicam: fix unaligned memory access (Matwey) * gigevision: fix uninitialized memory access (Emmanuel) * build: add missing include (Emmanuel) Stable release 0.8.33 ===================== * viewer: ignore exposure time feature representation (Emmanuel) Stable release 0.8.32 ===================== * viewer: workaround for Blackfly Mono16 pixel endianness issue (Berke) * gst: disconnect camera in stop method #907 (Emmanuel) * gst: stream object accessor (Marko) * gst: camera change signal (Marko) * gst: stream change signal (Marko) * gst: allow software trigger (WhaSukGo) * gvsp: fix chunk detection (Marco) * gv_device: crach fix on finalization (Marco) * Fix device_write_memory rust binding (Jacob) * introspection: add generic feature value accessors (Marko) * chore: fix libxml2 deprecation warnings (Emmanuel) * chore: appstream fixes (Chiara) Stable release 0.8.31 ===================== * viewer: better handling of exposure and gain setting (Momoko) * genicam: genicam URL stored as DomDocument URL property (Emmanuel) * genicam: fix arv_device_get_feature_access_mode return value (Marco) * camera: set/dup_register helper (Emmanuel) * camera: frame_rate setting improvements (Marco) * usb3vision: handle GenDC payload (Momoko) * gigevision: set a lower boundary to minimum socket size in auto mode (Emmanuel) * build: fix a libxml2 API breakage (Russel, Marco) Stable release 0.8.30 ===================== * gv: add EVK Helios and Automation Technology C6 devices to the legacy mode list (Václav, anathesys) * gv: don't ignore POINTTOPOINT interfaces, allows for direct connection to devices through VPNs (James) Stable release 0.8.29 ===================== * usb3vision: fix callbacks triggering in async mode (Emmanuel) * usb3vision: fix underrun counting (Emmanuel) Stable release 0.8.28 ===================== * usb3vision: make async libusb default (Emmanuel) * usb3vision: improve CPU use when no buffer is available for the stream thread (Emmanuel) * usb3vision: don't wait forever for USB transfer completion (Emmanuel) * usb3vision: enable stream after the receiving stream thread is started (Emmanuel) * viewer: avoid buffer exhaustion if GStreamer pipeline is late (Emmanuel) * build: fix clang 32 bits compilation (Nath) Stable release 0.8.27 ===================== * camera: add vendor specific quirks to frame rate (durnezj) * camera: multiple tap helpers #745 (Brian) * gv: start packet timeout after the first non leader packet #746 (Brian) * gv: auto packet size fix (Emmanuel) * gv: add more camera to the legacy endianness mechanism exception list (Emmanuel) * u3v: fix u3vcp_capability register size (Emmanuel) * chore: compilation warning fixes (Marco, Emmanuel) * ci: pipeline babysitting (Emmanuel) * doc: link fix (Henrique) Stable release 0.8.26 ===================== * viewer: fix pixel format selection #765 (Emmanuel) Stable release 0.8.25 ===================== * viewer: add component selection support (Emmanuel) * buffer: add component id getter (Emmanuel) * buffer: fix multipart informations (Emmanuel) Stable release 0.8.24 ===================== * gv: fix multipart data accessor (Emmanuel) * gv: improve error handling of network API (Emmanuel) Stable release 0.8.23 ===================== * python: fix crash in stream callback #714 (Cyril) * gv: multipart support (Emmanuel) * gv: chunk data payload support (Emmanuel) * gv: use the trailer packet for expected data size calculation (Emmanuel) * gv: reset SCPx register on stream finalization * gv: API to allow discovery ack broadcast (Emmanuel) * gv: add more legacy endianness entries (Tomohisa, Radim, Emmanuel) * gv: fix infinite loop in auto packet size (Emmanuel) * gv: don't use source port in socket packet mode (Emmanuel) * genicam: AccessMode improvements (Emmanuel) * camera: add a Component API (Emmanuel) * buffer: part access API (Emmanuel) * cameratest: allow to set features from a string (Emmanuel) * ci: windows build fixes (Momoko, Emmanuel) * doc: improvements (Masahiro, Emmanuel) Stable release 0.8.22 ===================== * camera: new network command for IP settings (Romain, Masahiro) * camera: don't fail if TriggerSelector is not available (Romain) * camera: make region setting work even without Offset features (Emmanuel) * camera: allow frame rate setting during acquisition on more devices #683 (Emmanuel) * gstplugin: allow low frame rate with external triggers (Edgar) * genicam: list for forced legacy mode (Romain) * genicam: access mode check policy (Emmanuel) * genicam: fix inifinite loop during availability check #678 (Emmanuel) * u3v: use libusb device-left event for control lost detection (Emmanuel) * genicam: fix information loss #656 (Emmanuel) * fake_camera: add TriggerSoftware support (Romain) * build: appdata fix (Chiara) * build: disable documentation by default (Emmanuel) * tests: fix ChunkFloat test on big endian platform #670 (Emmanuel) * introspection: fix user_data_destroy_func annotations (Thomas) * misc: memory leak and warning fixes (Emmanuel) Stable release 0.8.21 ===================== * build: FreeBSD support #620 (Rim) * gigevision: performance improvement of standard socket method #617 (Emilio) * usb3vision: prevent USB command mix up #622 (Edgar) * usb3vision: fix PacketResend size field in extended ID mode #642 (Alexander) * genicam: allow more Category child nodes #632 (Emmanuel) * genicam: AccessMode fixes #634 (Emmanuel) * genicam: fix pIsLocked when target is a Boolean node #638 (Emmanuel) * doc: port to gi-docgen #621 (Emmanuel) * git: default branch is main now (Emmanuel) Stable release 0.8.20 ===================== * camera: make set_frame_rate more robust for Basler cameras (Thies) * misc: add runtime version API (Emmanuel) * misc: ignore leading spaces in genicam data url (Emmanuel) * misc: add get_device_manufacturer_info API (Emmanuel) * qa: fix incorrect memory access (Jose) * gstreamer: allow to select asynchronous USB mode (Christian) * windows: fix thread priority helpers (Garfeng) * meson: fix compilation using meson 0.60 (Emmanuel) * doc: ubuntu 20.04 build do cimprovements (Brian) Stable release 0.8.19 ===================== * camera: black level support (Brian) * camera: GainAbs support for gain setting (Brian) * camera: FrameBurstStart support for trigger configuration (Emmanuel) * viewer: minor layout fixes (Emmanuel) * ci: MSVC improvements (Siim) Stable release 0.8.18 ===================== * usb3vision: fix header inclusion when USB is disabled (Marc) * tests: register cache test (Emmanuel) Stable release 0.8.17 ===================== * windows: Microsoft Visual C++ support (Siim) * usb3vision: async mode (Takuro, Constantine) * usb3vision: use GUID for device disambiguation (Takuro) * test: extend test coverage in arv-test (Emmanuel) * viewer: in-app notification for save error (Emmanuel) * camera: make set_trigger more robust (Emmanuel) * tool: add glob pattern based device and feature selection (Emmanuel) Stable release 0.8.16 ===================== * ci: use Github actions for linux and macOS (Emmanuel) * ci: minGW support (Václav) * gigevision: use proper broadcast addresses during discovery (Václav) * camera: accessor to float feature increment (Emmanuel) * camera: make set_trigger more robust (Emmanuel) * camera: fallback to Continuous mode if SingleFrame is not available (Emmanuel) * viewer: allow to save a snapshot as png or jpeg image (Emmanuel) * viewer: display all pixel formats in selector (Emmanuel) * simulator: now works on Windows and macOS (Václav, Emmanuel) * debug: fix output on Windows (Václav) * doc: Windows build documentation (Václav) * tests: new arv-test application for automated testing (Emmanuel) Stable release 0.8.15 ===================== * gigevision: only disable packet resend after a packet unavailable error * gigevision: add a new packet timeout for first packet resend request * doc: improve GvStream property documentation * code cleanup Stable release 0.8.14 ===================== * debug: fix debug timestamp on older platforms (Emmanuel) Stable release 0.8.13 ===================== * viewer: fix incorrect bandwith and frame rate computation Stable release 0.8.12 ===================== * usb3vision: initialize stream infos Stable release 0.8.11 ===================== * stream: add an extensible info API (Emmanuel) * gvstream: improve packet resend request behaviour in case of unordered gvsp packets (Emmanuel) * gvstream: wait for thread setup during ArvGvStream initialization (Emmanuel) * gigevision: use MAC as serial number fallback (Emmanuel) * genicam: fix arv_exposure_mode_to_string (Martin) * usb3vision: improve error reporting in case of libsub error (Emmanuel) * usb3vision: automatically detach kernel driver (Emmanuel) * cameratest: add a test duration parameter (Emmanuel) * misc: rename internal ArvStatistic to ArvHistogram (Emmanuel) Stable release 0.8.10 ===================== * arv-tool: make device enumeration faster (Emmanuel) * debug: rework debug log levels (Emmanuel) * fakegvcamera: improve streaming reliability - partly fix #499 (Emmanuel) * gvstream: fix use after reference release - fix #504 (Emmanuel) * genicam: String node support - fix #507 (Emmanuel) Stable release 0.8.9 ==================== * windows: build fix (Emmanuel) Stable release 0.8.8 ==================== * arv-camera-test: promote to installed application (Emmanuel) * applications: fix policy parameter consistency (Emmanuel) * debug: timestamped and modernized output (Emmanuel) * genicam: add a range check debug mode (Emmanuel) * gvdevice: faster finalization (Emmanuel) * camera: new DeviceSerialNumber getter (Emmanuel) Stable release 0.8.7 ==================== * camera: ignore Acquisitiontart feature setting failure (Emmanuel) * camera: fix trigger setting for Basler cameras (Casperoo) * camera: add set_exposure API (Emmanuel) * gigevision: Windows support (Václav) * usb3vision: better error packet handling (Emmanuel) * genicam: allow get/set float from an int node (Emmanuel) * genicam: allow multiple pIndex property nodes (Emmanuel) Stable release 0.8.6 ==================== * camera: handle GainRaw also as a float feature * camera: add arv_camera_new_with_device() * camera: ignore error on TriggerSelector and TriggerMode setting in arv_camera_set_trigger() * gigevision: fail quicker if a device is not found at ArvGvDevice instantiation * fakegvcamera: fix interface selection * genicam: implement optional range check for integer and float node values, as a runtime option * genicam: fix min/max computation for StructEntry and MaskedIntReg nodes * gstplugin: don't fail camera init if gain or exposure features are not available Stable release 0.8.5 ==================== * macOS: build fix * travis:enable macOS Stable release 0.8.4 ==================== * all: s/adjustement/adjustment/ Stable release 0.8.3 ==================== * gigevision: automatically adjust packet size if needed (Emmanuel) * gstreamer: don't try to set frame rate if feature is not available (Emmanuel) * genicam: fix pVariable name with dot (Arrigo) * genicam: fix parsing of genicam data url (Emmanuel) * buffer: add arv_buffer_set_frame_id API (Russel) * usb3vision: add Dahua Technology USB id (H.F) * build: preparatory work for windows compilation (Eudoxos) Stable release 0.8.2 ==================== * gvdevice: fix timeout race (casperoo) * fakecamera: implement Mono16 pixel format (Hinko) * Enable ppc64le in CI pipeline (nagesh) Stable release 0.8.1 ==================== * build: add more compilation warnings and fix them (Emmanuel) Stable release 0.8.0 ==================== * gigevision: ExtendedIds support (Hendrick, Emmanuel) * gigevision: add get_control_access API (casperoo) * genicam: implement proper AccessMode and ImposedAccessMode support (Siim) * genicam: add or extend support for Representation, Unit, DisplayNotation and DisplayPrecision proerties (Siim) * genicam: extend GcRregisterDescriptionNode API (Siim) * genicam: improve String register (Siim) * genicam: implement arv_gc_feature_get_name_space() (Siim) * gst-plugin: don't shadow GstBaseSrc num-buffers property (Marko) * usb3vision: add Daheng Imaging descriptors (Jakob) Unstable release 0.7.5 ====================== * gst-plugin: error handling and lock fixes (Marko) * build: fix when aravis is used as a subproject (Rihards) * build: fix viewer build without libusb (Guillaume) * fake camera: implement bayer pixel formats (Bernardo) * gcregister: don't try to read WO registers (Stefan) * viewer: fix buffer leak (Emmanuel) * gvstream: correctly handle resend request limit (Emmanuel) * stream: device reference leak fix (Emmanuel) Unstable release 0.7.4 ====================== * camera/device/stream: add an error parameter to object intantiation functions (Emmanuel) * camera: fix node type mismatch error in set_frame_rate (Arkadiusz) * gigevision: ignore duplicated packets (Joris) * build: make build of tests optional (Edgar) Unstable release 0.7.3 ====================== * all: use gobject macros for class declarations (Emmanuel) * camera: add a GError parameter to most functions (Emmanuel) * python: add python tests in test suite (Emmanuel) * usb3vision: improve reliability of camera connection (Dmitry) * introspection: fix PixefFormat type (Léo, Maarten) Unstable release 0.7.2 ====================== * gigevision: support for ImageExtendedChunkPayload (Nathan) * chunkparser: add a GError parameter to the getters (Emmanuel) * chunkparser: add a boolean accessor (Emmanuel) * arvtool: new `values` command that show the values of all available features (Emmanuel) * gcport: don't try to read a register when the port is an event (Emmanuel) * genicam: pSelect support (Emmanuel) * genicam: remove value_type property, replaced by ARV_IS_GC_(FLOAT|INTEGER`BOOLEAN|STRING|ENUMERATION) (Emmanuel) * genicam: simplify read/write feature values as/from string (Emmanuel) * genicam: fix min/max of non 64 bit integers * genicam: Float and Integer now get their min/max also from pValue (Emmanuel) * gigevision: correctly detect access denied errors Unstable release 0.7.1 ====================== * build: installation fixes (Emmanuel, Rafael) Unstable release 0.7.0 ====================== * build: aravis is now built using meson (and only meson) * camera: make set_region more robust (Aleksandr) * usb3vision: fix an align assertion (Johannes) * fakecamera: improve discovery (Russell) * fakecamera: add RGB8 pixel format support (Russell) * fakecamera: allow to instantiate several gvfakecamera (Emmanuel) * gigevision: pending ack fix (Emmanuel) * gigevision: better handling of GVCP error packets (Emmanuel) * gstplugin: packet size parameter (Emmanuel) * viewer: show stream statistics (Emmanuel) * genicam: fix register cache (Emmanuel) * camera: move feature read/write API with status here from ArvDevice (Emmanuel) * device: add a GError parameter to feature read/write functions (Emmanuel) * gvdevice: use Inc in auto packet size function (Emmanuel) * genicam: implementation of `` support (Emmanuel) * viewer: icon refresh (Emmanuel) Release 0.6.3 ============= * device: fix get_status return value (crabsmaps) * gigevision: add more pixel format enums (emmanuel) Release 0.6.2 ============= * stream: add stop/start thread API (Werner, Emmanuel) * gigevision: allow to discover more devices (Michael) * gigevision: stop stream thread quicker (Emmanuel) * genicam: add element support (Nathan) * genicam: let float node point to integer node (Emmanuel) * usb3vision: sanity checks during device initialization (crabsmaps) Release 0.6.1 ============= * gigevision: auto-packet size negociation improvements (Nils, Edgar) * gigevision: interface addess assignment improvement (Nils) * usb3vision: memory leak fixes (wuyuanyi) * usb3vision: payload size computation fixes (Johannes) Release 0.6.0 ============= Stable release. * camera: avoid clashes in device id generation * genicam: signedness and endianness related fix Release 0.5.13 ============== * usb3vision: chunk data support (Emmanuel) * camera: matrix vision device support (Nils) * camera: PointGrey / FLIR renaming support (Nils) * i18n: czech and slovak translations (Multiflexi) * build: remove libcap-ng dependency (Nils) Release 0.5.12 ============== * gigevision: disable GVSP extended id mode * genicam: fix for signedness of register nodes * genicam: add support for optional ROUND parameter (Aleksandr) * add man page (Chiara) Release 0.5.11 ============== * camera: ensure binning feature is present before using it (thecaptury) * viewer: don't overwrite buffers still used by gstreamer (Tim) * usb3vision: improve streaming reliability (Tim, Emmanuel) * buid instruction update (Raphie, Rehno) * translation updates (Rafael) Release 0.5.10 ============== * camera: fix for PointGrey cmeras using AcquisitionFrameRateEnabled (Emmanuel) * genicam: correctly implement integer mode of IntSwissKnife (Emmanuel) * genicam: implementtion of pIndex, ValueIndexed, ValueDefault (Emmanuel) * genicam: add Expression and Constant support to Converter and IntConverter (Emmanuel) * evaluator: implement ROUND function (Emmanuel) * evaluator: fix '~' precedence and associativity (Emmanuel) * gigevision: protocol fixes (Emmanuel) * gigevision: improve connection stability under high pressure (Michael) * gigevision: avoid using the same port twice (Emmanuel) * gvdevice: implement packet-resend-ration limit (Emmanuel) * usb3vision: transfer size configuration fixes (Konstantin) * build: improve OSX and Windows support (Emmanuel) * build: improve build outside of source directory (Janito) * viewer: improve Pixel format to GstCaps conversion (Emmanuel) * viewer: disable gtkglsink for now to avoid a crash when switching views (Emmanuel) * QA: extended code coverage (Emmanuel) * QA: make travis build on OSX (Emmanuel) Release 0.5.9 ============= * gige/usb3vision: device detection fixes (Nils) * stream: allow to handle circular buffer from callback (Nils) * build: OSX documentation fix (Mark) * viewer: latency improvements (Emmanuel) * viewer: show protocol in camera list view (Emmanuel) * gigevision: faster connection to devices (Emmanuel) * gigevision: PENDING ACK support (Emmanuel) * gige/usb3vision: use vendor aliases for default device id (Emmanuel) Release 0.5.8 ============= * buffer: save local system time in buffer metadate (Konstantin) * usb3vision: better handling of unexpected answers (Emmanuel) * stream: fix for self joining receiving thread issue (Michael) * fake: new class for instantiation of fake cameras (Emmanuel) * tests: new fakegv test that exercizes GigEVision protocol (Emmanuel * build system update (code-coverage, address sanitizer and valgrind support) (Emmanuel) * miscellaneous fixes for bug discovered vie the update build system (Emmanuel) Release 0.5.7 ============= * usb3vision: Ximea camera support (Steve) * usb3vision: TheImagingSource camera support (emmanuel) * usb3vision: Bandwith control (Steve) * usb3vision: USB endpoint detection improvments (Steve, Emmanuel) * fake_camera: miscellaneous fixes (Michael) * genicam: raw data support (Steve) * camera: multiframe acquisition mode (Lee) * camera: extension of the trigger API (Steve) * gigevision: instantiation by IP address or hostname (Blaz, Arkadiusz) * build: fix compilation with old glib (Blaz) * build: intructions and compilation fix for OSX (Christoffer, Fernando) * build: Travis and Coverity support on github (Emmanuel) Release 0.5.6 ============= * genicam: implement Expression and Constant nodes for [Int]SwissKnife * API: add ARAVIS_CHECK_VERSION macro * API: forbid direct inclusion of headers other than arv.h * usb3vision: PENDING_ACK support Release 0.5.5 ============= * build: fix library detection needed for packet socket support (Hubert) * gigevision: new API for automatic stream packet size (Emmanuel) Release 0.5.4 ============= * gigevision: support of packet socket in stream thread (Emmanuel) Release 0.5.3 ============= * usb3vision: fix USB3 device scan (Emmanuel) Release 0.5.2 ============= * viewer: only show compatbile pixel format in selector * viewer: fix binning setting on some cameras * viewer: add a fallback to autovideosink if gtk[gl]sink are not available * viewer: fix video position issue * usb3vision: avoid unnecessary memory during video data transfer * usb3vision: better handling of device disconnection * gigevision: compile time selection of fast heartbeat mode (Michele) * camera: helper function for single image acquisition Release 0.5.1 ============= * viewer: new design with support for region settings * usb3vision improvements * camera: improve Basler support for exposure and frame rate * camera: add support for x/y_offset and binning bounds * interface: add vendor, model and serial getters * genicam: implement min/max support for Converter elements Release 0.5.0 ============= * usb3vision initial support Release 0.4.0 ============= * buffer: add a timestamp setter * interface: don't touch device list on open device * viewer: fix appdata file Release 0.3.8 ============= * genicam: add bigendian support (Emmanuel) * camera: RICOH camera support (Anil) * build fixes (Emmanuel) * dox fixes (Jarek, Emmanuel) * translation updates (Jordi, Jiri, Anders, Μύρων, Pedro, Cédric, Samir, Necdet) Release 0.3.7 ============= * genicam: fix accuracy of division of integers (Emmanuel) * new arv_make_high_priority and arv_make_realtime API (Emmanuel) * viewer: make stream thread realtime if possible (Emmanuel) * camera: add GigEVision specific API for packet delay, packet size and stream selection (Emmanuel) * gst_pugins: add a number of buffers property (Patrick) * build fixes (Patrick) * translation updates (Tiago, Muhammet) Release 0.3.6 ============= * build: add missing test commands in configure.ac (Adrian) * camera: new abort_acquisition function (Davide) * gv_stream: missing frame detection fix (Davide) * buffer: user_data and frame_id accessors * chunk_parser: bug fixes * viewer: prevent use of broken coglsink from autovideosink * translations: updates (Matej, Balázs, Andika, Daniel, Rafael) Release 0.3.5 ============= * chunk_parser: new API for chunk data support * buffer: make internal data private and add accessors Release 0.3.4 ============= * viewer: add an appdata file * misc: compilation warning fixes Release 0.3.3 ============= * genicam: improve loading of genicam data from local files (Peter) * build: dependency check improvements * build: miscellaneous compilation fixes (Patrick) * gst_pugins_1.0: bayer format fix (Steve) Release 0.3.2 ============= * gst_plugin: new plugin based on gstreamer 1.0, in addition to the existing 0.10 version. * gst_plugin: add packet-resend property. * viewer: port to gstreamer 1.0, and drop support for gtk+ 2.0. * viewer: fix video output for camera with buggy time stamping. * camera: fix frame rate setting for Point Grey devices. Release 0.3.1 ============= * gv_interface: fix device discovery by using 255.255.255.255 as broadcast address (Csaba). * gv_interface: don't crash if there is no INET interface (Toby). * camera: make ArvCamera introspection friendly (John). * evaluator: fix comparison when one of the operand is a double. * python: don't crash on access to buffer data. * build: zlib check. * build: make configure pass even if gobject introspection is not available. * updated translations (Yuri). Release 0.3.0 ============= * updated translations (Marek, Matej, Martin, Daniel, Joe, Kenneth, Rūdolfs, Andika, Balázs, Piotr, Christian, Мирослав) * make framerate work for The Imaging Source cameras (Edgar) * fix exposure setting on Point Grey Blackfly cameras * added frame rate support for The Imaging Source(TIS) cameras (Edgar) * gv_interface: strip control character from device id (Csaba) * gst_plugin: make the plugin more useful for a use inside an application (Edgar) * gst_plugin: honor do-timestamp property (Csaba) * arv-tool: fix IntReg display and don't display min/max if they are no specified * gv_device: don't send packet resend requests when this feature is not available * gv_device: implement register workaround for schema < 1.1.0 * python: crash fixes (Olivier) * viewer: fix an issue when aravis buffer row stride is not a multiple of 4 * viewer: improve framerate entry * viewer: translation update * compilation warning suppression * suppress glib deprecation warnings * add a define for BAYER_GR_12_PACKED pixel format Release 0.2.0 ============= Stable release. * camera: add support for BAYER_GB_8 images used by ImageingSource DFK 23G445 cameras (Matthias) * camera: fix consistency of order of pixel format display names (Jure) * GV: fix parsing of hexadecimal address/length with 0x prefix in Genicam file URL * build: fix parallel compilation (make -j) Release 0.1.15 ============== * library: don't distribute arvconfig.h (Nial bug report) * viewer: desktop file and application icon * viewer: rotate and flip button * viewer: file save notification * viewer: use autovideosink instead of xvimagesink, for systems without XV extension * camera: pixel format API improvments (Jure) * system: disable the fake camera by default (Kai bug report) * genicam: fix "remaining operands" issue in evaluator (Tom bug report) * genicam: fix unzipping of genicam data on not supporting unaligned memory access (Nial) * GV stream: use machine time for buffer timestamp if GevTimestampTickFrequency register does not exist * gstreamer plugin: add auto gain, auto exposure, x and y offset properties (Philipp) Release 0.1.14 ============== API breaks: arv_stream_timed_pop_buffer is now arv_stream_timeout_pop_buffer arv_camera_(get/set)_gain use a double value for gain arv_camera_get_gain_bounds also use doubles Changes: * genicam: preliminary error handling * viewer: internationalization support * viewer: widget are now insensitive if feature is not available * viewer: fix gtk requirement * viewer: add optional support for gtk3 * viewer: fix flicker on window resize * camera: use Gain feature instead of GainRaw when available * genicam: fix formula evaluation when they contain entities Release 0.1.13 ============== This release hopefufly adds support for cameras depending on IntSwissKnife for the computation of register addresses, like JAI cameras. It also allows to manually tweak the PacketSize parameter. * genicam: add support for SwissKnife in register address. * GV device: don't force the packet size. * GV device: export interface and device IP addresses (Luca). * tests: more unit tests. * build: don't require a C++ compiler by default. Release 0.1.12 ============== API warning: arv_stream_pop_buffer is now blocking. Use arv_stream_try_pop_buffer for non blocking behaviour. * genicam: support for StructReg StructEntry * stream: new pop_buffer / try_pop_buffer functions, consistent with glib async_queue API. * camera: new get/set_trigger_source functions. * tool: support for wildcard in camera name. * tool: ability to perform the same command on several cameras. * interface: new get_device_physical_id function. * genicam: rewrite on top of a DOM API. Release 0.1.11 ============== * viewer: add a snapshot button. * documentation: more information on GigE Vision protocol. * all: memory leak fixes. Release 0.1.10 ============== This release should fix the lost of connection, at least better than the last release. It also allows to instantiate a GigE Vision device using its MAC address. * device: new "control-lost" signal. * GV device: allow use of MAC address for device instantiation. * GV device: retry gvcp command if wrong packet is received. * GV device: try harder to get control register value in heartbeat thread. * GVCP: don't use 0 for packet counter value. Release 0.1.9 ============= This release fixes the lost of the connection to the device after about 65600 seconds. * GV device: don't compare guint32 and guint16. Release 0.1.8 ============= In this release, arv-show-devices is renamed to arv-tool, and gain the capability to read/write and list device features. * Build: fix headers for C++ compatibility (Andre Gaschler) * Genicam: partly implement isAvailable and isImplemented. Release 0.1.7 ============= More work on GV stream reception thread. * GV Stream: handle error GVSP packets. * GV Stream: always set packet size. * GV Stream: check for control access before creating stream object. * GV Stream: check the number of available stream channels. * GVCP: more sanity checks on ack packets. * GV Device: reliably check for control access. * Fake GV Camera: make it work with viewer. * Debug: allow different debug levels for each debug category. Release 0.1.6 ============= Work on GV stream reception reliability, with improved packet resend mechanism. * Camera: software trigger support * GV Stream: rewrite of the receiving thread * Introspection: annotation fixes * Build: make stable release parallel installable Release 0.1.5 ============= Release mostly focused on color support improvements. * GstAravis: switch to negociated width, height, framerate and pixel format * Evaluator: fix support of variable name containing digits * Buffer: add a per buffer user data * Stream: add a timed buffer pop function * Stream: switch to gobject properties for packet resend and socket buffer size * Camera: more bounds functions Release 0.1.4 ============= * Viewer: support for some color pixel formats * Viewer: support for auto gain and auto exposure * Utils: Genicam xml data dump capability added to arv-show-devices * Camera API: Fix exposure setting for Ace Basler cameras * Genicam: Fix caching of registers on read Release 0.1.3 ============= * Simple viewer based on ArvCamera API (requires gtk and gstreamer) * Add a "new-buffer" signal to ArvStream * Fix stream IP address setting for JAI Pulnix cameras (Tom Cobb) * Fix use or Aravis from a C++ application (Tom Cobb) * Fix division of integers in ArvEvaluator when asked for float result (Tom Cobb) * Add an API for retrieving the genicam data (Tom Cobb) * Fix minimum offset of ROI (Tom Cobb) * Fake camera can now simulate gain and exposure setting Release 0.1.2 ============= * Add exposure and gain settings to the gstreamer source element * fix exposure setting in ArvCamera for Basler cameras * gather running statistics for the GV devices * fix GV stream fixed buffer size * add a new arv-show-devices utility * make API more consistent with the Genicam standard Release 0.1.1 ============= * Basic ethernet camera simulator * Allow detection of ethernet cameras on lo, for the GV camera simulator * Fix in gvcp support code * More unit tests * Add support for GENICAM element * Memory leak fixes * Record statistics on buffer reception times * Improve compilation on 64 bit platforms Release 0.1.0 ============= Initial release. aravis-0.8.34/README.md000066400000000000000000000066011475431451200143630ustar00rootroot00000000000000

Aravis
Aravis

[![Aravis-Linux](https://github.com/AravisProject/aravis/actions/workflows/aravis-linux.yml/badge.svg)](https://github.com/AravisProject/aravis/actions/workflows/aravis-linux.yml) [![Aravis-macOS](https://github.com/AravisProject/aravis/actions/workflows/aravis-macos.yml/badge.svg)](https://github.com/AravisProject/aravis/actions/workflows/aravis-macos.yml) [![Aravis-MinGW](https://github.com/AravisProject/aravis/actions/workflows/aravis-mingw.yml/badge.svg)](https://github.com/AravisProject/aravis/actions/workflows/aravis-mingw.yml) [![Aravis-MSVC](https://github.com/AravisProject/aravis/actions/workflows/aravis-msvc.yml/badge.svg)](https://github.com/AravisProject/aravis/actions/workflows/aravis-msvc.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/eaa741156c2041f19b35c336aedf426c)](https://www.codacy.com/gh/AravisProject/aravis/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AravisProject/aravis&utm_campaign=Badge_Grade) ### What is Aravis ? Aravis is a glib/gobject based library for video acquisition using Genicam cameras. It currently implements the gigabit ethernet and USB3 protocols used by industrial cameras. It also provides a basic ethernet camera simulator and a simple video viewer.

Aravis is released under [LGPL-2.1-or-later](https://spdx.org/licenses/LGPL-2.1-or-later.html). ### Documentation The latest documentation is available [here](https://aravisproject.github.io/aravis). You will find how to install Aravis on Linux, macOS and Windows, how to tweak your system in order to get the best performances, and the API documentation. ### Dependencies The Aravis library depends on zlib, libxml2 and glib2, with an optional USB support depending on libusb1. The GStreamer plugin depends on GStreamer1 in addition to the Aravis library dependencies. The simple viewer depends on GStreamer1, Gtk+3 and the Aravis library dependencies. The required versions are specified in the [meson.build](https://github.com/AravisProject/aravis/blob/main/meson.build) file in Aravis sources. It is perfectly possible to only build the library, reducing the dependencies to the bare minimum. ### Contributions As an open source and free software project, we welcome any contributions to the aravis project: code, bug reports, testing... However, contributions to both Gigabit Ethernet and USB3 protocol code (files `src/arvuv*.[ch]` `src/arvgv*.[ch]`) must not be based on the corresponding specification documents published by the [A3](https://www.automate.org/vision), as this organization forbids the use of their documents for the development of an open source implementation of the specifications. So, if you want to contribute to this part of Aravis, don't use the A3 documents and state clearly in the pull request your work is not based on them. ### Links * Forum: https://aravis-project.discourse.group * Github repository: https://github.com/AravisProject/aravis * Releases: https://github.com/AravisProject/aravis/releases * Release notes: https://github.com/AravisProject/aravis/blob/master/NEWS.md * Aravis 0.8 documentation: https://aravisproject.github.io/docs/aravis-0.8/ * Genicam standard : http://www.genicam.org aravis-0.8.34/RELEASING.md000066400000000000000000000041361475431451200147400ustar00rootroot00000000000000# Release of Aravis Here are the steps to follow to create a new aravis release: * Ensure that there are no local, uncommitted/unpushed modifications. You're probably in a good state if both `git diff HEAD` and `git log master..origin/master` give no output. * Fill out an entry in the **NEWS.md** file Sift through the logs since the last release. This is most easily done with a command such as: ``` git log --stat x.y.z.. ``` where x.y.z is the previous release version. Summarize major changes briefly in a style similar to other entries in NEWS. Take special care to note any additions in the API. These should be easy to find by noting modifications to .h files in the log command above. * Verify that the code passes `ninja dist` Running `ninja dist` should result in no warnings or errors and end with a message of the form: ``` Distribution package /home/pacaud/Sources/aravis/build/meson-dist/aravis-0.7.2.tar.xz tested ``` (But the tar file isn't actually ready yet, as we still have some more steps to follow). * Tag the release; This will allow you to make a branch later if you so desire but for now, at least it make it easy to see what was included in a particular release. The commit message for the tag will be included in a changes file in the release directory. ``` $ git tag -a x.y.z (from whatever branch you're releasing) $ git push origin x.y.z ``` If that fails because someone has pushed since you last updated, then you'll need to repeat the entire process. Well, update, add a new NEWS entry, and ninja dist again. * Upload the tarball to github. * Increment the version number in `meson.build`, and push the change. * Create a new topic on https://aravis-project.discourse.org to announce the new release adding the excerpt from NEWS, your signature, followed by the standard "What is aravis" and "Where to get more information about aravis" blurbs from README, and finally the shortlog of all changes since last release, generated by: ``` git shortlog ARAVIS_X_Y_Z... ``` where ARAVIS_X_Y_Z is the last released version. aravis-0.8.34/aravis.doap000066400000000000000000000015651475431451200152420ustar00rootroot00000000000000 Aravis Acquisition library for industrial cameras C Emmanuel Pacaud aravis-0.8.34/docs/000077500000000000000000000000001475431451200140315ustar00rootroot00000000000000aravis-0.8.34/docs/arv-camera-test-0.8.1000066400000000000000000000053071475431451200174160ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .TH ARV-CAMERA-TEST-0.8 "1" "dcembre 2022" "arv-camera-test-0.8 0.8.23" "User Commands" .SH NAME arv-camera-test-0.8 \- manual page for arv-camera-test-0.8 0.8.23 .SH DESCRIPTION .SS "Usage:" .IP arv\-camera\-test\-0.8 [OPTION?] .PP Small utility for basic device checks. .SS "Help Options:" .TP \-?, \fB\-\-help\fR Show help options .SS "Application Options:" .TP \fB\-n\fR, \fB\-\-name=\fR Camera name .TP \fB\-f\fR, \fB\-\-frequency=\fR Acquisition frequency .TP \fB\-t\fR, \fB\-\-trigger=\fR External trigger .TP \fB\-o\fR, \fB\-\-software\-trigger=\fR Emit software trigger .TP \fB\-w\fR, \fB\-\-width=\fR Width .TP \fB\-h\fR, \fB\-\-height=\fR Height .TP \fB\-\-h\-binning=\fR Horizontal binning .TP \fB\-\-v\-binning=\fR Vertical binning .TP \fB\-e\fR, \fB\-\-exposure=\fR Exposure time .TP \fB\-g\fR, \fB\-\-gain=\fR Gain (dB) .TP \fB\-a\fR, \fB\-\-auto\fR Auto socket buffer size .TP \fB\-\-features\fR Additional configuration as a space separated list of features .TP \fB\-j\fR, \fB\-\-packet\-size\-adjustment=\fR{never|always|once|on\-failure|on\-failure\-once} Packet size adjustment .TP \fB\-r\fR, \fB\-\-no\-packet\-resend\fR No packet resend .TP \fB\-q\fR, \fB\-\-packet\-request\-ratio\fR=\fI\,[0\/\fR..2.0] Packet resend request limit as a frame packet ratio .TP \fB\-l\fR, \fB\-\-initial\-packet\-timeout=\fR Initial packet timeout .TP \fB\-p\fR, \fB\-\-packet\-timeout=\fR Packet timeout .TP \fB\-m\fR, \fB\-\-frame\-retention=\fR Frame retention .TP \fB\-c\fR, \fB\-\-gv\-stream\-channel=\fR GigEVision stream channel id .TP \fB\-y\fR, \fB\-\-gv\-packet\-delay=\fR GigEVision packet delay .TP \fB\-i\fR, \fB\-\-gv\-packet\-size=\fR GigEVision packet size .TP \fB\-s\fR, \fB\-\-usb\-mode=\fR{sync|async} USB device I/O mode .TP \fB\-u\fR, \fB\-\-chunks=\fR[,[...]] Chunks .TP \fB\-\-realtime\fR Make stream thread realtime .TP \fB\-\-high\-priority\fR Make stream thread high priority .TP \fB\-\-no\-packet\-socket\fR Disable use of packet socket .TP \fB\-\-register\-cache=\fR{disable|enable|debug} Register cache policy .TP \fB\-\-range\-check=\fR{disable|enable|debug} Range check policy .TP \fB\-\-access\-check=\fR{disable|enable} Feature access check policy .TP \fB\-b\fR, \fB\-\-bandwidth\-limit=\fR Desired USB3 Vision device bandwidth limit .TP \fB\-\-duration=\fR Test duration (s) .TP \fB\-d\fR, \fB\-\-debug=\fR{[:][,...]|help} Debug output selection .TP \fB\-v\fR, \fB\-\-version\fR Show version .PP This tool configures a camera and starts video streaming, infinitely unless a duration is given. aravis-0.8.34/docs/arv-test-0.8.1000066400000000000000000000024411475431451200161640ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .TH ARV-TEST-0.8 "1" "dcembre 2022" "arv-test-0.8 0.8.23" "User Commands" .SH NAME arv-test-0.8 \- manual page for arv-test-0.8 0.8.23 .SH DESCRIPTION .SS "Usage:" .IP arv\-test\-0.8 [OPTION?] .PP Automated test utillity. .SS "Help Options:" .TP \fB\-h\fR, \fB\-\-help\fR Show help options .SS "Application Options:" .TP \fB\-n\fR, \fB\-\-name=\fR Device selection .TP \fB\-t\fR, \fB\-\-test=\fR Test selection .TP \fB\-c\fR, \fB\-\-configuration=\fR Alternative configuration .TP \fB\-i\fR, \fB\-\-iterations=\fR Number of test repetitions .TP \fB\-s\fR, \fB\-\-usb\-mode=\fR{sync|async} USB device I/O mode .TP \fB\-a\fR, \fB\-\-cache\-check\fR Register cache check .TP \fB\-\-packet\-socket\fR Enable use of packet socket .HP \fB\-d\fR, \fB\-\-debug=\fR{[:][,...]|help} .TP \fB\-v\fR, \fB\-\-version\fR Show version .PP arv\-test is an automated test utility that tries to exercise most of the Aravis functionalities. By default it runs all the tests on all the detected devices, but devices and tests can be selected using a glob pattern. .PP A default configuration file is bundled in the executable. An alternative one with entries specific to the camera you want to test can be specified. aravis-0.8.34/docs/arv-tool-0.8.1000066400000000000000000000033771475431451200161730ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .TH ARV-TOOL-0.8 "1" "dcembre 2022" "arv-tool-0.8 0.8.23" "User Commands" .SH NAME arv-tool-0.8 \- manual page for arv-tool-0.8 0.8.23 .SH DESCRIPTION .SS "Usage:" .TP arv\-tool\-0.8 [OPTION?] command .PP Small utility for basic control of a Genicam device. .SS "Help Options:" .TP \fB\-h\fR, \fB\-\-help\fR Show help options .SS "Application Options:" .HP \fB\-n\fR, \fB\-\-name=\fR .HP \fB\-a\fR, \fB\-\-address=\fR .TP \fB\-\-register\-cache=\fR{disable|enable|debug} Register cache policy .TP \fB\-\-range\-check=\fR{disable|enable|debug} Range check policy .TP \fB\-\-access\-check=\fR{disable|enable} Feature access check policy .TP \fB\-t\fR, \fB\-\-time\fR Show execution time .HP \fB\-d\fR, \fB\-\-debug=\fR{[:][,...]|help} .TP \fB\-v\fR, \fB\-\-version\fR Show version .PP Command may be one of the following possibilities: .TP genicam: dump the content of the Genicam xml data .TP features: list all features .TP values: list all available feature values .TP description [] ...: show the full feature description .TP control [=] ...: read/write device features .TP network [=]...: read/write network settings .PP If no command is given, this utility will list all the available devices. For the control command, direct access to device registers is provided using a R[address] syntax in place of a feature name. .SH EXAMPLES arv\-tool\-0.8 control Width=128 Height=128 Gain R[0x10000]=0x10 arv\-tool\-0.8 features arv\-tool\-0.8 description Width Height arv\-tool\-0.8 network mode=PersistentIP arv\-tool\-0.8 network ip=192.168.0.1 mask=255.255.255.0 gateway=192.168.0.254 arv\-tool\-0.8 \-n Basler\-210ab4 genicam aravis-0.8.34/docs/arv-viewer-0.8.1000066400000000000000000000023051475431451200165050ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .TH ARV-VIEWER-0.8 "1" "dcembre 2022" "arv-viewer-0.8 0.8.23" "User Commands" .SH NAME arv-viewer-0.8 \- manual page for arv-viewer-0.8 0.8.23 .SH DESCRIPTION .SS "Usage:" .IP arv\-viewer\-0.8 [OPTION\&.] .SS "Help Options:" .TP \fB\-h\fR, \fB\-\-help\fR Show help options .TP \fB\-\-help\-all\fR Show all help options .TP \fB\-\-help\-gtk\fR Show GTK+ Options .TP \fB\-\-help\-gst\fR Afficher les options de GStreamer .SS "Application Options:" .TP \fB\-a\fR, \fB\-\-auto\-buffer\-size\fR Auto socket buffer size .TP \fB\-r\fR, \fB\-\-no\-packet\-resend\fR No packet resend .TP \fB\-l\fR, \fB\-\-initial\-packet\-timeout\fR Initial packet timeout (ms) .TP \fB\-p\fR, \fB\-\-packet\-timeout\fR Packet timeout (ms) .TP \fB\-m\fR, \fB\-\-frame\-retention\fR Frame retention (ms) .TP \fB\-\-register\-cache=\fR{disable|enable|debug} Register cache policy .TP \fB\-\-range\-check=\fR{disable|enable} Range check policy .TP \fB\-s\fR, \fB\-\-usb\-mode=\fR{sync|async} USB device I/O mode .HP \fB\-d\fR, \fB\-\-debug=\fR{[:][,...]|help} .TP \fB\-v\fR, \fB\-\-version\fR Show version .TP \fB\-\-display\fR=\fI\,DISPLAY\/\fR X display to use aravis-0.8.34/docs/meson.build000066400000000000000000000002741475431451200161760ustar00rootroot00000000000000install_man ('arv-tool-0.8.1') install_man ('arv-test-0.8.1') install_man ('arv-camera-test-0.8.1') if viewer_enabled install_man ('arv-viewer-0.8.1') endif subdir ('reference/aravis') aravis-0.8.34/docs/reference/000077500000000000000000000000001475431451200157675ustar00rootroot00000000000000aravis-0.8.34/docs/reference/aravis/000077500000000000000000000000001475431451200172545ustar00rootroot00000000000000aravis-0.8.34/docs/reference/aravis/aravis.toml.in000066400000000000000000000024731475431451200220510ustar00rootroot00000000000000[library] version = "@version@" browse_url = "https://github.com/AravisProject/aravis" repository_url = "https://github.com/AravisProject/aravis.git" website_url = "https://github.com/AravisProject/aravis" docs_url = "https://aravisproject.github.io/docs/aravis-0.8/" authors = "Aravis Project Team" logo_url = "aravis.svg" license = "LGPL-2.1-or-later" description = "The Aravis Vision Library" dependencies = [ ] devhelp = true search_index = true [dependencies."GLib-2.0"] name = "GLib" description = "Portable utility library" docs_url = "https://docs.gtk.org/glib/" [dependencies."GObject-2.0"] name = "GObject" description = "The base type system library" docs_url = "https://docs.gtk.org/gobject/" [dependencies."GIO-2.0"] name = "GIO" description = "GObject Interfaces and Objects, Networking, IPC, and I/O" docs_url = "https://docs.gtk.org/gio/" [theme] name = "basic" show_index_summary = true show_class_hierarchy = true [source-location] base_url = "https://github.com/AravisProject/aravis/blob/main/" [extra] # The same order will be used when generating the index content_files = [ 'introduction.md', 'building.md', 'examples.md', 'utilities.md', 'ethernet.md', 'usb.md', 'thread-safety.md', 'porting-0.8.md', 'tests.md' ] content_images = [ '../../../viewer/icons/src/aravis.svg' ] urlmap_file = "urlmap.js" aravis-0.8.34/docs/reference/aravis/building.md000066400000000000000000000135401475431451200213760ustar00rootroot00000000000000Title: Installation and Debug # Installing Aravis Aravis uses the meson build system ( http://mesonbuild.com/ ). After you have downloaded the latest release from [https://github.com/AravisProject/aravis/releases](https://github.com/AravisProject/aravis/releases), you can build and install Aravis like [any other meson project](http://mesonbuild.com/Quick-guide.html#compiling-a-meson-project): ```sh meson setup build cd build ninja ninja install ``` The build can be configured at any time using `meson configure` in the build directory. `meson configure` invoked without any other argument will show the configuration options. On some platforms (like Ubuntu), you may have to configure the dynamic linker (ld) to let it know where the aravis libraries are installed, and run ldconfig as root in order to update ld cache. ```sh sudo ldconfig ``` ## Release versions Please use the released versions of Aravis and not a git checkout, unless you want to work on Aravis itself or test a commit. The current version naming follows a major.minor.micro scheme. Odd minor values indicates development releases, and even minor values stable releases. Prior to 1.0.0, stable releases with different minor are not compatible, and are parallel installable, with executables suffixed with `-major.minor`. ## Install dependencies on Ubuntu 20.04 Prior to running `meson` and `ninja`, dependencies can be installed using the following(tested on Ubuntu 20.04): ```sh sudo apt install libxml2-dev libglib2.0-dev cmake libusb-1.0-0-dev gobject-introspection \ libgtk-3-dev gtk-doc-tools xsltproc libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev \ libgirepository1.0-dev gettext ``` ## Install dependencies on Fedora 34/35 Due to differences in the deb and rpm package ecosystems dependencies can be installed on Fedora (tested on 34 and 35) with: ```sh sudo dnf install libxml2-devel glib2-devel cmake libusb1-devel gobject-introspection \ gobject-introspection-devel gstreamer1-plugins-base-devel gtk3-devel \ gtk-doc libxslt gstreamer1-devel gstreamer1-plugins-good python3-gobject \ g++ meson gettext ``` ## Building on macOS Using the GNU build system on macOS is not directly supported, but can be mimicked by augmenting the install procedure above with some environment settings (tested on macOS Catalina): ```sh brew install gettext intltool gtk-doc libxml2 meson libusb meson setup build ninja -C build ``` If you want to be able to build the viewer, you have to install some additional packages: ```sh brew install gtk+3 gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad libnotify gnome-icon-theme meson configure -Dviewer=enabled build ``` ## Building on Windows [MSYS2](https://msys2.org) provides native [Aravis packages](https://packages.msys2.org/base/mingw-w64-aravis). The package includes the DLL, headers and utilities (including the viewer). To build Aravis by yourself, install MSYS2 and enter the mingw64 shell. Refer to the [mingw CI configuration file](https://github.com/AravisProject/aravis/blob/main/.github/workflows/aravis-mingw.yml) for list of dependencies (such as `mingw-w64-x86_64-libxml2` and so on) which must be installed prior to building via `pacman -S ...`. The build process itself is the same as on other platforms (meson/ninja). Alternatively, you can build Aravis using Microsoft Visual C++ (MSVC) and Conan package manager. Have a look at the [msvc CI configuration file](https://github.com/AravisProject/aravis/blob/main/.github/workflows/aravis-msvc.yml). ### Cross-compilation for Windows Aravis for Windows can be also cross-compiled on Linux (and used in Wine) using [crossroad](https://pypi.org/project/crossroad/), provided that cross-compiler and native build tools (`sudo apt install gcc-mingw-w64-x86-64 meson ninja=build` on Debian/Ubuntu) are installed: ```sh # note: use the git version, not the one from pypi pip3 install --user git+git://git.tuxfamily.org/gitroot/crossroad/crossroad.git # create cross-compilation environment with architecture "w64" called "aravis" crossroad w64 aravis # install packages required for compilation; crossroad adds the mingw-w64_x86_64- prefix automatically crossroad install libnotify gstreamer gst-plugins-good gst-plugins-bad gst-plugins-bad gobject-introspection libusb gtk3 libxml2 zlib # clone aravis sources git clone https://github.com/AravisProject/aravis cd aravis # configure, crossroad adjusts meson for cross-compilation; build directory is created crossroad meson setup build # compile and install ninja -C build install ``` # Debugging Aravis The `ARV_DEBUG` environment variable can be set to a comma separated list of debugging categories, which will make Aravis print out different types of debugging informations to the console. A debug level can also be specified, using a number from 0 (none) to 4 (trace) separated from the category name by a colon. For example, the following command before running an Aravis based application will make Aravis print out all stream and device related informations: ``` export ARV_DEBUG=stream:3,device:3 ``` Available debug level and categories are: ``` Debug categories: interface : Device lookup for each supported protocol device : Device control stream : Video stream management stream-thread : Video stream thread (likely high volume output) cp : Control protocol packets sp : Stream protocol packets (likely high volume output) genicam : Genicam specialized DOM elements policies : Genicam runtime configurable policies chunk : Chunk data code dom : Genicam DOM document evaluator : Expression evaluator viewer : Simple viewer application misc : Miscellaneous code all : Everything Debug levels: 0: none 1: warning 2: info 3: debug 4: trace ``` aravis-0.8.34/docs/reference/aravis/ethernet.md000066400000000000000000000114671475431451200214250ustar00rootroot00000000000000Title: Ethernet Devices # Ethernet Device Performance ## Network Layout If you face a lots of lost packets, the first thing to try is to directly connect the device to your machine, using a wired connection. If it is possible, use a dedicated port. Most of the cameras are able to saturate their gigabit link, so any additional traffic may lead to packet lost. If your machine ethernet port has a lower bandwidth than the camera, make sure the stream you are requesting will not exceed its bandwidth. ## Socket Buffer Size Under heavy CPU load, the input socket buffer size may be increased in order to avoid lost packets because of a full buffer. You can try the `--auto` parameter of `arv-camera-test`. In you application, this can be tweaked using `g_object_set()` on the stream object. For example, this code, to be called before [method@Aravis.Camera.start_acquisition], will enable the auto buffer size mode: ```c g_object_set (stream, "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO, "socket-buffer-size", 0, NULL); ``` ## Receiving Thread Priority It is possible to increase the receiving thread priority. You can experiment with thread priority using the `--realtime` or `--high-priority` options of `arv-camera-test`. In your code, stream thread priority can be changed in the stream buffer callback: ```c static void stream_cb (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer) { if (type == ARV_STREAM_CALLBACK_TYPE_INIT) { if (!arv_make_thread_realtime (10)) printf ("Failed to make stream thread realtime\n"); } } ``` or: ```c static void stream_cb (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer) { if (type == ARV_STREAM_CALLBACK_TYPE_INIT) { if (!arv_make_thread_high_priority (-10)) printf ("Failed to make stream thread high priority\n"); } } ``` ## Stream Packet Size One way to increase streaming performance and lower the CPU use is to increase the stream packet size. [method@Aravis.Camera.gv_set_packet_size] and [method@Aravis.Camera.gv_auto_packet_size] allow you to change this parameter. By default, the network adapter of your machine will probably not let you receive packet bigger than 1500 bytes, which is the default Maximum Transfer Unit (MTU). It means if you want to use big packets, you also have to increase the network adapter MTU to a greater walue (8192 bytes being a recommended value). The exact procedure to change the MTU depends on the distribution you are using. Please refer to the administrator manual of your distribution. On Fedora, MTU can be changed using the network setting panel. Edit the wired network settings. MTU parameter is in the Identity page. On Debian / Ubuntu you can add a line "mtu 8192" to /etc/network/interfaces under the correct device. ``` iface ethusb0 inet static address 192.168.45.1 netmask 255.255.255.0 broadcast 192.168.45.255 mtu 8192 ``` Please note if your device is not connected directly to the machine, you may also have to tweak the active devices on your network. ## Packet Socket Support Aravis can use packet sockets for the video receiving thread. But this mode requires extended capabilities. If you want to allow your application to use packet socket, you must set the `cap_net_raw` capability using `setcap`. For example, the following command gives this capability to the Aravis viewer: ``` sudo setcap cap_net_raw+ep arv-viewer ``` # Legacy endianess mechanism Some GigEVision devices incorrectly report a Genicam schema version greater or equal to 1.1, while implementing the legacy behavior for register access. We maintain a list in [arvgcport.c](https://github.com/AravisProject/aravis/blob/6f1d65608dcecef2326ae2b3a542f5f59771ea32/src/arvgcport.c#L44) which allows to force the use of the legacy endianness mechanism. The documentation about the legacy endianness mechanism is in the 3.1 appendix ('Endianess of GigE Vision Cameras') of the GenICam Standard. There is a chance this part of Aravis is due to a misunderstanding of how a GigEVision device is supposed to behave (Remember we can not use the GigEVision specification documentation). But until now, there was no evidence in the issue reports it is the case. If you think this should be implemented differently, don't hesitate to explain your thoughts on Aravis issue report system, or even better, to open a pull request. The related Aravis issues are available here: [https://github.com/AravisProject/aravis/labels/5. Genicam 1.0 legacy mode](https://github.com/AravisProject/aravis/labels/5.%20Genicam%201.0%20legacy%20mode). Meanwhile, if you want to add a device to the exception list, please open an issue on github, giving the vendor name and model name as found in the Genicam data of your device, using the `genicam` command of `arv-tool`. They are stored in the ModelName and VendorName attributes of the RegisterDescription element. aravis-0.8.34/docs/reference/aravis/examples.md000066400000000000000000000006261475431451200214200ustar00rootroot00000000000000Title: Examples # Examples While most of the API is documented, Aravis documentation lacks some good tutorial about the many features if offers. But a good resource is the `tests` directory inside Aravis sources, where you will find a set of small samples showing different key features. Additionally, there is [an example repository with C samples](https://github.com/AravisProject/aravis-c-examples). aravis-0.8.34/docs/reference/aravis/gv.md000066400000000000000000000100351475431451200202110ustar00rootroot00000000000000 Ethernet protocol (GV) Introduction This part contains a description of the protocol implemented by Aravis for the control of the gigabit ethenet cameras. This protocol is a standard used by many video cameras manufacturers for their products. It is a closed standard which should not be confused by what is used on 'IP cameras'. In the case of 'IP cameras' the video stream is a compressed stream (jpeg, h264...) send over HTTP or RTSP, whereas in Aravis, the video stream is a raw stream, mainly targeted to industrial applications (though the protocol supports non raw payload, it is not yet implemented in Aravis). The protocol implemented by Aravis library is based on UDP packets transmited over an ethernet link. Two different types of packets are used: GVCP packets and GVSP packets. GVCP packets are used for the control of the devices, and GVSP for the transmission of the data stream. This documentation is the result of a reverse engineering, which means it is far from complete. A more complete description of the ethernet protocol can be found in the GVSP and GVCP dissectors found in Wireshark sources. GVCP packets Devices are waiting for GVCP packets listening to the GVCP port (3956). When the client sends a command GVCP packet to the device, the device sends back an acknowledge packets in return. Each command/acknowledge pair is identified by a 16 bit value, which allows to check if an acknowledge packet corresponds to the previsoulsy sent command. 0 is an error value for this identifier. If an acknowledge is not received after a given timeout period, another command packet is sent, until a maximum number of retries is reached. The content of a GVCP packet is composed by a 64 bit headers, followed or not by a data byte array, depending on the GVCP packet type. Multibyte values are in big endian encoding. Discovery A device discovery mechanism is implemented using a discovery GVCP packet, broadcasted by the client to the GVCP port. Each available device responds to this discovery packet with an acknowledge packet, containing a description of the device (device name, model name, manufacturer name, MAC Address...). Read and write register These packets are used for the read and write of 32 bit registers, accessed using 32 bit adresses. For the read command, a list of addresses is sent ot the client, which returns a list of 32 bit values. For the write command, a list of address/value pairs is sent to the client, which returns a list of 32 bit values. Address and data are encoded in the packet as big endian values. Read and write memory Write memory packet data area consists in a 32 bit address, followed by the data to write. Client returns an acknowledge packet with the target address. For read memory command, an address/size pair is sent to the device, which returns the content of the given memory area. GVSP packets GVSP packets are sent by the device for the video stream transmission. Depending on the device, it is possible to have more than one stream simultaneously, each directed to a different client port. Stream is splitted in frames. For each frame, identified by a frame id, three different packets are used: a leader packet, one or more data packets, and a trailer packet. Each packet in a frame is identified by a packet id, starting from 0. In the video stream reception code, client must take care to check if all frame packets are present, and ask for packet resend for missing ones, using a GVCP packet resend command. aravis-0.8.34/docs/reference/aravis/introduction.md000066400000000000000000000015371475431451200223250ustar00rootroot00000000000000Title: Introduction # What is Aravis ? Aravis is a GObject based library for the control and the video stream acquisition of digital cameras. While using GenICam xml files for the description of the camera registers, it does not try to be a complete implementation of the Genicam API as described in the GenAPI GenICam document. Nevertheless, a good knowledge of the [GenICam](http" url="http://www.genicam.org) standard should help to understand how Aravis operates. Aravis currently provides an implementation of the gigabit ethernet and USB3 protocols found in a lot of ethernet industrial cameras. [class@Aravis.Camera] is a simple API for easy access of standard camera features. [class@Aravis.Device] and [class@Aravis.Gc] are more low level APIs, which enable the full control of the cameras, allowing the use of the features specific to each model. aravis-0.8.34/docs/reference/aravis/meson.build000066400000000000000000000021701475431451200214160ustar00rootroot00000000000000toml_conf = configuration_data() toml_conf.set('version', meson.project_version()) gidocgen = find_program('gi-docgen') gidocgen_common_args = [ '--quiet', '--no-namespace-dir', ] docs_dir = get_option('datadir') / 'doc' expand_content_md_files = [ 'introduction.md', 'building.md', 'examples.md', 'utilities.md', 'ethernet.md', 'usb.md', 'thread-safety.md', 'porting-0.8.md', 'tests.md' ] aravis_toml=configure_file( input: 'aravis.toml.in', output: 'aravis.toml', configuration: toml_conf, install: true, install_dir: docs_dir / 'aravis-@0@'.format(aravis_api_version) ) custom_target( 'aravis-doc', input: [ aravis_toml, aravis_gir[0] ], output: 'aravis-@0@'.format(aravis_api_version), command: [ gidocgen, 'generate', gidocgen_common_args, '--add-include-path=@0@'.format(meson.current_build_dir() / '../../../src'), '--config=@INPUT0@', '--output-dir=@OUTPUT@', '--content-dir=@0@'.format(meson.current_source_dir()), '@INPUT1@' ], depend_files: [ expand_content_md_files ], build_by_default: true, install: true, install_dir: docs_dir ) aravis-0.8.34/docs/reference/aravis/porting-0.8.md000066400000000000000000000023071475431451200215650ustar00rootroot00000000000000Title: Porting to Aravis 0.8 ### Porting to Aravis 0.8 Aravis 0.8 has seen a major rewrite of how communication errors are handled. Instead of relying on a status API, each function that can fail has an additional error parameter now. This is the standard way of handling error in the glib ecosytem. The nice side effect is now errors throw exceptions in bindings where the language support them (rust, python, javascript). A quick port from the older series to 0.8 series is just a matter of adding a NULL parameter to most of the modified functions. But you are advised to take this opportunity to correctly handle errors. There is a page explaining Glib errors and how to manage them in the [Glib documentation](https://docs.gtk.org/glib/error-reporting.html). During the camera configuration, in C language it can be somehow cumbersome to check for errors at each function call. A convenient way to deal with this issue is the following construction: ```c GError **error = NULL; if (!error) arv_camera_... (..., &error); if (!error) arv_camera_... (..., &error); if (!error) arv_camera_... (..., &error); if (!error) arv_camera_... (..., &error); if (error) { handle error here; g_clear_error (&error); } ``` aravis-0.8.34/docs/reference/aravis/tests.md000066400000000000000000000011261475431451200207400ustar00rootroot00000000000000Title: Unit Tests # Unit Tests Aravis has a set of unit tests that helps to catch regressions and memory leaks during the development. The test suite is run using the following commands: ```sh ninja test ``` The is a small helper script that run the same tests under valgrind memmory checker ```sh ../tests/valgrind-memcheck ``` All the code is not covered yet by the tests. Code coverage can be obtained using: ```sh meson configure -Db_coverage=true ninja coverage ``` The report is published in `build/meson-logs/coveragereport/index.html`. Help on code coverage improvement is welcome. aravis-0.8.34/docs/reference/aravis/thread-safety.md000066400000000000000000000016111475431451200223350ustar00rootroot00000000000000Title: Thread Safety # Thread Safety Aravis is not thread safe, which means one can not use the same object simultaneously from different threads, without using mutexes. But it is perfectly fine to use different aravis objects in different threads, with the exception of the [class@Aravis.Interface] instances. A possible trap is that glib signal callbacks are called from the thread that emitted the corresponding signal. For example, the [signal@Aravis.Stream::new-buffer] callback is emitted from the stream packet receiving thread, which is an internal thread created by [class@Aravis.GvStream]. It is not safe to use [class@Aravis.Device] and some [class@Aravis.Stream] functions from this callback without using mutexes. The exceptions are [method@Aravis.Stream.push_buffer], [method@Aravis.Stream.pop_buffer], [method@Aravis.Stream.try_pop_buffer] and [method@Aravis.Stream.timeout_pop_buffer]. aravis-0.8.34/docs/reference/aravis/urlmap.js000066400000000000000000000002351475431451200211120ustar00rootroot00000000000000baseURLs = [ [ 'GLib', 'https://docs.gtk.org/glib/' ], [ 'GObject', 'https://docs.gtk.org/gobject/' ], [ 'Gio', 'https://docs.gtk.org/gio/' ], ] aravis-0.8.34/docs/reference/aravis/usb.md000066400000000000000000000027041475431451200203720ustar00rootroot00000000000000Title: USB Devices # USB ## Permissions By default, USB devices permissions may not be sufficient to allow any user to access the USB3 cameras. These permissions can be changed by using an udev rule file. There is a file example in [Aravis sources](https://github.com/AravisProject/aravis/blob/main/src/aravis.rules). This file must be placed in `/etc/udev/rules.d` directory (The exact location may depend on the distribution you are using). This file only contains declarations for a couple of vendors. If you want to add an entry with the vendor of your camera, the output of `lsusb` command will give you the vendor id, which is the first 4 digits of the ID field. Alternatively, you can give read/write access to all USB3Vision devices using the following rule: ``` # Read write access for all USB3Vision devices SUBSYSTEM=="usb", ATTRS{bDeviceClass}=="ef", ATTRS{bDeviceSubClass}=="02", ATTRS{bDeviceProtocol}=="01", ENV{ID_USB_INTERFACES}=="*:ef0500:*", MODE="0666" ``` ## Performance Aravis uses by default the synchronous libusb API. But it can be told to use the asynchronous API for better performances, especially on embedded platform like RapsberryPi or Nvidia Jetson boards. The function to use is [method@Aravis.Camera.uv_set_usb_mode]. `arv-viewer` and `arv-camera-test` can use the asynchronous API if `usb-mode` option is set to `async`. Similarly, the GStreamer plugin is using the asynchronous API if `usb-mode` property is set to `async`. aravis-0.8.34/docs/reference/aravis/utilities.md000066400000000000000000000007241475431451200216140ustar00rootroot00000000000000Title:Utilities # Utilities The main goal of Aravis is to provide a library that interfaces with industrial cameras. It also provides a set of utilities that help to debug the library, namely `arv-viewer-0.8`, `arv-tool-0.8`, `arv-camera-test-0.8` and `arv-fake-gv-camera-0.8`. The version suffix corresponds to the API version, as several stable series of Aravis can be installed at the same time. The options for each utility is obtained using `--help` argument. aravis-0.8.34/gst/000077500000000000000000000000001475431451200136765ustar00rootroot00000000000000aravis-0.8.34/gst/.gitignore000066400000000000000000000000001475431451200156540ustar00rootroot00000000000000aravis-0.8.34/gst/gst-aravis-inspect.in000077500000000000000000000001061475431451200177510ustar00rootroot00000000000000#!/bin/sh gst-inspect-1.0 --gst-plugin-load=@GST_PLUGIN_FILENAME@ $* aravis-0.8.34/gst/gst-aravis-launch.in000077500000000000000000000001111475431451200175520ustar00rootroot00000000000000#!/bin/sh gst-launch-1.0 --gst-plugin-load=@GST_PLUGIN_FILENAME@ -v $* aravis-0.8.34/gst/gstaravis.c000066400000000000000000001150551475431451200160540ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2006 Eric Jonas * Copyright © 2006 Antoine Tremblay * Copyright © 2010 United States Government, Joshua M. Doe * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION:element-aravissrc * * Source using the Aravis vision library * * * Example launch line * |[ * gst-launch -v aravissrc ! video/x-raw,format=UYVY,width=512,height=512,framerate=25/1 ! autovideosink * ]| * */ #include #include #include #include #include /* TODO: Add l10n */ #define _(x) (x) #define GST_ARAVIS_DEFAULT_N_BUFFERS 50 #define GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT 2000000 GST_DEBUG_CATEGORY_STATIC (aravis_debug); #define GST_CAT_DEFAULT aravis_debug enum { PROP_CAMERA_NAME = 1, PROP_CAMERA, PROP_GAIN, PROP_GAIN_AUTO, PROP_EXPOSURE, PROP_EXPOSURE_AUTO, PROP_H_BINNING, PROP_V_BINNING, PROP_OFFSET_X, PROP_OFFSET_Y, PROP_PACKET_DELAY, PROP_PACKET_SIZE, PROP_AUTO_PACKET_SIZE, PROP_PACKET_RESEND, PROP_FEATURES, PROP_NUM_ARV_BUFFERS, PROP_USB_MODE, PROP_STREAM, PROP_TRIGGER, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES]; enum { /* actions */ SIGNAL_SOFTWARE_TRIGGER, LAST_SIGNAL }; static guint gst_aravis_signals[LAST_SIGNAL] = { 0 }; #define GST_TYPE_ARV_AUTO (gst_arv_auto_get_type()) static GType gst_arv_auto_get_type (void) { static GType arv_auto_type = 0; static const GEnumValue arv_autos[] = { {ARV_AUTO_OFF, "Off", "off"}, {ARV_AUTO_ONCE, "Once", "once"}, {ARV_AUTO_CONTINUOUS, "Continuous", "on"}, {0, NULL, NULL}, }; if (!arv_auto_type) { arv_auto_type = g_enum_register_static("GstArvAuto", arv_autos); } return arv_auto_type; } #define GST_TYPE_ARV_USB_MODE (gst_arv_usb_mode_get_type()) static GType gst_arv_usb_mode_get_type (void) { static GType arv_usb_mode_type = 0; static const GEnumValue arv_usb_modes[] = { {ARV_UV_USB_MODE_SYNC, "Synchronous", "sync"}, {ARV_UV_USB_MODE_ASYNC, "Asynchronous", "async"}, {ARV_UV_USB_MODE_DEFAULT, "Default", "default"}, {0, NULL, NULL}, }; if (!arv_usb_mode_type) { arv_usb_mode_type = g_enum_register_static("GstArvUsbMode", arv_usb_modes); } return arv_usb_mode_type; } G_DEFINE_TYPE (GstAravis, gst_aravis, GST_TYPE_PUSH_SRC); static GstStaticPadTemplate aravis_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("ANY")); static GstCaps * gst_aravis_get_all_camera_caps (GstAravis *gst_aravis, GError **error) { GError *local_error = NULL; GstCaps *caps; gint64 *pixel_formats = NULL; double min_frame_rate, max_frame_rate; int min_height, min_width; int max_height, max_width; unsigned int n_pixel_formats, i; int min_frame_rate_numerator; int min_frame_rate_denominator; int max_frame_rate_numerator; int max_frame_rate_denominator; gboolean use_frame_rate; g_return_val_if_fail (GST_IS_ARAVIS (gst_aravis), NULL); if (!ARV_IS_CAMERA (gst_aravis->camera)) return NULL; GST_LOG_OBJECT (gst_aravis, "Get all camera caps"); arv_camera_get_width_bounds (gst_aravis->camera, &min_width, &max_width, &local_error); if (!local_error) arv_camera_get_height_bounds (gst_aravis->camera, &min_height, &max_height, &local_error); if (!local_error) pixel_formats = arv_camera_dup_available_pixel_formats (gst_aravis->camera, &n_pixel_formats, &local_error); use_frame_rate = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL) && gst_aravis->trigger_source == NULL; if (use_frame_rate) { if (!local_error) arv_camera_get_frame_rate_bounds (gst_aravis->camera, &min_frame_rate, &max_frame_rate, &local_error); if (!local_error) { gst_util_double_to_fraction (min_frame_rate, &min_frame_rate_numerator, &min_frame_rate_denominator); gst_util_double_to_fraction (max_frame_rate, &max_frame_rate_numerator, &max_frame_rate_denominator); } } if (local_error) { g_propagate_error (error, local_error); return NULL; } caps = gst_caps_new_empty (); for (i = 0; i < n_pixel_formats; i++) { const char *caps_string; caps_string = arv_pixel_format_to_gst_caps_string (pixel_formats[i]); if (caps_string != NULL) { GstStructure *structure; structure = gst_structure_from_string (caps_string, NULL); gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, min_width, max_width, "height", GST_TYPE_INT_RANGE, min_height, max_height, NULL); if (use_frame_rate) gst_structure_set (structure, "framerate", GST_TYPE_FRACTION_RANGE, min_frame_rate_numerator, min_frame_rate_denominator, max_frame_rate_numerator, max_frame_rate_denominator, NULL); gst_caps_append_structure (caps, structure); } } g_free (pixel_formats); return caps; } static GstCaps * gst_aravis_get_caps (GstBaseSrc * src, GstCaps * filter) { GstAravis* gst_aravis = GST_ARAVIS(src); GstCaps *caps; GST_OBJECT_LOCK (gst_aravis); if (gst_aravis->all_caps != NULL) caps = gst_caps_copy (gst_aravis->all_caps); else caps = gst_caps_new_any (); GST_OBJECT_UNLOCK (gst_aravis); GST_LOG_OBJECT (gst_aravis, "Available caps = %" GST_PTR_FORMAT, caps); return caps; } static gboolean gst_aravis_set_caps (GstBaseSrc *src, GstCaps *caps) { GError *error = NULL; GstAravis* gst_aravis = GST_ARAVIS(src); GstStructure *structure; ArvPixelFormat pixel_format; gint height, width; gint current_height, current_width; int depth = 0, bpp = 0; const GValue *frame_rate = NULL; const char *format_string; unsigned int i; ArvStream *orig_stream = NULL; gboolean result = FALSE; gboolean is_frame_rate_available; gboolean is_gain_available; gboolean is_gain_auto_available; gboolean is_exposure_time_available; gboolean is_exposure_auto_available; GST_LOG_OBJECT (gst_aravis, "Requested caps = %" GST_PTR_FORMAT, caps); structure = gst_caps_get_structure (caps, 0); GST_OBJECT_LOCK (gst_aravis); arv_camera_get_region (gst_aravis->camera, NULL, NULL, ¤t_width, ¤t_height, &error); if (error) goto errored; width = current_width; height = current_height; is_frame_rate_available = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL); is_gain_available = arv_camera_is_gain_available (gst_aravis->camera, NULL); is_gain_auto_available = arv_camera_is_gain_auto_available (gst_aravis->camera, NULL); is_exposure_time_available = arv_camera_is_exposure_time_available (gst_aravis->camera, NULL); is_exposure_auto_available = arv_camera_is_exposure_auto_available (gst_aravis->camera, NULL); gst_structure_get_int (structure, "width", &width); gst_structure_get_int (structure, "height", &height); gst_structure_get_int (structure, "depth", &depth); gst_structure_get_int (structure, "bpp", &bpp); if (is_frame_rate_available) frame_rate = gst_structure_get_value (structure, "framerate"); format_string = gst_structure_get_string (structure, "format"); pixel_format = arv_pixel_format_from_gst_caps (gst_structure_get_name (structure), format_string, bpp, depth); if (!pixel_format) { GST_ERROR_OBJECT (src, "did not find matching pixel_format"); goto failed; } arv_camera_stop_acquisition (gst_aravis->camera, &error); orig_stream = g_steal_pointer (&gst_aravis->stream); if (!error) arv_camera_set_pixel_format (gst_aravis->camera, pixel_format, &error); if (!error) arv_camera_set_binning (gst_aravis->camera, gst_aravis->h_binning, gst_aravis->v_binning, &error); if (!error) { if (width != current_width || height != current_height) arv_camera_set_region (gst_aravis->camera, gst_aravis->offset_x, gst_aravis->offset_y, width, height, &error); else arv_camera_set_region (gst_aravis->camera, gst_aravis->offset_x, gst_aravis->offset_y, -1, -1, &error); } if (!error && arv_camera_is_gv_device (gst_aravis->camera)) { if (gst_aravis->packet_delay >= 0) { gint64 delay = 0; arv_camera_gv_set_packet_delay (gst_aravis->camera, gst_aravis->packet_delay, &error); if (!error) delay = arv_camera_gv_get_packet_delay (gst_aravis->camera, &error); if (!error && delay != gst_aravis->packet_delay) GST_WARNING_OBJECT (gst_aravis, "Packet delay is %" G_GINT64_FORMAT " ns instead of %" G_GINT64_FORMAT, delay, gst_aravis->packet_delay); } if (!error && gst_aravis->packet_size > 0) arv_camera_gv_set_packet_size (gst_aravis->camera, gst_aravis->packet_size, &error); if (!error && gst_aravis->auto_packet_size) arv_camera_gv_auto_packet_size (gst_aravis->camera, &error); } if (!error && frame_rate != NULL) { gst_aravis->frame_rate = (double) gst_value_get_fraction_numerator (frame_rate) / (double) gst_value_get_fraction_denominator (frame_rate); GST_DEBUG_OBJECT (gst_aravis, "Frame rate = %g Hz", gst_aravis->frame_rate); arv_camera_set_frame_rate (gst_aravis->camera, gst_aravis->frame_rate, &error); if (gst_aravis->frame_rate > 0.0) gst_aravis->buffer_timeout_us = MAX (GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT, 3e6 / gst_aravis->frame_rate); else gst_aravis->buffer_timeout_us = GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT; } else gst_aravis->buffer_timeout_us = GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT; GST_DEBUG_OBJECT (gst_aravis, "Buffer timeout = %" G_GUINT64_FORMAT " µs", gst_aravis->buffer_timeout_us); if (is_frame_rate_available) GST_DEBUG_OBJECT (gst_aravis, "Actual frame rate = %g Hz", arv_camera_get_frame_rate (gst_aravis->camera, NULL)); if (is_gain_auto_available && !error && gst_aravis->gain_auto_set) { arv_camera_set_gain_auto (gst_aravis->camera, gst_aravis->gain_auto, &error); GST_DEBUG_OBJECT (gst_aravis, "Auto Gain = %s", arv_auto_to_string(gst_aravis->gain_auto)); } if (is_gain_available && gst_aravis->gain_auto == ARV_AUTO_OFF) { if (gst_aravis->gain >= 0) { GST_DEBUG_OBJECT (gst_aravis, "Gain = %g", gst_aravis->gain); if (is_gain_auto_available && !error && !gst_aravis->gain_auto_set) arv_camera_set_gain_auto (gst_aravis->camera, ARV_AUTO_OFF, &error); if (!error) arv_camera_set_gain (gst_aravis->camera, gst_aravis->gain, &error); } GST_DEBUG_OBJECT (gst_aravis, "Actual gain = %g", arv_camera_get_gain (gst_aravis->camera, NULL)); } if (is_exposure_auto_available && !error && gst_aravis->exposure_auto_set) { arv_camera_set_exposure_time_auto (gst_aravis->camera, gst_aravis->exposure_auto, &error); GST_DEBUG_OBJECT (gst_aravis, "Auto Exposure = %s", arv_auto_to_string(gst_aravis->exposure_auto)); } if (is_exposure_time_available && gst_aravis->exposure_auto == ARV_AUTO_OFF) { if (gst_aravis->exposure_time_us > 0.0) { GST_DEBUG_OBJECT (gst_aravis, "Exposure = %g µs", gst_aravis->exposure_time_us); if (is_exposure_auto_available && !error && !gst_aravis->exposure_auto_set) arv_camera_set_exposure_time_auto (gst_aravis->camera, ARV_AUTO_OFF, &error); if (!error) arv_camera_set_exposure_time (gst_aravis->camera, gst_aravis->exposure_time_us, &error); } GST_DEBUG_OBJECT (gst_aravis, "Actual exposure = %g µs", arv_camera_get_exposure_time (gst_aravis->camera, NULL)); } if (!error) arv_device_set_features_from_string (arv_camera_get_device (gst_aravis->camera), gst_aravis->features, &error); if (!error) gst_aravis->payload = arv_camera_get_payload (gst_aravis->camera, &error); if (!error) gst_aravis->stream = arv_camera_create_stream (gst_aravis->camera, NULL, NULL, &error); if (error) goto errored; if (ARV_IS_GV_STREAM (gst_aravis->stream)) { if (gst_aravis->packet_resend) g_object_set (gst_aravis->stream, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_ALWAYS, NULL); else g_object_set (gst_aravis->stream, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL); } for (i = 0; i < gst_aravis->num_arv_buffers; i++) arv_stream_push_buffer (gst_aravis->stream, arv_buffer_new (gst_aravis->payload, NULL)); GST_LOG_OBJECT (gst_aravis, "Start acquisition"); arv_camera_start_acquisition (gst_aravis->camera, &error); gst_aravis->timestamp_offset = 0; gst_aravis->last_timestamp = 0; if (error) goto errored; GST_OBJECT_UNLOCK (gst_aravis); g_object_notify_by_pspec (G_OBJECT (gst_aravis), properties[PROP_STREAM]); result = TRUE; goto unref; errored: GST_OBJECT_UNLOCK (gst_aravis); GST_ELEMENT_ERROR (gst_aravis, RESOURCE, WRITE, (_("Could not set caps on camera \"%s\": %s"), gst_aravis->camera_name ? gst_aravis->camera_name : "", error->message), (NULL)); g_error_free (error); goto unref; failed: GST_OBJECT_UNLOCK (gst_aravis); unref: if (orig_stream != NULL) g_object_unref (orig_stream); return result; } static void gst_aravis_software_trigger (GstAravis *src) { if (ARV_IS_CAMERA (src->camera)) { arv_camera_software_trigger(src->camera, NULL); } } static gboolean gst_aravis_init_camera (GstAravis *gst_aravis, gboolean *notify, GError **error) { GError *local_error = NULL; if (gst_aravis->camera != NULL) { g_object_unref (gst_aravis->camera); if (notify != NULL) *notify = TRUE; } else if (notify != NULL) *notify = FALSE; gst_aravis->camera = arv_camera_new (gst_aravis->camera_name, &local_error); if (!local_error) arv_camera_get_region (gst_aravis->camera, &gst_aravis->offset_x, &gst_aravis->offset_y, NULL, NULL, &local_error); if (!local_error) gst_aravis->payload = 0; if (!local_error && arv_camera_is_uv_device (gst_aravis->camera)) arv_camera_uv_set_usb_mode (gst_aravis->camera, gst_aravis->usb_mode); if (!local_error && gst_aravis->trigger_source != NULL) arv_camera_set_trigger (gst_aravis->camera, gst_aravis->trigger_source, &local_error); if (local_error) { g_clear_object (&gst_aravis->camera); g_propagate_error (error, local_error); return FALSE; } if (notify != NULL) *notify = TRUE; return TRUE; } static void gst_aravis_init_error (GstAravis *gst_aravis, GError *error) { if (error->domain == ARV_DEVICE_ERROR && error->code == ARV_DEVICE_ERROR_NOT_FOUND) { GST_ELEMENT_ERROR (gst_aravis, RESOURCE, NOT_FOUND, (_("Could not find camera \"%s\": %s"), gst_aravis->camera_name ? gst_aravis->camera_name : "", error->message), (NULL)); } else { GST_ELEMENT_ERROR (gst_aravis, RESOURCE, READ, (_("Could not read camera \"%s\": %s"), gst_aravis->camera_name ? gst_aravis->camera_name : "", error->message), (NULL)); } g_error_free (error); } static gboolean gst_aravis_start (GstBaseSrc *src) { GError *error = NULL; gboolean result = TRUE; gboolean notify = FALSE; GstAravis* gst_aravis = GST_ARAVIS(src); if (gst_aravis->camera_name != NULL) GST_LOG_OBJECT (gst_aravis, "Open camera '%s'", gst_aravis->camera_name); else GST_LOG_OBJECT (gst_aravis, "Open first available camera"); GST_OBJECT_LOCK (gst_aravis); if (gst_aravis->camera == NULL) result = gst_aravis_init_camera (gst_aravis, ¬ify, &error); if (result) gst_aravis->all_caps = gst_aravis_get_all_camera_caps (gst_aravis, &error); GST_OBJECT_UNLOCK (gst_aravis); if (notify) g_object_notify_by_pspec (G_OBJECT (gst_aravis), properties[PROP_CAMERA]); if (error) gst_aravis_init_error (gst_aravis, error); return result; } static gboolean gst_aravis_stop( GstBaseSrc * src ) { GError *error = NULL; GstAravis* gst_aravis = GST_ARAVIS(src); ArvCamera *camera; ArvStream *stream; GstCaps *all_caps; GST_OBJECT_LOCK (gst_aravis); arv_camera_stop_acquisition (gst_aravis->camera, &error); camera = g_steal_pointer (&gst_aravis->camera); stream = g_steal_pointer (&gst_aravis->stream); all_caps = g_steal_pointer (&gst_aravis->all_caps); GST_OBJECT_UNLOCK (gst_aravis); if (stream != NULL) g_object_unref (stream); if (camera != NULL) g_object_unref (camera); if (all_caps != NULL) gst_caps_unref (all_caps); GST_DEBUG_OBJECT (gst_aravis, "Stop acquisition"); if (error) { // GigEVision write_register timeout is common if // stopping due to lost camera GST_ERROR_OBJECT (src, "Acquisition stop error: %s", error->message); g_error_free (error); } return TRUE; } static void gst_aravis_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, GstClockTime * start, GstClockTime * end) { if (gst_base_src_is_live (basesrc)) { GstClockTime timestamp = GST_BUFFER_PTS (buffer); if (GST_CLOCK_TIME_IS_VALID (timestamp)) { GstClockTime duration = GST_BUFFER_DURATION (buffer); if (GST_CLOCK_TIME_IS_VALID (duration)) { *end = timestamp + duration; } *start = timestamp; } } else { *start = -1; *end = -1; } } static GstFlowReturn gst_aravis_create (GstPushSrc * push_src, GstBuffer ** buffer) { GstAravis *gst_aravis; int arv_row_stride; int width, height; char *buffer_data; size_t buffer_size; guint64 timestamp_ns; gboolean base_src_does_timestamp; ArvBuffer *arv_buffer = NULL; gst_aravis = GST_ARAVIS (push_src); base_src_does_timestamp = gst_base_src_get_do_timestamp(GST_BASE_SRC(push_src)); GST_OBJECT_LOCK (gst_aravis); do { if (arv_buffer) arv_stream_push_buffer (gst_aravis->stream, arv_buffer); arv_buffer = arv_stream_timeout_pop_buffer (gst_aravis->stream, gst_aravis->buffer_timeout_us); } while (arv_buffer != NULL && arv_buffer_get_status (arv_buffer) != ARV_BUFFER_STATUS_SUCCESS); if (arv_buffer == NULL) goto error; buffer_data = (char *) arv_buffer_get_data (arv_buffer, &buffer_size); arv_buffer_get_image_region (arv_buffer, NULL, NULL, &width, &height); arv_row_stride = width * ARV_PIXEL_FORMAT_BIT_PER_PIXEL (arv_buffer_get_image_pixel_format (arv_buffer)) / 8; timestamp_ns = arv_buffer_get_timestamp (arv_buffer); /* Gstreamer requires row stride to be a multiple of 4 */ if ((arv_row_stride & 0x3) != 0) { int gst_row_stride; size_t size; char *data; int i; gst_row_stride = (arv_row_stride & ~(0x3)) + 4; size = height * gst_row_stride; data = g_malloc (size); for (i = 0; i < height; i++) memcpy (data + i * gst_row_stride, buffer_data + i * arv_row_stride, arv_row_stride); *buffer = gst_buffer_new_wrapped (data, size); } else { // FIXME Should arv_stream_push_buffer when the GstBuffer is destroyed *buffer = gst_buffer_new_wrapped_full (0, buffer_data, buffer_size, 0, buffer_size, NULL, NULL); } if (!base_src_does_timestamp) { if (gst_aravis->timestamp_offset == 0) { gst_aravis->timestamp_offset = timestamp_ns; gst_aravis->last_timestamp = timestamp_ns; } GST_BUFFER_PTS (*buffer) = timestamp_ns - gst_aravis->timestamp_offset; GST_BUFFER_DURATION (*buffer) = timestamp_ns - gst_aravis->last_timestamp; gst_aravis->last_timestamp = timestamp_ns; } arv_stream_push_buffer (gst_aravis->stream, arv_buffer); GST_OBJECT_UNLOCK (gst_aravis); return GST_FLOW_OK; error: GST_OBJECT_UNLOCK (gst_aravis); return GST_FLOW_ERROR; } static GstCaps * gst_aravis_fixate_caps (GstBaseSrc * bsrc, GstCaps * caps) { GError *error = NULL; GstAravis *gst_aravis = GST_ARAVIS (bsrc); GstStructure *structure; gint width; gint height; double frame_rate = 0.0; gboolean use_frame_rate; g_return_val_if_fail (GST_IS_ARAVIS (bsrc), NULL); GST_OBJECT_LOCK (gst_aravis); arv_camera_get_region (gst_aravis->camera, NULL, NULL, &width, &height, &error); use_frame_rate = arv_camera_is_frame_rate_available (gst_aravis->camera, NULL) && gst_aravis->trigger_source == NULL; if (use_frame_rate) if (!error) frame_rate = arv_camera_get_frame_rate (gst_aravis->camera, &error); GST_OBJECT_UNLOCK (gst_aravis); if (error) { GST_ELEMENT_ERROR (gst_aravis, RESOURCE, READ, (_("Could not read camera \"%s\": %s"), gst_aravis->camera_name ? gst_aravis->camera_name : "", error->message), (NULL)); g_error_free (error); } else { structure = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (structure, "width", width); gst_structure_fixate_field_nearest_int (structure, "height", height); if (use_frame_rate) gst_structure_fixate_field_nearest_fraction (structure, "framerate", (double) (0.5 + frame_rate), 1); GST_LOG_OBJECT (gst_aravis, "Fixate caps"); } return GST_BASE_SRC_CLASS(gst_aravis_parent_class)->fixate(bsrc, caps); } static void gst_aravis_init (GstAravis *gst_aravis) { gst_base_src_set_live (GST_BASE_SRC (gst_aravis), TRUE); gst_base_src_set_format (GST_BASE_SRC (gst_aravis), GST_FORMAT_TIME); gst_aravis->camera_name = NULL; gst_aravis->gain = -1; gst_aravis->gain_auto = ARV_AUTO_OFF; gst_aravis->gain_auto_set = FALSE; gst_aravis->exposure_time_us = -1; gst_aravis->exposure_auto = ARV_AUTO_OFF; gst_aravis->exposure_auto_set = FALSE; gst_aravis->offset_x = 0; gst_aravis->offset_y = 0; gst_aravis->h_binning = -1; gst_aravis->v_binning = -1; gst_aravis->packet_delay = -1; gst_aravis->packet_size = -1; gst_aravis->auto_packet_size = FALSE; gst_aravis->packet_resend = TRUE; gst_aravis->num_arv_buffers = GST_ARAVIS_DEFAULT_N_BUFFERS; gst_aravis->payload = 0; gst_aravis->usb_mode = ARV_UV_USB_MODE_DEFAULT; gst_aravis->buffer_timeout_us = GST_ARAVIS_BUFFER_TIMEOUT_DEFAULT; gst_aravis->frame_rate = 0.0; gst_aravis->trigger_source = NULL; gst_aravis->camera = NULL; gst_aravis->stream = NULL; gst_aravis->all_caps = NULL; } static void gst_aravis_finalize (GObject * object) { GstAravis *gst_aravis = GST_ARAVIS (object); ArvCamera *camera; ArvStream *stream; GstCaps *all_caps; GST_OBJECT_LOCK (gst_aravis); camera = g_steal_pointer (&gst_aravis->camera); stream = g_steal_pointer (&gst_aravis->stream); all_caps = g_steal_pointer (&gst_aravis->all_caps); g_clear_pointer (&gst_aravis->camera_name, g_free); g_clear_pointer (&gst_aravis->features, g_free); g_clear_pointer (&gst_aravis->trigger_source, g_free); GST_OBJECT_UNLOCK (gst_aravis); if (camera != NULL) g_object_unref (camera); if (stream != NULL) g_object_unref (stream); if (all_caps != NULL) gst_caps_unref (all_caps); G_OBJECT_CLASS (gst_aravis_parent_class)->finalize (object); } static void gst_aravis_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GError *error = NULL; GstAravis *gst_aravis = GST_ARAVIS (object); GST_DEBUG_OBJECT (gst_aravis, "setting property %s", pspec->name); switch (prop_id) { gboolean notify; case PROP_CAMERA_NAME: notify= FALSE; GST_OBJECT_LOCK (gst_aravis); /* check if we are currently active prevent setting camera and other values to something not representing the active camera */ if (gst_aravis->stream == NULL) { g_free (gst_aravis->camera_name); gst_aravis->camera_name = g_strdup (g_value_get_string (value)); gst_aravis_init_camera (gst_aravis, ¬ify, &error); } GST_LOG_OBJECT (gst_aravis, "Set camera name to %s", gst_aravis->camera_name); GST_OBJECT_UNLOCK (gst_aravis); if (error) gst_aravis_init_error (gst_aravis, error); if (notify) g_object_notify_by_pspec (G_OBJECT (gst_aravis), properties[PROP_CAMERA]); break; case PROP_GAIN: GST_OBJECT_LOCK (gst_aravis); gst_aravis->gain = g_value_get_double (value); if (gst_aravis->camera != NULL && arv_camera_is_gain_available (gst_aravis->camera, NULL)) arv_camera_set_gain (gst_aravis->camera, gst_aravis->gain, NULL); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_GAIN_AUTO: GST_OBJECT_LOCK (gst_aravis); gst_aravis->gain_auto = g_value_get_enum (value); gst_aravis->gain_auto_set = TRUE; if (gst_aravis->camera != NULL && arv_camera_is_gain_auto_available (gst_aravis->camera, NULL)) arv_camera_set_gain_auto (gst_aravis->camera, gst_aravis->gain_auto, NULL); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_EXPOSURE: GST_OBJECT_LOCK (gst_aravis); gst_aravis->exposure_time_us = g_value_get_double (value); if (gst_aravis->camera != NULL && arv_camera_is_exposure_time_available (gst_aravis->camera, NULL)) arv_camera_set_exposure_time (gst_aravis->camera, gst_aravis->exposure_time_us, NULL); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_EXPOSURE_AUTO: GST_OBJECT_LOCK (gst_aravis); gst_aravis->exposure_auto = g_value_get_enum (value); gst_aravis->exposure_auto_set = TRUE; if (gst_aravis->camera != NULL && arv_camera_is_exposure_auto_available (gst_aravis->camera, NULL)) arv_camera_set_exposure_time_auto (gst_aravis->camera, gst_aravis->exposure_auto, NULL); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_OFFSET_X: gst_aravis->offset_x = g_value_get_int (value); break; case PROP_OFFSET_Y: gst_aravis->offset_y = g_value_get_int (value); break; case PROP_H_BINNING: gst_aravis->h_binning = g_value_get_int (value); break; case PROP_V_BINNING: gst_aravis->v_binning = g_value_get_int (value); break; case PROP_PACKET_DELAY: GST_OBJECT_LOCK (gst_aravis); gst_aravis->packet_delay = g_value_get_int64 (value); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_PACKET_SIZE: gst_aravis->packet_size = g_value_get_int (value); break; case PROP_AUTO_PACKET_SIZE: gst_aravis->auto_packet_size = g_value_get_boolean (value); break; case PROP_PACKET_RESEND: gst_aravis->packet_resend = g_value_get_boolean (value); break; case PROP_FEATURES: GST_OBJECT_LOCK (gst_aravis); g_free (gst_aravis->features); gst_aravis->features = g_value_dup_string (value); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_NUM_ARV_BUFFERS: gst_aravis->num_arv_buffers = g_value_get_int (value); break; case PROP_USB_MODE: gst_aravis->usb_mode = g_value_get_enum (value); break; case PROP_TRIGGER: GST_OBJECT_LOCK (gst_aravis); g_free (gst_aravis->trigger_source); gst_aravis->trigger_source = g_strdup (g_value_get_string (value)); GST_OBJECT_UNLOCK (gst_aravis); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_aravis_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstAravis *gst_aravis = GST_ARAVIS (object); GST_DEBUG_OBJECT (gst_aravis, "getting property %s", pspec->name); switch (prop_id) { case PROP_CAMERA_NAME: GST_OBJECT_LOCK (gst_aravis); g_value_set_string (value, gst_aravis->camera_name); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_CAMERA: GST_OBJECT_LOCK (gst_aravis); g_value_set_object (value, gst_aravis->camera); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_GAIN: GST_OBJECT_LOCK (gst_aravis); g_value_set_double (value, gst_aravis->gain); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_GAIN_AUTO: GST_OBJECT_LOCK (gst_aravis); if (!gst_aravis->gain_auto_set && gst_aravis->camera != NULL && arv_camera_is_gain_auto_available (gst_aravis->camera, NULL)) { gst_aravis->gain_auto = arv_camera_get_gain_auto(gst_aravis->camera, NULL); } g_value_set_enum (value, gst_aravis->gain_auto); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_EXPOSURE: GST_OBJECT_LOCK (gst_aravis); g_value_set_double (value, gst_aravis->exposure_time_us); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_EXPOSURE_AUTO: GST_OBJECT_LOCK (gst_aravis); if (!gst_aravis->exposure_auto_set && gst_aravis->camera != NULL && arv_camera_is_exposure_auto_available (gst_aravis->camera, NULL)) { gst_aravis->exposure_auto = arv_camera_get_exposure_time_auto(gst_aravis->camera, NULL); } g_value_set_enum (value, gst_aravis->exposure_auto); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_OFFSET_X: g_value_set_int (value, gst_aravis->offset_x); break; case PROP_OFFSET_Y: g_value_set_int (value, gst_aravis->offset_y); break; case PROP_H_BINNING: GST_OBJECT_LOCK (gst_aravis); if (gst_aravis->h_binning < 0 && gst_aravis->camera) { arv_camera_get_binning (gst_aravis->camera, &gst_aravis->h_binning, NULL, NULL); } g_value_set_int (value, gst_aravis->h_binning); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_V_BINNING: GST_OBJECT_LOCK (gst_aravis); if (gst_aravis->v_binning < 0 && gst_aravis->camera) { arv_camera_get_binning (gst_aravis->camera, NULL, &gst_aravis->v_binning, NULL); } g_value_set_int (value, gst_aravis->v_binning); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_PACKET_DELAY: GST_OBJECT_LOCK (gst_aravis); g_value_set_int64 (value, gst_aravis->packet_delay); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_PACKET_SIZE: g_value_set_int (value, gst_aravis->packet_size); break; case PROP_AUTO_PACKET_SIZE: g_value_set_boolean (value, gst_aravis->auto_packet_size); break; case PROP_PACKET_RESEND: g_value_set_boolean (value, gst_aravis->packet_resend); break; case PROP_FEATURES: g_value_set_string (value, gst_aravis->features); break; case PROP_NUM_ARV_BUFFERS: g_value_set_int (value, gst_aravis->num_arv_buffers); break; case PROP_USB_MODE: g_value_set_enum(value, gst_aravis->usb_mode); break; case PROP_STREAM: GST_OBJECT_LOCK (gst_aravis); g_value_set_object (value, gst_aravis->stream); GST_OBJECT_UNLOCK (gst_aravis); break; case PROP_TRIGGER: GST_OBJECT_LOCK (gst_aravis); g_value_set_string (value, gst_aravis->trigger_source); GST_OBJECT_UNLOCK (gst_aravis); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean gst_aravis_query (GstBaseSrc *bsrc, GstQuery *query) { GstAravis *src = GST_ARAVIS (bsrc); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY: { GstClockTime min_latency; GstClockTime max_latency; /* device must be open */ if (!src->stream) { GST_WARNING_OBJECT (src, "Can't give latency since device isn't open !"); goto done; } /* we must have a framerate */ if (src->frame_rate <= 0.0 || src->trigger_source != NULL) { GST_WARNING_OBJECT (src, "Can't give latency since framerate isn't fixated !"); goto done; } /* min latency is the time to capture one frame/field */ min_latency = gst_util_gdouble_to_guint64 (src->frame_rate); /* max latency is set to NONE because cameras may enter trigger mode and not deliver images for an unspecified amount of time */ max_latency = GST_CLOCK_TIME_NONE; GST_DEBUG_OBJECT (bsrc, "report latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT, GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); /* we are always live, the min latency is 1 frame and the max latency is * the complete buffer of frames. */ gst_query_set_latency (query, TRUE, min_latency, max_latency); res = TRUE; break; } default: { res = GST_BASE_SRC_CLASS (gst_aravis_parent_class)->query (bsrc, query); break; } } done: return res; } static void gst_aravis_class_init (GstAravisClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_aravis_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_aravis_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_aravis_get_property); properties[PROP_CAMERA_NAME] = g_param_spec_string ("camera-name", "Camera name", "Name of the camera", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_CAMERA] = g_param_spec_object ("camera", "Camera Object", "Camera instance to retrieve additional information", ARV_TYPE_CAMERA, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_GAIN] = g_param_spec_double ("gain", "Gain", "Gain (dB)", -1.0, 500.0, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_GAIN_AUTO] = g_param_spec_enum ("gain-auto", "Auto Gain", "Auto Gain Mode", GST_TYPE_ARV_AUTO, ARV_AUTO_OFF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_EXPOSURE] = g_param_spec_double ("exposure", "Exposure", "Exposure time (µs)", -1, 100000000.0, 500.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_EXPOSURE_AUTO] = g_param_spec_enum ("exposure-auto", "Auto Exposure", "Auto Exposure Mode", GST_TYPE_ARV_AUTO, ARV_AUTO_OFF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_OFFSET_X] = g_param_spec_int ("offset-x", "x Offset", "Offset in x direction", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_OFFSET_Y] = g_param_spec_int ("offset-y", "y Offset", "Offset in y direction", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_H_BINNING] = g_param_spec_int ("h-binning", "Horizontal binning", "CCD horizontal binning", 1, G_MAXINT, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_V_BINNING] = g_param_spec_int ("v-binning", "Vertical binning", "CCD vertical binning", 1, G_MAXINT, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_PACKET_DELAY] = g_param_spec_int64 ("packet-delay", "Packet delay", "GigEVision streaming inter packet delay (in ns, -1 = default)", 0, G_MAXINT64 / 1000000000LL, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_PACKET_SIZE] = g_param_spec_int ("packet-size", "Packet size", "GigEVision streaming packet size", ARV_GVSP_PACKET_PROTOCOL_OVERHEAD (FALSE), 65500, 1500, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_AUTO_PACKET_SIZE] = g_param_spec_boolean ("auto-packet-size", "Auto Packet Size", "Negotiate GigEVision streaming packet size", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_PACKET_RESEND] = g_param_spec_boolean ("packet-resend", "Packet Resend", "Request dropped packets to be reissued by the camera", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_FEATURES] = g_param_spec_string ("features", "String of feature values", "Additional configuration parameters as a space separated list of feature assignations", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_NUM_ARV_BUFFERS] = g_param_spec_int ("num-arv-buffers", "Number of Buffers allocated", "Number of video buffers to allocate for video frames", 1, G_MAXINT, GST_ARAVIS_DEFAULT_N_BUFFERS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_USB_MODE] = g_param_spec_enum ("usb-mode", "USB mode", "USB mode (synchronous/asynchronous)", GST_TYPE_ARV_USB_MODE, ARV_UV_USB_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_STREAM] = g_param_spec_object ("stream", "Stream Object", "Stream instance to retrieve additional information", ARV_TYPE_STREAM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_TRIGGER] = g_param_spec_string("trigger", "Configure the trigger mode", "Enable the trigger mode using the given source", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, G_N_ELEMENTS (properties), properties); gst_aravis_signals[SIGNAL_SOFTWARE_TRIGGER] = g_signal_new ("software-trigger", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAravisClass, software_trigger), NULL, NULL, NULL, G_TYPE_NONE, 0); GST_DEBUG_CATEGORY_INIT (aravis_debug, "aravissrc", 0, "Aravis interface"); gst_element_class_set_details_simple (element_class, "Aravis Video Source", "Source/Video", "Aravis based source", "Emmanuel Pacaud "); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&aravis_src_template)); gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_aravis_get_caps); gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_aravis_set_caps); gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_aravis_fixate_caps); gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_aravis_start); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_aravis_stop); gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_aravis_query); gstbasesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_aravis_get_times); gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_aravis_create); klass->software_trigger = gst_aravis_software_trigger; } static gboolean plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "aravissrc", GST_RANK_NONE, GST_TYPE_ARAVIS); } #define PACKAGE "aravis" GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, aravis, "Aravis Video Source", plugin_init, ARAVIS_VERSION, "LGPL", "aravis", "http://blogs.gnome.org/emmanuel") aravis-0.8.34/gst/gstaravis.h000066400000000000000000000047411475431451200160600ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2006 Eric Jonas * Copyright © 2006 Antoine Tremblay * Copyright © 2010 United States Government, Joshua M. Doe * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GST_H #define ARV_GST_H #include #include #include G_BEGIN_DECLS #define GST_TYPE_ARAVIS (gst_aravis_get_type()) #define GST_ARAVIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ARAVIS,GstAravis)) #define GST_ARAVIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ARAVIS,GstAravis)) #define GST_IS_ARAVIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ARAVIS)) #define GST_IS_ARAVIS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ARAVIS)) typedef struct _GstAravis GstAravis; typedef struct _GstAravisClass GstAravisClass; struct _GstAravis { GstPushSrc element; char *camera_name; double gain; ArvAuto gain_auto; gboolean gain_auto_set; double exposure_time_us; ArvAuto exposure_auto; gboolean exposure_auto_set; gint offset_x; gint offset_y; gint h_binning; gint v_binning; gint num_arv_buffers; /* GigEVision parameters */ int packet_size; gboolean auto_packet_size; gint64 packet_delay; gboolean packet_resend; ArvUvUsbMode usb_mode; gint payload; guint64 buffer_timeout_us; gdouble frame_rate; ArvCamera *camera; ArvStream *stream; GstCaps *all_caps; guint64 timestamp_offset; guint64 last_timestamp; char *trigger_source; char *features; }; struct _GstAravisClass { GstPushSrcClass parent_class; void (*software_trigger) (GstAravis *src); }; GType gst_aravis_get_type (void); G_END_DECLS #endif aravis-0.8.34/gst/meson.build000066400000000000000000000017311475431451200160420ustar00rootroot00000000000000gst_enabled = true gst_plugin_dir = get_option ('libdir') / 'gstreamer-1.0' gst_sources = [ 'gstaravis.c' ] gst_headers = [ 'gstaravis.h' ] gst_c_args = [ '-DPACKAGE_LOCALE_DIR=@0@'.format(get_option ('prefix')), '-DG_LOG_DOMAIN="Aravis"' ] gst_plugin_filename = 'gstaravis.@0@'.format (aravis_api_version) gst_plugin = shared_library (gst_plugin_filename, gst_sources, gst_headers, name_suffix: [], link_with: aravis_library, c_args: gst_c_args, include_directories: [library_inc], dependencies: gst_deps, install: true, install_dir: gst_plugin_dir) gst_script_config_data = configuration_data () gst_script_config_data.set ('GST_PLUGIN_FILENAME', gst_plugin.full_path ()) configure_file (input: 'gst-aravis-launch.in', output: 'gst-aravis-launch', configuration: gst_script_config_data) configure_file (input: 'gst-aravis-inspect.in', output: 'gst-aravis-inspect', configuration: gst_script_config_data) aravis-0.8.34/gst/pipelines.md000066400000000000000000000003131475431451200162050ustar00rootroot00000000000000Simple ====== ./gst-aravis-launch aravissrc ! videoconvert ! xvimagesink Format filter ============= ./gst-aravis-launch aravissrc ! video/x-raw,format=GRAY16_LE,depth=12 ! videoconvert ! xvimagesink aravis-0.8.34/meson.build000066400000000000000000000104501475431451200152430ustar00rootroot00000000000000project ('aravis', 'c', 'cpp', version: '0.8.34', meson_version: '>=0.57.0') gnome = import('gnome') pkg = import ('pkgconfig') aravis_version = meson.project_version () aravis_api_version = '0.8' versions = aravis_version.split ('.') aravis_major_version = versions[0].to_int () aravis_minor_version = versions[1].to_int () aravis_micro_version = versions[2].to_int () aravis_data_dir = get_option ('datadir') / 'aravis-@0@'.format (aravis_api_version) cc = meson.get_compiler ('c') cxx = meson.get_compiler ('cpp') if cc.get_id()=='gcc' or cc.get_id()=='clang' warning_c_args = [ '-Wduplicated-branches', '-Wimplicit-fallthrough', '-Wmisleading-indentation', '-Wstrict-prototypes', '-Wunused', '-Wunused-variable', '-Wdeclaration-after-statement', '-Wformat=2', '-Wimplicit-function-declaration', '-Winit-self', '-Wmissing-include-dirs', '-Wmissing-prototypes', '-Wpointer-arith', '-Wformat-nonliteral', '-Wenum-conversion', '-Wmaybe-uninitialized' ] add_project_arguments(cc.get_supported_arguments(warning_c_args), language: 'c') elif cc.get_id()=='msvc' warning_c_args = [ '/wd4244', # Disable int-float implicit casting warnings '/wd4090', # Disable const qualifiers warnings '/utf-8' ] add_project_arguments(cc.get_supported_arguments(warning_c_args), language: 'c') endif if cc.get_id() == 'msvc' cc_export_define = '__declspec(dllexport) extern' elif cc.has_argument('-fvisibility=hidden') add_project_arguments('-fvisibility=hidden', language: 'c') cc_export_define = 'extern __attribute__ ((visibility ("default")))' else cc_export_define = 'extern' endif glib_dep = dependency ('glib-2.0', version: '>=2.52', required: true) gobject_dep = dependency ('gobject-2.0', required: true) gio_dep = dependency ('gio-2.0', required: true) xml2_dep = dependency ('libxml-2.0', required: true) libz_dep = dependency ('zlib', required: true) usb_dep = dependency ('libusb-1.0', required: get_option ('usb')) aravis_public_dependencies = [glib_dep, gobject_dep, gio_dep] aravis_dependencies = [aravis_public_dependencies, xml2_dep, libz_dep] if cc.get_id()!='msvc' libm = cc.find_library ('m', required: true) aravis_dependencies += libm endif if usb_dep.found() aravis_dependencies += usb_dep endif if host_machine.system()=='windows' aravis_dependencies += [ cc.find_library('ws2_32', required: true), # socket functions cc.find_library('iphlpapi', required: true) # GetAdaptersAddresses ] endif packet_socket_option = get_option('packet-socket') if host_machine.system()=='linux' has_if_packet = cc.has_header ('linux' /'if_packet.h') if packet_socket_option.enabled() if not has_if_packet error ('missing header for packet-socket support') endif packet_socket_enabled = true else packet_socket_enabled = has_if_packet and packet_socket_option.auto() endif else # not Linux if packet_socket_option.enabled() warning('packet-socket option ignored on non-Linux') endif packet_socket_enabled = false endif subdir ('src') subdir ('tests') viewer_enabled = false viewer_option = get_option ('viewer') viewer_deps = aravis_dependencies + [dependency ('gtk+-3.0', version: '>=3.12', required: viewer_option), dependency ('gstreamer-base-1.0', required: viewer_option), dependency ('gstreamer-app-1.0', required: viewer_option), dependency ('gstreamer-video-1.0', required: viewer_option)] subdir ('po', if_found: viewer_deps) subdir ('viewer', if_found: viewer_deps) gst_enabled = false gst_option = get_option ('gst-plugin') gst_deps = aravis_dependencies + [dependency ('gstreamer-base-1.0', required: gst_option), dependency ('gstreamer-app-1.0', required: gst_option)] subdir('gst', if_found: gst_deps) doc_deps = dependency ('gi-docgen', version:'>= 2021.1', fallback: ['gi-docgen', 'dummy_dep'], required:get_option('documentation')) subdir('docs', if_found: doc_deps) meson.override_dependency ('aravis-' + aravis_api_version, aravis_library_dependencies) summary ( { 'prefix': get_option('prefix'), 'bindir': get_option('bindir'), 'libdir': get_option('libdir'), 'datadir': get_option('datadir'), }, section: 'Directories' ) summary ( { 'Viewer': viewer_enabled, 'GStreamer plugin': gst_enabled, 'USB support': usb_dep.found(), }, section: 'Options' ) aravis-0.8.34/meson_options.txt000066400000000000000000000017131475431451200165400ustar00rootroot00000000000000# Features option('viewer', type: 'feature', value: 'auto', description : 'Build simple viewer') option('gst-plugin', type: 'feature', value: 'auto', description : 'Build GStreamer plugin') option('usb', type: 'feature', value: 'auto', description : 'Enable USB support') option('packet-socket', type: 'feature', value: 'auto', description : 'Enable packet socket support') option('tests', type: 'boolean', value: true, description: 'Build tests') option('fast-heartbeat', type: 'boolean', value: false, description: 'Enable faster heartbeat rate') # Parameters option('gv-n-buffers', type: 'integer', min: 1, value: 16, description: 'Number of buffers used to receive GVSP packets') # Documentation and introspection option('introspection', type: 'feature', value: 'auto', description: 'Build introspection data (requires gobject-introspection)') option('documentation', type: 'feature', value: 'disabled', description: 'Build documentation (requires gi-docgen)') aravis-0.8.34/module/000077500000000000000000000000001475431451200143665ustar00rootroot00000000000000aravis-0.8.34/module/Makefile000066400000000000000000000022501475431451200160250ustar00rootroot00000000000000ifneq ($(KERNELRELEASE),) obj-m := aravis-module.o else ifeq ($(PREFIX),) PREFIX := /usr/local endif MODULE := aravis-module KDIR := /lib/modules/$(shell uname -r)/build INCLUDE := -isystem $(KDIR)/include MODCFLAGS := -DMODULE -D__KERNEL__ -Wall $(INCLUDE) KMAKE := $(MAKE) -C $(KDIR) SUBDIRS=$$PWD modules: $(MODULE).o insert_module: install modprobe -r aravis-module modprobe aravis-module ifneq ($(shell (uname -r | grep 2.6) > /dev/null ; echo -n $$?),0) echo A 2.6 kernel is required; exit 1 endif # build module $(MODULE).o: $(MODULE).c $(MODULE).h $(KMAKE) modules # Automake rules, as per "Third-Party Makefiles" in the automake manual all: $(MODULE).o distdir: cp aravis-module.c $(distdir) cp aravis-module.h $(distdir) cp Makefile $(distdir) install: $(KMAKE) modules_install [ -e /sbin/depmod ] && /sbin/depmod install-data: install-exec: uninstall: install-info: installdirs: check: installcheck: mostlyclean: clean: rm -f aravis-module.ko rm -f aravis-module.o rm -f aravis-module.mod.o rm -f aravis-module.mod.c rm -f modules.order rm -f Module.symvers distclean: clean maintainer-clean: dvi: pdf: info: html: tags: ctags: endif aravis-0.8.34/module/aravis-module.c000066400000000000000000000004101475431451200172750ustar00rootroot00000000000000#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Emmanuel Pacaud "); int init_module(void) { printk (KERN_ALERT "aravis: loaded\n"); return 0; } void cleanup_module(void) { printk (KERN_ALERT "aravis: unloaded\n"); } aravis-0.8.34/module/aravis-module.h000066400000000000000000000000001475431451200172750ustar00rootroot00000000000000aravis-0.8.34/po/000077500000000000000000000000001475431451200135175ustar00rootroot00000000000000aravis-0.8.34/po/LINGUAS000066400000000000000000000002041475431451200145400ustar00rootroot00000000000000# please keep this list sorted alphabetically # bs ca cs da de el es eu fi fr hu id lv oc pl pt pt_BR ru sl sr sr@latin sv tr zh_CN aravis-0.8.34/po/POTFILES.in000066400000000000000000000002551475431451200152760ustar00rootroot00000000000000# List of source files containing translatable strings. [type: gettext/glade]viewer/arv-viewer.ui viewer/data/arv-viewer.desktop.in.in viewer/data/arv-viewer.appdata.xml.in aravis-0.8.34/po/POTFILES.skip000066400000000000000000000002661475431451200156400ustar00rootroot00000000000000viewer/data/arv-viewer.desktop.in viewer/data/arv-viewer.appdata.xml # Workaround intltool bug https://bugs.launchpad.net/intltool/+bug/1117944 sub/viewer/data/arv-viewer.desktop.in aravis-0.8.34/po/bs.po000066400000000000000000000051751475431451200144730ustar00rootroot00000000000000# Bosnian translation for bosnianuniversetranslation # Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 # This file is distributed under the same license as the bosnianuniversetranslation package. # FIRST AUTHOR , 2014. # msgid "" msgstr "" "Project-Id-Version: bosnianuniversetranslation\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-02-27 03:48+0000\n" "PO-Revision-Date: 2015-02-19 17:15+0100\n" "Last-Translator: Samir Ribic \n" "Language-Team: Bosnian \n" "Language: bs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Launchpad-Export-Date: 2014-10-23 07:21+0000\n" "X-Generator: Launchpad (build 17203)\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Protok kadrova:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automatsko" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Ekspozicija:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Pojačanje:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Postavke" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Snimi snimak ekrana u direktorij s slikama" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis prosti preglednik" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Prikaži video tok iz kamera" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Aravis preglednik" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Prosti preglednik video tokova dobijenih programom Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Aravis Viewer je prosti preglednik za prikaz video tokova iz GENICAM-" "baziranih Ethernet industrijskih kamera." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Dopušta kontrolu osnovnih parametara dohvatanja, tj. broja kadrova, " "ekspozicije i pojačanja i mogućnost snimanja sirovih sporih slika" aravis-0.8.34/po/ca.po000066400000000000000000000033071475431451200144450ustar00rootroot00000000000000# Catalan translation for aravis. # Copyright (C) 2015 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Robert Antoni Buj Gelonch , 2015. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-11-29 15:53+0000\n" "PO-Revision-Date: 2015-11-30 00:20+0100\n" "Last-Translator: Robert Antoni Buj Gelonch \n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.6\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Velocitat dels fotogrames:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automàtic" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Exposició:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Guany:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Configuració" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Desa una instantània a la carpeta d'imatges" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visor simple Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Visualitzeu el flux de vídeo de les vostres càmeres" aravis-0.8.34/po/cs.po000066400000000000000000000052401475431451200144650ustar00rootroot00000000000000# Czech translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Marek Černocký , 2013, 2014. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-08-15 19:58+0000\n" "PO-Revision-Date: 2018-05-05 20:39+0200\n" "Last-Translator: Jaroslav Svoboda \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Gtranslator 2.91.6\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Snímková frekvence :" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automat" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Expozice:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Zisk:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Nastavení" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Uložit snímek do složky obrázků" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Jednoduchý prohlížeč Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Zobrazit video stream z vašich kamer" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Prohlížeč Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Jednoduchý prohlížeč video streamů získaných pomocí Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Prohlížeč Aravis je jednoduchý prohlížeč sloužící k zobrazení video " "streamů z průmyslových kamer typu GENICAM připojených přes Ethernet." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Umožňuje nastavit základní parametry snímání, tj. snímkovou frekvenci, expozici " "a zisk a také ukládat jednotlivé RAW snímky." aravis-0.8.34/po/da.po000066400000000000000000000034731475431451200144520ustar00rootroot00000000000000# Danish translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Joe Hansen , 2013. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-10-15 18:45+0200\n" "PO-Revision-Date: 2013-09-10 08:39+0000\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Billedhastighed:" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Belysning:" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Forstærkning:" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Auto" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Indstillinger" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Gem et øjebliksbillede i en billedmappe" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis - simpel fremviser" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Vis videostrømmen fra dine kameraer" aravis-0.8.34/po/de.po000066400000000000000000000037461475431451200144610ustar00rootroot00000000000000# German translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Christian Kirbach , 2013. # msgid "" msgstr "" "Project-Id-Version: aravis aravis-0-2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2013-06-09 21:12+0000\n" "PO-Revision-Date: 2013-06-14 08:14+0100\n" "Last-Translator: Christian Kirbach \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.5.5\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Bildrate:" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Belichtung:" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Verstärkungsgrad:" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Automatisch" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Einstellungen" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Einen Schnappschuss in einen Bilderordner speichern" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis Bildbetrachter" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Den Videostrom von Ihren Kameras anzeigen" aravis-0.8.34/po/el.po000066400000000000000000000061661475431451200144700ustar00rootroot00000000000000# Greek translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Dimitris Spingos , 2013. # Dimitris Spingos (Δημήτρης Σπίγγος) , 2013. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-03-12 15:48+0000\n" "PO-Revision-Date: 2015-08-30 13:08+0300\n" "Last-Translator: Tom Tryfonidis \n" "Language-Team: team@gnome.gr\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.4\n" "X-Project-Style: gnome\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Ρυθμός καρέ:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Αυτόματα" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Έκθεση:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Κέρδος:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Ρυθμίσεις" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Αποθήκευση ενός στιγμιοτύπου στον φάκελο εικόνας" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Απλό πρόγραμμα προβολής Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Εμφανίστε τα βίντεο από τις κάμερές σας" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Πρόγραμμα προβολής Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "" "Απλός προβολέας ροής βίντεο που έχει αποκτηθεί χρησιμοποιώντας το Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Ο Προβολέας Aravis είναι ένας απλός προβολέας ο οποίος χρησιμοποιείται για " "να απεικονίζει ροές βίντεο βασισμένα σε βιομηχανικές κάμερες GENICAM" #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Επιτρέπει τον έλεγχο βασικό παραμέτρων λήψης, π.χ. τον ρυθμό καρέ, έκθεσης, " "κέρδους και της αποθήκευσης μη συμπιεσμένων εικόνων." aravis-0.8.34/po/es.po000066400000000000000000000053011475431451200144650ustar00rootroot00000000000000# Spanish translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Daniel Mustieles , 2013, 2014. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-08-15 19:58+0000\n" "PO-Revision-Date: 2014-08-26 17:50+0200\n" "Last-Translator: Daniel Mustieles \n" "Language-Team: Español; Castellano \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 2.91.6\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Tasa de fotogramas:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automático" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Exposición:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Ganancia:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Configuración" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Guardar una captura en la carpeta de imágenes" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visor simple Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Mostrar el flujo de vídeo de sus cámaras" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 #| msgid "Aravis Simple Viewer" msgid "Aravis Viewer" msgstr "Visor Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Sencillo visor de flujos de vídeos adquiridos usando Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "El visor Aravis es un sencillo visor usado para mostrar flujos de vídeo de " "cámaras industriales basadas en Ethernet GENICAM." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Permite controlar los parámetros básicos de adquisición, como pueden ser la " "tasa de marcos, exposición y ganancia y guardar imágenes en formato RAW." aravis-0.8.34/po/eu.po000066400000000000000000000033461475431451200144760ustar00rootroot00000000000000# Basque translation for aravis # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Iñaki Larrañaga Murgoitio , 2014. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-05-01 12:09+0200\n" "PO-Revision-Date: 2014-05-01 12:12+0200\n" "Last-Translator: Iñaki Larrañaga Murgoitio \n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" #: ../viewer/arv-viewer-3.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Fotograma-emaria:" #: ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Automatikoa" #: ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Esposizioa:" #: ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Irabazia:" #: ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Ezarpenak" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Gorde pantaila-argazkia irudien karpetan" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis ikustaile xumea" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Bistaratu bideo-korronteak kameretatik" aravis-0.8.34/po/fi.po000066400000000000000000000046751475431451200144710ustar00rootroot00000000000000# Finnish translation for aravis. # Copyright (C) 2015 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Jiri Grönroos , 2015. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-10-07 14:55+0000\n" "PO-Revision-Date: 2015-10-07 19:10+0300\n" "Last-Translator: Jiri Grönroos \n" "Language-Team: suomi \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 2.91.6\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Kuvanopeus:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automaattinen" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Valotus:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Asetukset" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Tallenna tilannekuva kuvakansioon" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Katsele videovirtaa kameroista" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Aravis-katselin" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Yksinkertainen katselin Aravis-videovirtoihin" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Aravis-katselin on yksinkertainen katselin, jonka avulla voi katsella " "videovirtoja GENICAM-pohjaisista Ethernet-liitännäisistä " "teollisuuskameroista." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" aravis-0.8.34/po/fr.po000066400000000000000000000034411475431451200144700ustar00rootroot00000000000000# French translation for aravis # Copyright (C) 2010 Emmanuel Pacaud # This file is distributed under the same license as the aravis package. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-01-09 13:46+0100\n" "PO-Revision-Date: 2010-12-04 22:56+0100\n" "Last-Translator: Emmanuel Pacaud \n" "Language-Team: Emmanuel Pacaud \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Fréquence" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Exposition" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Gain" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Auto" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Paramètres" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Sauvegarde d'une capture dans le dossier images" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visualisateur de vidéo Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Affiche le flux vidéo de caméras Genicam" aravis-0.8.34/po/hu.po000066400000000000000000000065541475431451200145050ustar00rootroot00000000000000# Hungarian translation for aravis. # Copyright (C) 2013, 2014, 2016 Free Software Foundation, Inc. # This file is distributed under the same license as the aravis package. # # Balázs Úr , 2013, 2014. # Balázs Meskó , 2016. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: https://github.com/AravisProject/aravis/issues\n" "POT-Creation-Date: 2016-08-30 02:24+0000\n" "PO-Revision-Date: 2016-08-30 16:10+0200\n" "Last-Translator: Meskó Balázs \n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.8\n" #: ../viewer/arv-viewer.ui.h:1 msgid "Frame rate:" msgstr "Képkockasebesség:" #: ../viewer/arv-viewer.ui.h:2 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:3 msgid "Auto" msgstr "Automatikus" #: ../viewer/arv-viewer.ui.h:4 msgid "Exposure:" msgstr "Expozíció:" #: ../viewer/arv-viewer.ui.h:5 msgid "Gain:" msgstr "Nyereség:" #: ../viewer/arv-viewer.ui.h:6 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:7 msgid "dB" msgstr "dB" #: ../viewer/arv-viewer.ui.h:8 msgid "Id" msgstr "Azonosító" #: ../viewer/arv-viewer.ui.h:9 msgid "Manufacturer" msgstr "Gyártó" #: ../viewer/arv-viewer.ui.h:10 msgid "Model" msgstr "Modell" #: ../viewer/arv-viewer.ui.h:11 msgid "Serial" msgstr "Soros" #: ../viewer/arv-viewer.ui.h:12 msgid "Refresh camera list" msgstr "Kameralista frissítése" #: ../viewer/arv-viewer.ui.h:13 msgid "__glade_unnamed_23" msgstr "__glade_unnamed_23" #: ../viewer/arv-viewer.ui.h:14 msgid "Start video acquisition" msgstr "Videovétel indítása" #: ../viewer/arv-viewer.ui.h:15 msgid "__glade_unnamed_24" msgstr "__glade_unnamed_24" #: ../viewer/arv-viewer.ui.h:16 msgid "Pixel format:" msgstr "Képpontformátum:" #: ../viewer/arv-viewer.ui.h:17 msgid "Region size:" msgstr "Terület mérete:" #: ../viewer/arv-viewer.ui.h:18 msgid "x" msgstr "x" #: ../viewer/arv-viewer.ui.h:19 msgid "pixels" msgstr "képpont" #: ../viewer/arv-viewer.ui.h:20 msgid "Region position:" msgstr "Terület pozíciója:" #: ../viewer/arv-viewer.ui.h:21 msgid "Binning:" msgstr "Csoportokra osztás:" #: ../viewer/arv-viewer.ui.h:22 msgid "page0" msgstr "oldal0" #: ../viewer/arv-viewer.ui.h:23 msgid "page1" msgstr "oldal1" #: ../viewer/arv-viewer.ui.h:24 msgid "Return to camera list" msgstr "Vissza a kameralistához" #: ../viewer/arv-viewer.ui.h:25 msgid "Save a snapshot into image folder" msgstr "Pillanatkép mentése a képmappába" #: ../viewer/arv-viewer.ui.h:26 msgid "Rotate image to the right" msgstr "A kép elforgatása jobbra" #: ../viewer/arv-viewer.ui.h:27 msgid "Flip image horizontally" msgstr "A kép vízszintes tükrözése" #: ../viewer/arv-viewer.ui.h:28 msgid "Flip image vertically" msgstr "A kép függőleges tükrözése" #: ../viewer/arv-viewer.ui.h:29 msgid "Acquisition settings" msgstr "Vételi beállítások" #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis egyszerű megjelenítő" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "A videofolyam megjelenítése a kamerákról" aravis-0.8.34/po/id.po000066400000000000000000000050641475431451200144600ustar00rootroot00000000000000# Indonesian translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Andika Triwidada , 2013. # msgid "" msgstr "" "Project-Id-Version: aravis aravis-0-2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-08-15 19:58+0000\n" "PO-Revision-Date: 2014-09-05 20:17+0700\n" "Last-Translator: Andika Triwidada \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.6.9\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Laju frame" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Otomatis" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Eksposur:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Penguatan:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Pengaturan" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Simpan snapshot ke folder gambar" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis Peninjau Sederhana" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Menampilkan stream video dari kamera Anda" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Aravis Penampil" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Penampil stream video sederhana yang diperoleh memakai Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Penampil Aravis adalah sebuah penampil sederhana yang dipakai untuk " "menyajikan stream vide dari kamera industrial Ethernet berbasis GENICAM." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Ini memungkinkan untuk mengendalikan parameter akuisisi dasar, mis., laju " "rangka, pencahayaan dan penguatan, dan untuk menyimpan citra diam mentah." aravis-0.8.34/po/lv.po000066400000000000000000000036611475431451200145060ustar00rootroot00000000000000# This file is distributed under the same license as the aravis package. # # Rūdolfs Mazurs , 2013. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2013-09-28 08:58+0000\n" "PO-Revision-Date: 2013-10-03 15:44+0300\n" "Last-Translator: Rūdolfs Mazurs \n" "Language-Team: Latvian \n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " "2);\n" "X-Generator: Lokalize 1.5\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Kadru nomaiņas ātrums:" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Ekspozīcija:" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Pieaugums:" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Automātiski" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Iestatījumi" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Saglabāt momentuzņēmumu attēla mapē" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis vienkāršais skatītājs" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Rāda video straumes no jūsu kamerām" aravis-0.8.34/po/meson.build000066400000000000000000000002131475431451200156550ustar00rootroot00000000000000i18n = import ('i18n') i18n.gettext ('aravis-@0@'.format (aravis_api_version), args: '--directory=' + meson.project_source_root()) aravis-0.8.34/po/oc.po000066400000000000000000000047201475431451200144630ustar00rootroot00000000000000# Occitan translation for aravis # Copyright (C) 2010 Emmanuel Pacaud # This file is distributed under the same license as the aravis package. # Cédric Valmary (totenoc.org) , 2013. # Cédric Valmary (Tot en òc) , 2015. # Cédric Valmary (totenoc.eu) , 2015. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=aravis" "&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-05-20 16:18+0000\n" "PO-Revision-Date: 2015-06-07 15:47+0200\n" "Last-Translator: Cédric Valmary (totenoc.eu) \n" "Language-Team: Tot En Òc\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Virtaal 0.7.1\n" "X-Project-Style: gnome\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Frequéncia" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Auto" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Exposicion" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Ganh" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Paramètres" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Salvament d'una captura dins lo dorsièr imatges" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visualizador de vidèo Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Aficha lo flux vidèo de camèras Genicam" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 #| msgid "Aravis Simple Viewer" msgid "Aravis Viewer" msgstr "Visualizador de vidèo Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" aravis-0.8.34/po/pl.po000066400000000000000000000054311475431451200144750ustar00rootroot00000000000000# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # Aviary.pl # Jeśli masz jakiekolwiek uwagi odnoszące się do tłumaczenia lub chcesz # pomóc w jego rozwijaniu i pielęgnowaniu, napisz do nas: # gnomepl@aviary.pl # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- # Piotr Drąg , 2013-2014. # Aviary.pl , 2013-2014. msgid "" msgstr "" "Project-Id-Version: aravis\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-15 21:53+0200\n" "PO-Revision-Date: 2014-08-15 21:57+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Poedit-Language: Polish\n" "X-Poedit-Country: Poland\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Liczba klatek na sekundę:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automatycznie" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Naświetlenie:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Wzmocnienie:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Ustawienia" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Zapis migawki do katalogu obrazów" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Prosta przeglądarka Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Wyświetlanie strumienia wideo z kamery" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Przeglądarka Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Prosta przeglądarka strumieni wideo uzyskanych używając Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Prosta przeglądarka Aravis może być używana do wyświetlania strumieni wideo " "z przemysłowych kamer ethernetowych opartych o GENICAM." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Umożliwia kontrolowanie podstawowych parametrów, tzn. liczby klatek na " "sekundę, naświetlenia i wzmocnienia, a także zapisywanie surowych obrazów." aravis-0.8.34/po/pt.po000066400000000000000000000053451475431451200145110ustar00rootroot00000000000000# Portuguese translation for aravis. # Copyright (C) 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Tiagosdot , 2014. # Pedro Albuquerque , 2015. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-08-13 14:51+0000\n" "PO-Revision-Date: 2015-08-13 19:46+0100\n" "Last-Translator: Pedro Albuquerque \n" "Language-Team: Português \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 2.91.6\n" "X-Project-Style: gnome\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Taxa de molduras" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Auto" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Exposição:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Ganho:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Definições" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Gravar um instantâneo na pasta de imagens" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visualizador simples Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Mostrar o fluxo de vídeo das suas câmaras" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 #| msgid "Aravis Simple Viewer" msgid "Aravis Viewer" msgstr "Visualizador Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Visualizador simples de fluxos vídeo adquiridos com o Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "O Aravis é um visualizador simples usado para mostrar fluxos de vídeo de " "câmaras industriais Ethernet baseadas em GENICAM." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Permite controlar parâmetros básicos de aquisição, por exemplo, taxa de " "molduras, exposição, ganho e gravar imagens raw fixas." aravis-0.8.34/po/pt_BR.po000066400000000000000000000076331475431451200150760ustar00rootroot00000000000000# Brazilian Portuguese translation for aravis. # Copyright (C) 2017 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Rafael Fontenelle , 2013, 2014, 2017. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2017-10-02 04:14-0300\n" "PO-Revision-Date: 2017-10-02 04:16-0200\n" "Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Virtaal 1.0.0-beta1\n" #: ../viewer/arv-viewer.ui.h:1 msgid "Frame rate:" msgstr "Taxa de quadros:" #: ../viewer/arv-viewer.ui.h:2 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:3 msgid "Auto" msgstr "Auto" #: ../viewer/arv-viewer.ui.h:4 msgid "Exposure:" msgstr "Exposição:" #: ../viewer/arv-viewer.ui.h:5 msgid "Gain:" msgstr "Ganho:" #: ../viewer/arv-viewer.ui.h:6 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:7 msgid "dB" msgstr "dB" #: ../viewer/arv-viewer.ui.h:8 msgid "Id" msgstr "Id" #: ../viewer/arv-viewer.ui.h:9 msgid "Manufacturer" msgstr "Fabricante" #: ../viewer/arv-viewer.ui.h:10 msgid "Model" msgstr "Modelo" #: ../viewer/arv-viewer.ui.h:11 msgid "Serial" msgstr "Serial" #: ../viewer/arv-viewer.ui.h:12 msgid "Refresh camera list" msgstr "Atualizar lista de câmeras" #: ../viewer/arv-viewer.ui.h:13 msgid "Start video acquisition" msgstr "Iniciar aquisição de vídeo" #: ../viewer/arv-viewer.ui.h:14 msgid "Pixel format:" msgstr "Formato de pixels:" #: ../viewer/arv-viewer.ui.h:15 msgid "Region size:" msgstr "Tamanho da região:" #: ../viewer/arv-viewer.ui.h:16 msgid "x" msgstr "x" #: ../viewer/arv-viewer.ui.h:17 msgid "pixels" msgstr "pixels" #: ../viewer/arv-viewer.ui.h:18 msgid "Region position:" msgstr "Posição da região:" #: ../viewer/arv-viewer.ui.h:19 msgid "Binning:" msgstr "Agrupamento:" #: ../viewer/arv-viewer.ui.h:20 msgid "Return to camera list" msgstr "Retornar à lista de câmeras" #: ../viewer/arv-viewer.ui.h:21 msgid "Save a snapshot into image folder" msgstr "Salvar uma foto na pasta de imagens" #: ../viewer/arv-viewer.ui.h:22 msgid "Rotate image to the right" msgstr "Girar imagem para a direita" #: ../viewer/arv-viewer.ui.h:23 msgid "Flip image horizontally" msgstr "Virar imagem horizontalmente" #: ../viewer/arv-viewer.ui.h:24 msgid "Flip image vertically" msgstr "Virar imagem verticalmente" #: ../viewer/arv-viewer.ui.h:25 msgid "Acquisition settings" msgstr "Configurações de aquisição" #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Visualizador simples Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Exiba o fluxo de vídeo de suas câmeras" #~ msgid "Settings" #~ msgstr "Configurações" #~ msgid "Aravis Viewer" #~ msgstr "Visualizador Aravis" #~ msgid "Simple viewer of video stream acquired using Aravis" #~ msgstr "Visualizador simples de fluxo de vídeo obtido usando Aravis" #~ msgid "" #~ "Aravis Viewer is a simple viewer used to display video streams from " #~ "GENICAM-based Ethernet industrial cameras." #~ msgstr "" #~ "Visualizador Aravis é um visualizar simples usado para exibir fluxos de " #~ "vídeos de câmeras industriais Ethernet baseadas em GENICAM." #~ msgid "" #~ "It allows to control basic acquisition parameters, i.e. framerate, " #~ "exposure and gain, and to save raw still images." #~ msgstr "" #~ "Ele permite que você controle parâmetros básicos de aquisição, i.e taxa " #~ "de quadros, exposição e ganho, e para salvar imagens não tradadas (raw)." aravis-0.8.34/po/ru.po000066400000000000000000000042021475431451200145030ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: aravis-0-2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2013-11-28 22:26+0000\n" "PO-Revision-Date: 2013-12-18 22:38+0400\n" "Last-Translator: Yuri Myasoedov \n" "Language-Team: LANGUAGE \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Poedit 1.5.7\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "Частота кадров:" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "Экспозиция:" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "Усиление:" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "Гц" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "мкс" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "Авто" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "Настройки" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Сохранить снимок в папку изображений" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Приложение для просмотра видеопотоков" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Показать видеопоток с ваших камер" aravis-0.8.34/po/sk.po000066400000000000000000000051071475431451200144770ustar00rootroot00000000000000# Slovak translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Jaroslav Svoboda , 2018. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2018-05-05 20:26+0000\n" "Last-Translator: Jaroslav Svoboda \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Gtranslator 2.91.6\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Snímková frekvencia:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automat" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Expozícia:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Zisk:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Nastavenia" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Uložiť snímku do priečinka s obrázkami" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Jednoduchý prehliadač Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Zobrazit video stream z vašich kamier" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Prehliadač Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Jednoduchý prehliadač video streamu získaného pomocou Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Prehliadač Aravis je jednoduchý prehliadač slúžiaci na zobrazenie video " "streamov z priemyselných kamier typu GENICAM pripojených cez Ethernet." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Umožňuje nastaviť základné parametre snímania, tj. snímkovú frekvenciu, expozíciu " "a zisk a tiež ukladať jednotlivé RAW snímky." aravis-0.8.34/po/sl.po000066400000000000000000000052101475431451200144730ustar00rootroot00000000000000# Slovenian translation for aravis. # Copyright (C) 2013 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # # Martin Srebotnjak , 2013-2014. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-09-16 14:45+0000\n" "PO-Revision-Date: 2014-09-16 17:33+0100\n" "Last-Translator: Matej Urbančič \n" "Language-Team: Slovenian \n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n" "%100==4 ? 3 : 0);\n" "X-Poedit-SourceCharset: utf-8\n" "X-Generator: Poedit 1.5.4\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Hitrost:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Samodejno" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Osvetlitev:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Ojačitev:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Nastavitve" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Shrani zajeti fotogram v mapo slik" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Enostavni ogledovalnik Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Prikazovanje pretoka videa preko kamere" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Pregledovalnik Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Enostaven pregledovalnik video pretokov z uporabo protokola Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Aravis je enostaven pregledovalnik video vsebin preko industrijskih eternet " "kamer GENICAM." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Omogoča nadzor osnovnih parametrov, kot so: hitrost predvajanja, osvetlitev " "in shranjevanje zaslonskih sličic." aravis-0.8.34/po/sr.po000066400000000000000000000116711475431451200145110ustar00rootroot00000000000000# Serbian translation for aravis. # Copyright (C) 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Мирослав Николић , 2013—2016. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: https://github.com/AravisProject/aravis/issues\n" "POT-Creation-Date: 2016-09-04 02:24+0000\n" "PO-Revision-Date: 2016-09-04 10:57+0200\n" "Last-Translator: Мирослав Николић \n" "Language-Team: српски \n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : " "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Project-Style: gnome\n" #: ../viewer/arv-viewer.ui.h:1 msgid "Frame rate:" msgstr "Проток кадрова:" #: ../viewer/arv-viewer.ui.h:2 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:3 msgid "Auto" msgstr "Самостално" #: ../viewer/arv-viewer.ui.h:4 msgid "Exposure:" msgstr "Изложеност:" #: ../viewer/arv-viewer.ui.h:5 msgid "Gain:" msgstr "Појачање:" #: ../viewer/arv-viewer.ui.h:6 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:7 msgid "dB" msgstr "dB" #: ../viewer/arv-viewer.ui.h:8 msgid "Id" msgstr "Ид" #: ../viewer/arv-viewer.ui.h:9 msgid "Manufacturer" msgstr "Произвођач" #: ../viewer/arv-viewer.ui.h:10 msgid "Model" msgstr "Модел" #: ../viewer/arv-viewer.ui.h:11 msgid "Serial" msgstr "Серија" #: ../viewer/arv-viewer.ui.h:12 msgid "Refresh camera list" msgstr "Освежи списак камера" #: ../viewer/arv-viewer.ui.h:13 msgid "__glade_unnamed_23" msgstr "__глејд_неименовано_23" #: ../viewer/arv-viewer.ui.h:14 msgid "Start video acquisition" msgstr "Започни снимање" #: ../viewer/arv-viewer.ui.h:15 msgid "__glade_unnamed_24" msgstr "__глејд_неименовано_24" #: ../viewer/arv-viewer.ui.h:16 msgid "Pixel format:" msgstr "Формат пиксела:" #: ../viewer/arv-viewer.ui.h:17 msgid "Region size:" msgstr "Величина области:" #: ../viewer/arv-viewer.ui.h:18 msgid "x" msgstr "x" #: ../viewer/arv-viewer.ui.h:19 msgid "pixels" msgstr "пиксела" #: ../viewer/arv-viewer.ui.h:20 msgid "Region position:" msgstr "Положај области:" #: ../viewer/arv-viewer.ui.h:21 msgid "Binning:" msgstr "Смештање:" #: ../viewer/arv-viewer.ui.h:22 msgid "page0" msgstr "страница0" #: ../viewer/arv-viewer.ui.h:23 msgid "page1" msgstr "страница1" #: ../viewer/arv-viewer.ui.h:24 msgid "Return to camera list" msgstr "Врати се на списак камера" #: ../viewer/arv-viewer.ui.h:25 msgid "Save a snapshot into image folder" msgstr "Сачувај снимак екрана у фасциклу са сликама" #: ../viewer/arv-viewer.ui.h:26 msgid "Rotate image to the right" msgstr "Закрени слику на десно" #: ../viewer/arv-viewer.ui.h:27 msgid "Flip image horizontally" msgstr "Изврните слику водоравно" #: ../viewer/arv-viewer.ui.h:28 msgid "Flip image vertically" msgstr "Изврните слику усправно" #: ../viewer/arv-viewer.ui.h:29 msgid "Acquisition settings" msgstr "Подешавања снимања" #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Аравис" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Прегледач узорка Аравис" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Прикажите видео ток са ваше камере" #~ msgid "Settings" #~ msgstr "Подешавања" #~| msgid "Aravis Simple Viewer" #~ msgid "Aravis Viewer" #~ msgstr "Прегледач Аравис" #~ msgid "Simple viewer of video stream acquired using Aravis" #~ msgstr "Једноставан прегледач видео токова направљених коришћењем Арависа" #~ msgid "" #~ "Aravis Viewer is a simple viewer used to display video streams from " #~ "GENICAM-based Ethernet industrial cameras." #~ msgstr "" #~ "Прегледач Аравис је једноставан прегледач који се користи за приказивање " #~ "видео токова са етернет индустријских фото-апарата заснованих на ГЕНИКАМ-" #~ "у." #~ msgid "" #~ "It allows to control basic acquisition parameters, i.e. framerate, " #~ "exposure and gain, and to save raw still images." #~ msgstr "" #~ "Омогућава управљање основним параметрима снимања, тј. протоком кадрова, " #~ "изложеношћу и појачањем, као и чуватње сирових фотографија." aravis-0.8.34/po/sr@latin.po000066400000000000000000000054321475431451200156370ustar00rootroot00000000000000# Serbian translation for aravis. # Copyright (C) 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Miroslav Nikolić , 2013—2014. msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=aravis&" "keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-08-15 19:58+0000\n" "PO-Revision-Date: 2014-10-25 08:58+0200\n" "Last-Translator: Miroslav Nikolić \n" "Language-Team: Serbian \n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : " "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Project-Style: gnome\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Protok kadrova:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Samostalno" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Izloženost:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Pojačanje:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Podešavanja" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Sačuvaj snimak ekrana u fasciklu sa slikama" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Pregledač uzorka Aravis" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Prikažite video tok sa vašeg foto-aparata" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 #| msgid "Aravis Simple Viewer" msgid "Aravis Viewer" msgstr "Pregledač Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Jednostavan pregledač video tokova napravljenih korišćenjem Aravisa" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Pregledač Aravis je jednostavan pregledač koji se koristi za prikazivanje " "video tokova sa eternet industrijskih foto-aparata zasnovanih na GENIKAM-u." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Omogućava upravljanje osnovnim parametrima snimanja, tj. protokom kadrova, " "izloženošću i pojačanjem, kao i čuvatnje sirovih fotografija." aravis-0.8.34/po/sv.po000066400000000000000000000051651475431451200145160ustar00rootroot00000000000000# Swedish translation for aravis. # Copyright © 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Kristoffer Grundström , 2014. # msgid "" msgstr "" "Project-Id-Version: aravis aravis-0-2\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-08-23 02:51+0000\n" "PO-Revision-Date: 2015-08-22 08:49+0100\n" "Last-Translator: Josef Andersson \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.7.5\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Bildfrekvens:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Automatiskt" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Exponering:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Ökning:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Inställningar" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Spara en ögonblicksbild i mappen för bilder" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis enkel visare" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Visa upp videoströmmen från dina kameror" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Aravis visare" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "Enkel visare av videoströmmar inhämtade med Aravis" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Aravis visare är en enkel visare för att visa videoströmmar från trådbundna " "GENICAM-baserade industrikameror." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Det erbjuder kontroll av grundläggande anskaffningsparametrar, det vill säga " "bildfrekvens, exponering och ökning, samt att spara råa stillbilder." aravis-0.8.34/po/tr.po000066400000000000000000000053651475431451200145150ustar00rootroot00000000000000# Turkish translation for aravis. # Copyright (C) 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Muhammet Kara , 2014. # İşbaran Akçayır , 2015. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=aravis&" "keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2015-01-12 03:47+0000\n" "PO-Revision-Date: 2015-02-18 08:20+0000\n" "Last-Translator: İşbaran Akçayır \n" "Language-Team: Türkçe \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Pootle 2.5.1.1\n" "X-POOTLE-MTIME: 1424247649.000000\n" #: ../viewer/arv-viewer.ui.h:1 ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer.ui.h:2 msgid "Frame rate:" msgstr "Kare hızı:" #: ../viewer/arv-viewer.ui.h:3 msgid "Hz" msgstr "Hz" #: ../viewer/arv-viewer.ui.h:4 msgid "Auto" msgstr "Otomatik" #: ../viewer/arv-viewer.ui.h:5 msgid "Exposure:" msgstr "Pozlandırma:" #: ../viewer/arv-viewer.ui.h:6 msgid "Gain:" msgstr "Kazanç:" #: ../viewer/arv-viewer.ui.h:7 msgid "µs" msgstr "µs" #: ../viewer/arv-viewer.ui.h:8 msgid "Settings" msgstr "Ayarlar" #: ../viewer/arv-viewer.ui.h:9 msgid "Save a snapshot into image folder" msgstr "Resim klasörüne bir ekran görüntüsü kaydet" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis Basit Görüntüleyici" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "Kameralarınızdan gelen video akışını görüntüleyin" #: ../viewer/data/arv-viewer.appdata.xml.in.h:1 msgid "Aravis Viewer" msgstr "Aravis Görüntüleyici" #: ../viewer/data/arv-viewer.appdata.xml.in.h:2 msgid "Simple viewer of video stream acquired using Aravis" msgstr "" "Aravis kullanılarak elde edilen video akışı için basit bir görüntüleyici" #: ../viewer/data/arv-viewer.appdata.xml.in.h:3 msgid "" "Aravis Viewer is a simple viewer used to display video streams from GENICAM-" "based Ethernet industrial cameras." msgstr "" "Aravis Görüntüleyici GENICAM-temelli Eternet endüstriyel kameralardan video " "akışlarını görüntülemeye yarayan basit bir görüntüleyicidir." #: ../viewer/data/arv-viewer.appdata.xml.in.h:4 msgid "" "It allows to control basic acquisition parameters, i.e. framerate, exposure " "and gain, and to save raw still images." msgstr "" "Temel edinim parametrelerini kontrol etmeye yarar, örn. çerçevehızı, poz ve " "kazanç, ayrıca ham durağan resimleri kaydetmeye." aravis-0.8.34/po/zh_CN.po000066400000000000000000000035431475431451200150650ustar00rootroot00000000000000# Chinese (China) translation for aravis. # Copyright (C) 2014 aravis's COPYRIGHT HOLDER # This file is distributed under the same license as the aravis package. # Wylmer Wang , 2014. # msgid "" msgstr "" "Project-Id-Version: aravis master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" "product=aravis&keywords=I18N+L10N&component=general\n" "POT-Creation-Date: 2014-01-09 09:29+0000\n" "PO-Revision-Date: 2014-01-12 20:05+0800\n" "Last-Translator: Wylmer Wang \n" "Language-Team: Chinese (China) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../viewer/arv-viewer-2.ui.h:1 ../viewer/arv-viewer-3.ui.h:1 #: ../viewer/data/arv-viewer.desktop.in.in.h:1 msgid "Aravis" msgstr "Aravis" #: ../viewer/arv-viewer-2.ui.h:2 ../viewer/arv-viewer-3.ui.h:2 msgid "Frame rate:" msgstr "帧率:" #: ../viewer/arv-viewer-2.ui.h:3 ../viewer/arv-viewer-3.ui.h:5 msgid "Exposure:" msgstr "曝光:" #: ../viewer/arv-viewer-2.ui.h:4 ../viewer/arv-viewer-3.ui.h:6 msgid "Gain:" msgstr "增益:" #: ../viewer/arv-viewer-2.ui.h:5 ../viewer/arv-viewer-3.ui.h:3 msgid "Hz" msgstr "赫兹" #: ../viewer/arv-viewer-2.ui.h:6 ../viewer/arv-viewer-3.ui.h:7 msgid "µs" msgstr "微秒" #: ../viewer/arv-viewer-2.ui.h:7 ../viewer/arv-viewer-3.ui.h:4 msgid "Auto" msgstr "自动" #: ../viewer/arv-viewer-2.ui.h:8 ../viewer/arv-viewer-3.ui.h:8 msgid "Settings" msgstr "设置" #: ../viewer/arv-viewer-3.ui.h:9 msgid "Save a snapshot into image folder" msgstr "保存截屏到图片文件夹" #: ../viewer/data/arv-viewer.desktop.in.in.h:2 msgid "Aravis Simple Viewer" msgstr "Aravis 简章查看器" #: ../viewer/data/arv-viewer.desktop.in.in.h:3 msgid "Display the video stream from your cameras" msgstr "显示摄像头视频流" aravis-0.8.34/scripts/000077500000000000000000000000001475431451200145705ustar00rootroot00000000000000aravis-0.8.34/scripts/apply-format000077500000000000000000000242551475431451200171410ustar00rootroot00000000000000#! /bin/bash # # Copyright 2018 Undo Ltd. # # https://github.com/barisione/clang-format-hooks # Force variable declaration before access. set -u # Make any failure in piped commands be reflected in the exit code. set -o pipefail readonly bash_source="${BASH_SOURCE[0]:-$0}" ################## # Misc functions # ################## function error_exit() { for str in "$@"; do echo -n "$str" >&2 done echo >&2 exit 1 } ######################## # Command line parsing # ######################## function show_help() { if [ -t 1 ] && hash tput 2> /dev/null; then local -r b=$(tput bold) local -r i=$(tput sitm) local -r n=$(tput sgr0) else local -r b= local -r i= local -r n= fi cat << EOF ${b}SYNOPSIS${n} To reformat git diffs: ${i}$bash_source [OPTIONS] [FILES-OR-GIT-DIFF-OPTIONS]${n} To reformat whole files, including unchanged parts: ${i}$bash_source [-f | --whole-file] FILES${n} ${b}DESCRIPTION${n} Reformat C or C++ code to match a specified formatting style. This command can either work on diffs, to reformat only changed parts of the code, or on whole files (if -f or --whole-file is used). ${b}FILES-OR-GIT-DIFF-OPTIONS${n} List of files to consider when applying clang-format to a diff. This is passed to "git diff" as is, so it can also include extra git options or revisions. For example, to apply clang-format on the changes made in the last few revisions you could use: ${i}\$ $bash_source HEAD~3${n} ${b}FILES${n} List of files to completely reformat. ${b}-f, --whole-file${n} Reformat the specified files completely (including parts you didn't change). The fix is printed on stdout by default. Use -i if you want to modify the files on disk. ${b}--staged, --cached${n} Reformat only code which is staged for commit. The fix is printed on stdout by default. Use -i if you want to modify the files on disk. ${b}-i${n} Reformat the code and apply the changes to the files on disk (instead of just printing the fix on stdout). ${b}--apply-to-staged${n} This is like specifying both --staged and -i, but the formatting changes are also staged for commit (so you can just use "git commit" to commit what you planned to, but formatted correctly). ${b}--style STYLE${n} The style to use for reformatting code. If no style is specified, then it's assumed there's a .clang-format file in the current directory or one of its parents. ${b}--help, -h, -?${n} Show this help. EOF } # getopts doesn't support long options. # getopt mangles stuff. # So we parse manually... declare positionals=() declare has_positionals=false declare whole_file=false declare apply_to_staged=false declare staged=false declare in_place=false declare style=file declare ignored=() while [ $# -gt 0 ]; do declare arg="$1" shift # Past option. case "$arg" in -h | -\? | --help ) show_help exit 0 ;; -f | --whole-file ) whole_file=true ;; --apply-to-staged ) apply_to_staged=true ;; --cached | --staged ) staged=true ;; -i ) in_place=true ;; --style=* ) style="${arg//--style=/}" ;; --style ) [ $# -gt 0 ] || \ error_exit "No argument for --style option." style="$1" shift ;; --internal-opt-ignore-regex=* ) ignored+=("${arg//--internal-opt-ignore-regex=/}") ;; --internal-opt-ignore-regex ) ignored+=("${arg//--internal-opt-ignore-regex=/}") [ $# -gt 0 ] || \ error_exit "No argument for --internal-opt-ignore-regex option." ignored+=("$1") shift ;; -- ) # Stop processing further arguments. if [ $# -gt 0 ]; then positionals+=("$@") has_positionals=true fi break ;; -* ) error_exit "Unknown argument: $arg" ;; *) positionals+=("$arg") ;; esac done # Restore positional arguments, access them from "$@". if [ ${#positionals[@]} -gt 0 ]; then set -- "${positionals[@]}" has_positionals=true fi [ -n "$style" ] || \ error_exit "If you use --style you need to specify a valid style." ####################################### # Detection of clang-format & friends # ####################################### # clang-format. declare format="${CLANG_FORMAT:-}" if [ -z "$format" ]; then format=$(type -p clang-format) fi if [ -z "$format" ]; then error_exit \ $'You need to install clang-format.\n' \ $'\n' \ $'On Ubuntu/Debian this is available in the clang-format package or, in\n' \ $'older distro versions, clang-format-VERSION.\n' \ $'On Fedora it\'s available in the clang package.\n' \ $'You can also specify your own path for clang-format by setting the\n' \ $'$CLANG_FORMAT environment variable.' fi # clang-format-diff. if [ "$whole_file" = false ]; then invalid="/dev/null/invalid/path" if [ "${OSTYPE:-}" = "linux-gnu" ]; then readonly sort_version=-V else # On macOS, sort doesn't have -V. readonly sort_version=-n fi declare paths_to_try=() # .deb packages directly from upstream. # We try these first as they are probably newer than the system ones. while read -r f; do paths_to_try+=("$f") done < <(compgen -G "/usr/share/clang/clang-format-*/clang-format-diff.py" | sort "$sort_version" -r) # LLVM official releases (just untarred in /usr/local). while read -r f; do paths_to_try+=("$f") done < <(compgen -G "/usr/local/clang+llvm*/share/clang/clang-format-diff.py" | sort "$sort_version" -r) # Maybe it's in the $PATH already? This is true for Ubuntu and Debian. paths_to_try+=( \ "$(type -p clang-format-diff 2> /dev/null || echo "$invalid")" \ "$(type -p clang-format-diff.py 2> /dev/null || echo "$invalid")" \ ) # Fedora. paths_to_try+=( \ /usr/share/clang/clang-format-diff.py \ ) # Gentoo. while read -r f; do paths_to_try+=("$f") done < <(compgen -G "/usr/lib/llvm/*/share/clang/clang-format-diff.py" | sort -n -r) # Homebrew. while read -r f; do paths_to_try+=("$f") done < <(compgen -G "/usr/local/Cellar/clang-format/*/share/clang/clang-format-diff.py" | sort -n -r) declare format_diff= # Did the user specify a path? if [ -n "${CLANG_FORMAT_DIFF:-}" ]; then format_diff="$CLANG_FORMAT_DIFF" else for path in "${paths_to_try[@]}"; do if [ -e "$path" ]; then # Found! format_diff="$path" if [ ! -x "$format_diff" ]; then format_diff="python $format_diff" fi break fi done fi if [ -z "$format_diff" ]; then error_exit \ $'Cannot find clang-format-diff which should be shipped as part of the same\n' \ $'package where clang-format is.\n' \ $'\n' \ $'Please find out where clang-format-diff is in your distro and report an issue\n' \ $'at https://github.com/barisione/clang-format-hooks/issues with details about\n' \ $'your operating system and setup.\n' \ $'\n' \ $'You can also specify your own path for clang-format-diff by setting the\n' \ $'$CLANG_FORMAT_DIFF environment variable, for instance:\n' \ $'\n' \ $' CLANG_FORMAT_DIFF="python /.../clang-format-diff.py" \\\n' \ $' ' "$bash_source" fi readonly format_diff fi ############################ # Actually run the command # ############################ if [ "$whole_file" = true ]; then [ "$has_positionals" = true ] || \ error_exit "No files to reformat specified." [ "$staged" = false ] || \ error_exit "--staged/--cached only make sense when applying to a diff." read -r -a format_args <<< "$format" format_args+=("-style=file") [ "$in_place" = true ] && format_args+=("-i") "${format_args[@]}" "$@" else # Diff-only. if [ "$apply_to_staged" = true ]; then [ "$staged" = false ] || \ error_exit "You don't need --staged/--cached with --apply-to-staged." [ "$in_place" = false ] || \ error_exit "You don't need -i with --apply-to-staged." staged=true readonly patch_dest=$(mktemp) trap '{ rm -f "$patch_dest"; }' EXIT else readonly patch_dest=/dev/stdout fi declare git_args=(git diff -U0 --no-color) [ "$staged" = true ] && git_args+=("--staged") # $format_diff may contain a command ("python") and the script to excute, so we # need to split it. read -r -a format_diff_args <<< "$format_diff" [ "$in_place" = true ] && format_diff_args+=("-i") # Build the regex for paths to consider or ignore. # We use negative lookahead assertions which preceed the list of allowed patterns # (that is, the extensions we want). exclusions_regex= if [ "${#ignored[@]}" -gt 0 ]; then for pattern in "${ignored[@]}"; do exclusions_regex="$exclusions_regex(?!$pattern)" done fi "${git_args[@]}" "$@" \ | "${format_diff_args[@]}" \ -p1 \ -style="$style" \ -iregex="$exclusions_regex"'.*\.(c|cpp|cxx|cc|m|mm|js|java)' \ > "$patch_dest" \ || exit 1 if [ "$apply_to_staged" = true ]; then if [ ! -s "$patch_dest" ]; then echo "No formatting changes to apply." exit 0 fi patch -p0 < "$patch_dest" || \ error_exit "Cannot apply fix to local files." git apply -p0 --cached < "$patch_dest" || \ error_exit "Cannot apply fix to git staged changes." fi fi aravis-0.8.34/src/000077500000000000000000000000001475431451200136705ustar00rootroot00000000000000aravis-0.8.34/src/GenApiSchema_Version_1_0.xsd000066400000000000000000001143221475431451200210430ustar00rootroot00000000000000 Cache on write and write into register Write w/o cache, read updates cache Do not perform caching aravis-0.8.34/src/GenApiSchema_Version_1_1.xsd000066400000000000000000001651011475431451200210450ustar00rootroot00000000000000 The outer element of a device description file. It contains a set of nodes which are connected via pointers and which describe a mapping from features (like Gain) to a register space defined in terms of address and length. List of all possible node tpyes. Used for structuring device descripition files. Groups can be nested in any depth. They have no meaning with respect to the device functionaliy and are stripped off the device description file in a pre-processing step. Lists the elements which are common to (nearly) all nodes. Lists the attributes which are common to all nodes. Lists the elements which are common to all register nodes with a pLength element The simple node with no functionality. Contins references to features which are exposed to the user via API or GUI. Can be nested in any depth. Integer node which can be used to collect Value, Min, Max, and Inc or be used as Multiplexer or be used as Replicator. Maps to a register of Integer type. Maps to a subset (bit range) of a register of Integer type. Describes a collection of subsets (bit ranges) on the same integer register Describes a one subset (bit range) of a StructReg A node for issuing commands. A node for issuing boolean values based on an integer node Float node which can be used to collect Value, Min, and Max, or be used as Multiplexer. Bi-directional Float swiss knife. Bi-directional Integer swiss knife. Maps to a Float register. Enumeration mapping to an Integer. Possible value of an Enumeration node String node String mapping to a register Register exposed as byte array. Uno-directional formula interpreter Uno-directional formula interpreter Node mapping to a port accessing the device's registers. Accesses a IEEE1212 configuration ROM address block Accesses a Text entry in a IEEE1212 configuration ROM address block Accesses an Integer entry in a IEEE1212 configuration ROM address block Gets the address of a DCAM smart feature Gets the address of a smart feature Content to custom extend a device description file. The extension entries are stripped away during pre-processing of the device description file. Cache on write and write into register Write w/o cache, read updates cache Do not perform caching aravis-0.8.34/src/aravis.rules000066400000000000000000000015421475431451200162330ustar00rootroot00000000000000# Basler SUBSYSTEM=="usb", ATTRS{idVendor}=="2676", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # The Imaging Source SUBSYSTEM=="usb", ATTRS{idVendor}=="199e", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # Point Grey Research SUBSYSTEM=="usb", ATTRS{idVendor}=="1e10", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # Daheng Imaging SUBSYSTEM=="usb", ATTRS{idVendor}=="2ba2", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # Dahua Technology SUBSYSTEM=="usb", ATTRS{idVendor}=="2e03", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # Omron Sentech SUBSYSTEM=="usb", ATTRS{idVendor}=="1421", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # IDS Imaging Development Systems GmbH SUBSYSTEM=="usb", ATTRS{idVendor}=="1409", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" # Hikrobot SUBSYSTEM=="usb", ATTRS{idVendor}=="2bdf", MODE:="0666", TAG+="uaccess", TAG+="udev-acl" aravis-0.8.34/src/arv-fake-camera.xml000066400000000000000000000371471475431451200173500ustar00rootroot00000000000000 DeviceControl ImageFormatControl AcquisitionControl TransportLayerControl Debug DeviceVendorName DeviceModelName DeviceManufacturerInfo DeviceID DeviceVersion Vendor Name
0x48
32 RO Device
Model Name
0x68
32 RO Device
Manufacturer Info
0xa8
48 RO Device
Device ID
0xd8
16 RO Device
Device Version
0x88
32 RO Device
SensorHeight SensorWidth OffsetX OffsetY Width Height BinningHorizontal BinningVertical PixelFormat Full height of image sensor. SensorHeightRegister
0x118
4 RO Device Unsigned BigEndian
Full height of image sensor. SensorWidthRegister
0x11c
4 RO Device Unsigned BigEndian
X offset of image, in pixels. OffsetXRegister 0 SensorWidth 1
0x130
4 RW Device Unsigned BigEndian
Y offset of image, in pixels. OffsetYRegister 0 SensorHeight 1
0x134
4 RW Device Unsigned BigEndian
Width of image, in pixels. WidthRegister 1 SensorWidth 1
0x100
4 RW Device Unsigned BigEndian
Height of image, in pixels. HeightRegister 1 SensorHeight 1
0x104
4 RW Device Unsigned BigEndian
Binning in horizontal direction. BinningHorizontalRegister 1 16
0x108
4 RW Device Unsigned BigEndian
Binning in vertical direction. BinningVerticalRegister 1 16
0x10c
4 RW Device Unsigned BigEndian
Pixel format 17301515 17301514 17301512 17301513 17301505 35127316 17825799 PixelFormatRegister
0x128
4 RW Device Unsigned BigEndian
AcquisitionMode AcquisitionStart AcquisitionStop TriggerSelector TriggerMode TriggerSoftware TriggerSource TriggerActivation ExposureTimeAbs Start acquisition. AcquisitionCommandRegister 1 Stop acquisition. AcquisitionCommandRegister 0
0x124
4 WO Device Unsigned BigEndian
Acquisition mode 1 2 3 AcquisitionModeRegister
0x12c
4 RW Device Unsigned BigEndian
AcquisitionFrameRateConverter Frame rate, in frames per second. (1000000 / FROM) (1000000 / TO) AcquisitionFramePeriod AcquisitionFramePeriodRegister 1000 10000000
0x138
4 RW Device Unsigned BigEndian
0 1 TriggerSelectorInteger 0 Trigger mode 0 1 TriggerModeRegister
0x300
TriggerSelectorInteger 4 RW Device Unsigned BigEndian
Trigger source 0 1 TriggerSourceRegister
0x304
TriggerSelectorInteger 4 RW Device Unsigned BigEndian
Trigger activation 0 TriggerActivationRegister
0x308
TriggerSelectorInteger 4 RW Device Unsigned BigEndian
Generates an internal trigger. TriggerSource must be set to Software. TriggerSoftwareCommandRegister 1
0x30c
4 WO Device Unsigned BigEndian
Exposure duration, in microseconds. ExposureTimeAbsConverter 10.0 10000000.0 FROM TO ExposureTimeAbsRegister
0x120
4 RW Device Unsigned BigEndian
GainRaw GainAuto Raw gain. GainRawRegister 0 10
0x110
4 RW Device Unsigned BigEndian
Automatic gain mode. 1 3 2 GainAutoRegister
0x114
4 RW Device Unsigned BigEndian
PayloadSize Width Height PixelFormat WIDTH * HEIGHT * ((PIXELFORMAT>>16)&0xFF) / 8 Indicates whether a live grab is under way Invisible 0 0 1 TestRegister TestRegister 321 123
0x1f0
4 RW Device Unsigned BigEndian
0x1f0
4 RW Device BigEndian 31 16 Signed 15 0 Unsigned 15 31 0 Unsigned
0x200
32 RW Device
aravis-0.8.34/src/arv-gev-bootstrap.xml000066400000000000000000000256211475431451200200020ustar00rootroot00000000000000 Major version of the specification. Major version GevVersionMajorRegister
0x0
4 RO Device 15 0 Unsigned BigEndian
Minor version of the specification. Minor version GevVersionMinorRegister
0x0
4 RO Device 31 16 Unsigned BigEndian
Endianess of the device registers. Device mode is big endian GevDeviceModeIsBigEndianRegister
0x4
4 RO Device 0 0 Unsigned BigEndian
Character set used by all the strings of the bootstrap registers. Character Set GevDeviceModeCharacterSetRegister
0x4
4 RO Device 31 24 Unsigned BigEndian
MAC address of the network interface. MAC Address GevMACAddressFormula GevMACAddressHighRegister GevMACAddressLowRegister (HIGH << 32) | LOW
0x8
4 RO Device 31 16 Unsigned BigEndian
0xc
4 RO Device 31 0 Unsigned BigEndian
Reports the IP address for the given network interface. Current IP Address GevCurrentIPAddressRegister
0x24
4 RO Device 31 0 Unsigned BigEndian
Provides the subnet mask of the given interface. Current Subnet Mask GevCurrentSubnetMaskRegister
0x34
4 RO Device 31 0 Unsigned BigEndian
Vendor Name
0x48
32 RO Device
Model Name
0x68
32 RO Device
Device Version
0x88
32 RO Device
Manufacturer Info
0xa8
48 RO Device
Device ID
0xd8
16 RO Device
Indicates the number of physical network interfaces supported by this device. Number of Interfaces GevNumberOfInterfacesRegister Indicates the number of message channels supported by this device. Message Channel Count GevMessageChannelCountRegister
0x900
4 RO Device 31 0 Unsigned BigEndian
Indicates the number of stream channels supported by this device. Stream Channel Count GevStreamChannelCountRegister
0x904
4 RO Device 31 0 Unsigned BigEndian
Indicates the current heartbeat timeout in milliseconds. Heartbeat Timeout GevHeartbeatTimeoutRegister
0x938
4 RW Device 31 0 Unsigned BigEndian
Indicates the number of timestamp ticks during 1 second (frequency in Hz). Timestamp Tick Frequency GevTimestampTickFrequencySwissknife 0 4294967295 GevTimestampTickFrequencyHighRegister GevTimestampTickFrequencyLowRegister (HIGH << 32) | LOW
0x93c
4 RO Device 31 0 Unsigned BigEndian
0x940
4 RO Device 31 0 Unsigned BigEndian
Resets the Timestamp counter to 0. Timestamp Reset GevTimestampControlRegister 1 Latches current timestamp counter into GevTimestampValue. Timestamp Control Latch GevTimestampControlRegister 2
0x944
4 WO Device NoCache Unsigned BigEndian
Timestamp Value Guru GevTimestampValueSwissKnife PureNumber GevTimestampValueHighRegister GevTimestampValueLowRegister (HIGH << 32) | LOW
0x948
4 RO Device 31 0 Unsigned BigEndian
0x94c
4 RO Device 31 0 Unsigned BigEndian
Controls the device access privilege of an application. Control Channel Privilege CCP open access 1 CCP exclusive access 2 CCP control access Control 3 GevCCPRegister
0xa00
4 RW Device 31 0 Unsigned BigEndian
aravis-0.8.34/src/arv-test.cfg000066400000000000000000000135321475431451200161220ustar00rootroot00000000000000# arv-test configuration file # # The available tests are: # # * Genicam: Genicam data retrieval and schema validation # * Properties: Basic device feature check # * MultipleAcquisitionA: Acquire 10 buffers in continuous mode # * SingleAcquisition: Acquire a single buffer # * SoftwareTrigger: Acquire 5 buffers using software trigger # * MultipleAcquisitionB: Same as MultipleAcquisitionA but with a different frame rate # * Multipart: Acquire multipart buffers (GigEVision only) # * Chunks: Acquire a buffer with chunk data # * GigEVision: GigEVision specific checks # * USB3Vision: USB3Vision specific checks # # A Test can be ignored using `TestName=false`. # A delay can be added at the start of the test using `TestName=`. # # Genicam # ------- # # Schema={1.1|1.0|Invalid} (default: Invalid) # # Define the schema version used for validation. If 'Invalid' is specified, the Genicam data are retrieved but not # validated. # # Properties # ---------- # # SensorSize=; # # Define the sensor size. If it is not present, the sensor size is read, but not checked. # # GainAvailable={true|false} (default: true) # # Define if gain setting is available. # # ExposureTimeAvailable={true|false} (default: true) # # Define if exposure time setting is available. # # MultipleAcquisitionA, MultipleAcquisitionB # ------------------------------------------ # # UseSystemTimestamp={true|false} (default: false) # # Some devices set an incorrect timestamp in the buffer metadata. By setting UseSystemTimestamp to true, arv-test will # use the system timestamp which is set by the stream receiving thread using the system clock. # # FrameRateA= (default: 10) # # Set the frame rate for MultipleAcquisitionA test. # # FrameRateB= (default: 5) # # Set the frame rate for MultipleAcquisitionB test. # # SoftwareTrigger # --------------- # # SoftwareTriggerSupport={true,false} (default:true) # # Define if software trigger is supported by the device. # # SoftwareTriggerWait= (default: 0) # # Wait time between 2 software triggers. Some devices don't accept a software trigger as soon as the previous buffer was # received. # # Chunks # ------ # # ChunksSupport={true|false} (default: true) # # Define if chunk data are supported by the device. # # ChunkList= [] ... (default:OffsetX OffsetY) # # Define an alternative chunk list in case the default chunks are not available. # # ChunksNParts= (default: 1) # # Define the number of parts in image buffers when chunks are enabled. # # GigEVision # ---------- # # NNetworkInterfaces= (default: 1) # # Number of network interfaces # # NStreamChannels= (default: 1) # # Number of stream channels [Aravis:Fake] ChunksSupport=false Schema=Invalid SensorSize=2048;2048 SoftwareTriggerSupport=true [Basler:acA1300-30gc] ChunkList=Timestamp Framecounter ChunksNParts=0 Schema=1.1 SensorSize=1294;964 # A delay is needed for SoftwareTrigger test to pass. Not sure why for now. SoftwareTriggerDelay=1.0 [Basler:acA1300-60gmNIR] Schema=1.1 SensorSize=1282;1026 # Without the delay, the test fails and the camera will not stream anymore # Seen with original firmware 3.15-6 and the latest one 4.0-5 SingleAcquisitionDelay=1.0 ChunksNParts=0 [Basler:acA1920-25um] ChunkList=Timestamp CounterValue Schema=Invalid SensorSize=1920;1080 [Basler:acA1920-255um] Schema=1.1 SensorSize=1936;1216 [Basler:acA1920-155um] ChunkList=Timestamp CounterValue Schema=Invalid SensorSize=1936;1216 [Basler:daA1920-160um] Schema=Invalid SensorSize=1936;1216 SoftwareTriggerDelay=0.1 SoftwareTriggerWait=0.006 ChunkList=FrameID [Basler:daA1440-220uc] Schema=Invalid SensorSize=1456;1088 SoftwareTriggerDelay=0.1 SoftwareTriggerWait=0.006 ChunkList=FrameID [Basler:daA3840-45uc] Schema=Invalid SensorSize=3860;2178 ChunkList=FrameID [FLIR:Firefly FFY-U3-16S2M-DL] Schema=Invalid SensorSize=1456;1098 [Hikrobot:MV-CB013-A0UM-S] Schema=Invalid SensorSize=1280;1024 ChunksSupport=false # Camera timestamp unit is wrong by a factor 10 UseSystemTimestamp=true [Hikrobot:MV-CB060-10UC-S] Schema=Invalid SensorSize=3072;2048 ChunksSupport=false # Camera timestamp unit is wrong by a factor 0.694 UseSystemTimestamp=true [Lucid Vision Labs:TRI004S-C] Schema=Invalid SensorSize=720;540 [Point Grey Research:Blackfly S BFS-U3-13Y3C] Schema=Invalid SensorSize=1280;1024 [Point Grey Research:Blackfly BFLY-PGE-14S2C] ChunksNParts=0 Schema=1.1 SensorSize=1296;1032 SoftwareTrigger=true SoftwareTriggerWait=0.2 [SICK:Visionary-S CX V3S102-1x] Schema=Invalid SensorSize=640;512 GainAvailable=false MultipartDelay=0.0 Multipart=Range Intensity SoftwareTrigger=true # A delay is needed for SoftwareTrigger test to pass. Not sure why for now. # GigEVision write_memory error (busy) SoftwareTriggerDelay=1.0 # A delay between each TriggerSoftware command is needed. Not sure why for now. # GigEVision write_memory error (busy) SoftwareTriggerWait=0.2 ChunksDelay=1.0 ChunkList=Timestamp [Smartek:GC651M] ChunksSupport=false Schema=1.1 SensorSize=659;494 # A delay is needed for SoftwareTrigger test to pass. Not sure why for now. SoftwareTriggerDelay=1.0 [Smartek:GC1281M-S90] ChunksSupport=false Schema=1.1 SensorSize=1280;1024 # A delay is needed for SoftwareTrigger test to pass. Not sure why for now. SoftwareTriggerDelay=1.0 [Smartek:GC1392M-A90] ChunksSupport=false Schema=1.1 SensorSize=1392;1040 [The Imaging Source Europe GmbH:DFK 33GX265] ChunksSupport=false Schema=Invalid SensorSize=2048;1536 # Camera timestamp unit is wrong (µs instead of ns) UseSystemTimestamp=true [The Imaging Source Europe GmbH:DFK 33UJ003] ChunksSupport=false Schema=Invalid SensorSize=3856;2764 # Camera timestamp unit is wrong (µs instead of ns) UseSystemTimestamp=true [The Imaging Source Europe GmbH:DMK 23G618] FrameRateA=15.0 FrameRateB=7.5 ChunksSupport=false Schema=1.1 SensorSize=640;480 aravis-0.8.34/src/arv.h000066400000000000000000000060751475431451200146410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_H #define ARV_H #define ARV_H_INSIDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ARAVIS_HAS_USB #include #include #include #endif #include #include #include #undef ARV_H_INSIDE #endif aravis-0.8.34/src/arvapi.h.in000066400000000000000000000020631475431451200157310ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_API_H #define ARV_API_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #define ARV_API @ARV_API@ #endif aravis-0.8.34/src/arvbuffer.c000066400000000000000000000606341475431451200160270ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * ArvBuffer: * * [class@ArvBuffer] provides a class for the instantiation of buffers used for the storage of the separate images of * the video stream. * * The actual data space may either be allocated by [class@ArvBuffer] during an object instantiation, of pre-allocated. * [class@ArvBuffer] also allows the transmission of image metadata, such as offsets and size of the transmitted region of * interest, pixel format and time stamp. */ #include static gboolean arv_buffer_part_is_image (ArvBuffer *buffer, guint part_id) { return (ARV_IS_BUFFER (buffer) && buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS && buffer->priv->n_parts > 0 && part_id < buffer->priv->n_parts && (buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA || buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_MULTIPART || buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER ) && (buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_BIPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_TRIPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_QUADPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_3D_IMAGE || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_BIPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_TRIPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_QUADPLANAR || buffer->priv->parts[part_id].data_type == ARV_BUFFER_PART_DATA_TYPE_CONFIDENCE_MAP)); } /** * arv_buffer_new_full: * @size: payload size * @preallocated: (transfer none): preallocated memory buffer * @user_data: (transfer none): a pointer to user data associated to this buffer * @user_data_destroy_func: (nullable): an optional user data destroy callback * * Creates a new buffer for the storage of the video stream images. * The data space can be either preallocated, and the caller is responsible * for it's deallocation, or allocated by this function. If it is the case, * data memory will be freed when the buffer is destroyed. * * If @user_data_destroy_func is non NULL, it will be called in order to destroy * user_data when the buffer is destroyed. * * Returns: a new [class@ArvBuffer] object * * Since: 0.2.0 */ ArvBuffer * arv_buffer_new_full (size_t size, void *preallocated, void *user_data, GDestroyNotify user_data_destroy_func) { ArvBuffer *buffer; buffer = g_object_new (ARV_TYPE_BUFFER, NULL); buffer->priv->allocated_size = size; buffer->priv->user_data = user_data; buffer->priv->user_data_destroy_func = user_data_destroy_func; buffer->priv->chunk_endianness = G_BIG_ENDIAN; buffer->priv->payload_type = ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN; buffer->priv->parts = g_new0 (ArvBufferPartInfos, 1); buffer->priv->n_parts = 1; if (preallocated != NULL) { buffer->priv->is_preallocated = TRUE; buffer->priv->data = preallocated; } else { buffer->priv->is_preallocated = FALSE; buffer->priv->data = g_malloc (size); } return buffer; } /** * arv_buffer_new: * @size: payload size * @preallocated: (transfer none): preallocated memory buffer * * Creates a new buffer for the storage of the video stream images. * The data space can be either preallocated, and the caller is responsible * for it's deallocation, or allocated by this function. If it is the case, * data memory will be freed when the buffer is destroyed. * * Returns: a new #ArvBuffer object * * Since: 0.2.0 */ ArvBuffer * arv_buffer_new (size_t size, void *preallocated) { return arv_buffer_new_full (size, preallocated, NULL, NULL); } /** * arv_buffer_new_allocate: * @size: payload size * * Creates a new buffer for the storage of the video stream images. * The data space is allocated by this function, and will * be freed when the buffer is destroyed. * * Returns: a new #ArvBuffer object * * Since: 0.2.3 */ ArvBuffer * arv_buffer_new_allocate (size_t size) { return arv_buffer_new_full (size, NULL, NULL, NULL); } /** * arv_buffer_get_data: * @buffer: a #ArvBuffer * @size: (out) (optional): location to store data size, or %NULL * * Buffer data accessor. * * Returns: (array length=size) (element-type guint8): a pointer to the buffer data. * * Since: 0.4.0 **/ const void * arv_buffer_get_data (ArvBuffer *buffer, size_t *size) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), NULL); if (size != NULL) *size = buffer->priv->received_size; return buffer->priv->data; } typedef struct ARAVIS_PACKED_STRUCTURE { guint32 id; guint32 size; } ArvChunkInfos; /** * arv_buffer_has_chunks: * @buffer: a #ArvBuffer * * Returns: %TRUE if @buffer has a payload type that contains chunk data. * * Since: 0.8.0 */ gboolean arv_buffer_has_chunks (ArvBuffer *buffer) { return ARV_IS_BUFFER (buffer) && buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS && buffer->priv->has_chunks; } /** * arv_buffer_get_chunk_data: * @buffer: a #ArvBuffer * @chunk_id: chunk id * @size: (allow-none): location to store chunk data size, or %NULL * * Chunk data accessor. * * Returns: (array length=size) (element-type guint8): a pointer to the chunk data. * * Since: 0.4.0 **/ const void * arv_buffer_get_chunk_data (ArvBuffer *buffer, guint64 chunk_id, size_t *size) { ArvChunkInfos *infos; unsigned char *data; ptrdiff_t offset; if (size != NULL) *size = 0; g_return_val_if_fail (arv_buffer_has_chunks (buffer), NULL); g_return_val_if_fail (buffer->priv->data != NULL, NULL); data = buffer->priv->data; offset = buffer->priv->received_size - sizeof (ArvChunkInfos); while (offset > 0) { guint32 id; guint32 chunk_size; infos = (ArvChunkInfos *) &data[offset]; if (buffer->priv->chunk_endianness == G_BIG_ENDIAN) { id = GUINT32_FROM_BE (infos->id); chunk_size = GUINT32_FROM_BE (infos->size); } else { id = GUINT32_FROM_LE (infos->id); chunk_size = GUINT32_FROM_LE (infos->size); } if (id == chunk_id) { ptrdiff_t data_offset; data_offset = offset - chunk_size; if (data_offset >= 0) { if (size != NULL) *size = chunk_size; return &data[data_offset]; } else return NULL; } if (chunk_size > 0) offset = offset - chunk_size - sizeof (ArvChunkInfos); else offset = 0; }; return NULL; } /** * arv_buffer_has_gendc: * @buffer: a #ArvBuffer * * Returns: %TRUE if @buffer has a payload type that contains GenDC Data. * * Since: 0.8.31 */ gboolean arv_buffer_has_gendc (ArvBuffer *buffer) { return ARV_IS_BUFFER (buffer) && buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS && buffer->priv->has_gendc; } /** * arv_buffer_get_gendc_data: * @buffer: a #ArvBuffer * @size: (allow-none): location to store chunk data size, or %NULL * * GenDC Data accessor. * * Returns: (array length=size) (element-type guint8): a pointer to the GenDC Data . * * Since: 0.8.31 **/ const void * arv_buffer_get_gendc_data (ArvBuffer *buffer, size_t *size) { g_return_val_if_fail (arv_buffer_has_gendc (buffer), NULL); g_return_val_if_fail (buffer->priv->data != NULL, NULL); if (size != NULL) *size = buffer->priv->gendc_data_size; if (*size == 0) return NULL; return buffer->priv->data + buffer->priv->gendc_data_offset; } /** * arv_buffer_get_gendc_descriptor: * @buffer: a #ArvBuffer * @size: (allow-none): location to store chunk data size, or %NULL * * GenDC Descriptor accessor. * * Returns: (array length=size) (element-type guint8): a pointer to the GenDC Descriptor. * * Since: 0.8.31 **/ const void * arv_buffer_get_gendc_descriptor (ArvBuffer *buffer, size_t *size) { g_return_val_if_fail (arv_buffer_has_gendc (buffer), NULL); g_return_val_if_fail (buffer->priv->data != NULL, NULL); if (size != NULL) *size = buffer->priv->gendc_descriptor_size; if (*size == 0) return NULL; return buffer->priv->data; } /** * arv_buffer_get_user_data: * @buffer: a #ArvBuffer * * Gets a pointer to user data set by arv_buffer_new_full. * * Returns: user data, or NULL if not set. * * Since: 0.4.0 */ const void * arv_buffer_get_user_data (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), NULL); return buffer->priv->user_data; } /** * arv_buffer_get_status: * @buffer: a #ArvBuffer * * Gets the buffer acquisition status. * * Returns: buffer acquisition status. * * Since: 0.4.0 */ ArvBufferStatus arv_buffer_get_status (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), ARV_BUFFER_STATUS_UNKNOWN); return buffer->priv->status; } /** * arv_buffer_get_payload_type: * @buffer: a #ArvBuffer * * Gets the buffer payload type. * * Returns: payload type. * * Since: 0.4.0 */ ArvBufferPayloadType arv_buffer_get_payload_type (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), -1); return buffer->priv->payload_type; } /** * arv_buffer_get_timestamp: * @buffer: a #ArvBuffer * * Gets the buffer camera timestamp, expressed as nanoseconds. Not all devices * provide reliable timestamp, which means sometimes its better to rely on the * buffer completion host local time, or to use * arv_buffer_get_system_timestamp(). * * Returns: buffer timestamp, in nanoseconds. * * Since: 0.4.0 */ guint64 arv_buffer_get_timestamp (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0); return buffer->priv->timestamp_ns; } /** * arv_buffer_set_timestamp: * @buffer: a #ArvBuffer * @timestamp_ns: a timestamp, expressed as nanoseconds * * Sets the buffer timestamp, which allows to override the timpestamp set by * the camera, which in some case is incorrect. * * Since: 0.4.0 */ void arv_buffer_set_timestamp (ArvBuffer *buffer, guint64 timestamp_ns) { g_return_if_fail (ARV_IS_BUFFER (buffer)); buffer->priv->timestamp_ns = timestamp_ns; } /** * arv_buffer_get_system_timestamp: * @buffer: a #ArvBuffer * * Gets the system timestamp for when the frame was received. Expressed in * nanoseconds. * * Returns: buffer system timestamp, in nanoseconds. * * Since: 0.6.0 */ guint64 arv_buffer_get_system_timestamp (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0); return buffer->priv->system_timestamp_ns; } /** * arv_buffer_set_system_timestamp: * @buffer: a #ArvBuffer * @timestamp_ns: a timestamp, expressed as nanoseconds * * Sets the system timestamp for when the frame was received. Expressed in * nanoseconds. * * Since: 0.6.0 */ void arv_buffer_set_system_timestamp (ArvBuffer *buffer, guint64 timestamp_ns) { g_return_if_fail (ARV_IS_BUFFER (buffer)); buffer->priv->system_timestamp_ns = timestamp_ns; } /** * arv_buffer_get_frame_id: * @buffer: a #ArvBuffer * * Gets the buffer frame id. For GigEVision devices, 0 is an invalid value. * * Returns: frame id, 0 on error. * * Since: 0.8.0 */ guint64 arv_buffer_get_frame_id (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0); return buffer->priv->frame_id; } /** * arv_buffer_set_frame_id: * @buffer: a #ArvBuffer * @frame_id: a #guint64 * * Sets the buffer frame id. For GigEVision devices, 0 is an invalid value. * * Since: 0.8.3 */ void arv_buffer_set_frame_id (ArvBuffer *buffer, guint64 frame_id) { g_return_if_fail (ARV_IS_BUFFER (buffer)); buffer->priv->frame_id = frame_id; } /** * arv_buffer_get_n_parts: * @buffer: a #ArvBuffer * * Returns: the number of part in the buffer. * * Since: 0.8.23 */ guint arv_buffer_get_n_parts (ArvBuffer *buffer) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0); return buffer->priv->n_parts; } /** * arv_buffer_find_component: * @buffer: a #ArvBuffer * @component_id: the component id to find * * Search for the part corresponding to @component_id * * Return: the corresponding part id, -1 if not found. * * Since: 0.8.25 */ gint arv_buffer_find_component (ArvBuffer *buffer, guint component_id) { guint i; g_return_val_if_fail (ARV_IS_BUFFER (buffer), -1); for (i = 0; i < buffer->priv->n_parts; i++) if (buffer->priv->parts[i].component_id == component_id) return i; return -1; } /** * arv_buffer_get_part_data: * @buffer: a #ArvBuffer * @part_id: part id * @size: (out) (optional): data size placeholder * * Returns: (array length=size) (element-type guint8): a pointer to the part data. * * Since: 0.8.23 */ const void * arv_buffer_get_part_data (ArvBuffer *buffer, guint part_id, size_t *size) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), NULL); g_return_val_if_fail (part_id < buffer->priv->n_parts, NULL); if (size != NULL) *size = buffer->priv->parts[part_id].size; return buffer->priv->data + buffer->priv->parts[part_id].data_offset; } /** * arv_buffer_get_part_component_id: * @buffer: a #ArvBuffer * @part_id: part id * * Returns: the part component id value * * Since: 0.8.25 */ guint arv_buffer_get_part_component_id (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0); g_return_val_if_fail (part_id < buffer->priv->n_parts, 0); return buffer->priv->parts[part_id].component_id; } /** * arv_buffer_get_part_data_type: * @buffer: a #ArvBuffer * @part_id: part id * * Returns: the part #ArvBufferPartDataType * * Since: 0.8.23 */ ArvBufferPartDataType arv_buffer_get_part_data_type (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (ARV_IS_BUFFER (buffer), ARV_BUFFER_PART_DATA_TYPE_UNKNOWN); g_return_val_if_fail (part_id < buffer->priv->n_parts, ARV_BUFFER_PART_DATA_TYPE_UNKNOWN); return buffer->priv->parts[part_id].data_type; } /** * arv_buffer_get_part_pixel_format: * @buffer: a #ArvBuffer * @part_id: a part id * * Gets the part pixel format. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: part pixel format. * * Since: 0.8.23 */ ArvPixelFormat arv_buffer_get_part_pixel_format (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (arv_buffer_part_is_image (buffer, part_id), 0); return buffer->priv->parts[part_id].pixel_format; } /** * arv_buffer_get_part_region: * @buffer: a #ArvBuffer * @part_id: a part id * @x: (out) (optional): x offset placeholder * @y: (out) (optional): y offset placeholder * @width: (out) (optional): width placeholder * @height: (out) (optional): height placeholder * * Gets the part region. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Since: 0.8.23 */ void arv_buffer_get_part_region (ArvBuffer *buffer, guint part_id, gint *x, gint *y, gint *width, gint *height) { g_return_if_fail (arv_buffer_part_is_image (buffer, part_id)); if (x != NULL) *x = buffer->priv->parts[part_id].x_offset; if (y != NULL) *y = buffer->priv->parts[part_id].y_offset; if (width != NULL) *width = buffer->priv->parts[part_id].width; if (height != NULL) *height = buffer->priv->parts[part_id].height; } /** * arv_buffer_get_part_padding: * @buffer: a #ArvBuffer * @part_id: a part id * @x_padding: (out) (optional): x offset placeholder * @y_padding: (out) (optional): y offset placeholder * * Gets the part padding. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Since: 0.8.23 */ void arv_buffer_get_part_padding (ArvBuffer *buffer, guint part_id, gint *x_padding, gint *y_padding) { g_return_if_fail (arv_buffer_part_is_image (buffer, part_id)); if (x_padding != NULL) *x_padding = buffer->priv->parts[part_id].x_padding; if (y_padding != NULL) *y_padding = buffer->priv->parts[part_id].y_padding; } /** * arv_buffer_get_part_width: * @buffer: a #ArvBuffer * @part_id: a part id * * Gets the part width. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: width, in pixels. * * Since: 0.8.23 */ gint arv_buffer_get_part_width (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (arv_buffer_part_is_image (buffer, part_id), 0); return buffer->priv->parts[part_id].width; } /** * arv_buffer_get_part_height: * @buffer: a #ArvBuffer * @part_id: a part id * * Gets the part height. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: height, in pixels. * * Since: 0.8.23 */ gint arv_buffer_get_part_height (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (arv_buffer_part_is_image (buffer, part_id), 0); return buffer->priv->parts[part_id].height; } /** * arv_buffer_get_part_x: * @buffer: a #ArvBuffer * @part_id: a part id * * Gets the part x offset. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: x offset, in pixels. * * Since: 0.8.23 */ gint arv_buffer_get_part_x (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (arv_buffer_part_is_image (buffer, part_id), 0); return buffer->priv->parts[part_id].x_offset; } /** * arv_buffer_get_part_y: * @buffer: a #ArvBuffer * @part_id: a part id * * Gets the part y_offset. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: y offset, in pixels. * * Since: 0.8.23 */ gint arv_buffer_get_part_y (ArvBuffer *buffer, guint part_id) { g_return_val_if_fail (arv_buffer_part_is_image (buffer, part_id), 0); return buffer->priv->parts[part_id].y_offset; } /** * arv_buffer_get_image_data: * @buffer: a #ArvBuffer * @size: (out) (optional): data size placeholder * * Returns: (array length=size) (element-type guint8): a pointer to the image data. * * Since: 0.8.25 */ const void * arv_buffer_get_image_data (ArvBuffer *buffer, size_t *size) { return arv_buffer_get_part_data (buffer, 0, size); } /** * arv_buffer_get_image_pixel_format: * @buffer: a #ArvBuffer * * Gets the image pixel format. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: image pixel format. * * Since: 0.4.0 */ ArvPixelFormat arv_buffer_get_image_pixel_format (ArvBuffer *buffer) { return arv_buffer_get_part_pixel_format (buffer, 0); } /** * arv_buffer_get_image_region: * @buffer: a #ArvBuffer * @x: (out) (optional): image x offset placeholder * @y: (out) (optional): image y offset placeholder * @width: (out) (optional): image width placholder * @height: (out) (optional): image height placeholder * * Gets the image region. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Since: 0.4.0 */ void arv_buffer_get_image_region (ArvBuffer *buffer, gint *x, gint *y, gint *width, gint *height) { arv_buffer_get_part_region (buffer, 0, x, y, width, height); } /** * arv_buffer_get_image_padding: * @buffer: a #ArvBuffer * @x_padding: (out) (optional): image x offset placeholder * @y_padding: (out) (optional): image y offset placeholder * * Gets the image padding. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Since: 0.8.23 */ void arv_buffer_get_image_padding (ArvBuffer *buffer, gint *x_padding, gint *y_padding) { arv_buffer_get_part_padding (buffer, 0, x_padding, y_padding); } /** * arv_buffer_get_image_width: * @buffer: a #ArvBuffer * * Gets the image width. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: image width, in pixels. * * Since: 0.4.0 */ gint arv_buffer_get_image_width (ArvBuffer *buffer) { return arv_buffer_get_part_width (buffer, 0); } /** * arv_buffer_get_image_height: * @buffer: a #ArvBuffer * * Gets the image width. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: image height, in pixels. * * Since: 0.4.0 */ gint arv_buffer_get_image_height (ArvBuffer *buffer) { return arv_buffer_get_part_height (buffer, 0); } /** * arv_buffer_get_image_x: * @buffer: a #ArvBuffer * * Gets the image x offset. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: image x offset, in pixels. * * Since: 0.4.0 */ gint arv_buffer_get_image_x (ArvBuffer *buffer) { return arv_buffer_get_part_x (buffer, 0); } /** * arv_buffer_get_image_y: * @buffer: a #ArvBuffer * * Gets the image y offset. * * This function must only be called if buffer payload is either @ARV_BUFFER_PAYLOAD_TYPE_IMAGE, * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA or @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART. * * Returns: image y offset, in pixels. * * Since: 0.4.0 */ gint arv_buffer_get_image_y (ArvBuffer *buffer) { return arv_buffer_get_part_y (buffer, 0); } void arv_buffer_set_n_parts (ArvBuffer* buffer, guint n_parts) { g_return_if_fail (ARV_IS_BUFFER(buffer)); if (G_UNLIKELY (n_parts == 0)) { buffer->priv->n_parts = 0; g_clear_pointer (&buffer->priv->parts, g_free); return; } if (buffer->priv->n_parts != n_parts) buffer->priv->parts = g_realloc_n (buffer->priv->parts, n_parts, sizeof (ArvBufferPartInfos)); memset (buffer->priv->parts, 0, n_parts * sizeof (ArvBufferPartInfos)); buffer->priv->n_parts = n_parts; } G_DEFINE_TYPE_WITH_CODE (ArvBuffer, arv_buffer, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvBuffer)) static void arv_buffer_init (ArvBuffer *buffer) { buffer->priv = arv_buffer_get_instance_private (buffer); buffer->priv->status = ARV_BUFFER_STATUS_CLEARED; } static void arv_buffer_finalize (GObject *object) { ArvBuffer *buffer = ARV_BUFFER (object); buffer->priv->n_parts = 0; g_clear_pointer (&buffer->priv->parts, g_free); if (!buffer->priv->is_preallocated) { g_free (buffer->priv->data); buffer->priv->data = NULL; buffer->priv->allocated_size = 0; } if (buffer->priv->user_data && buffer->priv->user_data_destroy_func) buffer->priv->user_data_destroy_func (buffer->priv->user_data); G_OBJECT_CLASS (arv_buffer_parent_class)->finalize (object); } static void arv_buffer_class_init (ArvBufferClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_buffer_finalize; } aravis-0.8.34/src/arvbuffer.h000066400000000000000000000203671475431451200160330ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_BUFFER_H #define ARV_BUFFER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * ArvBufferStatus: * @ARV_BUFFER_STATUS_UNKNOWN: unknown status * @ARV_BUFFER_STATUS_SUCCESS: the buffer contains a valid image * @ARV_BUFFER_STATUS_CLEARED: the buffer is cleared * @ARV_BUFFER_STATUS_TIMEOUT: timeout was reached before all packets are received * @ARV_BUFFER_STATUS_MISSING_PACKETS: stream has missing packets * @ARV_BUFFER_STATUS_WRONG_PACKET_ID: stream has packet with wrong id * @ARV_BUFFER_STATUS_SIZE_MISMATCH: the received image didn't fit in the buffer data space * @ARV_BUFFER_STATUS_FILLING: the image is currently being filled * @ARV_BUFFER_STATUS_ABORTED: the filling was aborted before completion * @ARV_BUFFER_STATUS_PAYLOAD_NOT_SUPPORTED: payload not yet supported */ typedef enum { ARV_BUFFER_STATUS_UNKNOWN = -1, ARV_BUFFER_STATUS_SUCCESS = 0, ARV_BUFFER_STATUS_CLEARED, ARV_BUFFER_STATUS_TIMEOUT, ARV_BUFFER_STATUS_MISSING_PACKETS, ARV_BUFFER_STATUS_WRONG_PACKET_ID, ARV_BUFFER_STATUS_SIZE_MISMATCH, ARV_BUFFER_STATUS_FILLING, ARV_BUFFER_STATUS_ABORTED, ARV_BUFFER_STATUS_PAYLOAD_NOT_SUPPORTED } ArvBufferStatus; /** * ArvBufferPayloadType: * @ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN: unknown payload type * @ARV_BUFFER_PAYLOAD_TYPE_NO_DATA: no data * @ARV_BUFFER_PAYLOAD_TYPE_IMAGE: image data * @ARV_BUFFER_PAYLOAD_TYPE_RAWDATA: raw data (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_FILE: file (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA: chunk data (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA: extended chunk data * @ARV_BUFFER_PAYLOAD_TYPE_JPEG: JPEG data (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_JPEG2000: JPEG2000 data (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_H264: h264 data (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE: multizone image (not supported) * @ARV_BUFFER_PAYLOAD_TYPE_MULTIPART: multipart data */ typedef enum { ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN = -1, ARV_BUFFER_PAYLOAD_TYPE_NO_DATA = 0x0000, ARV_BUFFER_PAYLOAD_TYPE_IMAGE = 0x0001, ARV_BUFFER_PAYLOAD_TYPE_RAWDATA = 0x0002, ARV_BUFFER_PAYLOAD_TYPE_FILE = 0x0003, ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA = 0x0004, ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA = 0x0005, /* Deprecated */ ARV_BUFFER_PAYLOAD_TYPE_JPEG = 0x0006, ARV_BUFFER_PAYLOAD_TYPE_JPEG2000 = 0x0007, ARV_BUFFER_PAYLOAD_TYPE_H264 = 0x0008, ARV_BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE = 0x0009, ARV_BUFFER_PAYLOAD_TYPE_MULTIPART = 0x000a, ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER = 0x000b, ARV_BUFFER_PAYLOAD_TYPE_GENDC_COMPONENT_DATA = 0x000c } ArvBufferPayloadType; typedef enum { ARV_BUFFER_PART_DATA_TYPE_UNKNOWN = -1, ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE = 0x0001, ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_BIPLANAR = 0x0002, ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_TRIPLANAR = 0x0003, ARV_BUFFER_PART_DATA_TYPE_2D_PLANE_QUADPLANAR = 0x0004, ARV_BUFFER_PART_DATA_TYPE_3D_IMAGE = 0x0005, ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_BIPLANAR = 0x0006, ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_TRIPLANAR = 0x0007, ARV_BUFFER_PART_DATA_TYPE_3D_PLANE_QUADPLANAR = 0x0008, ARV_BUFFER_PART_DATA_TYPE_CONFIDENCE_MAP = 0x0009, ARV_BUFFER_PART_DATA_TYPE_CHUNK_DATA = 0x000A, ARV_BUFFER_PART_DATA_TYPE_JPEG = 0x000B, ARV_BUFFER_PART_DATA_TYPE_JPEG2000 = 0x000C, ARV_BUFFER_PART_DATA_TYPE_DEVICE_SPECIFIC = 0x8000, } ArvBufferPartDataType; #define ARV_TYPE_BUFFER (arv_buffer_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvBuffer, arv_buffer, ARV, BUFFER, GObject) ARV_API ArvBuffer * arv_buffer_new_allocate (size_t size); ARV_API ArvBuffer * arv_buffer_new (size_t size, void *preallocated); ARV_API ArvBuffer * arv_buffer_new_full (size_t size, void *preallocated, void *user_data, GDestroyNotify user_data_destroy_func); ARV_API ArvBufferStatus arv_buffer_get_status (ArvBuffer *buffer); ARV_API const void * arv_buffer_get_user_data (ArvBuffer *buffer); ARV_API ArvBufferPayloadType arv_buffer_get_payload_type (ArvBuffer *buffer); ARV_API guint64 arv_buffer_get_timestamp (ArvBuffer *buffer); ARV_API void arv_buffer_set_timestamp (ArvBuffer *buffer, guint64 timestamp_ns); ARV_API guint64 arv_buffer_get_system_timestamp (ArvBuffer *buffer); ARV_API void arv_buffer_set_system_timestamp (ArvBuffer *buffer, guint64 timestamp_ns); ARV_API void arv_buffer_set_frame_id (ArvBuffer *buffer, guint64 frame_id); ARV_API guint64 arv_buffer_get_frame_id (ArvBuffer *buffer); ARV_API const void * arv_buffer_get_data (ArvBuffer *buffer, size_t *size); ARV_API guint arv_buffer_get_n_parts (ArvBuffer *buffer); ARV_API gint arv_buffer_find_component (ArvBuffer *buffer, guint component_id); ARV_API const void * arv_buffer_get_part_data (ArvBuffer *buffer, guint part_id, size_t *size); ARV_API guint arv_buffer_get_part_component_id (ArvBuffer *buffer, guint part_id); ARV_API ArvBufferPartDataType arv_buffer_get_part_data_type (ArvBuffer *buffer, guint part_id); ARV_API ArvPixelFormat arv_buffer_get_part_pixel_format (ArvBuffer *buffer, guint part_id); ARV_API void arv_buffer_get_part_region (ArvBuffer *buffer, guint part_id, gint *x, gint *y, gint *width, gint *height); ARV_API void arv_buffer_get_part_padding (ArvBuffer *buffer, guint part_id, gint *x_padding, gint *y_padding); ARV_API gint arv_buffer_get_part_width (ArvBuffer *buffer, guint part_id); ARV_API gint arv_buffer_get_part_height (ArvBuffer *buffer, guint part_id); ARV_API gint arv_buffer_get_part_x (ArvBuffer *buffer, guint part_id); ARV_API gint arv_buffer_get_part_y (ArvBuffer *buffer, guint part_id); ARV_API const void * arv_buffer_get_image_data (ArvBuffer *buffer, size_t *size); ARV_API ArvPixelFormat arv_buffer_get_image_pixel_format (ArvBuffer *buffer); ARV_API void arv_buffer_get_image_region (ArvBuffer *buffer, gint *x, gint *y, gint *width, gint *height); ARV_API void arv_buffer_get_image_padding (ArvBuffer *buffer, gint *x_padding, gint *y_padding); ARV_API gint arv_buffer_get_image_width (ArvBuffer *buffer); ARV_API gint arv_buffer_get_image_height (ArvBuffer *buffer); ARV_API gint arv_buffer_get_image_x (ArvBuffer *buffer); ARV_API gint arv_buffer_get_image_y (ArvBuffer *buffer); ARV_API gboolean arv_buffer_has_chunks (ArvBuffer *buffer); ARV_API const void * arv_buffer_get_chunk_data (ArvBuffer *buffer, guint64 chunk_id, size_t *size); ARV_API gboolean arv_buffer_has_gendc (ArvBuffer *buffer); ARV_API const void * arv_buffer_get_gendc_data (ArvBuffer *buffer, size_t *size); ARV_API const void * arv_buffer_get_gendc_descriptor (ArvBuffer *buffer, size_t *size); G_END_DECLS #endif aravis-0.8.34/src/arvbufferprivate.h000066400000000000000000000041241475431451200174170ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_BUFFER_PRIVATE_H #define ARV_BUFFER_PRIVATE_H #include #include #include G_BEGIN_DECLS typedef struct { ptrdiff_t data_offset; size_t size; guint component_id; ArvBufferPartDataType data_type; ArvPixelFormat pixel_format; guint32 width; guint32 height; guint32 x_offset; guint32 y_offset; guint32 x_padding; guint32 y_padding; } ArvBufferPartInfos; typedef struct { size_t allocated_size; gboolean is_preallocated; unsigned char *data; void *user_data; GDestroyNotify user_data_destroy_func; ArvBufferStatus status; size_t received_size; ArvBufferPayloadType payload_type; gboolean has_chunks; guint32 chunk_endianness; guint64 frame_id; guint64 timestamp_ns; guint64 system_timestamp_ns; guint n_parts; ArvBufferPartInfos *parts; gboolean has_gendc; guint32 gendc_descriptor_size; guint64 gendc_data_size; guint64 gendc_data_offset; } ArvBufferPrivate; struct _ArvBuffer { GObject object; ArvBufferPrivate *priv; }; struct _ArvBufferClass { GObjectClass parent_class; }; void arv_buffer_set_n_parts (ArvBuffer* buffer, guint n_parts); G_END_DECLS #endif aravis-0.8.34/src/arvcamera.c000066400000000000000000003555731475431451200160170ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION:arvcamera * @short_description: Class for generic camera control * * [class@Aravis.Camera] is a class for the generic control of cameras. It hides the complexity of the genicam interface * by providing a simple API, with the drawback of not exposing all the available features. See [class@Aravis.Device] * and [class@Aravis.Gc] for a more advanced use of the Aravis library. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ARAVIS_HAS_USB #include #endif #include #include static void arv_camera_get_integer_bounds_as_gint (ArvCamera *camera, const char *feature, gint *min, gint *max, GError **error); static void arv_camera_get_integer_bounds_as_guint (ArvCamera *camera, const char *feature, guint *min, guint *max, GError **error); static void arv_camera_get_integer_bounds_as_double (ArvCamera *camera, const char *feature, double *min, double *max, GError **error); /** * ArvCameraVendor: * @ARV_CAMERA_VENDOR_UNKNOWN: unknown camera vendor * @ARV_CAMERA_VENDOR_BASLER: Basler * @ARV_CAMERA_VENDOR_PROSILICA: Prosilica * @ARV_CAMERA_VENDOR_TIS: The Imaging Source * @ARV_CAMERA_VENDOR_POINT_GREY_FLIR: PointGrey / FLIR * @ARV_CAMERA_VENDOR_XIMEA: XIMEA GmbH * @ARV_CAMERA_VENDOR_MATRIX_VISION: Matrix Vision GmbH * @ARV_CAMERA_VENDOR_IMPERX: Imperx, Inc */ typedef enum { ARV_CAMERA_VENDOR_UNKNOWN, ARV_CAMERA_VENDOR_BASLER, ARV_CAMERA_VENDOR_DALSA, ARV_CAMERA_VENDOR_PROSILICA, ARV_CAMERA_VENDOR_TIS, ARV_CAMERA_VENDOR_POINT_GREY_FLIR, ARV_CAMERA_VENDOR_RICOH, ARV_CAMERA_VENDOR_XIMEA, ARV_CAMERA_VENDOR_MATRIX_VISION, ARV_CAMERA_VENDOR_IMPERX } ArvCameraVendor; typedef enum { ARV_CAMERA_SERIES_UNKNOWN, ARV_CAMERA_SERIES_BASLER_ACE, ARV_CAMERA_SERIES_BASLER_SCOUT, ARV_CAMERA_SERIES_BASLER_OTHER, ARV_CAMERA_SERIES_DALSA, ARV_CAMERA_SERIES_PROSILICA, ARV_CAMERA_SERIES_TIS, ARV_CAMERA_SERIES_POINT_GREY_FLIR, ARV_CAMERA_SERIES_RICOH, ARV_CAMERA_SERIES_XIMEA, ARV_CAMERA_SERIES_MATRIX_VISION, ARV_CAMERA_SERIES_IMPERX_CHEETAH, ARV_CAMERA_SERIES_IMPERX_OTHER } ArvCameraSeries; typedef struct { char *name; ArvDevice *device; ArvGc *genicam; ArvCameraVendor vendor; ArvCameraSeries series; gboolean has_serial_number; gboolean has_gain; gboolean gain_raw_as_float; gboolean gain_abs_as_float; gboolean has_brightness; gboolean has_black_level_raw; gboolean has_black_level; gboolean has_exposure_time; gboolean has_acquisition_frame_rate; gboolean has_acquisition_frame_rate_auto; gboolean has_acquisition_frame_rate_enabled; gboolean has_region_offset; GError *init_error; } ArvCameraPrivate; static void arv_camera_initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (ArvCamera, arv_camera, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvCamera) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, arv_camera_initable_iface_init)) enum { PROP_0, PROP_CAMERA_NAME, PROP_CAMERA_DEVICE }; /** * arv_camera_create_stream: (skip) * @camera: a #ArvCamera * @callback: (scope call) (allow-none) (closure user_data): a frame processing callback * @user_data: (allow-none): user data for @callback * @error: a #GError placeholder, %NULL to ignore * * Creates a new [class@ArvStream] for video stream reception. See * [callback@ArvStreamCallback] for details regarding the callback function. * * Returns: (transfer full): a new [class@ArvStream], to be freed after use with [method@GObject.Object.unref]. * * Since: 0.2.0 */ ArvStream * arv_camera_create_stream (ArvCamera *camera, ArvStreamCallback callback, gpointer user_data, GError **error) { return arv_camera_create_stream_full(camera, callback, user_data, NULL, error); } /** * arv_camera_create_stream_full: (rename-to arv_camera_create_stream) * @camera: a #ArvCamera * @callback: (scope notified) (allow-none) (closure user_data): a frame processing callback * @user_data: (allow-none): user data for @callback * @destroy: a #GDestroyNotify placeholder, %NULL to ignore * @error: a #GError placeholder, %NULL to ignore * * Creates a new [class@ArvStream] for video stream reception. See * [callback@ArvStreamCallback] for details regarding the callback function. * * Returns: (transfer full): a new [class@ArvStream], to be freed after use with [method@GObject.Object.unref]. * * Since: 0.8.23 */ ArvStream * arv_camera_create_stream_full (ArvCamera *camera, ArvStreamCallback callback, gpointer user_data, GDestroyNotify destroy, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_create_stream_full (priv->device, callback, user_data, destroy, error); } /* Device control */ /** * arv_camera_get_vendor_name: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the camera vendor name. * * Since: 0.8.0 */ const char * arv_camera_get_vendor_name (ArvCamera *camera, GError **error) { return arv_camera_get_string (camera, "DeviceVendorName", error); } /** * arv_camera_get_model_name: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the camera model name. * * Since: 0.8.0 */ const char * arv_camera_get_model_name (ArvCamera *camera, GError **error) { return arv_camera_get_string (camera, "DeviceModelName", error); } /** * arv_camera_get_device_serial_number: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the camera device serial number. * * Since: 0.8.8 */ const char * arv_camera_get_device_serial_number (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); if (priv->has_serial_number) return arv_camera_get_string (camera, "DeviceSerialNumber", error); return arv_camera_get_device_id (camera, error); } /** * arv_camera_get_device_id: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the camera device ID. * * Since: 0.8.0 */ const char * arv_camera_get_device_id (ArvCamera *camera, GError **error) { return arv_camera_get_string (camera, "DeviceID", error); } /* Image format control */ /** * arv_camera_get_sensor_size: * @camera: a #ArvCamera * @width: (out): camera sensor width * @height: (out): camera sensor height * @error: a #GError placeholder, %NULL to ignore * * Since: 0.8.0 */ void arv_camera_get_sensor_size (ArvCamera *camera, gint *width, gint *height, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (width != NULL) *width = arv_camera_get_integer (camera, "SensorWidth", &local_error); if (height != NULL && local_error == NULL) *height = arv_camera_get_integer (camera, "SensorHeight", &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_is_region_offset_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if OffsetX and OffsetY features are available. * * Since: 0.8.22 */ gboolean arv_camera_is_region_offset_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; gboolean has_offset_x, has_offset_y; g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); if (!priv->has_region_offset) return FALSE; has_offset_x = arv_camera_is_feature_available (camera, "OffsetX", &local_error); if (local_error == NULL) has_offset_y = arv_camera_is_feature_available (camera, "OffsetY", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } return has_offset_x && has_offset_y; } /** * arv_camera_set_region: * @camera: a #ArvCamera * @x: x offset * @y: y_offset * @width: region width * @height: region height * @error: a #GError placeholder, %NULL to ignore * * Defines the region of interest which will be transmitted in the video * stream. Negative @x or @y values, or not strictly positive @width or @height values are ignored. * * Since: 0.8.0 */ void arv_camera_set_region (ArvCamera *camera, gint x, gint y, gint width, gint height, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (priv->has_region_offset) { if (x >= 0) arv_camera_set_integer (camera, "OffsetX", 0, &local_error); if (y >= 0 && local_error == NULL) arv_camera_set_integer (camera, "OffsetY", 0, &local_error); } if (width > 0 && local_error == NULL) arv_camera_set_integer (camera, "Width", width, &local_error); if (height > 0 && local_error == NULL) arv_camera_set_integer (camera, "Height", height, &local_error); if (priv->has_region_offset) { if (x >= 0 && local_error == NULL) arv_camera_set_integer (camera, "OffsetX", x, &local_error); if (y >= 0 && local_error == NULL) arv_camera_set_integer (camera, "OffsetY", y, &local_error); } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_region: * @camera: a #ArvCamera * @x: (out): x offset * @y: (out): y_offset * @width: (out): region width * @height: (out): region height * @error: a #GError placeholder, %NULL to ignore * * Retrieves the current region of interest. * * Since: 0.8.0 */ void arv_camera_get_region (ArvCamera *camera, gint *x, gint *y, gint *width, gint *height, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (x != NULL) *x = priv->has_region_offset ? arv_camera_get_integer (camera, "OffsetX", &local_error) : 0; if (y != NULL && local_error == NULL) *y = priv->has_region_offset ? arv_camera_get_integer (camera, "OffsetY", &local_error) : 0; if (width != NULL && local_error == NULL) *width = arv_camera_get_integer (camera, "Width", &local_error); if (height != NULL && local_error == NULL) *height = arv_camera_get_integer (camera, "Height", &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_x_offset_bounds: * @camera: a #ArvCamera * @min: (out): minimum offset * @max: (out): maximum offset * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image horizontal offset. * * Since: 0.8.0 */ void arv_camera_get_x_offset_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "OffsetX", min, max, error); } /** * arv_camera_get_x_offset_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: horizontal offset value increment. * * Since: 0.8.0 */ gint arv_camera_get_x_offset_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "OffsetX", error); } /** * arv_camera_get_y_offset_bounds: * @camera: a #ArvCamera * @min: (out): minimum offset * @max: (out): maximum offset * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image vertical offset. * * Since: 0.8.0 */ void arv_camera_get_y_offset_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "OffsetY", min, max, error); } /** * arv_camera_get_y_offset_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: vertical offset value increment. * * Since: 0.8.0 */ gint arv_camera_get_y_offset_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "OffsetY", error); } /** * arv_camera_get_width_bounds: * @camera: a #ArvCamera * @min: (out): minimum width * @max: (out): maximum width * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image width. * * Since: 0.8.0 */ void arv_camera_get_width_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "Width", min, max, error); } /** * arv_camera_get_width_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: width value increment. * * Since: 0.8.0 */ gint arv_camera_get_width_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "Width", error); } /** * arv_camera_get_height_bounds: * @camera: a #ArvCamera * @min: (out): minimum height * @max: (out): maximum height * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image height. * * Since: 0.8.0 */ void arv_camera_get_height_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "Height", min, max, error); } /** * arv_camera_get_height_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: height value increment. * * Since: 0.8.0 */ gint arv_camera_get_height_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "Height", error); } /** * arv_camera_set_binning: * @camera: a #ArvCamera * @dx: horizontal binning * @dy: vertical binning * @error: a #GError placeholder, %NULL to ignore * * Defines binning in both directions. Not all cameras support this * feature. Negative @dx or @dy values are ignored. * * Since: 0.8.0 */ void arv_camera_set_binning (ArvCamera *camera, gint dx, gint dy, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (dx > 0) arv_camera_set_integer (camera, "BinningHorizontal", dx, &local_error); if (dy > 0 && local_error == NULL) arv_camera_set_integer (camera, "BinningVertical", dy, &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_binning: * @camera: a #ArvCamera * @dx: (out): horizontal binning placeholder * @dy: (out): vertical binning placeholder * @error: a #GError placeholder, %NULL to ignore * * Retrieves binning in both directions. * * Since: 0.8.0 */ void arv_camera_get_binning (ArvCamera *camera, gint *dx, gint *dy, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (dx != NULL) *dx = arv_camera_get_integer (camera, "BinningHorizontal", &local_error); if (dy != NULL && local_error == NULL) *dy = arv_camera_get_integer (camera, "BinningVertical", &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_x_binning_bounds: * @camera: a #ArvCamera * @min: (out): minimum binning * @max: (out): maximum binning * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image horizontal binning. * * Since: 0.8.0 */ void arv_camera_get_x_binning_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "BinningHorizontal", min, max, error); } /** * arv_camera_get_x_binning_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: horizontal binning value increment. * * Since: 0.8.0 */ gint arv_camera_get_x_binning_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "BinningHorizontal", error); } /** * arv_camera_get_y_binning_bounds: * @camera: a #ArvCamera * @min: (out): minimum binning * @max: (out): maximum binning * @error: a #GError placeholder, %NULL to ignore * * Retrieves the valid range for image vertical binning. * * Since: 0.8.0 */ void arv_camera_get_y_binning_bounds (ArvCamera *camera, gint *min, gint *max, GError **error) { arv_camera_get_integer_bounds_as_gint (camera, "BinningVertical", min, max, error); } /** * arv_camera_get_y_binning_increment: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: vertical binning value increment. * * Since: 0.8.0 */ gint arv_camera_get_y_binning_increment (ArvCamera *camera, GError **error) { return arv_camera_get_integer_increment (camera, "BinningVertical", error); } /** * arv_camera_set_pixel_format: * @camera: a #ArvCamera * @format: pixel format * @error: a #GError placeholder, %NULL to ignore * * Defines pixel format. * * Since: 0.8.0 */ void arv_camera_set_pixel_format (ArvCamera *camera, ArvPixelFormat format, GError **error) { arv_camera_set_integer (camera, "PixelFormat", format, error); } /** * arv_camera_set_pixel_format_from_string: * @camera: a #ArvCamera * @format: pixel format * @error: a #GError placeholder, %NULL to ignore * * Defines pixel format described by a string. * * Since: 0.8.0 */ void arv_camera_set_pixel_format_from_string (ArvCamera *camera, const char * format, GError **error) { arv_camera_set_string (camera, "PixelFormat", format, error); } /** * arv_camera_get_pixel_format: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: pixel format. * * Since: 0.8.0 */ ArvPixelFormat arv_camera_get_pixel_format (ArvCamera *camera, GError **error) { return arv_camera_get_integer (camera, "PixelFormat", error); } /** * arv_camera_get_pixel_format_as_string: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Retuns: pixel format as string, NULL on error. * * Since: 0.8.0 */ const char * arv_camera_get_pixel_format_as_string (ArvCamera *camera, GError **error) { return arv_camera_get_string (camera, "PixelFormat", error); } /** * arv_camera_dup_available_pixel_formats: * @camera: a #ArvCamera * @n_pixel_formats: (out): number of different pixel formats * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of all available pixel formats. * * Returns: (array length=n_pixel_formats) (transfer container): a newly allocated array of #ArvPixelFormat, to be freed after use with * g_free(). * * Since: 0.8.0 */ gint64 * arv_camera_dup_available_pixel_formats (ArvCamera *camera, guint *n_pixel_formats, GError **error) { return arv_camera_dup_available_enumerations (camera, "PixelFormat", n_pixel_formats, error); } /** * arv_camera_dup_available_pixel_formats_as_strings: * @camera: a #ArvCamera * @n_pixel_formats: (out): number of different pixel formats * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of all available pixel formats as strings. * * Returns: (array length=n_pixel_formats) (transfer container): a newly allocated array of strings, to be freed after use with * g_free(). * * Since: 0.8.0 */ const char ** arv_camera_dup_available_pixel_formats_as_strings (ArvCamera *camera, guint *n_pixel_formats, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_camera_dup_available_enumerations_as_strings (camera, "PixelFormat", n_pixel_formats, error); } /** * arv_camera_dup_available_pixel_formats_as_display_names: * @camera: a #ArvCamera * @n_pixel_formats: (out): number of different pixel formats * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of all available pixel formats as display names. * In general, these human-readable strings cannot be used as settings. * * Returns: (array length=n_pixel_formats) (transfer container): a newly allocated array of string constants, to be freed after use with * g_free(). * * Since: 0.8.0 */ const char ** arv_camera_dup_available_pixel_formats_as_display_names (ArvCamera *camera, guint *n_pixel_formats, GError **error) { return arv_camera_dup_available_enumerations_as_display_names (camera, "PixelFormat", n_pixel_formats, error); } /* Acquisition control */ /** * arv_camera_start_acquisition: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Starts video stream acquisition. * * Since: 0.8.0 */ void arv_camera_start_acquisition (ArvCamera *camera, GError **error) { arv_camera_execute_command (camera, "AcquisitionStart", error); } /** * arv_camera_stop_acquisition: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Stops video stream acquisition. * * Since: 0.8.0 */ void arv_camera_stop_acquisition (ArvCamera *camera, GError **error) { arv_camera_execute_command (camera, "AcquisitionStop", error); } /** * arv_camera_abort_acquisition: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Aborts video stream acquisition. * * Since: 0.8.0 */ void arv_camera_abort_acquisition (ArvCamera *camera, GError **error) { arv_camera_execute_command (camera, "AcquisitionAbort", error); } /** * arv_camera_acquisition: * @camera: a #ArvCamera * @timeout: acquisition timeout in µs. Zero means no timeout. * @error: a #GError placeholder, %NULL to ignore * * Acquire one image buffer. * * * arv_camera_acquisition() sets the camera in SingleFrame acquisition mode. You may have to put back the camera in * Continuous acquisition mode for later operations, using arv_camera_set_acquisition_mode(). * * * Returns: (transfer full): A new #ArvBuffer, NULL on error. The returned buffer must be freed using * [method@GObject.Object.unref]. * * Since: 0.8.0 */ ArvBuffer * arv_camera_acquisition (ArvCamera *camera, guint64 timeout, GError **error) { GError *local_error = NULL; ArvStream *stream; ArvBuffer *buffer = NULL; gint payload; g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); stream = arv_camera_create_stream (camera, NULL, NULL, &local_error); if (ARV_IS_STREAM(stream)) { payload = arv_camera_get_payload (camera, &local_error); if (local_error == NULL) { arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_SINGLE_FRAME, &local_error); if (local_error != NULL && local_error->code == ARV_GC_ERROR_ENUM_ENTRY_NOT_FOUND) { g_clear_error (&local_error); /* Some cameras don't support SingleFrame, fall back to Continuous */ arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, &local_error); } } if (local_error == NULL) arv_camera_start_acquisition (camera, &local_error); if (local_error == NULL) { if (timeout > 0) buffer = arv_stream_timeout_pop_buffer (stream, timeout); else buffer = arv_stream_pop_buffer (stream); arv_camera_stop_acquisition (camera, &local_error); } g_object_unref (stream); } if (local_error != NULL) g_propagate_error (error, local_error); return buffer; } /* * arv_camera_set_acquisition_mode: * @camera: a #ArvCamera * @acquisition_mode: acquisition mode * @error: a #GError placeholder, %NULL to ignore * * Defines acquisition mode. * * Since: 0.8.0 */ void arv_camera_set_acquisition_mode (ArvCamera *camera, ArvAcquisitionMode acquisition_mode, GError **error) { arv_camera_set_string (camera, "AcquisitionMode", arv_acquisition_mode_to_string (acquisition_mode), error); } /** * arv_camera_get_acquisition_mode: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: acquisition mode. * * Since: 0.8.0 */ ArvAcquisitionMode arv_camera_get_acquisition_mode (ArvCamera *camera, GError **error) { const char *string; g_return_val_if_fail (ARV_IS_CAMERA (camera), 0); string = arv_camera_get_string (camera, "AcquisitionMode", error); return arv_acquisition_mode_from_string (string); } /** * arv_camera_set_frame_count: * @camera: a #ArvCamera * @frame_count: number of frames to capture in MultiFrame mode * @error: a #GError placeholder, %NULL to ignore * * Sets the number of frames to capture in MultiFrame mode. * * Since: 0.8.0 */ void arv_camera_set_frame_count (ArvCamera *camera, gint64 frame_count, GError **error) { GError *local_error = NULL; gint64 minimum; gint64 maximum; g_return_if_fail (ARV_IS_CAMERA (camera)); if (frame_count <= 0) return; arv_camera_get_frame_count_bounds (camera, &minimum, &maximum, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (frame_count < minimum) frame_count = minimum; if (frame_count > maximum) frame_count = maximum; arv_camera_set_integer (camera, "AcquisitionFrameCount", frame_count, error); } /** * arv_camera_get_frame_count: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: number of frames to capture in MultiFrame mode. * * Since: 0.8.0 */ gint64 arv_camera_get_frame_count (ArvCamera *camera, GError **error) { return arv_camera_get_integer (camera, "AcquisitionFrameCount", error); } /** * arv_camera_get_frame_count_bounds: * @camera: a #ArvCamera * @min: (out): minimal possible frame count * @max: (out): maximum possible frame count * @error: a #GError placeholder, %NULL to ignore * * Retrieves allowed range for frame count. * * Since: 0.8.0 */ void arv_camera_get_frame_count_bounds (ArvCamera *camera, gint64 *min, gint64 *max, GError **error) { arv_camera_get_integer_bounds (camera, "AcquisitionFrameCount", min, max, error); } /** * arv_camera_set_frame_rate: * @camera: a #ArvCamera * @frame_rate: frame rate, in Hz * @error: a #GError placeholder, %NULL to ignore * * Configures a fixed frame rate mode. Once acquisition start is triggered, the video stream will be acquired with the * given frame rate. A negative or zero @frame_rate value disables the frame rate limit. * * All triggers are disabled. * * Since: 0.8.0 */ void arv_camera_set_frame_rate (ArvCamera *camera, double frame_rate, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; ArvGcNode *feature; double minimum; double maximum; g_return_if_fail (ARV_IS_CAMERA (camera)); if (frame_rate <= 0.0) { arv_camera_set_frame_rate_enable(camera, FALSE, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); } return; } /* Ignore the error in order to be able to change the frame rate during the acquisition, as some devices don't * allow to change TriggerMode if the acquisition is already started. */ arv_camera_clear_triggers (camera, NULL); arv_camera_get_frame_rate_bounds (camera, &minimum, &maximum, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (frame_rate < minimum) frame_rate = minimum; if (frame_rate > maximum) frame_rate = maximum; switch (priv->vendor) { case ARV_CAMERA_VENDOR_BASLER: if (local_error == NULL){ arv_camera_set_frame_rate_enable(camera, TRUE, &local_error); } if (local_error == NULL) arv_camera_set_float (camera, priv->has_acquisition_frame_rate ? "AcquisitionFrameRate": "AcquisitionFrameRateAbs", frame_rate, &local_error); break; case ARV_CAMERA_VENDOR_PROSILICA: if (local_error == NULL) arv_camera_set_float (camera, "AcquisitionFrameRateAbs", frame_rate, &local_error); break; case ARV_CAMERA_VENDOR_TIS: if (local_error == NULL) { feature = arv_device_get_feature (priv->device, "FPS"); if (ARV_IS_GC_ENUMERATION (feature)) { gint64 *values; guint n_values; guint i; values = arv_camera_dup_available_enumerations (camera, "FPS", &n_values, &local_error); for (i = 0; i < n_values && local_error == NULL; i++) { if (values[i] > 0) { double e; e = (int)((10000000/(double) values[i]) * 100 + 0.5) / 100.0; if (e == frame_rate) { arv_camera_set_integer (camera, "FPS", values[i], &local_error); break; } } } g_free (values); } else arv_camera_set_float (camera, "FPS", frame_rate, &local_error); } break; case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: arv_camera_set_frame_rate_enable(camera, TRUE, &local_error); if (local_error == NULL && priv->has_acquisition_frame_rate_auto) { arv_camera_set_string (camera, "AcquisitionFrameRateAuto", "Off", &local_error); } if (local_error == NULL) { arv_camera_set_float (camera, "AcquisitionFrameRate", frame_rate, &local_error); } break; case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: arv_camera_set_frame_rate_enable(camera, TRUE, &local_error); if (local_error == NULL) arv_camera_set_float (camera, priv->has_acquisition_frame_rate ? "AcquisitionFrameRate": "AcquisitionFrameRateAbs", frame_rate, &local_error); break; } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_frame_rate: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: actual frame rate, in Hz. * * Since: 0.8.0 */ double arv_camera_get_frame_rate (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); ArvGcNode *feature; g_return_val_if_fail (ARV_IS_CAMERA (camera), 0); switch (priv->vendor) { case ARV_CAMERA_VENDOR_PROSILICA: return arv_camera_get_float (camera, "AcquisitionFrameRateAbs", error); case ARV_CAMERA_VENDOR_TIS: { feature = arv_device_get_feature (priv->device, "FPS"); if (ARV_IS_GC_ENUMERATION (feature)) { gint64 i; i = arv_camera_get_integer (camera, "FPS", error); if (i > 0) return (int)((10000000/(double) i) * 100 + 0.5) / 100.0; else return 0; } else return arv_camera_get_float (camera, "FPS", error); } case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_BASLER: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: return arv_camera_get_float (camera, priv->has_acquisition_frame_rate ? "AcquisitionFrameRate": "AcquisitionFrameRateAbs", error); } return 0; } /** * arv_camera_get_frame_rate_bounds: * @camera: a #ArvCamera * @min: (out): minimal possible framerate * @max: (out): maximum possible framerate * @error: a #GError placeholder, %NULL to ignore * * Retrieves allowed range for framerate. * * Since 0.8.0 */ void arv_camera_get_frame_rate_bounds (ArvCamera *camera, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); ArvGcNode *feature; g_return_if_fail (ARV_IS_CAMERA (camera)); switch (priv->vendor) { case ARV_CAMERA_VENDOR_TIS: feature = arv_device_get_feature (priv->device, "FPS"); if (ARV_IS_GC_ENUMERATION (feature)) { GError *local_error = NULL; gint64 *values; guint n_values; guint i; values = arv_camera_dup_available_enumerations (camera, "FPS", &n_values, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (max != NULL) *max = 0; if (min != NULL) *min = 0; for (i = 0; i < n_values; i++) { if (values[i] > 0) { double s; s = (int)((10000000/(double) values[i]) * 100 + 0.5) / 100.0; if (max != NULL && s > *max) *max = s; if (min != NULL && (*min == 0 || *min > s)) *min = s; } } g_free (values); return; } else arv_camera_get_float_bounds (camera, "FPS", min, max, error); break; case ARV_CAMERA_VENDOR_PROSILICA: arv_camera_get_float_bounds (camera, "AcquisitionFrameRateAbs", min, max, error); break; case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_BASLER: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: arv_camera_get_float_bounds (camera, priv->has_acquisition_frame_rate ? "AcquisitionFrameRate": "AcquisitionFrameRateAbs", min, max, error); break; } } /* * arv_camera_set_frame_rate_enable: * @camera: an #ArvCamera * @enable: true to enable, false to disable * @error: a #GError placeholer, %NULL to ignore * * Configures whether to enable the upper frame rate limit set by #arv_camera_set_frame_rate. * Implements vendor specific quirks if needed. * Since: 0.8.26 */ void arv_camera_set_frame_rate_enable(ArvCamera *camera, gboolean enable, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); switch (priv->vendor) { case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: if (local_error == NULL) { if (priv->has_acquisition_frame_rate_enabled) arv_camera_set_boolean (camera, "AcquisitionFrameRateEnabled", enable, &local_error); else arv_camera_set_boolean (camera, "AcquisitionFrameRateEnable", enable, &local_error); } break; case ARV_CAMERA_VENDOR_BASLER: case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: if (local_error == NULL) { if (arv_camera_is_feature_available (camera, "AcquisitionFrameRateEnable", &local_error)) { if (local_error == NULL) arv_camera_set_boolean (camera, "AcquisitionFrameRateEnable", enable, &local_error); } } break; case ARV_CAMERA_VENDOR_PROSILICA: case ARV_CAMERA_VENDOR_TIS: default: break; /* No specific frame rate enable code */ } if (local_error != NULL) { g_propagate_error (error, local_error); } } /** * arv_camera_get_frame_rate_enable: * @camera: an #ArvCamera * @error: a #GError placeholer, %NULL to ignore * * Returns: whether the upper frame rate limit is enabled. * * Implements vendor specific quirks if needed. * Since: 0.8.31 */ gboolean arv_camera_get_frame_rate_enable(ArvCamera* camera, GError** error) { ArvCameraPrivate* priv = arv_camera_get_instance_private(camera); g_return_val_if_fail(ARV_IS_CAMERA(camera), TRUE); switch (priv->vendor) { case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: return arv_camera_get_boolean(camera, priv->has_acquisition_frame_rate_enabled ? "AcquisitionFrameRateEnabled" : "AcquisitionFrameRateEnable", error); case ARV_CAMERA_VENDOR_BASLER: case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: if (arv_camera_is_feature_available(camera, "AcquisitionFrameRateEnable", NULL)) { return arv_camera_get_boolean(camera, "AcquisitionFrameRateEnable", error); } break; case ARV_CAMERA_VENDOR_PROSILICA: case ARV_CAMERA_VENDOR_TIS: default: break; /* No specific frame rate enable code */ } return TRUE; } /** * arv_camera_set_trigger: * @camera: a #ArvCamera * @source: trigger source as string * @error: a #GError placeholder, %NULL to ignore * * Configures the camera in trigger mode. Typical values for source are "Line1" or "Line2". See the camera documentation * for the allowed values. Source can also be "Software". In this case, an acquisition is triggered by a call to * arv_camera_software_trigger(). * * The trigger set is "FrameStart". "AcquisitionStart" is used as a fallback if "FrameStart" is not present. * All other triggers are disabled. "TriggerActivation" is set to rising edge. * * For an advanced trigger configuration, use the underlying #ArvDevice object returned by arv_camera_get_device(). * * Since: 0.8.0 */ void arv_camera_set_trigger (ArvCamera *camera, const char *source, GError **error) { GError *local_error = NULL; gboolean has_frame_start = FALSE; gboolean has_frame_burst_start = FALSE; /* Hikrobot, Basler devices */ gboolean has_acquisition_start = FALSE; /* Smartek devices */ gboolean has_trigger_selector = FALSE; g_return_if_fail (ARV_IS_CAMERA (camera)); g_return_if_fail (source != NULL); if (arv_camera_get_frame_rate_enable(camera, &local_error)) { if (local_error == NULL) { arv_camera_set_frame_rate_enable (camera, FALSE, &local_error); } } if (local_error == NULL) { has_trigger_selector = arv_camera_is_feature_available(camera, "TriggerSelector", &local_error); if (has_trigger_selector == TRUE) { const char **triggers = NULL; guint n_triggers = 0; unsigned int i; triggers = arv_camera_dup_available_enumerations_as_strings (camera, "TriggerSelector", &n_triggers, &local_error); for (i = 0; i < n_triggers && local_error == NULL; i++) { arv_camera_set_string (camera, "TriggerSelector", triggers[i], &local_error); if (local_error == NULL) { if (g_strcmp0 (triggers[i], "FrameStart") == 0) has_frame_start = TRUE; else if (g_strcmp0 (triggers[i], "FrameBurstStart") == 0) has_frame_burst_start = TRUE; else if (g_strcmp0 (triggers[i], "AcquisitionStart") == 0) has_acquisition_start = TRUE; arv_camera_set_string (camera, "TriggerMode", "Off", &local_error); } } g_free (triggers); } } if (local_error == NULL) { if (has_trigger_selector == TRUE) { if (has_frame_start) { arv_camera_set_string (camera, "TriggerSelector", "FrameStart", &local_error); } else if (has_frame_burst_start) { arv_camera_set_string (camera, "TriggerSelector", "FrameBurstStart", &local_error); } else if (has_acquisition_start) { arv_camera_set_string (camera, "TriggerSelector", "AcquisitionStart", &local_error); } else { local_error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, " or feature missing " "for trigger setting"); } } if (local_error == NULL) arv_camera_set_string (camera, "TriggerMode", "On", &local_error); if (local_error == NULL && arv_camera_is_enumeration_entry_available (camera, "TriggerActivation", "RisingEdge", NULL)) arv_camera_set_string (camera, "TriggerActivation", "RisingEdge", &local_error); if (local_error == NULL) arv_camera_set_string (camera, "TriggerSource", source, &local_error); } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_set_trigger_source: * @camera: a #ArvCamera * @source: source name * @error: a #GError placeholder, %NULL to ignore * * Sets the trigger source. This function doesn't check if the camera is configured * to actually use this source as a trigger. * * Since: 0.8.0 */ void arv_camera_set_trigger_source (ArvCamera *camera, const char *source, GError **error) { arv_camera_set_string (camera, "TriggerSource", source, error); } /** * arv_camera_get_trigger_source: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Gets the trigger source. This function doesn't check if the camera is configured * to actually use this source as a trigger. * * Returns: a string containing the trigger source name, NULL on error. * * Since: 0.8.0 */ const char * arv_camera_get_trigger_source (ArvCamera *camera, GError **error) { return arv_camera_get_string (camera, "TriggerSource", error); } /** * arv_camera_dup_available_trigger_sources: * @camera: a #ArvCamera * @n_sources: (out): number of sources * @error: a #GError placeholder, %NULL to ignore * * Gets the list of all available trigger sources. * * Returns: (array length=n_sources) (transfer container): a newly allocated array of strings, which must be freed using * [func@GLib.free]. * * Since: 0.8.0 */ const char ** arv_camera_dup_available_trigger_sources (ArvCamera *camera, guint *n_sources, GError **error) { return arv_camera_dup_available_enumerations_as_strings (camera, "TriggerSource", n_sources, error); } /** * arv_camera_dup_available_triggers: * @camera: a #ArvCamera * @n_triggers: (out): number of available triggers * @error: a #GError placeholder, %NULL to ignore * * Gets a list of all available triggers: FrameStart, ExposureActive, etc... * * Returns: (array length=n_triggers) (transfer container): a newly allocated array of strings, which must be freed * using [func@GLib.free]. * * Since: 0.8.0 */ const char ** arv_camera_dup_available_triggers (ArvCamera *camera, guint *n_triggers, GError **error) { return arv_camera_dup_available_enumerations_as_strings (camera, "TriggerSelector", n_triggers, error); } /** * arv_camera_clear_triggers: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Disables all triggers. * * Since: 0.8.0 */ void arv_camera_clear_triggers (ArvCamera* camera, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (arv_camera_is_feature_available(camera, "TriggerSelector", &local_error)) { const char **triggers; guint n_triggers; unsigned i; triggers = arv_camera_dup_available_triggers (camera, &n_triggers, &local_error); for (i = 0; i < n_triggers && local_error == NULL; i++) { arv_camera_set_string (camera, "TriggerSelector", triggers[i], &local_error); if (local_error == NULL) arv_camera_set_string (camera, "TriggerMode", "Off", &local_error); } g_free (triggers); } else arv_camera_set_string (camera, "TriggerMode", "Off", &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_is_software_trigger_supported: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if software trigger is supported by @camera. * * Since: 0.8.17 */ gboolean arv_camera_is_software_trigger_supported (ArvCamera *camera, GError **error) { return arv_camera_is_feature_implemented (camera, "TriggerSoftware", error); } /** * arv_camera_software_trigger: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Sends a software trigger command to @camera. The camera must be previously * configured to use a software trigger, using @arv_camera_set_trigger(). * * Since: 0.8.0 */ void arv_camera_software_trigger (ArvCamera *camera, GError **error) { arv_camera_execute_command (camera, "TriggerSoftware", error); } /** * arv_camera_set_exposure_time: * @camera: a #ArvCamera * @exposure_time_us: exposure time, in µs * @error: a #GError placeholder, %NULL to ignore * * Sets exposure time. User should take care to set a value compatible with * the desired frame rate. Negative @exposure_time_us is ignored. * * Since: 0.8.0 */ void arv_camera_set_exposure_time (ArvCamera *camera, double exposure_time_us, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); if (exposure_time_us <= 0) return; switch (priv->series) { case ARV_CAMERA_SERIES_BASLER_SCOUT: arv_camera_set_float (camera, "ExposureTimeBaseAbs", exposure_time_us, &local_error); if (local_error == NULL) arv_camera_set_integer (camera, "ExposureTimeRaw", 1, &local_error); break; case ARV_CAMERA_SERIES_RICOH: arv_camera_set_integer (camera, "ExposureTimeRaw", exposure_time_us, &local_error); break; case ARV_CAMERA_SERIES_XIMEA: arv_camera_set_integer (camera, "ExposureTime", exposure_time_us, &local_error); break; case ARV_CAMERA_SERIES_IMPERX_CHEETAH: case ARV_CAMERA_SERIES_MATRIX_VISION: arv_camera_set_string (camera, "ExposureMode", "Timed", &local_error); if (local_error == NULL) arv_camera_set_float (camera, "ExposureTime", exposure_time_us, &local_error); break; case ARV_CAMERA_SERIES_BASLER_ACE: default: arv_camera_set_float (camera, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeAbs", exposure_time_us, &local_error); break; } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_exposure_time: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: current exposure time, in µs. * * Since: 0.8.0 */ double arv_camera_get_exposure_time (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 0.0); switch (priv->series) { case ARV_CAMERA_SERIES_XIMEA: return arv_camera_get_integer (camera,"ExposureTime", error); case ARV_CAMERA_SERIES_RICOH: return arv_camera_get_integer (camera,"ExposureTimeRaw", error); default: return arv_camera_get_float (camera, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeAbs", error); } } /** * arv_camera_get_exposure_time_bounds: * @camera: a #ArvCamera * @min: (out): minimum exposure time * @max: (out): maximum exposure time * @error: a #GError placeholder, %NULL to ignore * * Retrieves exposure time bounds, in µs. * * Since: 0.8.0 */ void arv_camera_get_exposure_time_bounds (ArvCamera *camera, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); switch (priv->series) { case ARV_CAMERA_SERIES_BASLER_SCOUT: arv_camera_get_float_bounds (camera, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeBaseAbs", min, max, error); break; case ARV_CAMERA_SERIES_BASLER_ACE: if (priv->has_exposure_time) arv_camera_get_float_bounds (camera, "ExposureTime", min, max, error); else arv_camera_get_integer_bounds_as_double (camera, "ExposureTimeRaw", min, max, error); break; case ARV_CAMERA_SERIES_XIMEA: arv_camera_get_integer_bounds_as_double (camera, "ExposureTime", min, max, error); break; case ARV_CAMERA_SERIES_RICOH: arv_camera_get_integer_bounds_as_double (camera, "ExposureTimeRaw", min, max, error); break; default: arv_camera_get_float_bounds (camera, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeAbs", min, max, error); break; } } /** * arv_camera_set_exposure_time_auto: * @camera: a #ArvCamera * @auto_mode: auto exposure mode selection * @error: a #GError placeholder, %NULL to ignore * * Configures automatic exposure feature. * * Since: 0.8.0 */ void arv_camera_set_exposure_time_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error) { arv_camera_set_string (camera, "ExposureAuto", arv_auto_to_string (auto_mode), error); } /** * arv_camera_get_exposure_time_auto: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: auto exposure mode selection * * Since: 0.8.0 */ ArvAuto arv_camera_get_exposure_time_auto (ArvCamera *camera, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_AUTO_OFF); return arv_auto_from_string (arv_camera_get_string (camera, "ExposureAuto", error)); } /* * arv_camera_set_exposure_mode: * @camera: a #ArvCamera * @exspoure_mode: exposure mode * @error: a #GError placeholder, %NULL to ignore * * Defines acquisition mode. * * Since: 0.8.7 */ void arv_camera_set_exposure_mode (ArvCamera *camera, ArvExposureMode acquisition_mode, GError **error) { arv_camera_set_string (camera, "ExposureMode", arv_exposure_mode_to_string (acquisition_mode), error); } /* Analog control */ /** * arv_camera_set_gain: * @camera: a #ArvCamera * @gain: gain value * @error: a #GError placeholder, %NULL to ignore * * Sets the gain of the ADC converter. Negative @gain is ignored. * * Since: 0.8.0 */ void arv_camera_set_gain (ArvCamera *camera, double gain, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); if (gain < 0) return; if (priv->has_gain) arv_camera_set_float (camera, "Gain", gain, error); else { if (priv->gain_raw_as_float) arv_camera_set_float (camera, "GainRaw", gain, error); else if (priv->gain_abs_as_float) arv_camera_set_float (camera, "GainAbs", gain, error); else arv_camera_set_integer (camera, "GainRaw", gain, error); } } /** * arv_camera_get_gain: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the current gain setting. * * Since: 0.8.0 */ double arv_camera_get_gain (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 0.0); if (priv->has_gain) return arv_camera_get_float (camera, "Gain", error); else if (priv->gain_raw_as_float) return arv_camera_get_float (camera, "GainRaw", error); else if (priv->gain_abs_as_float) return arv_camera_get_float (camera, "GainAbs", error); return arv_camera_get_integer (camera, "GainRaw", error); } /** * arv_camera_get_gain_bounds: * @camera: a #ArvCamera * @min: (out): minimum gain * @max: (out): maximum gain * @error: a #GError placeholder, %NULL to ignore * * Retrieves gain bounds. * * Since: 0.8.0 */ void arv_camera_get_gain_bounds (ArvCamera *camera, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); if (priv->has_gain) { arv_camera_get_float_bounds (camera, "Gain", min, max, error); return; } else if (priv->gain_abs_as_float) { arv_camera_get_float_bounds (camera, "GainAbs", min, max, error); return; } else if (priv->gain_raw_as_float) { arv_camera_get_float_bounds (camera, "GainRaw", min, max, error); return; } arv_camera_get_integer_bounds_as_double (camera, "GainRaw", min, max, error); return; } /** * arv_camera_set_gain_auto: * @camera: a #ArvCamera * @auto_mode: auto gain mode selection * @error: a #GError placeholder, %NULL to ignore * * Configures automatic gain feature. * * Since: 0.8.0 **/ void arv_camera_set_gain_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error) { arv_camera_set_string (camera, "GainAuto", arv_auto_to_string (auto_mode), error); } /** * arv_camera_get_gain_auto: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: auto gain mode selection * * Since: 0.8.0 **/ ArvAuto arv_camera_get_gain_auto (ArvCamera *camera, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_AUTO_OFF); return arv_auto_from_string (arv_camera_get_string (camera, "GainAuto", error)); } /** * arv_camera_select_gain: * @camera: a #ArvCamera * @selector: gain selector * @error: a #GError placeholder, %NULL to ignore * * Configures Gain Selector feature. * * Since: 0.8.27 **/ void arv_camera_select_gain (ArvCamera *camera, const char *selector, GError **error) { arv_camera_set_string (camera, "GainSelector", selector, error); } /** * arv_camera_dup_available_gains: * @camera: a #ArvCamera * @n_selectors: (out): number of different gain selectors * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of all available gain selectors as strings. * * Returns: (array length=n_selectors) (transfer container): a newly allocated array of strings, to be freed after use with * g_free(). * * Since: 0.8.27 */ const char ** arv_camera_dup_available_gains (ArvCamera *camera, guint *n_selectors, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_camera_dup_available_enumerations_as_strings (camera, "GainSelector", n_selectors, error); } /** * arv_camera_is_black_level_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if BlackLevel feature is available. * * Since: 0.8.19 */ gboolean arv_camera_is_black_level_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); if (priv->has_brightness) return arv_camera_is_feature_available (camera, "Brightness", error); else if (priv->has_black_level_raw) return arv_camera_is_feature_available (camera, "BlackLevelRaw", error); else if (priv->has_black_level) return arv_camera_is_feature_available (camera, "BlackLevel", error); return FALSE; } /** * arv_camera_set_black_level: * @camera: a #ArvCamera * @blacklevel: blacklevel value * @error: a #GError placeholder, %NULL to ignore * * Since: 0.8.19 */ void arv_camera_set_black_level (ArvCamera *camera, double blacklevel, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); if (priv->has_brightness) arv_camera_set_float (camera, "Brightness", blacklevel, error); else if (priv->has_black_level_raw) arv_camera_set_integer (camera, "BlackLevelRaw", blacklevel, error); else arv_camera_set_float (camera, "BlackLevel", blacklevel, error); } /** * arv_camera_get_black_level: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the current blacklevel setting. * * Since: 0.8.19 */ double arv_camera_get_black_level (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 0.0); if (priv->has_brightness) return arv_camera_get_float (camera, "Brightness", error); else if (priv->has_black_level_raw) return arv_camera_get_integer (camera, "BlackLevelRaw", error); else return arv_camera_get_float (camera, "BlackLevel", error); } /** * arv_camera_select_black_level: * @camera: a #ArvCamera * @selector: black level selection * @error: a #GError placeholder, %NULL to ignore * * Configures Black Level Selector feature. * * Since: 0.8.27 **/ void arv_camera_select_black_level (ArvCamera *camera, const char *selector, GError **error) { arv_camera_set_string (camera, "BlackLevelSelector", selector, error); } /** * arv_camera_dup_available_black_levels: * @camera: a #ArvCamera * @n_selectors: (out): number of different black level selectors * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of all available black level selectors as strings. * * Returns: (array length=n_selectors) (transfer container): a newly allocated array of strings, to be freed after use with * g_free(). * * Since: 0.8.27 */ const char ** arv_camera_dup_available_black_levels (ArvCamera *camera, guint *n_selectors, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_camera_dup_available_enumerations_as_strings (camera, "BlackLevelSelector", n_selectors, error); } /** * arv_camera_get_black_level_bounds: * @camera: a #ArvCamera * @min: (out): minimum blacklevel * @max: (out): maximum blacklevel * @error: a #GError placeholder, %NULL to ignore * * Retrieves blacklevel bounds. * * Since: 0.8.19 */ void arv_camera_get_black_level_bounds (ArvCamera *camera, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); if (priv->has_brightness) arv_camera_get_float_bounds (camera, "Brightness", min, max, error); else if (priv->has_black_level_raw) arv_camera_get_integer_bounds_as_double (camera, "BlackLevelRaw", min, max, error); else arv_camera_get_float_bounds (camera, "BlackLevel", min, max, error); } /** * arv_camera_set_black_level_auto: * @camera: a #ArvCamera * @auto_mode: auto black_level mode selection * @error: a #GError placeholder, %NULL to ignore * * Configures automatic black level feature. * * Since: 0.8.19 **/ void arv_camera_set_black_level_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error) { arv_camera_set_string (camera, "BlackLevelAuto", arv_auto_to_string (auto_mode), error); } /** * arv_camera_get_black_level_auto: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: auto black level mode selection * * Since: 0.8.19 **/ ArvAuto arv_camera_get_black_level_auto (ArvCamera *camera, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_AUTO_OFF); return arv_auto_from_string (arv_camera_get_string (camera, "BlackLevelAuto", error)); } /* Component control */ /** * arv_camera_is_component_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if Component features are available. * * Since: 0.8.25 */ gboolean arv_camera_is_component_available (ArvCamera *camera, GError **error) { return arv_camera_is_feature_available (camera, "ComponentSelector", error); } /** * arv_camera_dup_available_components: * @camera: a #ArvCamera * @n_components: (out) (optional) : number of available components * @error: a #GError placeholder, %NULL to ignore * * Retrieves the list of available components. * * Returns: (array length=n_components) (transfer container): a newly allocated array of strings, to be freed after use with * g_free(). * * Since: 0.8.23 */ const char ** arv_camera_dup_available_components (ArvCamera *camera, guint *n_components, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_camera_dup_available_enumerations_as_strings (camera, "ComponentSelector", n_components, error); } /** * arv_camera_select_component: * @camera: a #ArvCamera * @component: component to select * @flags: a #ArvComponentSelectionFlags * @component_id: (out) (optional): a placeholder for the component id * @error: a #GError placeholder, %NULL to ignore * * Select and enable or disable the given @component. * * Returns: %TRUE if the component is enabled * * Since: 0.8.25 */ gboolean arv_camera_select_component (ArvCamera *camera, const char *component, ArvComponentSelectionFlags flags, guint *component_id, GError **error) { GError *local_error = NULL; gboolean is_enabled = !(flags == ARV_COMPONENT_SELECTION_FLAGS_DISABLE); g_return_val_if_fail (ARV_IS_CAMERA(camera), FALSE); if (flags == ARV_COMPONENT_SELECTION_FLAGS_EXCLUSIVE_ENABLE || flags == ARV_COMPONENT_SELECTION_FLAGS_ENABLE_ALL) { guint n_components, i; const char **components = arv_camera_dup_available_components(camera, &n_components, &local_error); for (i = 0; i < n_components && local_error == NULL; i++) { arv_camera_set_string (camera, "ComponentSelector", components[i], &local_error); if (local_error == NULL) arv_camera_set_boolean(camera, "ComponentEnable", flags == ARV_COMPONENT_SELECTION_FLAGS_ENABLE_ALL, &local_error); } } if (local_error == NULL) arv_camera_set_string (camera, "ComponentSelector", component, &local_error); if (local_error == NULL && flags != ARV_COMPONENT_SELECTION_FLAGS_NONE && flags != ARV_COMPONENT_SELECTION_FLAGS_ENABLE_ALL) arv_camera_set_boolean(camera, "ComponentEnable", flags == ARV_COMPONENT_SELECTION_FLAGS_DISABLE ? FALSE: TRUE, &local_error); if (component_id != NULL && local_error == NULL) *component_id = arv_camera_get_integer(camera, "ComponentIDValue", &local_error); if (local_error == NULL && flags == ARV_COMPONENT_SELECTION_FLAGS_NONE) is_enabled = arv_camera_get_boolean(camera, "ComponentEnable", &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } return is_enabled; } /** * arv_camera_select_and_enable_component: * @camera: a #ArvCamera * @component: component to select * @disable_others: %TRUE to disable all the other components * @error: a #GError placeholder, %NULL to ignore * * Select and enable the given component. * * Since: 0.8.23 */ void arv_camera_select_and_enable_component (ArvCamera *camera, const char *component, gboolean disable_others, GError **error) { arv_camera_select_component(camera, component, disable_others ? ARV_COMPONENT_SELECTION_FLAGS_EXCLUSIVE_ENABLE : ARV_COMPONENT_SELECTION_FLAGS_ENABLE, NULL, error); } /* Transport layer control */ /** * arv_camera_get_payload: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Retrieves the size needed for the storage of an image. This value is used * for the creation of the stream buffers. * * Returns: frame storage size, in bytes. * * Since: 0.8.0 */ guint arv_camera_get_payload (ArvCamera *camera, GError **error) { return arv_camera_get_integer (camera, "PayloadSize", error); } /** * arv_camera_get_device: * @camera: a #ArvCamera * * Retrieves the #ArvDevice object for more complete access to camera features. * * Returns: (transfer none): underlying device object. * * Since: 0.2.0 */ ArvDevice * arv_camera_get_device (ArvCamera *camera) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return priv->device; } /** * arv_camera_is_frame_rate_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if FrameRate feature is available * * Since: 0.8.0 */ gboolean arv_camera_is_frame_rate_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); switch (priv->vendor) { case ARV_CAMERA_VENDOR_PROSILICA: return arv_camera_is_feature_available (camera, "AcquisitionFrameRateAbs", error); case ARV_CAMERA_VENDOR_TIS: return arv_camera_is_feature_available (camera, "FPS", error); case ARV_CAMERA_VENDOR_POINT_GREY_FLIR: case ARV_CAMERA_VENDOR_DALSA: case ARV_CAMERA_VENDOR_RICOH: case ARV_CAMERA_VENDOR_BASLER: case ARV_CAMERA_VENDOR_XIMEA: case ARV_CAMERA_VENDOR_MATRIX_VISION: case ARV_CAMERA_VENDOR_IMPERX: case ARV_CAMERA_VENDOR_UNKNOWN: return arv_camera_is_feature_available (camera, priv->has_acquisition_frame_rate ? "AcquisitionFrameRate": "AcquisitionFrameRateAbs", error); } return FALSE; } /** * arv_camera_is_exposure_time_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if Exposure Time feature is available. * * Since: 0.8.0 */ gboolean arv_camera_is_exposure_time_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); switch (priv->series) { case ARV_CAMERA_SERIES_XIMEA: return arv_camera_is_feature_available (camera, "ExposureTime", error); case ARV_CAMERA_SERIES_RICOH: return arv_camera_is_feature_available (camera, "ExposureTimeRaw", error); case ARV_CAMERA_SERIES_IMPERX_CHEETAH: return arv_camera_is_feature_available (camera, "ExposureMode", error); default: return arv_camera_is_feature_available (camera, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeAbs", error); } } /** * arv_camera_is_exposure_auto_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if Exposure Auto feature is available. * * Since: 0.8.0 */ gboolean arv_camera_is_exposure_auto_available (ArvCamera *camera, GError **error) { return arv_camera_is_feature_available (camera, "ExposureAuto", error); } /** * arv_camera_is_gain_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if Gain feature is available. * * Since: 0.8.0 */ gboolean arv_camera_is_gain_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); if (priv->has_gain) return arv_camera_is_feature_available (camera, "Gain", error); if (priv->gain_raw_as_float) return arv_camera_is_feature_available (camera, "GainRaw", error); if (priv->gain_abs_as_float) return arv_camera_is_feature_available (camera, "GainAbs", error); return arv_camera_is_feature_available (camera, "GainRaw", error); } /** * arv_camera_is_gain_auto_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if auto gain feature is available. * * Since: 0.8.0 */ gboolean arv_camera_is_gain_auto_available (ArvCamera *camera, GError **error) { return arv_camera_is_feature_available (camera, "GainAuto", error); } /** * arv_camera_is_black_level_auto_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if auto black level feature is available. * * Since: 0.8.19 */ gboolean arv_camera_is_black_level_auto_available (ArvCamera *camera, GError **error) { return arv_camera_is_feature_available (camera, "BlackLevelAuto", error); } /** * arv_camera_is_binning_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ingore * * Returns: %TRUE if Binning feature is available. * * Since: 0.8.0 */ gboolean arv_camera_is_binning_available (ArvCamera *camera, GError **error) { GError *local_error = NULL; gboolean horizontal = FALSE; gboolean vertical = FALSE; g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); horizontal = arv_camera_is_feature_available (camera, "BinningHorizontal", &local_error); if (local_error == NULL) vertical = arv_camera_is_feature_available (camera, "BinningVertical", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } return horizontal && vertical; } /** * arv_camera_get_gain_representation: * @camera: a #ArvCamera * * Return: gain representation, %ARV_GC_REPRESENTATION_UNDEFINED if not available. * * Since: 0.8.31 */ ArvGcRepresentation arv_camera_get_gain_representation (ArvCamera *camera) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_GC_REPRESENTATION_UNDEFINED); if (priv->has_gain) return arv_device_get_feature_representation (priv->device, "Gain"); if (priv->gain_raw_as_float) return arv_device_get_feature_representation (priv->device, "GainRaw"); if (priv->gain_abs_as_float) return arv_device_get_feature_representation (priv->device, "GainAbs"); return arv_device_get_feature_representation (priv->device, "GainRaw"); } /** * arv_camera_get_exposure_time_representation: * @camera: a #ArvCamera * * Return: exposure time representation, %ARV_GC_REPRESENTATION_UNDEFINED if not available. * * Since: 0.8.31 */ ArvGcRepresentation arv_camera_get_exposure_time_representation (ArvCamera *camera) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_GC_REPRESENTATION_UNDEFINED); switch (priv->series) { case ARV_CAMERA_SERIES_XIMEA: return arv_device_get_feature_representation (priv->device, "ExposureTime"); case ARV_CAMERA_SERIES_RICOH: return arv_device_get_feature_representation (priv->device, "ExposureTimeRaw"); case ARV_CAMERA_SERIES_IMPERX_CHEETAH: return arv_device_get_feature_representation (priv->device, "ExposureMode"); default: return arv_device_get_feature_representation (priv->device, priv->has_exposure_time ? "ExposureTime" : "ExposureTimeAbs"); } } /** * arv_camera_execute_command: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ingore * * Execute a Genicam command. * * Since: 0.8.0 */ void arv_camera_execute_command (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_execute_command (priv->device, feature, error); } /** * arv_camera_set_boolean: * @camera: a #ArvCamera * @feature: feature name * @value: new feature value * @error: a #GError placeholder, %NULL to ingore * * Set a boolean feature value. * * Since: 0.8.0 */ void arv_camera_set_boolean (ArvCamera *camera, const char *feature, gboolean value, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_boolean_feature_value (priv->device, feature, value, error); } /** * arv_camera_get_boolean: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: the boolean feature value, %FALSE on error. * * Since: 0.8.0 */ gboolean arv_camera_get_boolean (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_device_get_boolean_feature_value (priv->device, feature, error); } /** * arv_camera_get_boolean_gi: (rename-to arv_camera_get_boolean) * @camera: a #ArvCamera * @feature: feature name * @value: (out): output value * @error: a #GError placeholder, %NULL to ignore * * Since: 0.8.0 */ void arv_camera_get_boolean_gi (ArvCamera *camera, const char *feature, gboolean *value, GError **error) { g_return_if_fail (value != NULL); *value = arv_camera_get_boolean (camera, feature, error); } /** * arv_camera_set_string: * @camera: a #ArvCamera * @feature: feature name * @value: new feature value * @error: a #GError placeholder, %NULL to ignore * * Set an string feature value. * * Since: 0.8.0 */ void arv_camera_set_string (ArvCamera *camera, const char *feature, const char *value, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_string_feature_value (priv->device, feature, value, error); } /** * arv_camera_get_string: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: the string feature value, %NULL on error. * * Since: 0.8.0 */ const char * arv_camera_get_string (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_device_get_string_feature_value (priv->device, feature, error); } /** * arv_camera_set_integer: * @camera: a #ArvCamera * @feature: feature name * @value: new feature value * @error: a #GError placeholder, %NULL to ignore * * Set an integer feature value. * * Since: 0.8.0 */ void arv_camera_set_integer (ArvCamera *camera, const char *feature, gint64 value, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_integer_feature_value (priv->device, feature, value, error); } /** * arv_camera_get_integer: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: the integer feature value, 0 on error. * * Since: 0.8.0 */ gint64 arv_camera_get_integer (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 0); return arv_device_get_integer_feature_value (priv->device, feature, error); } /** * arv_camera_get_integer_bounds: * @camera: a #ArvCamera * @feature: feature name * @min: (out): minimum feature value * @max: (out): maximum feature value * @error: a #GError placeholder, %NULL to ignore * * Retrieves integer feature bounds. * * Since: 0.8.0 */ void arv_camera_get_integer_bounds (ArvCamera *camera, const char *feature, gint64 *min, gint64 *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (min != NULL) *min = G_MININT64; if (max != NULL) *max = G_MAXINT64; g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_get_integer_feature_bounds (priv->device, feature, min, max, error); } /** * arv_camera_get_integer_increment: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: @feature value increment, or 1 on error. * * Since: 0.8.0 */ gint64 arv_camera_get_integer_increment (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 1); g_return_val_if_fail (feature != NULL, 1); return arv_device_get_integer_feature_increment (priv->device, feature, error); } static void arv_camera_get_integer_bounds_as_gint (ArvCamera *camera, const char *feature, gint *min, gint *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; gint64 min64, max64; if (min != NULL) *min = G_MININT; if (max != NULL) *max = G_MAXINT; g_return_if_fail (ARV_IS_CAMERA (camera)); g_return_if_fail (feature != NULL); arv_device_get_integer_feature_bounds (priv->device, feature, &min64, &max64, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); } else { if (min != NULL) *min = CLAMP (min64, G_MININT, G_MAXINT); if (max != NULL) *max = CLAMP (max64, G_MININT, G_MAXINT); } } static void arv_camera_get_integer_bounds_as_guint (ArvCamera *camera, const char *feature, guint *min, guint *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; gint64 min64, max64; if (min != NULL) *min = 0; if (max != NULL) *max = G_MAXUINT; g_return_if_fail (ARV_IS_CAMERA (camera)); g_return_if_fail (feature != NULL); arv_device_get_integer_feature_bounds (priv->device, feature, &min64, &max64, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); } else { if (min != NULL) *min = CLAMP (min64, 0, G_MAXUINT); if (max != NULL) *max = CLAMP (max64, 0, G_MAXUINT); } } static void arv_camera_get_integer_bounds_as_double (ArvCamera *camera, const char *feature, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); GError *local_error = NULL; gint64 min64, max64; if (min != NULL) *min = -G_MAXDOUBLE; if (max != NULL) *max = G_MAXDOUBLE; g_return_if_fail (ARV_IS_CAMERA (camera)); g_return_if_fail (feature != NULL); arv_device_get_integer_feature_bounds (priv->device, feature, &min64, &max64, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); } else { if (min != NULL) *min = min64; if (max != NULL) *max = max64; } } /** * arv_camera_set_float: * @camera: a #ArvCamera * @feature: feature name * @value: new feature value * @error: a #GError placeholder, %NULL to ignore * * Set a float feature value. * * Since: 0.8.0 */ void arv_camera_set_float (ArvCamera *camera, const char *feature, double value, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_float_feature_value (priv->device, feature, value, error); } /** * arv_camera_get_float: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: the float feature value, 0.0 on error. * * Since: 0.8.0 */ double arv_camera_get_float (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 0.0); return arv_device_get_float_feature_value (priv->device, feature, error); } /** * arv_camera_get_float_bounds: * @camera: a #ArvCamera * @feature: feature name * @min: (out): minimum feature value * @max: (out): maximum feature value * @error: a #GError placeholder, %NULL to ignore * * Retrieves float feature bounds. * * Since: 0.8.0 */ void arv_camera_get_float_bounds (ArvCamera *camera, const char *feature, double *min, double *max, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (min != NULL) *min = -G_MAXDOUBLE; if (max != NULL) *max = G_MAXDOUBLE; g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_get_float_feature_bounds (priv->device, feature, min, max, error); } /** * arv_camera_get_float_increment: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Returns: @feature value increment, or #G_MINDOUBLE on error. * * Since: 0.8.16 */ double arv_camera_get_float_increment (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), 1); g_return_val_if_fail (feature != NULL, 1); return arv_device_get_float_feature_increment (priv->device, feature, error); } /** * arv_camera_set_register: * @camera: a #ArvCamera * @feature: feature name * @length: buffer length * @value: new feature value * @error: a #GError placeholder, %NULL to ignore * * Set a register content. * * Since: 0.8.31 */ void arv_camera_set_register (ArvCamera *camera, const char *feature, guint64 length, void *value, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_register_feature_value (priv->device, feature, length, value, error); } /** * arv_camera_dup_register: * @camera: a #ArvCamera * @feature: feature name * @length: (out) (allow-none): register length * @error: a #GError placeholder, %NULL to ignore * * Returns: register content, must be freed using [func@GLib.free]. * * Since: 0.8.31 */ void * arv_camera_dup_register (ArvCamera *camera, const char *feature, guint64 *length, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (length != NULL) *length = 0; g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_dup_register_feature_value (priv->device, feature, length, error); } /** * arv_camera_dup_available_enumerations: * @camera: a #ArvCamera * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder, %NULL to ignore * * Get all the available values of @feature, as 64 bit integers. * * Returns: (array length=n_values) (transfer container): a newly created array of integers, which must freed after use using g_free, or * NULL on error. * * Since: 0.8.0 */ gint64 * arv_camera_dup_available_enumerations (ArvCamera *camera, const char *feature, guint *n_values, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (n_values != NULL) *n_values = 0; g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_dup_available_enumeration_feature_values (priv->device, feature, n_values, error); } /** * arv_camera_dup_available_enumerations_as_strings: * @camera: a #ArvCamera * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder, %NULL to ignore * * Get all the available values of @feature, as strings. * * Returns: (array length=n_values) (transfer container): a newly created array of const strings, which must freed after use using g_free, * or %NULL on error. * * Since: 0.8.0 */ const char ** arv_camera_dup_available_enumerations_as_strings (ArvCamera *camera, const char *feature, guint *n_values, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (n_values != NULL) *n_values = 0; g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_dup_available_enumeration_feature_values_as_strings (priv->device, feature, n_values, error); } /** * arv_camera_dup_available_enumerations_as_display_names: * @camera: a #ArvCamera * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder, %NULL to ignore * * Get display names of all the available entries of @feature. * * Returns: (array length=n_values) (transfer container): a newly created array of const strings, to be freed after use using g_free, or * %NULL on error. * * Since: 0.8.0 */ const char ** arv_camera_dup_available_enumerations_as_display_names (ArvCamera *camera, const char *feature, guint *n_values, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (n_values != NULL) *n_values = 0; g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_dup_available_enumeration_feature_values_as_display_names (priv->device, feature, n_values, error); } /** * arv_camera_is_enumeration_entry_available: * @camera: a #ArvCamera * @feature: enumeration feature name * @entry: entry name * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if the feature and the feature entry are available * * Since: 0.8.17 */ gboolean arv_camera_is_enumeration_entry_available (ArvCamera *camera, const char *feature, const char *entry, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_device_is_enumeration_entry_available (priv->device, feature, entry, error); } /** * arv_camera_is_feature_available: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Return: %TRUE if feature is available, %FALSE if not or on error. * * Since: 0.8.0 */ gboolean arv_camera_is_feature_available (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_device_is_feature_available (priv->device, feature, error); } /** * arv_camera_is_feature_implemented: * @camera: a #ArvCamera * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Return: %TRUE if feature is implemented, %FALSE if not or on error. * * Since: 0.8.23 */ gboolean arv_camera_is_feature_implemented (ArvCamera *camera, const char *feature, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_device_is_feature_implemented (priv->device, feature, error); } /** * arv_camera_get_feature_representation: * @camera: a #ArvCamera * @feature: feature name * * Return: the feature representation, %ARV_GC_REPRESENTATION_UNDEFINED if not available. * * Since: 0.8.31 */ ArvGcRepresentation arv_camera_get_feature_representation (ArvCamera *camera, const char *feature) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), ARV_GC_REPRESENTATION_UNDEFINED); return arv_device_get_feature_representation (priv->device, feature); } /** * arv_camera_set_register_cache_policy: * @camera: a #ArvCamera * @policy: cache policy * * Sets the Genicam register cache policy. * * Be aware that some camera may have wrong Cachable properties defined in their Genicam metadata, which * may lead to incorrect readouts. Using the debug cache policy, and activating genicam debug output (export * ARV_DEBUG=genicam), can help you to check the cache validity. In this mode, every time the cache content is not in * sync with the actual register value, a debug message is printed on the console. * * Since: 0.8.8 */ void arv_camera_set_register_cache_policy (ArvCamera *camera, ArvRegisterCachePolicy policy) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_register_cache_policy (priv->device, policy); } /** * arv_camera_set_range_check_policy: * @camera: a #ArvCamera * @policy: range check policy * * Sets the feature range check policy. When enabled, before being set, the value of all nodes with an #ArvGcFloat or * #ArvGcInteger interface will be checked against their Min and Max properties. * * Be aware that some camera may have wrong definition of Min and Max, as this check is defined as not * mandatory in the Genicam specification. If this is the case, it will not possible to set the value of the features * with faulty Min or Max definition. Range check is disabled by default. * * Since: 0.8.8 */ void arv_camera_set_range_check_policy (ArvCamera *camera, ArvRangeCheckPolicy policy) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_range_check_policy (priv->device, policy); } /** * arv_camera_set_access_check_policy: * @camera: a #ArvCamera * @policy: access check policy * * Sets the feature access check policy. When enabled, before being accessed, the actual read/write access of register * is checked using AccessMode properties. On some devices, it helps to avoid forbidden writes to registers that may put * the device in a bad state. * * Access check is disabled by default. * * Since: 0.8.22 */ void arv_camera_set_access_check_policy (ArvCamera *camera, ArvAccessCheckPolicy policy) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (ARV_IS_CAMERA (camera)); arv_device_set_access_check_policy (priv->device, policy); } /** * arv_camera_is_gv_device: * @camera: a #ArvCamera * * Returns: %TRUE if @camera is a GigEVision device. * * Since: 0.4.0 */ gboolean arv_camera_is_gv_device (ArvCamera *camera) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return ARV_IS_GV_DEVICE (priv->device); } /** * arv_camera_gv_get_n_network_interfaces: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the number of device network interfaces. * * Since: 0.8.25 */ gint arv_camera_gv_get_n_network_interfaces (ArvCamera *camera, GError **error) { g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_camera_get_integer (camera, "ArvGevNumberOfNetworkInterfaces", error); } /** * arv_camera_gv_get_n_stream_channels: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the number of supported stream channels. * * Since: 0.8.0 */ gint arv_camera_gv_get_n_stream_channels (ArvCamera *camera, GError **error) { g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_camera_get_integer (camera, "ArvGevStreamChannelCount", error); } /** * arv_camera_gv_select_stream_channel: * @camera: a #ArvCamera * @channel_id: id of the channel to select * @error: a #GError placeholder, %NULL to ignore * * Select the current stream channel. Negative @channel_id is ignored. * * Since: 0.8.0 */ void arv_camera_gv_select_stream_channel (ArvCamera *camera, gint channel_id, GError **error) { if (channel_id < 0) return; g_return_if_fail (arv_camera_is_gv_device (camera)); arv_camera_set_integer (camera, "ArvGevStreamChannelSelector", channel_id, error); } /** * arv_camera_gv_get_current_stream_channel: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: The current stream channel id. * * Since: 0.8.0 */ int arv_camera_gv_get_current_stream_channel (ArvCamera *camera, GError **error) { g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_camera_get_integer (camera, "ArvGevStreamChannelSelector", error); } /** * arv_camera_gv_set_multipart: * @camera: a #ArvCamera * @enable: %TRUE to enable multipart * @error: a #GError placeholder, %NULL to ignore * * Control multipart payload support * * Since: 0.8.23 */ void arv_camera_gv_set_multipart (ArvCamera *camera, gboolean enable, GError **error) { g_return_if_fail (arv_camera_is_gv_device (camera)); arv_camera_set_boolean (camera, "ArvGevSCCFGMultipart", enable, error); } /** * arv_camera_gv_get_multipart: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if multipart payload support is enabled. * * Since: 0.8.23 */ gboolean arv_camera_gv_get_multipart (ArvCamera *camera, GError **error) { g_return_val_if_fail (arv_camera_is_gv_device (camera), FALSE); return arv_camera_get_boolean (camera, "ArvGevSCCFGMultipart", error); } /** * arv_camera_gv_is_multipart_supported: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if multipart payload is supported by @camera. * * Since: 0.8.23 */ gboolean arv_camera_gv_is_multipart_supported (ArvCamera *camera, GError **error) { GError *local_error = NULL; gboolean is_supported; g_return_val_if_fail (arv_camera_is_gv_device (camera), FALSE); is_supported = arv_camera_is_feature_implemented (camera, "ArvGevSCCFGMultipart", &local_error); /* Ignore invalid address error, the needed registers are optional */ if (local_error != NULL) { if (local_error->domain == ARV_DEVICE_ERROR && (local_error->code == ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_ADDRESS || local_error->code == ARV_DEVICE_ERROR_PROTOCOL_ERROR_ACCESS_DENIED)) g_clear_error (&local_error); else g_propagate_error(error, local_error); } return is_supported; } /** * arv_camera_gv_set_packet_delay: * @camera: a #ArvCamera * @delay_ns: inter packet delay, in nanoseconds * @error: a #GError placeholder, %NULL to ignore * * Configure the inter packet delay to insert between each packet for the current stream * channel. This can be used as a crude flow-control mechanism if the application or the network * infrastructure cannot keep up with the packets coming from the device. Negative @delay_ns is ignored. * * Since: 0.8.0 */ void arv_camera_gv_set_packet_delay (ArvCamera *camera, gint64 delay_ns, GError **error) { GError *local_error = NULL; gint64 tick_frequency; gint64 value; if (delay_ns < 0) return; g_return_if_fail (arv_camera_is_gv_device (camera)); tick_frequency = arv_camera_get_integer (camera, "ArvGevTimestampTickFrequency", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (tick_frequency <= 0) { if (!arv_camera_is_feature_available (camera, "GevSCPD", NULL)) g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "GevSCPD not not found"); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Device returned an invalid timestamp tick frequency"); return; } value = tick_frequency * delay_ns / 1000000000LL; arv_camera_set_integer (camera, "GevSCPD", value, error); } /** * arv_camera_gv_get_packet_delay: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: The inter packet delay, in nanoseconds. * * Since: 0.8.0 */ gint64 arv_camera_gv_get_packet_delay (ArvCamera *camera, GError **error) { GError *local_error = NULL; gint64 tick_frequency; gint64 value; gboolean available; g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); available = arv_camera_is_feature_available (camera, "GevSCPD", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } if (!available) return 0; tick_frequency = arv_camera_get_integer (camera, "ArvGevTimestampTickFrequency", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } if (tick_frequency <= 0) return 0; value = arv_camera_get_integer (camera, "GevSCPD", &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } return value * 1000000000LL / tick_frequency; } /** * arv_camera_gv_set_packet_size: * @camera: a #ArvCamera * @packet_size: packet size, in bytes * @error: a #GError placeholder, %NULL to ignore * * Specifies the stream packet size, in bytes, to send on the selected channel for a GVSP transmitter * or specifies the maximum packet size supported by a GVSP receiver. * * This does not include data leader and data trailer and the last data packet which might be of * smaller size (since packet size is not necessarily a multiple of block size for stream channel). * Negative @packet_size is ignored. * * Since: 0.8.0 */ void arv_camera_gv_set_packet_size (ArvCamera *camera, gint packet_size, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (packet_size <= 0) return; g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_packet_size (ARV_GV_DEVICE (priv->device), packet_size, error); } /** * arv_camera_gv_get_packet_size: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: The stream packet size, in bytes. * * Since: 0.8.0 */ guint arv_camera_gv_get_packet_size (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_gv_device_get_packet_size (ARV_GV_DEVICE (priv->device), error); } /** * arv_camera_gv_auto_packet_size: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Automatically determine the biggest packet size that can be used data * streaming, and set GevSCPSPacketSize value accordingly. This function relies * on the GevSCPSFireTestPacket feature. If this feature is not available, the * packet size will be set to a default value (1500 bytes). * * Returns: The packet size, in bytes. * * Since: 0.8.0 */ guint arv_camera_gv_auto_packet_size (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_gv_device_auto_packet_size (ARV_GV_DEVICE (priv->device), error); } /** * arv_camera_gv_set_stream_options: * @camera: a #ArvCamera * @options: option for stream creation * * Sets the options used during stream object creation. These options mus be * set before the call to arv_camera_create_stream(). * * Since: 0.6.0 */ void arv_camera_gv_set_stream_options (ArvCamera *camera, ArvGvStreamOption options) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_stream_options (ARV_GV_DEVICE (priv->device), options); } /** * arv_camera_gv_set_packet_size_adjustment: * @camera: a #ArvCamera * @adjustment: a #ArvGvPacketSizeAdjustment option * * Sets the option for packet size adjustment that happens at stream object creation. * * Since: 0.8.3 */ void arv_camera_gv_set_packet_size_adjustment (ArvCamera *camera, ArvGvPacketSizeAdjustment adjustment) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_packet_size_adjustment (ARV_GV_DEVICE (priv->device), adjustment); } /** * arv_camera_gv_get_persistent_ip: * @camera: a #ArvCamera * @ip: (out): a IP address placeholder * @mask: (out) (optional): a netmask placeholder, %NULL to ignore * @gateway: (out) (optional): a gateway IP address placeholder, %NULL to ignore * @error: a #GError placeholder, %NULL to ignore * * Get the persistent IP address setting of camera. * * Since: 0.8.22 */ void arv_camera_gv_get_persistent_ip (ArvCamera *camera, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_get_persistent_ip (ARV_GV_DEVICE (priv->device), ip, mask, gateway, error); } /** * arv_camera_gv_set_persistent_ip_from_string: * @camera: a #ArvCamera * @ip: IPv4 address in string format * @mask: netmask in string format * @gateway: Gateway IPv4 address in string format * @error: a #GError placeholder, %NULL to ignore * * Sets the persistent IP address to camera. * * Since: 0.8.22 */ void arv_camera_gv_set_persistent_ip_from_string (ArvCamera *camera, const char *ip, const char *mask, const char *gateway, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_persistent_ip_from_string (ARV_GV_DEVICE (priv->device), ip, mask, gateway, error); } /** * arv_camera_gv_set_persistent_ip: * @camera: a #ArvCamera * @ip: IPv4 address * @mask: Netmask * @gateway: Gateway IPv4 address * @error: a #GError placeholder, %NULL to ignore * * Sets the persistent IP address to camera. * * Since: 0.8.22 */ void arv_camera_gv_set_persistent_ip (ArvCamera *camera, GInetAddress *ip, GInetAddressMask *mask, GInetAddress *gateway, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_persistent_ip (ARV_GV_DEVICE (priv->device), ip, mask, gateway, error); } /** * arv_camera_gv_get_ip_configuration_mode: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Get the IP address configuration mode. * * Returns: IP address configuration mode * * Since: 0.8.22 */ ArvGvIpConfigurationMode arv_camera_gv_get_ip_configuration_mode(ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (arv_camera_is_gv_device (camera), 0); return arv_gv_device_get_ip_configuration_mode (ARV_GV_DEVICE (priv->device), error); } /** * arv_camera_gv_set_ip_configuration_mode: * @camera: a #ArvCamera * @mode: IP address configuration mode * @error: a #GError placeholder, %NULL to ignore * * Sets the IP address configuration mode. * Available modes are ARV_GV_IP_CONFIGURATION_MODE_DHCP, ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP, * ARV_GV_IP_CONFIGURATION_MODE_LLA * * Since: 0.8.22 */ void arv_camera_gv_set_ip_configuration_mode (ArvCamera *camera, ArvGvIpConfigurationMode mode, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_if_fail (arv_camera_is_gv_device (camera)); arv_gv_device_set_ip_configuration_mode (ARV_GV_DEVICE (priv->device), mode, error); } /** * arv_camera_is_uv_device: * @camera: a #ArvCamera * * Returns: %TRUE if @camera is a USB3Vision device. * * Since: 0.6.0 */ gboolean arv_camera_is_uv_device (ArvCamera *camera) { #if ARAVIS_HAS_USB ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); #endif g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); #if ARAVIS_HAS_USB return ARV_IS_UV_DEVICE (priv->device); #else return FALSE; #endif } /** * arv_camera_uv_is_bandwidth_control_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: wether bandwidth limits are available on this camera * * Since: 0.8.0 */ gboolean arv_camera_uv_is_bandwidth_control_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (arv_camera_is_uv_device (camera), FALSE); switch (priv->vendor) { case ARV_CAMERA_VENDOR_XIMEA: return arv_camera_is_feature_available (camera, "DeviceLinkThroughputLimit", error); default: return FALSE; } } /** * arv_camera_uv_set_bandwidth: * @camera: a #ArvCamera * @bandwidth: Bandwith limit, in megabits/sec * @error: a #GError placeholder, %NULL to ignore * * Set the bandwith limit or, if @bandwith is not strictly positive, disable the limit. * * Since: 0.8.0 */ void arv_camera_uv_set_bandwidth (ArvCamera *camera, guint bandwidth, GError **error) { GError *local_error = NULL; g_return_if_fail (arv_camera_is_uv_device (camera)); if (bandwidth > 0) { arv_camera_set_integer (camera, "DeviceLinkThroughputLimit", bandwidth, &local_error); if (local_error == NULL) arv_camera_set_integer (camera, "DeviceLinkThroughputLimitMode", 1, &local_error); } else { arv_camera_set_integer (camera, "DeviceLinkThroughputLimitMode", 0, &local_error); } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_uv_get_bandwidth: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: the current bandwidth limit * * Since: 0.8.0 */ guint arv_camera_uv_get_bandwidth (ArvCamera *camera, GError **error) { g_return_val_if_fail (arv_camera_is_uv_device (camera), 0); return arv_camera_get_integer (camera, "DeviceLinkThroughputLimit", error); } /** * arv_camera_uv_get_bandwidth_bounds: * @camera: a #ArvCamera * @min: (out): minimum bandwidth * @max: (out): maximum bandwidth * @error: a #GError placeholder, %NULL to ignore * * Since: 0.8.0 */ void arv_camera_uv_get_bandwidth_bounds (ArvCamera *camera, guint *min, guint *max, GError **error) { if (min != NULL) *min = 0; if (max != NULL) *max = 0; g_return_if_fail (arv_camera_is_uv_device (camera)); arv_camera_get_integer_bounds_as_guint (camera, "DeviceLinkThroughputLimit", min, max, error); } /** * arv_camera_uv_set_usb_mode: * @camera: a #ArvCamera * @usb_mode: a #ArvUvUsbMode option * * Since: 0.8.17 */ void arv_camera_uv_set_usb_mode (ArvCamera *camera, ArvUvUsbMode usb_mode) { #if ARAVIS_HAS_USB ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); #endif g_return_if_fail (arv_camera_is_uv_device (camera)); #if ARAVIS_HAS_USB arv_uv_device_set_usb_mode (ARV_UV_DEVICE (priv->device), usb_mode); #endif } /** * arv_camera_are_chunks_available: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Returns: %TRUE if chunk data are available * * Since: 0.8.8 */ gboolean arv_camera_are_chunks_available (ArvCamera *camera, GError **error) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); if (!arv_camera_is_feature_available (camera, "ChunkModeActive", error)) return FALSE; return ARV_IS_GC_ENUMERATION (arv_device_get_feature (priv->device, "ChunkSelector")); } /** * arv_camera_set_chunk_mode: * @camera: a #ArvCamera * @is_active: wether to enable chunk data mode * @error: a #GError placeholder, %NULL to ignore * * Controls wether chunk data mode is active. When active, chunk data * are appended to image data in #ArvBuffer. A #ArvChunkParser must be used in * order to extract chunk data. * * Since: 0.8.0 **/ void arv_camera_set_chunk_mode (ArvCamera *camera, gboolean is_active, GError **error) { g_return_if_fail (ARV_IS_CAMERA (camera)); arv_camera_set_boolean (camera, "ChunkModeActive", is_active, error); } /** * arv_camera_get_chunk_mode: * @camera: a #ArvCamera * @error: a #GError placeholder, %NULL to ignore * * Check wether chunk data mode is active. Please see arv_camera_set_chunk_mode(). * * Returns: %TRUE if chunk data mode is active. * * Since: 0.8.0 **/ gboolean arv_camera_get_chunk_mode (ArvCamera *camera, GError **error) { g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); return arv_camera_get_boolean (camera, "ChunkModeActive", error); } /** * arv_camera_set_chunk_state: * @camera: a #ArvCamera * @chunk: chunk data name * @is_enabled: wether to enable this chunk * @error: a #GError placeholder, %NULL to ignore * * Sets state of a chunk data. Chunk data are be embedded in #ArvBuffer only * if chunk mode is active. Please see arv_camera_set_chunk_mode(). * * Since: 0.8.0 **/ void arv_camera_set_chunk_state (ArvCamera *camera, const char *chunk, gboolean is_enabled, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_CAMERA (camera)); g_return_if_fail (chunk != NULL && chunk[0] != '\0'); arv_camera_set_string (camera, "ChunkSelector", chunk, &local_error); if (local_error == NULL) arv_camera_set_boolean (camera, "ChunkEnable", is_enabled, &local_error); if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_camera_get_chunk_state: * @camera: a #ArvCamera * @chunk: chunk data name * @error: a #GError placeholder, %NULL to ignore * * Gets state of chunk data. Chunk data are be embedded in #ArvBuffer only * if chunk mode is active. Please see arv_camera_set_chunk_mode(). * * Returns: %TRUE if @chunk is enabled. * * Since: 0.8.0 */ gboolean arv_camera_get_chunk_state (ArvCamera *camera, const char *chunk, GError **error) { GError *local_error = NULL; g_return_val_if_fail (ARV_IS_CAMERA (camera), FALSE); g_return_val_if_fail (chunk != NULL && chunk[0] != '\0', FALSE); arv_camera_set_string (camera, "ChunkSelector", chunk, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } return arv_camera_get_boolean (camera, "ChunkEnable", error); } /** * arv_camera_set_chunks: * @camera: a #ArvCamera * @chunk_list: chunk data names, as a comma or space separated list * @error: a #GError placeholder, %NULL to ignore * * Convenience function for enabling a set of chunk data. Chunk mode is activated, or deactivated * if @chunk_list is %NULL or empty. All chunk data not listed are disabled. * * Since: 0.8.0 */ void arv_camera_set_chunks (ArvCamera *camera, const char *chunk_list, GError **error) { GError *local_error = NULL; const char **available_chunks; char **chunks; char *striped_chunk_list; guint i, j; guint n_values; g_return_if_fail (ARV_IS_CAMERA (camera)); if (chunk_list == NULL) { arv_camera_set_chunk_mode (camera, FALSE, error); return; } striped_chunk_list = g_strdup (chunk_list); arv_str_strip (striped_chunk_list, " ,:;", ','); chunks = g_strsplit_set (striped_chunk_list, " ,:;", -1); g_free (striped_chunk_list); if (chunks == NULL || chunks[0] == NULL) { g_strfreev (chunks); arv_camera_set_chunk_mode (camera, FALSE, error); return; } arv_camera_set_chunk_mode (camera, TRUE, &local_error); if (local_error == NULL) { available_chunks = arv_camera_dup_available_enumerations_as_strings (camera, "ChunkSelector", &n_values, &local_error); for (j = 0; chunks[j] != NULL && local_error == NULL; j++) { gboolean found = FALSE; for (i = 0; i < n_values && local_error == NULL; i++) { if (g_strcmp0 (available_chunks[i], chunks[j]) == 0) { found = TRUE; break; } } if (!found) { g_set_error (&local_error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "%s not found in available chunks", chunks[j]); } } for (i = 0; i < n_values && local_error == NULL; i++) { gboolean found = FALSE; for (j = 0; chunks[j] != NULL && local_error == NULL; j++) { if (g_strcmp0 (available_chunks[i], chunks[j]) == 0) { found = TRUE; break; } } /* This is normally not needed, but is there as a workaround for an issue with a Basler * acA1300-30gc which seems to reset ChunkActiveMode on every ChunkEnable=false */ arv_camera_set_chunk_mode (camera, TRUE, &local_error); if (local_error == NULL) arv_camera_set_chunk_state (camera, available_chunks[i], found, &local_error); } g_free (available_chunks); } g_strfreev (chunks); if (local_error != NULL) { g_propagate_error (error, local_error); return; } } /** * arv_camera_create_chunk_parser: * @camera: a #ArvCamera * * Creates a new [class@ArvChunkParser] object, used for the extraction of chunk data from [class@ArvBuffer]. * * Returns: (transfer full): a new [class@ArvChunkParser]. * * Since: 0.4.0 */ ArvChunkParser * arv_camera_create_chunk_parser (ArvCamera *camera) { ArvCameraPrivate *priv = arv_camera_get_instance_private (camera); g_return_val_if_fail (ARV_IS_CAMERA (camera), NULL); return arv_device_create_chunk_parser (priv->device); } /** * arv_camera_new: * @name: (allow-none): name of the camera. * @error: a #GError placeholder, %NULL to ignore * * Creates a new #ArvCamera. If @name is null, it will instantiate the * first available camera. * * If the camera is a GigEVision, @name can be either: * * - <vendor>-<model>-<serial> * - <vendor_alias>-<serial> * - <vendor>-<serial> * - <user_id> * - <ip_address> * - <mac_address> * * For example: * * - The Imaging Source Europe GmbH-DFK 33GX265-39020369 * - The Imaging Source Europe GmbH-39020369 * - TIS-39020369 * - 192.168.0.2 * - 00:07:48:af:a2:61 * * If the camera is a USB3Vision device, @name is either: * * - <vendor_alias>-<serial> * - <vendor>-<serial> * * Returns: a new #ArvCamera. * * Since: 0.8.0 */ ArvCamera * arv_camera_new (const char *name, GError **error) { /* if you need to apply or test for fixups based on the camera model please do so in arv_camera_constructed and not here, as this breaks objects created with g_object_new, which includes but is not limited to introspection users */ return g_initable_new (ARV_TYPE_CAMERA, NULL, error, "name", name, NULL); } /** * arv_camera_new_with_device: * @device: (transfer none): a #ArvDevice * @error: a #GError placeholder, %NULL to ignore * * Creates a new #ArvCamera, using @device as its internal device object. * * Returns: a new #ArvCamera. * * Since: 0.8.6 */ ArvCamera * arv_camera_new_with_device (ArvDevice *device, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); /* if you need to apply or test for fixups based on the camera model please do so in arv_camera_constructed and not here, as this breaks objects created with g_object_new, which includes but is not limited to introspection users */ return g_initable_new (ARV_TYPE_CAMERA, NULL, error, "device", device, NULL); } static void arv_camera_init (ArvCamera *camera) { } static void arv_camera_finalize (GObject *object) { ArvCameraPrivate *priv = arv_camera_get_instance_private (ARV_CAMERA (object)); g_clear_pointer (&priv->name, g_free); g_clear_object (&priv->device); g_clear_error (&priv->init_error); G_OBJECT_CLASS (arv_camera_parent_class)->finalize (object); } static void arv_camera_constructed (GObject *object) { ArvCamera *camera = ARV_CAMERA (object); ArvCameraPrivate *priv; ArvCameraVendor vendor; ArvCameraSeries series; const char *vendor_name; const char *model_name; GError *error = NULL; G_OBJECT_CLASS (arv_camera_parent_class)->constructed (object); priv = arv_camera_get_instance_private (camera); if (!priv->device) priv->device = arv_open_device (priv->name, &error); if (!ARV_IS_DEVICE (priv->device)) { if (error == NULL) { if (priv->name != NULL) error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "Device '%s' not found", priv->name); else error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "No supported device found"); } g_clear_error (&priv->init_error); priv->init_error = error; return; } priv->genicam = arv_device_get_genicam (priv->device); vendor_name = arv_camera_get_vendor_name (camera, NULL); model_name = arv_camera_get_model_name (camera, NULL); if (g_strcmp0 (vendor_name, "Basler") == 0) { vendor = ARV_CAMERA_VENDOR_BASLER; if (g_str_has_prefix (model_name, "acA")) series = ARV_CAMERA_SERIES_BASLER_ACE; else if (g_str_has_prefix (model_name, "scA")) series = ARV_CAMERA_SERIES_BASLER_SCOUT; else series = ARV_CAMERA_SERIES_BASLER_OTHER; } else if (g_strcmp0 (vendor_name, "Prosilica") == 0) { vendor = ARV_CAMERA_VENDOR_PROSILICA; series = ARV_CAMERA_SERIES_PROSILICA; } else if (g_strcmp0 (vendor_name, "The Imaging Source Europe GmbH") == 0) { vendor = ARV_CAMERA_VENDOR_TIS; series = ARV_CAMERA_SERIES_TIS; } else if (g_strcmp0 (vendor_name, "DALSA") == 0) { vendor = ARV_CAMERA_VENDOR_DALSA; series = ARV_CAMERA_SERIES_DALSA; } else if (g_strcmp0 (vendor_name, "Point Grey Research") == 0 || g_strcmp0 (vendor_name, "FLIR") == 0) { vendor = ARV_CAMERA_VENDOR_POINT_GREY_FLIR; series = ARV_CAMERA_SERIES_POINT_GREY_FLIR; } else if (g_strcmp0 (vendor_name, "Ricoh Company, Ltd.") == 0) { vendor = ARV_CAMERA_VENDOR_RICOH; series = ARV_CAMERA_SERIES_RICOH; } else if (g_strcmp0 (vendor_name, "XIMEA GmbH") == 0) { vendor = ARV_CAMERA_VENDOR_XIMEA; series = ARV_CAMERA_SERIES_XIMEA; } else if (g_strcmp0 (vendor_name, "MATRIX VISION GmbH") == 0) { vendor = ARV_CAMERA_VENDOR_MATRIX_VISION; series = ARV_CAMERA_SERIES_MATRIX_VISION; } else if (g_strcmp0 (vendor_name, "Imperx, Inc") == 0) { vendor = ARV_CAMERA_VENDOR_IMPERX; if (g_str_has_prefix (model_name, "POE-C")) series = ARV_CAMERA_SERIES_IMPERX_CHEETAH; else series = ARV_CAMERA_SERIES_IMPERX_OTHER; } else { vendor = ARV_CAMERA_VENDOR_UNKNOWN; series = ARV_CAMERA_SERIES_UNKNOWN; } priv->vendor = vendor; priv->series = series; priv->has_serial_number = ARV_IS_GC_STRING (arv_device_get_feature (priv->device, "DeviceSerialNumber")); priv->has_gain = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "Gain")); priv->gain_raw_as_float = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "GainRaw")); priv->gain_abs_as_float = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "GainAbs")); priv->has_brightness = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "Brightness")); priv->has_black_level_raw = ARV_IS_GC_INTEGER (arv_device_get_feature (priv->device, "BlackLevelRaw")); priv->has_black_level = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "BlackLevel")); priv->has_exposure_time = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "ExposureTime")); priv->has_acquisition_frame_rate = ARV_IS_GC_FLOAT (arv_device_get_feature (priv->device, "AcquisitionFrameRate")); priv->has_acquisition_frame_rate_auto = ARV_IS_GC_STRING (arv_device_get_feature (priv->device, "AcquisitionFrameRateAuto")); priv->has_acquisition_frame_rate_enabled = ARV_IS_GC_BOOLEAN (arv_device_get_feature (priv->device, "AcquisitionFrameRateEnabled")); priv->has_region_offset = ARV_IS_GC_INTEGER(arv_device_get_feature(priv->device, "OffsetX")) && ARV_IS_GC_INTEGER(arv_device_get_feature(priv->device, "OffsetY")); } static void arv_camera_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ArvCameraPrivate *priv = arv_camera_get_instance_private (ARV_CAMERA (object)); switch (prop_id) { case PROP_CAMERA_DEVICE: priv->device = g_value_dup_object (value); break; case PROP_CAMERA_NAME: g_free (priv->name); priv->name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_camera_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ArvCameraPrivate *priv = arv_camera_get_instance_private (ARV_CAMERA (object)); switch (prop_id) { case PROP_CAMERA_DEVICE: g_value_set_object (value, priv->device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_camera_class_init (ArvCameraClass *camera_class) { GObjectClass *object_class = G_OBJECT_CLASS (camera_class); object_class->finalize = arv_camera_finalize; object_class->constructed = arv_camera_constructed; object_class->set_property = arv_camera_set_property; object_class->get_property = arv_camera_get_property; /** * ArvCamera:name: * * Internal device name for object construction * * Stability: Private */ g_object_class_install_property (object_class, PROP_CAMERA_NAME, g_param_spec_string ("name", "Camera name", "The camera name", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); /** * ArvCamera:device: * * Internal device object * * Stability: Private */ g_object_class_install_property (object_class, PROP_CAMERA_DEVICE, g_param_spec_object ("device", "Device", "The device associated with this camera", ARV_TYPE_DEVICE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static gboolean arv_camera_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { ArvCamera *self = ARV_CAMERA (initable); ArvCameraPrivate *priv = arv_camera_get_instance_private (ARV_CAMERA (initable)); g_return_val_if_fail (ARV_IS_CAMERA (self), FALSE); if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialization not supported"); return FALSE; } if (priv->init_error != NULL) { if (error != NULL) *error = g_error_copy (priv->init_error); return FALSE; } return TRUE; } static void arv_camera_initable_iface_init (GInitableIface *iface) { iface->init = arv_camera_initable_init; } aravis-0.8.34/src/arvcamera.h000066400000000000000000000436401475431451200160110ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_CAMERA_H #define ARV_CAMERA_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include #include G_BEGIN_DECLS typedef enum { ARV_COMPONENT_SELECTION_FLAGS_NONE, ARV_COMPONENT_SELECTION_FLAGS_ENABLE, ARV_COMPONENT_SELECTION_FLAGS_DISABLE, ARV_COMPONENT_SELECTION_FLAGS_EXCLUSIVE_ENABLE, ARV_COMPONENT_SELECTION_FLAGS_ENABLE_ALL, } ArvComponentSelectionFlags; #define ARV_TYPE_CAMERA (arv_camera_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvCamera, arv_camera, ARV, CAMERA, GObject) struct _ArvCameraClass { GObjectClass parent_class; }; ARV_API ArvCamera * arv_camera_new (const char *name, GError **error); ARV_API ArvCamera * arv_camera_new_with_device (ArvDevice *device, GError **error); ARV_API ArvDevice * arv_camera_get_device (ArvCamera *camera); ARV_API ArvStream * arv_camera_create_stream (ArvCamera *camera, ArvStreamCallback callback, void *user_data, GError **error); ARV_API ArvStream * arv_camera_create_stream_full (ArvCamera *camera, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error); /* Device informations */ ARV_API const char * arv_camera_get_vendor_name (ArvCamera *camera, GError **error); ARV_API const char * arv_camera_get_model_name (ArvCamera *camera, GError **error); ARV_API const char * arv_camera_get_device_serial_number (ArvCamera *camera, GError **error); ARV_API const char * arv_camera_get_device_id (ArvCamera *camera, GError **error); /* Image format control */ ARV_API gboolean arv_camera_is_region_offset_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_sensor_size (ArvCamera *camera, gint *width, gint *height, GError **error); ARV_API void arv_camera_set_region (ArvCamera *camera, gint x, gint y, gint width, gint height, GError **error); ARV_API void arv_camera_get_region (ArvCamera *camera, gint *x, gint *y, gint *width, gint *height, GError **error); ARV_API void arv_camera_get_x_offset_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_x_offset_increment (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_y_offset_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_y_offset_increment (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_width_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_width_increment (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_height_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_height_increment (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_binning (ArvCamera *camera, gint dx, gint dy, GError **error); ARV_API void arv_camera_get_binning (ArvCamera *camera, gint *dx, gint *dy, GError **error); ARV_API void arv_camera_get_x_binning_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_x_binning_increment (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_y_binning_bounds (ArvCamera *camera, gint *min, gint *max, GError **error); ARV_API gint arv_camera_get_y_binning_increment (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_binning_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_pixel_format (ArvCamera *camera, ArvPixelFormat format, GError **error); ARV_API void arv_camera_set_pixel_format_from_string (ArvCamera *camera, const char * format, GError **error); ARV_API ArvPixelFormat arv_camera_get_pixel_format (ArvCamera *camera, GError **error); ARV_API const char * arv_camera_get_pixel_format_as_string (ArvCamera *camera, GError **error); ARV_API gint64 * arv_camera_dup_available_pixel_formats (ArvCamera *camera, guint *n_pixel_formats, GError **error); ARV_API const char ** arv_camera_dup_available_pixel_formats_as_strings (ArvCamera *camera, guint *n_pixel_formats, GError **error); ARV_API const char ** arv_camera_dup_available_pixel_formats_as_display_names (ArvCamera *camera, guint *n_pixel_formats, GError **error); /* Acquisition control */ ARV_API void arv_camera_start_acquisition (ArvCamera *camera, GError **error); ARV_API void arv_camera_stop_acquisition (ArvCamera *camera, GError **error); ARV_API void arv_camera_abort_acquisition (ArvCamera *camera, GError **error); ARV_API ArvBuffer * arv_camera_acquisition (ArvCamera *camera, guint64 timeout, GError **error); ARV_API void arv_camera_set_acquisition_mode (ArvCamera *camera, ArvAcquisitionMode value, GError **error); ARV_API ArvAcquisitionMode arv_camera_get_acquisition_mode (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_frame_count (ArvCamera *camera, gint64 frame_count, GError **error); ARV_API gint64 arv_camera_get_frame_count (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_frame_count_bounds (ArvCamera *camera, gint64 *min, gint64 *max, GError **error); ARV_API gboolean arv_camera_get_frame_rate_enable (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_frame_rate_enable (ArvCamera *camera, gboolean enable, GError **error); ARV_API gboolean arv_camera_is_frame_rate_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_frame_rate (ArvCamera *camera, double frame_rate, GError **error); ARV_API double arv_camera_get_frame_rate (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_frame_rate_bounds (ArvCamera *camera, double *min, double *max, GError **error); ARV_API void arv_camera_set_trigger (ArvCamera *camera, const char *source, GError **error); ARV_API void arv_camera_set_trigger_source (ArvCamera *camera, const char *source, GError **error); ARV_API const char * arv_camera_get_trigger_source (ArvCamera *camera, GError **error); ARV_API const char ** arv_camera_dup_available_trigger_sources(ArvCamera *camera, guint *n_sources, GError **error); ARV_API const char ** arv_camera_dup_available_triggers (ArvCamera *camera, guint *n_triggers, GError **error); ARV_API void arv_camera_clear_triggers (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_software_trigger_supported(ArvCamera *camera, GError **error); ARV_API void arv_camera_software_trigger (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_exposure_time_available (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_exposure_auto_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_exposure_time (ArvCamera *camera, double exposure_time_us, GError **error); ARV_API double arv_camera_get_exposure_time (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_exposure_time_bounds (ArvCamera *camera, double *min, double *max, GError **error); ARV_API void arv_camera_set_exposure_time_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error); ARV_API ArvAuto arv_camera_get_exposure_time_auto (ArvCamera *camera, GError **error); ARV_API ArvGcRepresentation arv_camera_get_exposure_time_representation (ArvCamera *camera); ARV_API void arv_camera_set_exposure_mode (ArvCamera *camera, ArvExposureMode mode, GError **error); /* Analog control */ ARV_API gboolean arv_camera_is_gain_available (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_gain_auto_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_select_gain (ArvCamera *camera, const char *selector, GError **error); ARV_API const char ** arv_camera_dup_available_gains (ArvCamera *camera, guint *n_selectors, GError **error); ARV_API void arv_camera_set_gain (ArvCamera *camera, double gain, GError **error); ARV_API double arv_camera_get_gain (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_gain_bounds (ArvCamera *camera, double *min, double *max, GError **error); ARV_API void arv_camera_set_gain_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error); ARV_API ArvAuto arv_camera_get_gain_auto (ArvCamera *camera, GError **error); ARV_API ArvGcRepresentation arv_camera_get_gain_representation (ArvCamera *camera); ARV_API gboolean arv_camera_is_black_level_available (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_is_black_level_auto_available(ArvCamera *camera, GError **error); ARV_API void arv_camera_select_black_level (ArvCamera *camera, const char *selector, GError **error); ARV_API const char ** arv_camera_dup_available_black_levels (ArvCamera *camera, guint *n_selectors, GError **error); ARV_API void arv_camera_set_black_level (ArvCamera *camera, double blacklevel, GError **error); ARV_API double arv_camera_get_black_level (ArvCamera *camera, GError **error); ARV_API void arv_camera_get_black_level_bounds (ArvCamera *camera, double *min, double *max, GError **error); ARV_API void arv_camera_set_black_level_auto (ArvCamera *camera, ArvAuto auto_mode, GError **error); ARV_API ArvAuto arv_camera_get_black_level_auto (ArvCamera *camera, GError **error); /* Component control */ ARV_API gboolean arv_camera_is_component_available (ArvCamera *camera, GError **error); ARV_API const char ** arv_camera_dup_available_components (ArvCamera *camera, guint *n_components, GError **error); ARV_API void arv_camera_select_and_enable_component (ArvCamera *camera, const char *component, gboolean disable_others, GError **error); ARV_API gboolean arv_camera_select_component (ArvCamera *camera, const char *component, ArvComponentSelectionFlags flags, guint *component_id, GError **error); /* Transport layer control */ ARV_API guint arv_camera_get_payload (ArvCamera *camera, GError **error); /* Generic feature control */ ARV_API void arv_camera_execute_command (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_set_boolean (ArvCamera *camera, const char *feature, gboolean value, GError **error); ARV_API gboolean arv_camera_get_boolean (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_get_boolean_gi (ArvCamera *camera, const char *feature, gboolean *value, GError **error); ARV_API void arv_camera_set_string (ArvCamera *camera, const char *feature, const char *value, GError **error); ARV_API const char * arv_camera_get_string (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_set_integer (ArvCamera *camera, const char *feature, gint64 value, GError **error); ARV_API gint64 arv_camera_get_integer (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_get_integer_bounds (ArvCamera *camera, const char *feature, gint64 *min, gint64 *max, GError **error); ARV_API gint64 arv_camera_get_integer_increment (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_set_float (ArvCamera *camera, const char *feature, double value, GError **error); ARV_API double arv_camera_get_float (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_get_float_bounds (ArvCamera *camera, const char *feature, double *min, double *max, GError **error); ARV_API double arv_camera_get_float_increment (ArvCamera *camera, const char *feature, GError **error); ARV_API void arv_camera_set_register (ArvCamera *camera, const char *feature, guint64 length, void* value, GError **error); ARV_API void * arv_camera_dup_register (ArvCamera *camera, const char *feature, guint64 *length, GError **error); ARV_API gint64 * arv_camera_dup_available_enumerations (ArvCamera *camera, const char *feature, guint *n_values, GError **error); ARV_API const char ** arv_camera_dup_available_enumerations_as_strings (ArvCamera *camera, const char *feature, guint *n_values, GError **error); ARV_API const char ** arv_camera_dup_available_enumerations_as_display_names (ArvCamera *camera, const char *feature, guint *n_values, GError **error); ARV_API gboolean arv_camera_is_enumeration_entry_available (ArvCamera *camera, const char *feature, const char *entry, GError **error); ARV_API gboolean arv_camera_is_feature_available (ArvCamera *camera, const char *feature, GError **error); ARV_API gboolean arv_camera_is_feature_implemented (ArvCamera *camera, const char *feature, GError **error); ARV_API ArvGcRepresentation arv_camera_get_feature_representation (ArvCamera *camera, const char *feature); /* Runtime policies */ ARV_API void arv_camera_set_register_cache_policy (ArvCamera *camera, ArvRegisterCachePolicy policy); ARV_API void arv_camera_set_range_check_policy (ArvCamera *camera, ArvRangeCheckPolicy policy); ARV_API void arv_camera_set_access_check_policy (ArvCamera *camera, ArvAccessCheckPolicy policy); /* GigEVision specific API */ ARV_API gboolean arv_camera_is_gv_device (ArvCamera *camera); ARV_API gint arv_camera_gv_get_n_network_interfaces (ArvCamera *camera, GError **error); ARV_API gint arv_camera_gv_get_n_stream_channels (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_select_stream_channel (ArvCamera *camera, gint channel_id, GError **error); ARV_API int arv_camera_gv_get_current_stream_channel (ArvCamera *camera, GError **error); ARV_API gboolean arv_camera_gv_is_multipart_supported (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_set_multipart (ArvCamera *camera, gboolean enable, GError **error); ARV_API gboolean arv_camera_gv_get_multipart (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_set_packet_delay (ArvCamera *camera, gint64 delay_ns, GError **error); ARV_API gint64 arv_camera_gv_get_packet_delay (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_set_packet_size (ArvCamera *camera, gint packet_size, GError **error); ARV_API guint arv_camera_gv_get_packet_size (ArvCamera *camera, GError **error); ARV_API guint arv_camera_gv_auto_packet_size (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_set_packet_size_adjustment (ArvCamera *camera, ArvGvPacketSizeAdjustment adjustment); ARV_API void arv_camera_gv_set_stream_options (ArvCamera *camera, ArvGvStreamOption options); ARV_API void arv_camera_gv_get_persistent_ip (ArvCamera *camera, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error); ARV_API void arv_camera_gv_set_persistent_ip_from_string (ArvCamera *camera, const char *ip, const char *mask, const char *gateway, GError **error); ARV_API void arv_camera_gv_set_persistent_ip (ArvCamera *camera, GInetAddress *ip, GInetAddressMask *mask, GInetAddress *gateway, GError **error); ARV_API ArvGvIpConfigurationMode arv_camera_gv_get_ip_configuration_mode (ArvCamera *camera, GError **error); ARV_API void arv_camera_gv_set_ip_configuration_mode (ArvCamera *camera, ArvGvIpConfigurationMode mode, GError **error); ARV_API gboolean arv_camera_is_uv_device (ArvCamera *camera); ARV_API gboolean arv_camera_uv_is_bandwidth_control_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_uv_set_bandwidth (ArvCamera *camera, guint bandwidth, GError **error); ARV_API guint arv_camera_uv_get_bandwidth (ArvCamera *camera, GError **error); ARV_API void arv_camera_uv_get_bandwidth_bounds (ArvCamera *camera, guint *min, guint *max, GError **error); ARV_API void arv_camera_uv_set_usb_mode (ArvCamera *camera, ArvUvUsbMode usb_mode); /* Chunk data */ ARV_API gboolean arv_camera_are_chunks_available (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_chunk_mode (ArvCamera *camera, gboolean is_active, GError **error); ARV_API gboolean arv_camera_get_chunk_mode (ArvCamera *camera, GError **error); ARV_API void arv_camera_set_chunk_state (ArvCamera *camera, const char *chunk, gboolean is_enabled, GError **error); ARV_API gboolean arv_camera_get_chunk_state (ArvCamera *camera, const char *chunk, GError **error); ARV_API void arv_camera_set_chunks (ArvCamera *camera, const char *chunk_list, GError **error); ARV_API ArvChunkParser * arv_camera_create_chunk_parser (ArvCamera *camera); G_END_DECLS #endif aravis-0.8.34/src/arvcameratest.c000066400000000000000000000606401475431451200167030ustar00rootroot00000000000000#include #include #include #include #include #include static char *arv_option_camera_name = NULL; static char *arv_option_debug_domains = NULL; static char *arv_option_trigger = NULL; static double arv_option_software_trigger = -1; static double arv_option_frequency = -1.0; static int arv_option_width = -1; static int arv_option_height = -1; static int arv_option_horizontal_binning = -1; static int arv_option_vertical_binning = -1; static double arv_option_exposure_time_us = -1; static int arv_option_gain = -1; static char *arv_option_features = NULL; static gboolean arv_option_auto_socket_buffer = FALSE; static char *arv_option_packet_size_adjustment = NULL; static gboolean arv_option_no_packet_resend = FALSE; static double arv_option_packet_request_ratio = -1.0; static unsigned int arv_option_initial_packet_timeout = ARV_GV_STREAM_INITIAL_PACKET_TIMEOUT_US_DEFAULT / 1000; static unsigned int arv_option_packet_timeout = ARV_GV_STREAM_PACKET_TIMEOUT_US_DEFAULT / 1000; static unsigned int arv_option_frame_retention = ARV_GV_STREAM_FRAME_RETENTION_US_DEFAULT / 1000; static int arv_option_gv_stream_channel = -1; static int arv_option_gv_packet_delay = -1; static int arv_option_gv_packet_size = -1; static gboolean arv_option_realtime = FALSE; static gboolean arv_option_high_priority = FALSE; static gboolean arv_option_no_packet_socket = FALSE; static gboolean arv_option_multipart = FALSE; static char *arv_option_chunks = NULL; static int arv_option_bandwidth_limit = -1; static char *arv_option_register_cache = NULL; static char *arv_option_range_check = NULL; static char *arv_option_access_check = NULL; static int arv_option_duration_s = -1; static char *arv_option_uv_usb_mode = NULL; static gboolean arv_option_show_version = FALSE; static gboolean arv_option_gv_allow_broadcast_discovery_ack = FALSE; static char *arv_option_gv_discovery_interface = NULL; /* clang-format off */ static const GOptionEntry arv_option_entries[] = { { "name", 'n', 0, G_OPTION_ARG_STRING, &arv_option_camera_name, "Camera name", "" }, { "frequency", 'f', 0, G_OPTION_ARG_DOUBLE, &arv_option_frequency, "Acquisition frequency", "" }, { "trigger", 't', 0, G_OPTION_ARG_STRING, &arv_option_trigger, "External trigger", "" }, { "software-trigger", 'o', 0, G_OPTION_ARG_DOUBLE, &arv_option_software_trigger, "Emit software trigger", "" }, { "width", 'w', 0, G_OPTION_ARG_INT, &arv_option_width, "Width", "" }, { "height", 'h', 0, G_OPTION_ARG_INT, &arv_option_height, "Height", "" }, { "h-binning", '\0', 0, G_OPTION_ARG_INT, &arv_option_horizontal_binning, "Horizontal binning", "" }, { "v-binning", '\0', 0, G_OPTION_ARG_INT, &arv_option_vertical_binning, "Vertical binning", "" }, { "exposure", 'e', 0, G_OPTION_ARG_DOUBLE, &arv_option_exposure_time_us, "Exposure time", "" }, { "gain", 'g', 0, G_OPTION_ARG_INT, &arv_option_gain, "Gain (dB)", "" }, { "auto", 'a', 0, G_OPTION_ARG_NONE, &arv_option_auto_socket_buffer, "Auto socket buffer size", NULL }, { "features", '\0', 0, G_OPTION_ARG_STRING, &arv_option_features, "Additional configuration as a space separated list of features", NULL }, { "packet-size-adjustment", 'j', 0, G_OPTION_ARG_STRING, &arv_option_packet_size_adjustment, "Packet size adjustment", "{never|always|once|on-failure|on-failure-once}" }, { "no-packet-resend", 'r', 0, G_OPTION_ARG_NONE, &arv_option_no_packet_resend, "No packet resend", NULL }, { "packet-request-ratio", 'q', 0, G_OPTION_ARG_DOUBLE, &arv_option_packet_request_ratio, "Packet resend request limit as a frame packet ratio", "[0..2.0]" }, { "initial-packet-timeout", 'l', 0, G_OPTION_ARG_INT, &arv_option_initial_packet_timeout, "Initial packet timeout", "" }, { "packet-timeout", 'p', 0, G_OPTION_ARG_INT, &arv_option_packet_timeout, "Packet timeout", "" }, { "frame-retention", 'm', 0, G_OPTION_ARG_INT, &arv_option_frame_retention, "Frame retention", "" }, { "gv-stream-channel", 'c', 0, G_OPTION_ARG_INT, &arv_option_gv_stream_channel, "GigEVision stream channel id", "" }, { "gv-packet-delay", 'y', 0, G_OPTION_ARG_INT, &arv_option_gv_packet_delay, "GigEVision packet delay", "" }, { "gv-packet-size", 'i', 0, G_OPTION_ARG_INT, &arv_option_gv_packet_size, "GigEVision packet size", "" }, { "usb-mode", 's', 0, G_OPTION_ARG_STRING, &arv_option_uv_usb_mode, "USB device I/O mode", "{sync|async}" }, { "chunks", 'u', 0, G_OPTION_ARG_STRING, &arv_option_chunks, "Chunks", "[,[...]]" }, { "realtime", '\0', 0, G_OPTION_ARG_NONE, &arv_option_realtime, "Make stream thread realtime", NULL }, { "high-priority", '\0', 0, G_OPTION_ARG_NONE, &arv_option_high_priority, "Make stream thread high priority", NULL }, { "no-packet-socket", '\0', 0, G_OPTION_ARG_NONE, &arv_option_no_packet_socket, "Disable use of packet socket", NULL }, { "multipart", '\0', 0, G_OPTION_ARG_NONE, &arv_option_multipart, "Enable multipart payload", NULL }, { "register-cache", '\0', 0, G_OPTION_ARG_STRING, &arv_option_register_cache, "Register cache policy", "{disable|enable|debug}" }, { "range-check", '\0', 0, G_OPTION_ARG_STRING, &arv_option_range_check, "Range check policy", "{disable|enable|debug}" }, { "access-check", '\0', 0, G_OPTION_ARG_STRING, &arv_option_access_check, "Feature access check policy", "{disable|enable}" }, { "bandwidth-limit", 'b', 0, G_OPTION_ARG_INT, &arv_option_bandwidth_limit, "Desired USB3 Vision device bandwidth limit", "" }, { "duration", '\0', 0, G_OPTION_ARG_INT, &arv_option_duration_s, "Test duration (s)", "" }, { "gv-allow-broadcast-discovery-ack", '\0', 0, G_OPTION_ARG_NONE, &arv_option_gv_allow_broadcast_discovery_ack, "Allow broadcast discovery ack", NULL }, { "gv-discovery-interface", '\0', 0, G_OPTION_ARG_STRING, &arv_option_gv_discovery_interface, "Discovery using the interface", "" }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug output selection", "{[:][,...]|help}" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &arv_option_show_version, "Show version", NULL }, { NULL } }; /* clang-format on */ static const char description_content[] = "This tool configures a camera and starts video streaming, infinitely unless a duration is given."; typedef struct { GMainLoop *main_loop; int buffer_count; int error_count; size_t transferred; ArvChunkParser *chunk_parser; char **chunks; gint64 start_time; } ApplicationData; static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static void new_buffer_cb (ArvStream *stream, ApplicationData *data) { ArvBuffer *buffer; buffer = arv_stream_try_pop_buffer (stream); if (buffer != NULL) { if (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) { size_t size = 0; data->buffer_count++; arv_buffer_get_data (buffer, &size); data->transferred += size; } else { data->error_count++; } if (arv_buffer_has_chunks (buffer) && data->chunks != NULL) { int i; for (i = 0; data->chunks[i] != NULL; i++) { gint64 integer_value; GError *error = NULL; integer_value = arv_chunk_parser_get_integer_value (data->chunk_parser, buffer, data->chunks[i], &error); if (error == NULL) g_print ("%s = %" G_GINT64_FORMAT "\n", data->chunks[i], integer_value); else { double float_value; g_clear_error (&error); float_value = arv_chunk_parser_get_float_value (data->chunk_parser, buffer, data->chunks[i], &error); if (error == NULL) g_print ("%s = %g\n", data->chunks[i], float_value); else g_clear_error (&error); } } } /* Image processing here */ arv_stream_push_buffer (stream, buffer); } } static void stream_cb (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer) { if (type == ARV_STREAM_CALLBACK_TYPE_INIT) { if (arv_option_realtime) { if (!arv_make_thread_realtime (10)) printf ("Failed to make stream thread realtime\n"); } else if (arv_option_high_priority) { if (!arv_make_thread_high_priority (-10)) printf ("Failed to make stream thread high priority\n"); } } } static gboolean periodic_task_cb (void *abstract_data) { ApplicationData *data = abstract_data; printf ("%3d frame%s - %7.3g MiB/s", data->buffer_count, data->buffer_count > 1 ? "s/s" : "/s ", (double) data->transferred / 1e6); if (data->error_count > 0) printf (" - %d error%s\n", data->error_count, data->error_count > 1 ? "s" : ""); else printf ("\n"); data->buffer_count = 0; data->error_count = 0; data->transferred = 0; if (cancel || (arv_option_duration_s > 0 && (g_get_monotonic_time() - data->start_time) > 1000000 * arv_option_duration_s)) { g_main_loop_quit (data->main_loop); return FALSE; } return TRUE; } static gboolean emit_software_trigger (void *abstract_data) { ArvCamera *camera = abstract_data; arv_camera_software_trigger (camera, NULL); return TRUE; } static void control_lost_cb (ArvGvDevice *gv_device) { printf ("Control lost\n"); cancel = TRUE; } int main (int argc, char **argv) { ApplicationData data; ArvCamera *camera; ArvStream *stream; ArvRegisterCachePolicy register_cache_policy; ArvRangeCheckPolicy range_check_policy; ArvAccessCheckPolicy access_check_policy; ArvGvPacketSizeAdjustment adjustment; ArvUvUsbMode usb_mode; GOptionContext *context; GError *error = NULL; int i; data.buffer_count = 0; data.error_count = 0; data.transferred = 0; data.chunks = NULL; data.chunk_parser = NULL; context = g_option_context_new (NULL); g_option_context_set_summary (context, "Small utility for basic device checks."); g_option_context_set_description (context, description_content); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); if (arv_option_show_version) { printf ("%u.%u.%u\n", arv_get_major_version (), arv_get_minor_version (), arv_get_micro_version ()); return EXIT_SUCCESS; } if (arv_option_register_cache == NULL) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_register_cache, "disable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DISABLE; else if (g_strcmp0 (arv_option_register_cache, "enable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_ENABLE; else if (g_strcmp0 (arv_option_register_cache, "debug") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEBUG; else { printf ("Invalid register cache policy\n"); return EXIT_FAILURE; } if (arv_option_range_check == NULL) range_check_policy = ARV_RANGE_CHECK_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_range_check, "disable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DISABLE; else if (g_strcmp0 (arv_option_range_check, "enable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_ENABLE; else if (g_strcmp0 (arv_option_range_check, "debug") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DEBUG; else { printf ("Invalid range check policy\n"); return EXIT_FAILURE; } if (arv_option_access_check == NULL) access_check_policy = ARV_ACCESS_CHECK_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_access_check, "disable") == 0) access_check_policy = ARV_ACCESS_CHECK_POLICY_DISABLE; else if (g_strcmp0 (arv_option_access_check, "enable") == 0) access_check_policy = ARV_ACCESS_CHECK_POLICY_ENABLE; else { printf ("Invalid access check policy\n"); return EXIT_FAILURE; } if (arv_option_packet_size_adjustment == NULL) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_DEFAULT; else if (g_strcmp0 (arv_option_packet_size_adjustment, "always") == 0) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_ALWAYS; else if (g_strcmp0 (arv_option_packet_size_adjustment, "never") == 0) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_NEVER; else if (g_strcmp0 (arv_option_packet_size_adjustment, "once") == 0) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_ONCE; else if (g_strcmp0 (arv_option_packet_size_adjustment, "on-failure") == 0) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE; else if (g_strcmp0 (arv_option_packet_size_adjustment, "on-failure-once") == 0) adjustment = ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE; else { printf ("Invalid GigEVision packet size adjustment\n"); return EXIT_FAILURE; } if (arv_option_uv_usb_mode == NULL) usb_mode = ARV_UV_USB_MODE_DEFAULT; else if (g_strcmp0 (arv_option_uv_usb_mode, "sync") == 0) usb_mode = ARV_UV_USB_MODE_SYNC; else if (g_strcmp0 (arv_option_uv_usb_mode, "async") == 0) usb_mode = ARV_UV_USB_MODE_ASYNC; else { printf ("Invalid USB device I/O mode\n"); return EXIT_FAILURE; } if (!arv_debug_enable (arv_option_debug_domains)) { if (g_strcmp0 (arv_option_debug_domains, "help") != 0) printf ("Invalid debug selection\n"); else arv_debug_print_infos (); return EXIT_FAILURE; } if (arv_option_gv_allow_broadcast_discovery_ack) arv_set_interface_flags ("GigEVision", ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK); if (arv_option_gv_discovery_interface) arv_gv_interface_set_discovery_interface_name (arv_option_gv_discovery_interface); arv_enable_interface ("Fake"); arv_debug_enable (arv_option_debug_domains); #ifdef G_OS_WIN32 setbuf(stderr,NULL); setbuf(stdout,NULL); #endif if (arv_option_camera_name == NULL) g_print ("Looking for the first available camera\n"); else g_print ("Looking for camera '%s'\n", arv_option_camera_name); camera = arv_camera_new (arv_option_camera_name, &error); if (camera != NULL) { const char *vendor_name =NULL; const char *model_name = NULL; const char *serial_number = NULL; void (*old_sigint_handler)(int); gint payload = 0; gint width, height; gint dx, dy; double exposure = 0; guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; guint uv_bandwidth = 0; guint min, max; guint gv_n_channels = 0; guint gv_channel_id = 0; guint gv_packet_delay = 0; int gain = 0; guint software_trigger_source = 0; gboolean success = TRUE; arv_camera_set_register_cache_policy (camera, register_cache_policy); arv_camera_set_range_check_policy (camera, range_check_policy); arv_camera_set_access_check_policy (camera, access_check_policy); if (arv_option_chunks != NULL) { char *striped_chunks; striped_chunks = g_strdup (arv_option_chunks); arv_str_strip (striped_chunks, " ,:;", ','); data.chunks = g_strsplit_set (striped_chunks, ",", -1); g_free (striped_chunks); data.chunk_parser = arv_camera_create_chunk_parser (camera); for (i = 0; data.chunks[i] != NULL; i++) { char *chunk = g_strdup_printf ("Chunk%s", data.chunks[i]); g_free (data.chunks[i]); data.chunks[i] = chunk; } } error = NULL; if (error == NULL && arv_camera_are_chunks_available (camera, NULL)) arv_camera_set_chunks (camera, arv_option_chunks, &error); if (error == NULL) arv_camera_set_region (camera, -1, -1, arv_option_width, arv_option_height, &error); if (error == NULL) arv_camera_set_binning (camera, arv_option_horizontal_binning, arv_option_vertical_binning, &error); if (error == NULL) arv_camera_set_exposure_time (camera, arv_option_exposure_time_us, &error); if (error == NULL) arv_camera_set_gain (camera, arv_option_gain, &error); if (arv_camera_is_uv_device (camera)) { if (error == NULL) arv_camera_uv_set_usb_mode (camera, usb_mode); if (error == NULL && arv_option_bandwidth_limit >= 0) arv_camera_uv_set_bandwidth (camera, arv_option_bandwidth_limit, &error); } if (arv_camera_is_gv_device (camera)) { if (error == NULL) arv_camera_gv_select_stream_channel (camera, arv_option_gv_stream_channel, &error); if (error == NULL) arv_camera_gv_set_packet_delay (camera, arv_option_gv_packet_delay, &error); if (error == NULL) arv_camera_gv_set_packet_size (camera, arv_option_gv_packet_size, &error); arv_camera_gv_set_stream_options (camera, arv_option_no_packet_socket ? ARV_GV_STREAM_OPTION_PACKET_SOCKET_DISABLED : ARV_GV_STREAM_OPTION_NONE); if (arv_option_packet_size_adjustment != NULL) arv_camera_gv_set_packet_size_adjustment (camera, adjustment); if (error == NULL) arv_camera_gv_set_multipart (camera, TRUE, arv_option_multipart ? &error : NULL); } if (error == NULL && arv_option_features != NULL) arv_device_set_features_from_string (arv_camera_get_device (camera), arv_option_features, &error); if (error != NULL) { printf ("Failed to configure the device: %s\n", error->message); g_clear_error (&error); success = FALSE; } if (success) { if (error == NULL) vendor_name = arv_camera_get_vendor_name (camera, &error); if (error == NULL) model_name = arv_camera_get_model_name (camera, &error); if (error == NULL) serial_number = arv_camera_get_device_serial_number (camera, &error); if (error == NULL) arv_camera_get_region (camera, NULL, NULL, &width, &height, &error); if (error == NULL && arv_camera_is_binning_available (camera, NULL)) arv_camera_get_binning (camera, &dx, &dy, &error); if (error == NULL && arv_camera_is_exposure_time_available (camera, NULL)) exposure = arv_camera_get_exposure_time (camera, &error); if (error == NULL && arv_camera_is_gain_available (camera, NULL)) gain = arv_camera_get_gain (camera, &error); if (error == NULL) payload = arv_camera_get_payload (camera, &error); if (arv_camera_is_uv_device (camera)) { if (arv_camera_uv_is_bandwidth_control_available (camera, NULL)) { if (error == NULL) arv_camera_uv_get_bandwidth_bounds (camera, &min, &max, &error); if (error == NULL) uv_bandwidth = arv_camera_uv_get_bandwidth (camera, &error); } } if (arv_camera_is_gv_device (camera)) { if (error == NULL) gv_n_channels = arv_camera_gv_get_n_stream_channels (camera, &error); if (error == NULL) gv_channel_id = arv_camera_gv_get_current_stream_channel (camera, &error); if (error == NULL) gv_packet_delay = arv_camera_gv_get_packet_delay (camera, &error); } if (error != NULL) { printf ("Failed to read the current device configuration: %s\n", error->message); g_clear_error (&error); success = FALSE; } } if (success) { printf ("vendor name = %s\n", vendor_name); printf ("model name = %s\n", model_name); printf ("device serial number = %s\n", serial_number); printf ("image width = %d\n", width); printf ("image height = %d\n", height); if (arv_camera_is_binning_available (camera, NULL)) { printf ("horizontal binning = %d\n", dx); printf ("vertical binning = %d\n", dy); } if (arv_camera_is_exposure_time_available (camera, NULL)) printf ("exposure = %g µs\n", exposure); if (arv_camera_is_gain_available (camera, NULL)) printf ("gain = %d dB\n", gain); printf ("payload = %d bytes\n", payload); if (arv_camera_is_uv_device (camera)) { if (arv_camera_uv_is_bandwidth_control_available (camera, NULL)) { printf ("uv bandwidth limit = %d [%d..%d]\n", uv_bandwidth, min, max); } } if (arv_camera_is_gv_device (camera)) { printf ("gv n_stream channels = %d\n", gv_n_channels); printf ("gv current channel = %d\n", gv_channel_id); printf ("gv packet delay = %d ns\n", gv_packet_delay); } } if (success) { stream = arv_camera_create_stream (camera, stream_cb, NULL, &error); if (arv_camera_is_gv_device (camera)) { guint gv_packet_size; gv_packet_size = arv_camera_gv_get_packet_size (camera, &error); printf ("gv packet size = %d bytes\n", gv_packet_size); } if (ARV_IS_STREAM (stream)) { if (ARV_IS_GV_STREAM (stream)) { if (arv_option_auto_socket_buffer) g_object_set (stream, "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO, "socket-buffer-size", 0, NULL); if (arv_option_no_packet_resend) g_object_set (stream, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL); if (arv_option_packet_request_ratio >= 0.0) g_object_set (stream, "packet-request-ratio", arv_option_packet_request_ratio, NULL); g_object_set (stream, "initial-packet-timeout", (unsigned) arv_option_initial_packet_timeout * 1000, "packet-timeout", (unsigned) arv_option_packet_timeout * 1000, "frame-retention", (unsigned) arv_option_frame_retention * 1000, NULL); } for (i = 0; i < 50; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_CONTINUOUS, NULL); if (arv_option_frequency > 0.0) arv_camera_set_frame_rate (camera, arv_option_frequency, NULL); if (arv_option_trigger != NULL) arv_camera_set_trigger (camera, arv_option_trigger, NULL); if (arv_option_software_trigger > 0.0) { arv_camera_set_trigger (camera, "Software", NULL); software_trigger_source = g_timeout_add ((double) (0.5 + 1000.0 / arv_option_software_trigger), emit_software_trigger, camera); } arv_camera_start_acquisition (camera, NULL); g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &data); arv_stream_set_emit_signals (stream, TRUE); g_signal_connect (arv_camera_get_device (camera), "control-lost", G_CALLBACK (control_lost_cb), NULL); data.start_time = g_get_monotonic_time(); g_timeout_add (1000, periodic_task_cb, &data); data.main_loop = g_main_loop_new (NULL, FALSE); old_sigint_handler = signal (SIGINT, set_cancel); g_main_loop_run (data.main_loop); if (software_trigger_source > 0) g_source_remove (software_trigger_source); signal (SIGINT, old_sigint_handler); g_main_loop_unref (data.main_loop); arv_stream_get_statistics (stream, &n_completed_buffers, &n_failures, &n_underruns); for (i = 0; i < arv_stream_get_n_infos (stream); i++) { if (arv_stream_get_info_type (stream, i) == G_TYPE_UINT64) { g_print ("%-22s = %" G_GUINT64_FORMAT "\n", arv_stream_get_info_name (stream, i), arv_stream_get_info_uint64 (stream, i)); } } arv_camera_stop_acquisition (camera, NULL); arv_stream_set_emit_signals (stream, FALSE); g_object_unref (stream); } else { printf ("Can't create stream thread%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } } g_object_unref (camera); } else { printf ("No camera found%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } if (data.chunks != NULL) g_strfreev (data.chunks); g_clear_object (&data.chunk_parser); return 0; } aravis-0.8.34/src/arvchunkparser.c000066400000000000000000000215471475431451200171030ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * ArvChunkParser: * * [class@ArvChunkParser] provides a class for the instantiation of chunk parsers used for the extraction of chunk data * stored in the stream payload. * * Chunks are tagged blocks of data stored in a [class@ArvBuffer] containing a @ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA * payload. The tags allow a chunk parser to dissect the data payload into its elements and to identify the content. * * Chunk data are enabled using either [method@ArvCamera.set_chunks] or [method@ArvCamera.set_chunk_mode]. Both * functions are simple convenience wrappers that handle the setting of ChunkModeActive, ChunkSelector and ChunkEnable * GENICAM features. * * Here is an example of this API in use: [tests/arvchunkparsertest.c](https://github.com/AravisProject/aravis/blob/main/tests/arvchunkparsertest.c) */ #include #include #include #include #include #include #include enum { ARV_CHUNK_PARSER_PROPERTY_0, ARV_CHUNK_PARSER_PROPERTY_GENICAM, ARV_CHUNK_PARSER_PROPERTY_LAST } ArvChunkParserProperties; GQuark arv_chunk_parser_error_quark (void) { return g_quark_from_static_string ("arv-chunk-parser-error-quark"); } typedef struct { ArvGc *genicam; } ArvChunkParserPrivate; struct _ArvChunkParser { GObject object; ArvChunkParserPrivate *priv; }; struct _ArvChunkParserClass { GObjectClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvChunkParser, arv_chunk_parser, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvChunkParser)) /** * arv_chunk_parser_get_boolean_value: * @parser: a #ArvChunkParser * @buffer: a #ArvBuffer with a #ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA payload * @chunk: chunk data name * @error: a #GError placeholder * * Returns: the boolean chunk data value. */ gboolean arv_chunk_parser_get_boolean_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error) { ArvGcNode *node; gboolean value = FALSE; g_return_val_if_fail (ARV_IS_CHUNK_PARSER (parser), 0.0); g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0.0); node = arv_gc_get_node (parser->priv->genicam, chunk); arv_gc_set_buffer (parser->priv->genicam, buffer); if (ARV_IS_GC_BOOLEAN (node)) { GError *local_error = NULL; value = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), &local_error); if (local_error != NULL) { arv_warning_chunk ("%s", local_error->message); g_propagate_error (error, local_error); } } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE, "[%s] Not a boolean", chunk); } return value; } /** * arv_chunk_parser_get_string_value: * @parser: a #ArvChunkParser * @buffer: a #ArvBuffer with a #ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA payload * @chunk: chunk data name * @error: a #GError placeholder * * Returns: the string chunk data value. */ const char * arv_chunk_parser_get_string_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error) { ArvGcNode *node; const char *string = NULL; g_return_val_if_fail (ARV_IS_CHUNK_PARSER (parser), NULL); g_return_val_if_fail (ARV_IS_BUFFER (buffer), NULL); node = arv_gc_get_node (parser->priv->genicam, chunk); arv_gc_set_buffer (parser->priv->genicam, buffer); if (ARV_IS_GC_STRING (node)) { GError *local_error = NULL; string = arv_gc_string_get_value (ARV_GC_STRING (node), &local_error); if (local_error != NULL) { arv_warning_chunk ("%s", local_error->message); g_propagate_error (error, local_error); } } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE, "[%s] Not a string", chunk); } return string; } /** * arv_chunk_parser_get_integer_value: * @parser: a #ArvChunkParser * @buffer: a #ArvBuffer with a #ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA payload * @chunk: chunk data name * @error: a #GError placeholder * * Returns: the integer chunk data integer. */ gint64 arv_chunk_parser_get_integer_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error) { ArvGcNode *node; gint64 value = 0; g_return_val_if_fail (ARV_IS_CHUNK_PARSER (parser), 0.0); g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0.0); node = arv_gc_get_node (parser->priv->genicam, chunk); arv_gc_set_buffer (parser->priv->genicam, buffer); if (ARV_IS_GC_INTEGER (node)) { GError *local_error = NULL; value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { arv_warning_chunk ("%s", local_error->message); g_propagate_error (error, local_error); } } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE, "[%s] Not an integer", chunk); } return value; } /** * arv_chunk_parser_get_float_value: * @parser: a #ArvChunkParser * @buffer: a #ArvBuffer with a #ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA payload * @chunk: chunk data name * @error: a #GError placeholder * * Returns: the float chunk data value. */ double arv_chunk_parser_get_float_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error) { ArvGcNode *node; double value = 0.0; g_return_val_if_fail (ARV_IS_CHUNK_PARSER (parser), 0.0); g_return_val_if_fail (ARV_IS_BUFFER (buffer), 0.0); node = arv_gc_get_node (parser->priv->genicam, chunk); arv_gc_set_buffer (parser->priv->genicam, buffer); if (ARV_IS_GC_FLOAT (node)) { GError *local_error = NULL; value = arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { arv_warning_chunk ("%s", local_error->message); g_propagate_error (error, local_error); } } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE, "[%s] Not a float", chunk); } return value; } /** * arv_chunk_parser_new: * @xml: XML genicam data * @size: genicam data size, -1 if NULL terminated * * Creates a new chunk_parser. * * Returns: a new #ArvChunkParser object * * Since: 0.4.0 */ ArvChunkParser * arv_chunk_parser_new (const char *xml, gsize size) { ArvChunkParser *chunk_parser; ArvGc *genicam; genicam = arv_gc_new (NULL, xml, size); g_return_val_if_fail (ARV_IS_GC (genicam), NULL); chunk_parser = g_object_new (ARV_TYPE_CHUNK_PARSER, "genicam", genicam, NULL); g_object_unref (genicam); return chunk_parser; } static void _set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ArvChunkParser *parser = ARV_CHUNK_PARSER (object); switch (prop_id) { case ARV_CHUNK_PARSER_PROPERTY_GENICAM: g_clear_object (&parser->priv->genicam); parser->priv->genicam = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void _get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { ArvChunkParser *parser = ARV_CHUNK_PARSER (object); switch (prop_id) { case ARV_CHUNK_PARSER_PROPERTY_GENICAM: g_value_set_object (value, parser->priv->genicam); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_chunk_parser_init (ArvChunkParser *chunk_parser) { chunk_parser->priv = arv_chunk_parser_get_instance_private (chunk_parser); } static void _finalize (GObject *object) { ArvChunkParser *chunk_parser = ARV_CHUNK_PARSER (object); g_clear_object (&chunk_parser->priv->genicam); G_OBJECT_CLASS (arv_chunk_parser_parent_class)->finalize (object); } static void arv_chunk_parser_class_init (ArvChunkParserClass *node_class) { GObjectClass *object_class = G_OBJECT_CLASS (node_class); object_class->finalize = _finalize; object_class->set_property = _set_property; object_class->get_property = _get_property; /** * ArvChunkParser:genicam: * * Internal Genicam object * * Stability: Private */ g_object_class_install_property ( object_class, ARV_CHUNK_PARSER_PROPERTY_GENICAM, g_param_spec_object ("genicam", "genicam", "Genicam instance", ARV_TYPE_GC, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY) ); } aravis-0.8.34/src/arvchunkparser.h000066400000000000000000000047641475431451200171120ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_CHUNK_PARSER_H #define ARV_CHUNK_PARSER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_CHUNK_PARSER_ERROR arv_chunk_parser_error_quark() ARV_API GQuark arv_chunk_parser_error_quark (void); /** * ArvChunkParserError: * @ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE: invalid feature type * @ARV_CHUNK_PARSER_ERROR_BUFFER_NOT_FOUND: a buffer is not attached to the chunk parser * @ARV_CHUNK_PARSER_ERROR_CHUNK_NOT_FOUND: the requested chunk is not found in the buffer data */ typedef enum { ARV_CHUNK_PARSER_ERROR_INVALID_FEATURE_TYPE, ARV_CHUNK_PARSER_ERROR_BUFFER_NOT_FOUND, ARV_CHUNK_PARSER_ERROR_CHUNK_NOT_FOUND } ArvChunkParserError; #define ARV_TYPE_CHUNK_PARSER (arv_chunk_parser_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvChunkParser, arv_chunk_parser, ARV, CHUNK_PARSER, GObject) ARV_API ArvChunkParser * arv_chunk_parser_new (const char *xml, gsize size); ARV_API gboolean arv_chunk_parser_get_boolean_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error); ARV_API const char * arv_chunk_parser_get_string_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error); ARV_API gint64 arv_chunk_parser_get_integer_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error); ARV_API double arv_chunk_parser_get_float_value (ArvChunkParser *parser, ArvBuffer *buffer, const char *chunk, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvchunkparserprivate.h000066400000000000000000000021631475431451200204740ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_CHUNK_PARSER_PRIVATE_H #define ARV_CHUNK_PARSER_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS G_END_DECLS #endif aravis-0.8.34/src/arvdebug.c000066400000000000000000000211501475431451200156320ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * ArvDebug: */ #include #include #include #include #include #ifdef _MSC_VER #define STDERR_FILENO _fileno(stderr) #else #include #endif ArvDebugCategoryInfos arv_debug_category_infos[] = { { .name = "interface", .description = "Device lookup for each supported protocol" }, { .name = "device", .description = "Device control" }, { .name = "stream", .description = "Video stream management" }, { .name = "stream-thread", .description = "Video stream thread (likely high volume output)" }, { .name = "cp", .description = "Control protocol packets" }, { .name = "sp", .description = "Stream protocol packets (likely high volume output)" }, { .name = "genicam", .description = "Genicam specialized DOM elements" }, { .name = "policies", .description = "Genicam runtime configurable policies", .level = ARV_DEBUG_LEVEL_INFO }, { .name = "chunk", .description = "Chunk data code" }, { .name = "dom", .description = "Genicam DOM document" }, { .name = "evaluator", .description = "Expression evaluator" }, { .name = "viewer", .description = "Simple viewer application" }, { .name = "misc", .description = "Miscellaneous code" }, }; typedef struct { const char *color; const char *symbol; } ArvDebugLevelInfos; ArvDebugLevelInfos arv_debug_level_infos[] = { { .color = "", .symbol = ""}, { .color = "\033[1;31m", .symbol = "🆆 "}, { .color = "\033[1;32m", .symbol = "🅸 "}, { .color = "\033[1;34m", .symbol = "🅳 "}, { .color = "\033[0m", .symbol = "🆃 "} }; static gboolean arv_debug_initialize (const char *debug_var) { gboolean success = TRUE; char **categories; int i; if (debug_var == NULL) return TRUE; categories = g_strsplit (debug_var, ",", -1); for (i = 0; categories[i] != NULL; i++) { char **infos; unsigned int j; infos = g_strsplit (categories[i], ":", -1); if (infos[0] != NULL) { gboolean found = FALSE; for (j = 0; j < G_N_ELEMENTS (arv_debug_category_infos); j++) { if (g_strcmp0 (arv_debug_category_infos[j].name, infos[0]) == 0 || g_strcmp0 ("all", infos[0]) == 0) { if (infos[1] != NULL) arv_debug_category_infos[j].level = atoi (infos[1]); else arv_debug_category_infos[j].level = ARV_DEBUG_LEVEL_INFO; found = TRUE; } } if (!found) success = FALSE; } g_strfreev (infos); } g_strfreev (categories); return success; } static gboolean stderr_has_color_support (void) { #if GLIB_CHECK_VERSION(2,50,0) static int has_color_support = -1; if (has_color_support >= 0) return has_color_support > 0; has_color_support = g_log_writer_supports_color (STDERR_FILENO) ? 1 : 0; return has_color_support; #else return FALSE; #endif } gboolean arv_debug_check (ArvDebugCategory category, ArvDebugLevel level) { if (category < 0 || category >= ARV_DEBUG_CATEGORY_N_ELEMENTS) return FALSE; if (level <= 0 || level >= ARV_DEBUG_LEVEL_N_ELEMENTS) return FALSE; if ((int) level <= (int) arv_debug_category_infos[category].level) return TRUE; return FALSE; } static void arv_debug_with_level (ArvDebugCategory category, ArvDebugLevel level, const char *format, va_list args) G_GNUC_PRINTF(3,0); static void arv_debug_with_level (ArvDebugCategory category, ArvDebugLevel level, const char *format, va_list args) { char *text = NULL; char *header = NULL; char *time_str = NULL; GDateTime *date = NULL; char **lines; gint i; if (!arv_debug_check (category, level)) return; date = g_date_time_new_now_local (); time_str = g_date_time_format (date, "%H:%M:%S"); if (stderr_has_color_support ()) header = g_strdup_printf ("[\033[34m%s.%03d\033[0m] %s%s%s\033[0m> ", time_str, g_date_time_get_microsecond (date) / 1000, arv_debug_level_infos[level].color, arv_debug_level_infos[level].symbol, arv_debug_category_infos[category].name); else header = g_strdup_printf ("[%s.%03d] %s%s> ", time_str, g_date_time_get_microsecond (date) / 1000, arv_debug_level_infos[level].symbol, arv_debug_category_infos[category].name); if (header != NULL) { int header_length = 19 + strlen (arv_debug_category_infos[category].name); g_fprintf (stderr, "%s", header); text = g_strdup_vprintf (format, args); lines = g_strsplit (text, "\n", -1); for (i = 0; lines[i] != NULL; i++) { if (strlen (lines[i]) >0) g_fprintf (stderr, "%*s%s\n", i > 0 ? header_length : 0, "", lines[i]); } g_strfreev (lines); #ifdef G_OS_WIN32 fflush(stderr); #endif } g_free (text); g_free (header); g_free (time_str); g_date_time_unref (date); } void arv_warning (ArvDebugCategory category, const char *format, ...) { va_list args; va_start (args, format); arv_debug_with_level (category, ARV_DEBUG_LEVEL_WARNING, format, args); va_end (args); } void arv_info (ArvDebugCategory category, const char *format, ...) { va_list args; va_start (args, format); arv_debug_with_level (category, ARV_DEBUG_LEVEL_INFO, format, args); va_end (args); } void arv_debug (ArvDebugCategory category, const char *format, ...) { va_list args; va_start (args, format); arv_debug_with_level (category, ARV_DEBUG_LEVEL_DEBUG, format, args); va_end (args); } void arv_trace (ArvDebugCategory category, const char *format, ...) { va_list args; va_start (args, format); arv_debug_with_level (category, ARV_DEBUG_LEVEL_TRACE, format, args); va_end (args); } /** * arv_debug_enable: * @category_selection: debug category configuration string * * Configures the debug output using a configuration string consisting of a comma separated list of debug categories or * category/debug level pair. * * This function overwrites the configuration done by `ARV_DEBUG` environment variable. For example, enabling debug level * 3 of the gvcp category and default debug level of category genicam is done using: * * ```C * arv_debug_enable ("gvcp:3,genicam"); * ``` * * Returns: %TRUE on success * Since: 0.8.8 */ gboolean arv_debug_enable (const char *category_selection) { return arv_debug_initialize (category_selection); } static char * arv_debug_dup_infos_as_string (void) { GEnumClass *debug_level_class = g_type_class_ref (ARV_TYPE_DEBUG_LEVEL); GString *string = g_string_new (""); unsigned int i; g_string_append (string, "Debug categories:\n"); for (i = 0; i < ARV_DEBUG_CATEGORY_N_ELEMENTS; i++) { g_string_append_printf (string, "%-15s: %s\n", arv_debug_category_infos[i].name, arv_debug_category_infos[i].description); } g_string_append (string, "all : Everything\n"); g_string_append (string, "\nDebug levels:\n"); for (i = 0; i < ARV_DEBUG_LEVEL_N_ELEMENTS; i++) { GEnumValue *enum_value; enum_value = g_enum_get_value (g_type_class_ref (ARV_TYPE_DEBUG_LEVEL), i); if (enum_value != NULL) g_string_append_printf (string, "%d: %s\n", i, enum_value->value_nick); } g_type_class_unref (debug_level_class); return arv_g_string_free_and_steal(string); } void arv_debug_print_infos (void) { char *str; str = arv_debug_dup_infos_as_string (); printf ("%s", str); g_free (str); } ARV_DEFINE_CONSTRUCTOR (arv_initialize_debug) static void arv_initialize_debug (void) { arv_debug_initialize (g_getenv ("ARV_DEBUG")); } aravis-0.8.34/src/arvdebug.h000066400000000000000000000022451475431451200156430ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_DEBUG_H #define ARV_DEBUG_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API gboolean arv_debug_enable (const char *category_selection); G_END_DECLS #endif aravis-0.8.34/src/arvdebugprivate.h000066400000000000000000000132431475431451200172360ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_DEBUG_PRIVATE_H #define ARV_DEBUG_PRIVATE_H #include G_BEGIN_DECLS typedef enum { ARV_DEBUG_LEVEL_NONE, ARV_DEBUG_LEVEL_WARNING, ARV_DEBUG_LEVEL_INFO, ARV_DEBUG_LEVEL_DEBUG, ARV_DEBUG_LEVEL_TRACE, ARV_DEBUG_LEVEL_N_ELEMENTS } ArvDebugLevel; typedef struct { const char *name; const char *description; ArvDebugLevel level; } ArvDebugCategoryInfos; typedef enum { ARV_DEBUG_CATEGORY_INTERFACE, ARV_DEBUG_CATEGORY_DEVICE, ARV_DEBUG_CATEGORY_STREAM, ARV_DEBUG_CATEGORY_STREAM_THREAD, ARV_DEBUG_CATEGORY_CP, ARV_DEBUG_CATEGORY_SP, ARV_DEBUG_CATEGORY_GENICAM, ARV_DEBUG_CATEGORY_POLICIES, ARV_DEBUG_CATEGORY_CHUNK, ARV_DEBUG_CATEGORY_DOM, ARV_DEBUG_CATEGORY_EVALUATOR, ARV_DEBUG_CATEGORY_VIEWER, ARV_DEBUG_CATEGORY_MISC, ARV_DEBUG_CATEGORY_N_ELEMENTS } ArvDebugCategory; extern ArvDebugCategoryInfos arv_debug_category_infos[]; #define arv_warning_dom(...) arv_warning (ARV_DEBUG_CATEGORY_DOM, __VA_ARGS__) #define arv_info_dom(...) arv_info (ARV_DEBUG_CATEGORY_DOM, __VA_ARGS__) #define arv_debug_dom(...) arv_debug (ARV_DEBUG_CATEGORY_DOM, __VA_ARGS__) #define arv_warning_interface(...) arv_warning (ARV_DEBUG_CATEGORY_INTERFACE, __VA_ARGS__) #define arv_info_interface(...) arv_info (ARV_DEBUG_CATEGORY_INTERFACE, __VA_ARGS__) #define arv_debug_interface(...) arv_debug (ARV_DEBUG_CATEGORY_INTERFACE, __VA_ARGS__) #define arv_warning_device(...) arv_warning (ARV_DEBUG_CATEGORY_DEVICE, __VA_ARGS__) #define arv_info_device(...) arv_info (ARV_DEBUG_CATEGORY_DEVICE, __VA_ARGS__) #define arv_debug_device(...) arv_debug (ARV_DEBUG_CATEGORY_DEVICE, __VA_ARGS__) #define arv_warning_chunk(...) arv_warning (ARV_DEBUG_CATEGORY_CHUNK, __VA_ARGS__) #define arv_info_chunk(...) arv_info (ARV_DEBUG_CATEGORY_CHUNK, __VA_ARGS__) #define arv_debug_chunk(...) arv_debug (ARV_DEBUG_CATEGORY_CHUNK, __VA_ARGS__) #define arv_warning_stream(...) arv_warning (ARV_DEBUG_CATEGORY_STREAM, __VA_ARGS__) #define arv_info_stream(...) arv_info (ARV_DEBUG_CATEGORY_STREAM, __VA_ARGS__) #define arv_debug_stream(...) arv_debug (ARV_DEBUG_CATEGORY_STREAM, __VA_ARGS__) #define arv_warning_stream_thread(...) arv_warning (ARV_DEBUG_CATEGORY_STREAM_THREAD, __VA_ARGS__) #define arv_info_stream_thread(...) arv_info (ARV_DEBUG_CATEGORY_STREAM_THREAD, __VA_ARGS__) #define arv_debug_stream_thread(...) arv_debug (ARV_DEBUG_CATEGORY_STREAM_THREAD, __VA_ARGS__) #define arv_warning_cp(...) arv_warning (ARV_DEBUG_CATEGORY_CP, __VA_ARGS__) #define arv_info_cp(...) arv_info (ARV_DEBUG_CATEGORY_CP, __VA_ARGS__) #define arv_debug_cp(...) arv_debug (ARV_DEBUG_CATEGORY_CP, __VA_ARGS__) #define arv_trace_cp(...) arv_trace (ARV_DEBUG_CATEGORY_CP, __VA_ARGS__) #define arv_warning_sp(...) arv_warning (ARV_DEBUG_CATEGORY_SP, __VA_ARGS__) #define arv_info_sp(...) arv_info (ARV_DEBUG_CATEGORY_SP, __VA_ARGS__) #define arv_debug_sp(...) arv_debug (ARV_DEBUG_CATEGORY_SP, __VA_ARGS__) #define arv_trace_sp(...) arv_trace (ARV_DEBUG_CATEGORY_SP, __VA_ARGS__) #define arv_warning_genicam(...) arv_warning (ARV_DEBUG_CATEGORY_GENICAM, __VA_ARGS__) #define arv_info_genicam(...) arv_info (ARV_DEBUG_CATEGORY_GENICAM, __VA_ARGS__) #define arv_debug_genicam(...) arv_debug (ARV_DEBUG_CATEGORY_GENICAM, __VA_ARGS__) #define arv_warning_policies(...) arv_warning (ARV_DEBUG_CATEGORY_POLICIES, __VA_ARGS__) #define arv_info_policies(...) arv_info (ARV_DEBUG_CATEGORY_POLICIES, __VA_ARGS__) #define arv_debug_policies(...) arv_debug (ARV_DEBUG_CATEGORY_POLICIES, __VA_ARGS__) #define arv_warning_evaluator(...) arv_warning (ARV_DEBUG_CATEGORY_EVALUATOR, __VA_ARGS__) #define arv_info_evaluator(...) arv_info (ARV_DEBUG_CATEGORY_EVALUATOR, __VA_ARGS__) #define arv_debug_evaluator(...) arv_debug (ARV_DEBUG_CATEGORY_EVALUATOR, __VA_ARGS__) #define arv_warning_misc(...) arv_warning (ARV_DEBUG_CATEGORY_MISC, __VA_ARGS__) #define arv_info_misc(...) arv_info (ARV_DEBUG_CATEGORY_MISC, __VA_ARGS__) #define arv_debug_misc(...) arv_debug (ARV_DEBUG_CATEGORY_MISC, __VA_ARGS__) #define arv_warning_viewer(...) arv_warning (ARV_DEBUG_CATEGORY_VIEWER, __VA_ARGS__) #define arv_info_viewer(...) arv_info (ARV_DEBUG_CATEGORY_VIEWER, __VA_ARGS__) #define arv_debug_viewer(...) arv_debug (ARV_DEBUG_CATEGORY_VIEWER, __VA_ARGS__) gboolean arv_debug_check (ArvDebugCategory category, ArvDebugLevel level); /* private, but used by viewer */ ARV_API void arv_warning (ArvDebugCategory category, const char *format, ...) G_GNUC_PRINTF (2,3); ARV_API void arv_info (ArvDebugCategory category, const char *format, ...) G_GNUC_PRINTF (2,3); ARV_API void arv_debug (ArvDebugCategory category, const char *format, ...) G_GNUC_PRINTF (2,3); ARV_API void arv_trace (ArvDebugCategory category, const char *format, ...) G_GNUC_PRINTF (2,3); /* private, but export for use in debugging tools */ ARV_API void arv_debug_print_infos (void); G_END_DECLS #endif aravis-0.8.34/src/arvdevice.c000066400000000000000000001152131475431451200160070ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvdevice * @short_description: Abstract base class for device handling * * #ArvDevice is an abstract base class for the control of cameras. It provides * an easy access to the camera settings, and to its genicam interface for more * advanced uses. */ #include #include #include #include #include #include #include #include #include #include #include #include #include enum { ARV_DEVICE_SIGNAL_CONTROL_LOST, ARV_DEVICE_SIGNAL_LAST } ArvDeviceSignals; static guint arv_device_signals[ARV_DEVICE_SIGNAL_LAST] = {0}; GQuark arv_device_error_quark (void) { return g_quark_from_static_string ("arv-device-error-quark"); } typedef struct { GError *init_error; } ArvDevicePrivate; static void arv_device_initable_iface_init (GInitableIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvDevice, arv_device, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvDevice) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, arv_device_initable_iface_init)) /** * arv_device_create_stream: (skip) * @device: a #ArvDevice * @callback: (scope call) (closure user_data) : a frame processing callback * @user_data: (allow-none) : user data for @callback * @error: a #GError placeholder, %NULL to ignore * * Creates a new #ArvStream for video stream handling. See * @ArvStreamCallback for details regarding the callback function. * * Return value: (transfer full): a new #ArvStream. * * Since: 0.2.0 */ ArvStream * arv_device_create_stream (ArvDevice *device, ArvStreamCallback callback, void *user_data, GError **error) { return arv_device_create_stream_full(device, callback, user_data, NULL, error); } /** * arv_device_create_stream_full: (rename-to arv_device_create_stream) * @device: a #ArvDevice * @callback: (scope notified) (closure user_data) : a frame processing callback * @user_data: (allow-none) : user data for @callback * @destroy: a #GDestroyNotify placeholder, %NULL to ignore * @error: a #GError placeholder, %NULL to ignore * * Creates a new #ArvStream for video stream handling. See * @ArvStreamCallback for details regarding the callback function. * * Return value: (transfer full): a new #ArvStream. * * Since: 0.8.23 */ ArvStream * arv_device_create_stream_full (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); return ARV_DEVICE_GET_CLASS (device)->create_stream (device, callback, user_data, destroy, error); } /** * arv_device_read_memory: * @device: a #ArvDevice * @address: memory address * @size: number of bytes to read * @buffer: a buffer for the storage of the read data * @error: (out) (allow-none): a #GError placeholder * * Reads @size bytes from the device memory. * * Return value: (skip): TRUE on success. * * Since: 0.2.0 **/ gboolean arv_device_read_memory (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (size > 0, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return ARV_DEVICE_GET_CLASS (device)->read_memory (device, address, size, buffer, error); } /** * arv_device_write_memory: * @device: a #ArvDevice * @address: memory address * @size: size of the returned buffer * @buffer: (transfer full): the buffer read from memory * @error: (out) (allow-none): a #GError placeholder * * Writes @size bytes to the device memory. * * Return value: (skip): TRUE on success. * * Since: 0.2.0 **/ gboolean arv_device_write_memory (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (size > 0, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return ARV_DEVICE_GET_CLASS (device)->write_memory (device, address, size, buffer, error); } /** * arv_device_read_register: * @device: a #ArvDevice * @address: register address * @value: (out): a placeholder for the read value * @error: (out) (allow-none): a #GError placeholder * * Reads the value of a device register. * * Return value: (skip): TRUE on success. * * Since: 0.2.0 **/ gboolean arv_device_read_register (ArvDevice *device, guint64 address, guint32 *value, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (value != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return ARV_DEVICE_GET_CLASS (device)->read_register (device, address, value, error); } /** * arv_device_write_register: * @device: a #ArvDevice * @address: the register address * @value: value to write * @error: (out) (allow-none): a #GError placeholder * * Writes @value to a device register. * * Return value: (skip): TRUE on success. * * Since: 0.2.0 **/ gboolean arv_device_write_register (ArvDevice *device, guint64 address, guint32 value, GError **error) { g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return ARV_DEVICE_GET_CLASS (device)->write_register (device, address, value, error); } /** * arv_device_get_genicam: * @device: a #ArvDevice * * Retrieves the genicam interface of the given device. * * Return value: (transfer none): the genicam interface. * * Since: 0.2.0 */ ArvGc * arv_device_get_genicam (ArvDevice *device) { g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); return ARV_DEVICE_GET_CLASS (device)->get_genicam (device); } /** * arv_device_get_genicam_xml: * @device: a #ArvDevice * @size: (out) (allow-none): placeholder for the returned data size (bytes) * * Gets the Genicam XML data stored in the device memory. * * Returns: (transfer none): a pointer to the Genicam XML data, owned by the device. * * Since: 0.2.0 **/ const char * arv_device_get_genicam_xml (ArvDevice *device, size_t *size) { g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); g_return_val_if_fail (size != NULL, NULL); if (ARV_DEVICE_GET_CLASS (device)->get_genicam_xml != NULL) return ARV_DEVICE_GET_CLASS (device)->get_genicam_xml (device, size); *size = 0; return NULL; } /** * arv_device_create_chunk_parser: * @device: a #ArvDevice * * Create a #ArvChunkParser object, to be used for chunk data extraction from #ArvBuffer. * * Returns: (transfer full): a new #ArvChunkParser object, NULL on error. * * Since: 0.4.0 **/ ArvChunkParser * arv_device_create_chunk_parser (ArvDevice *device) { const char *xml = NULL; gsize size = 0; g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); xml = arv_device_get_genicam_xml (device, &size); return arv_chunk_parser_new (xml, size); } /** * arv_device_get_feature: * @device: a #ArvDevice * @feature: feature name * * Return value: (transfer none): the genicam node corresponding to the feature name, NULL if not found. * * Since: 0.2.0 */ ArvGcNode * arv_device_get_feature (ArvDevice *device, const char *feature) { ArvGc *genicam; genicam = arv_device_get_genicam (device); g_return_val_if_fail (ARV_IS_GC (genicam), NULL); return arv_gc_get_node (genicam, feature); } /** * arv_device_get_feature_access_mode: * @device: a #ArvDevice * @feature: feature name * * Return value: The actual feature access mode, which is a combination of ImposedAccessMode property and actual * register access mode. * * Since: 0.8.22 */ ArvGcAccessMode arv_device_get_feature_access_mode (ArvDevice *device, const char *feature) { ArvGcNode* node; g_return_val_if_fail (ARV_IS_DEVICE (device), ARV_GC_ACCESS_MODE_UNDEFINED); g_return_val_if_fail (feature != NULL, ARV_GC_ACCESS_MODE_UNDEFINED); node = arv_device_get_feature (device, feature); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), ARV_GC_ACCESS_MODE_UNDEFINED); return arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); } /** * arv_device_is_feature_available: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Return: %TRUE if feature is available, %FALSE if not or on error. * * Since: 0.8.0 */ gboolean arv_device_is_feature_available (ArvDevice *device, const char *feature, GError **error) { ArvGcNode* node; g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (feature != NULL, FALSE); node = arv_device_get_feature (device, feature); return ARV_IS_GC_FEATURE_NODE (node) && arv_gc_feature_node_is_available (ARV_GC_FEATURE_NODE (node), error); } /** * arv_device_is_feature_implemented: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder, %NULL to ignore * * Return: %TRUE if feature is implemented, %FALSE if not or on error. * * Since: 0.8.23 */ gboolean arv_device_is_feature_implemented (ArvDevice *device, const char *feature, GError **error) { ArvGcNode* node; g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); g_return_val_if_fail (feature != NULL, FALSE); node = arv_device_get_feature (device, feature); return ARV_IS_GC_FEATURE_NODE (node) && arv_gc_feature_node_is_implemented (ARV_GC_FEATURE_NODE (node), error); } static ArvGcNode * _get_feature (ArvDevice *device, GType node_type, const char *feature, GError **error) { ArvGcNode *node; g_return_val_if_fail (ARV_IS_DEVICE (device), NULL); g_return_val_if_fail (feature != NULL, NULL); node = arv_device_get_feature (device, feature); if (node == NULL) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "[%s] Not found", feature); return NULL; } if (!(G_TYPE_CHECK_INSTANCE_TYPE ((node), node_type))) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a %s", feature, G_OBJECT_TYPE_NAME (node), g_type_name (node_type)); return NULL; } return node; } /** * arv_device_get_feature_representation: * @device: a #ArvDevice * @feature: feature name * * Return: enum ArvGcRepresentation, ARV_GC_REPRESENTATION_UNDEFINED if not available. * * Since: 0.8.31 */ ArvGcRepresentation arv_device_get_feature_representation (ArvDevice *device, const char *feature) { ArvGcNode* node; g_return_val_if_fail (ARV_IS_DEVICE (device), ARV_GC_REPRESENTATION_UNDEFINED); g_return_val_if_fail (feature != NULL, ARV_GC_REPRESENTATION_UNDEFINED); node = arv_device_get_feature (device, feature); if (ARV_IS_GC_FLOAT(node)) { return arv_gc_float_get_representation(ARV_GC_FLOAT(node)); }else if (ARV_IS_GC_INTEGER(node)){ return arv_gc_integer_get_representation(ARV_GC_INTEGER (node)); } return ARV_GC_REPRESENTATION_UNDEFINED; } /** * arv_device_execute_command: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Execute a genicam command. * * Since: 0.8.0 */ void arv_device_execute_command (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_COMMAND, feature, error); if (node != NULL) arv_gc_command_execute (ARV_GC_COMMAND (node), error); } /** * arv_device_get_feature_value: * @device: a #ArvDevice object * @feature: feature name * @value: (out): a uninitialized #GValue placeholder * @error: a #GError placeholder * * Get the value of a feature. Enumeration values are returned as strings. * * Since: 0.8.32 */ void arv_device_get_feature_value (ArvDevice *device, const char *feature, GValue *value, GError **error) { ArvGcNode *node; GError *local_error = NULL; node = arv_device_get_feature (device, feature); if (node == NULL) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "[%s] Not found", feature); return; } if (ARV_IS_GC_FLOAT (node)) { g_value_init (value, G_TYPE_DOUBLE); g_value_set_double (value, arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error)); } else if (ARV_IS_GC_ENUMERATION (node)) { // ARV_IS_GC_INTEGER is true for an enum, so keep this before the int g_value_init (value, G_TYPE_STRING); g_value_set_string (value, arv_gc_enumeration_get_string_value (ARV_GC_ENUMERATION (node), &local_error)); } else if (ARV_IS_GC_INTEGER (node)) { g_value_init (value, G_TYPE_INT64); g_value_set_int64 (value, arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error)); } else if (ARV_IS_GC_BOOLEAN (node)) { g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), &local_error)); } else if (ARV_IS_GC_STRING (node)) { g_value_init (value, G_TYPE_STRING); g_value_set_string (value, arv_gc_string_get_value (ARV_GC_STRING (node), &local_error)); } else { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a feature", feature, G_OBJECT_TYPE_NAME (node)); } } /** * arv_device_set_feature_value: * @device: a #ArvDevice object * @feature: feature name * @value: a #GValue * @error: a #GError placeholder * * Set value of a feature. * * Since: 0.8.32 */ void arv_device_set_feature_value (ArvDevice *device, const char *feature, const GValue *value, GError **error) { ArvGcNode *node; GValue value_copy = G_VALUE_INIT; GError *local_error = NULL; node = arv_device_get_feature (device, feature); if (node == NULL) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "[%s] Not found", feature); return; } if (ARV_IS_GC_FLOAT (node)) { g_value_init(&value_copy, G_TYPE_DOUBLE); if (g_value_transform(value, &value_copy)) arv_gc_float_set_value (ARV_GC_FLOAT (node), g_value_get_double (&value_copy), &local_error); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a double", feature, G_OBJECT_TYPE_NAME (node)); } else if (ARV_IS_GC_INTEGER (node)) { g_value_init(&value_copy, G_TYPE_INT64); if (g_value_transform(value, &value_copy)) arv_gc_integer_set_value (ARV_GC_INTEGER (node), g_value_get_int64 (&value_copy), &local_error); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not an int64", feature, G_OBJECT_TYPE_NAME (node)); } else if (ARV_IS_GC_BOOLEAN (node)) { g_value_init(&value_copy, G_TYPE_BOOLEAN); if (g_value_transform(value, &value_copy)) arv_gc_boolean_set_value (ARV_GC_BOOLEAN (node), g_value_get_boolean (&value_copy), &local_error); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a boolean", feature, G_OBJECT_TYPE_NAME (node)); } else if (ARV_IS_GC_STRING (node)) { g_value_init(&value_copy, G_TYPE_STRING); if (g_value_transform(value, &value_copy)) arv_gc_string_set_value (ARV_GC_STRING (node), g_value_get_string (&value_copy), &local_error); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a string", feature, G_OBJECT_TYPE_NAME (node)); } else if (ARV_IS_GC_ENUMERATION (node)) { if (G_VALUE_HOLDS_STRING (value)) arv_gc_enumeration_set_string_value (ARV_GC_ENUMERATION (node), g_value_get_string (value), &local_error); else { g_value_init(&value_copy, G_TYPE_INT64); if (g_value_transform(value, &value_copy)) arv_gc_enumeration_set_int_value (ARV_GC_ENUMERATION (node), g_value_get_int64 (&value_copy), &local_error); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a string or int64", feature, G_OBJECT_TYPE_NAME (node)); } } else { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_WRONG_FEATURE, "[%s:%s] Not a feature", feature, G_OBJECT_TYPE_NAME (node)); } } /** * arv_device_set_boolean_feature_value: * @device: a #ArvDevice * @feature: feature name * @value: feature value * @error: a #GError placeholder * * Set the value of a boolean feature. * * Since: 0.8.0 */ void arv_device_set_boolean_feature_value (ArvDevice *device, const char *feature, gboolean value, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_BOOLEAN, feature, error); if (node != NULL) arv_gc_boolean_set_value (ARV_GC_BOOLEAN (node), value, error); } /** * arv_device_get_boolean_feature_value: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Returns: the feature value, %FALSE on error. * * Since: 0.8.0 */ gboolean arv_device_get_boolean_feature_value (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_BOOLEAN, feature, error); if (node != NULL) return arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), error); return FALSE; } /** * arv_device_get_boolean_feature_value_gi: (rename-to arv_device_get_boolean_feature_value) * @device: a #ArvDevice * @feature: feature name * @value: (out): feature value * @error: a #GError placeholder * * Get the feature value, or %FALSE on error. * * Since: 0.8.0 */ void arv_device_get_boolean_feature_value_gi (ArvDevice *device, const char *feature, gboolean *value, GError **error) { g_return_if_fail (value != NULL); *value = arv_device_get_boolean_feature_value (device, feature, error); } /** * arv_device_set_string_feature_value: * @device: a #ArvDevice * @feature: feature name * @value: new feature value * @error: a #GError placeholder * * Set the string feature value. * * Since: 0.8.0 */ void arv_device_set_string_feature_value (ArvDevice *device, const char *feature, const char *value, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_STRING, feature, error); if (node != NULL) arv_gc_string_set_value (ARV_GC_STRING (node), value, error); } /** * arv_device_get_string_feature_value: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Returns: the string feature value, %NULL on error. * * Since: 0.8.0 */ const char * arv_device_get_string_feature_value (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_STRING, feature, error); if (node != NULL) return arv_gc_string_get_value (ARV_GC_STRING (node), error); return NULL; } /** * arv_device_set_integer_feature_value: * @device: a #ArvDevice * @feature: feature name * @value: new feature value * @error: a #GError placeholder * * Set the integer feature value. * * Since: 0.8.0 */ void arv_device_set_integer_feature_value (ArvDevice *device, const char *feature, gint64 value, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_INTEGER, feature, error); if (node != NULL) arv_gc_integer_set_value (ARV_GC_INTEGER (node), value, error); } /** * arv_device_get_integer_feature_value: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Returns: the integer feature value, 0 on error. * * Since: 0.8.0 */ gint64 arv_device_get_integer_feature_value (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_INTEGER, feature, error); if (node != NULL) return arv_gc_integer_get_value (ARV_GC_INTEGER (node), error); return 0; } /** * arv_device_get_integer_feature_bounds: * @device: a #ArvDevice * @feature: feature name * @min: (out): minimum feature value * @max: (out): maximum feature value * @error: a #GError placeholder * * Retrieves feature bounds. * * Since: 0.8.0 */ void arv_device_get_integer_feature_bounds (ArvDevice *device, const char *feature, gint64 *min, gint64 *max, GError **error) { ArvGcNode *node; if (min != NULL) *min = G_MININT64; if (max != NULL) *max = G_MAXINT64; node = _get_feature (device, ARV_TYPE_GC_INTEGER, feature, error); if (node != NULL) { GError *local_error = NULL; if (min != NULL) { gint64 minimum; minimum = arv_gc_integer_get_min (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } *min = minimum; } if (max != NULL) { gint64 maximum; maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } *max = maximum; } } } /** * arv_device_get_integer_feature_increment: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Not all integer features have evenly distributed allowed values, which means the returned increment may not reflect the allowed value * set. * * Returns: feature value increment, or 1 on error. * * Since: 0.8.0 */ gint64 arv_device_get_integer_feature_increment (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_INTEGER, feature, error); if (node != NULL) { GError *local_error = NULL; gint64 increment; increment = arv_gc_integer_get_inc (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 1; } return increment; } return 1; } /** * arv_device_set_float_feature_value: * @device: a #ArvDevice * @feature: feature name * @value: new feature value * @error: a #GError placeholder * * Set the float feature value. * * Since: 0.8.0 */ void arv_device_set_float_feature_value (ArvDevice *device, const char *feature, double value, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_FLOAT, feature, error); if (node != NULL) arv_gc_float_set_value (ARV_GC_FLOAT (node), value, error); } /** * arv_device_get_float_feature_value: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Returns: the float feature value, 0.0 on error. * * Since: 0.8.0 */ double arv_device_get_float_feature_value (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_FLOAT, feature, error); if (node != NULL) return arv_gc_float_get_value (ARV_GC_FLOAT (node), error); return 0.0; } /** * arv_device_get_float_feature_bounds: * @device: a #ArvDevice * @feature: feature name * @min: (out): minimum feature value * @max: (out): maximum feature value * @error: a #GError placeholder * * Retrieves feature bounds. * * Since: 0.8.0 */ void arv_device_get_float_feature_bounds (ArvDevice *device, const char *feature, double *min, double *max, GError **error) { ArvGcNode *node; if (min != NULL) *min = -G_MAXDOUBLE; if (max != NULL) *max = G_MAXDOUBLE; node = _get_feature (device, ARV_TYPE_GC_FLOAT, feature, error); if (node != NULL) { GError *local_error = NULL; if (min != NULL) { double minimum; minimum = arv_gc_float_get_min (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } *min = minimum; } if (max != NULL) { double maximum; maximum = arv_gc_float_get_max (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } *max = maximum; } } } /** * arv_device_get_float_feature_increment: * @device: a #ArvDevice * @feature: feature name * @error: a #GError placeholder * * Not all float features have evenly distributed allowed values, which means the returned increment may not reflect the allowed value * set. * * Returns: feature value increment, or #G_MINDOUBLE on error. * * Since: 0.8.16 */ double arv_device_get_float_feature_increment (ArvDevice *device, const char *feature, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_FLOAT, feature, error); if (node != NULL) { GError *local_error = NULL; double increment; increment = arv_gc_float_get_inc (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MINDOUBLE; } return increment; } return G_MINDOUBLE; } /** * arv_device_set_register_feature_value: * @device: a #ArvDevice * @feature: feature name * @value: new feature value * @error: a #GError placeholder * * Set the register feature value. * * Since: */ void arv_device_set_register_feature_value (ArvDevice *device, const char *feature, guint64 length, void* value, GError **error) { ArvGcNode *node; node = _get_feature (device, ARV_TYPE_GC_REGISTER, feature, error); if (node != NULL) arv_gc_register_set (ARV_GC_REGISTER (node), value, length, error); } /** * arv_device_dup_register_feature_value: * @device: a #ArvDevice * @feature: feature name * @length: (out) (allow-none): register length * @error: a #GError placeholder * * Returns: the register feature content, must be freed using [func@GLib.free]. * * Since: 0.8.31 */ void * arv_device_dup_register_feature_value (ArvDevice *device, const char *feature, guint64 *length, GError **error) { ArvGcNode *node; if (length != NULL) *length = 0; node = _get_feature (device, ARV_TYPE_GC_REGISTER, feature, error); if (node != NULL) return arv_gc_register_dup (ARV_GC_REGISTER(node), length, error); return NULL; } /** * arv_device_dup_available_enumeration_feature_values: * @device: an #ArvDevice * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder * * Get all the available values of @feature, as integers. * * Returns: (array length=n_values) (transfer container): a newly created array of 64 bit integers, which must freed after use using g_free, * or NULL on error. * * Since: 0.8.0 */ gint64 * arv_device_dup_available_enumeration_feature_values (ArvDevice *device, const char *feature, guint *n_values, GError **error) { ArvGcNode *node; if (n_values != NULL) *n_values = 0; node = _get_feature (device, ARV_TYPE_GC_ENUMERATION, feature, error); if (node != NULL) return arv_gc_enumeration_dup_available_int_values (ARV_GC_ENUMERATION (node), n_values, error); return NULL; } /** * arv_device_dup_available_enumeration_feature_values_as_strings: * @device: an #ArvDevice * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder * * Get all the available values of @feature, as strings. * * Returns: (array length=n_values) (transfer container): a newly created array of const strings, which must freed after use using g_free, * or NULL on error. * * Since: 0.8.0 */ const char ** arv_device_dup_available_enumeration_feature_values_as_strings (ArvDevice *device, const char *feature, guint *n_values, GError **error) { ArvGcNode *node; if (n_values != NULL) *n_values = 0; node = _get_feature (device, ARV_TYPE_GC_ENUMERATION, feature, error); if (node != NULL) return arv_gc_enumeration_dup_available_string_values (ARV_GC_ENUMERATION (node), n_values, error); return NULL; } /** * arv_device_dup_available_enumeration_feature_values_as_display_names: * @device: an #ArvDevice * @feature: feature name * @n_values: placeholder for the number of returned values * @error: a #GError placeholder * * Get display names of all the available entries of @feature. * * Returns: (array length=n_values) (transfer container): a newly created array of const strings, to be freed after use using g_free, or * %NULL on error. * * Since: 0.8.0 */ const char ** arv_device_dup_available_enumeration_feature_values_as_display_names (ArvDevice *device, const char *feature, guint *n_values, GError **error) { ArvGcNode *node; if (n_values != NULL) *n_values = 0; node = _get_feature (device, ARV_TYPE_GC_ENUMERATION, feature, error); if (node != NULL) return arv_gc_enumeration_dup_available_display_names (ARV_GC_ENUMERATION (node), n_values, error); return NULL; } /** * arv_device_is_enumeration_entry_available: * @device: an #ArvDevice instance * @feature: enumeration feature name * @entry: entry name * @error: a #GError placeholder * * Returns: %TRUE if the feature and the feature entry are available * * Since: 0.8.17 */ gboolean arv_device_is_enumeration_entry_available (ArvDevice *device, const char *feature, const char *entry, GError **error) { GError *local_error = NULL; const char **entries = NULL; guint n_entries = 0; gboolean is_available = FALSE; unsigned int i; if (!arv_device_is_feature_available (device, feature, &local_error)) { if (local_error != NULL) g_propagate_error (error, local_error); return FALSE; } entries = arv_device_dup_available_enumeration_feature_values_as_strings (device, feature, &n_entries, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } for (i = 0; i < n_entries && !is_available; i++) { if (g_strcmp0 (entry, entries[i]) == 0) is_available = TRUE; } g_free (entries); return is_available; } /** * arv_device_set_features_from_string: * @device: a #ArvDevice * @string: a space separated list of features assignments * @error: a #GError placeholder, %NULL to ignore * * Set features from a string containing a list of space separated feature assignments or command names. For example: * * |[ * arv_device_set_features_from_string (device, "Width=256 Height=256 PixelFormat='Mono8' TriggerStart", &error); * ]| * * Since: 0.8.0 */ gboolean arv_device_set_features_from_string (ArvDevice *device, const char *string, GError **error) { GMatchInfo *match_info = NULL; GError *local_error = NULL; GRegex *regex; g_return_val_if_fail (ARV_IS_DEVICE (device), FALSE); if (string == NULL) return TRUE; regex = g_regex_new ("((?[^\\s\"'\\=]+)|\"(?[^\"]*)\"|'(?[^']*)')" "(?:\\=((?[^\\s\"']+)|\"(?[^\"]*)\"|'(?[^']*)'))?", G_REGEX_DUPNAMES, 0, NULL); if (g_regex_match (regex, string, 0, &match_info)) { while (g_match_info_matches (match_info) && local_error == NULL) { ArvGcNode *feature; char *key = g_match_info_fetch_named (match_info, "Key"); char *value = g_match_info_fetch_named (match_info, "Value"); size_t key_length = key != NULL ? strlen (key) : 0; if (key_length > 4 && key[0] == 'R' && key[1] == '[' && key[key_length - 1] == ']') { char *end; gint64 address; gint64 int_value; address = g_ascii_strtoll (&key[2], &end, 0); if (end == NULL || end != key + key_length -1) { g_set_error (&local_error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Invalid address in %s", key); } else { int_value = g_ascii_strtoll (value, &end, 0); if (end == NULL || end[0] != '\0') { g_set_error (&local_error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Invalid %s value for %s", value, key); } else { arv_device_write_register (device, address, int_value, &local_error); } } } else { feature = arv_device_get_feature (device, key); if (ARV_IS_GC_FEATURE_NODE (feature)) { if (ARV_IS_GC_COMMAND (feature)) { arv_device_execute_command (device, key, &local_error); } else if (value != NULL) { arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (feature), value, &local_error); } else { g_set_error (&local_error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "[%s] Require a parameter value to set", key); } } else g_set_error (&local_error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, "[%s] Not found", key); } g_free (key); g_free (value); g_match_info_next (match_info, NULL); } g_match_info_unref (match_info); } g_regex_unref (regex); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } return TRUE; } /** * arv_device_set_register_cache_policy: * @device: a #ArvDevice * @policy: cache policy * * Sets the register cache policy. * * Be aware that some camera may have wrong Cachable properties defined in their Genicam metadata, which * may lead to incorrect readouts. Using the debug cache policy, and activating genicam debug output (export * ARV_DEBUG=genicam), can help you to check the cache validity. In this mode, every time the cache content is not in * sync with the actual register value, a debug message is printed on the console. * * Since: 0.8.0 */ void arv_device_set_register_cache_policy (ArvDevice *device, ArvRegisterCachePolicy policy) { ArvGc *genicam; g_return_if_fail (ARV_IS_DEVICE (device)); genicam = arv_device_get_genicam (device); arv_gc_set_register_cache_policy (genicam, policy); } /** * arv_device_set_range_check_policy: * @device: a #ArvDevice * @policy: range check policy * * Sets the range check policy. When enabled, before being set, the value of all nodes with an #ArvGcFloat or * #ArvGcInteger interface will be checked against their Min and Max properties. * * Be aware that some camera may have wrong definition of Min and Max, as this check is defined as not * mandatory in the Genicam specification. If this is the case, it will not possible to set the value of the features * with faulty Min or Max definition. Range check is disabled by default. * * Since: 0.8.6 */ void arv_device_set_range_check_policy (ArvDevice *device, ArvRangeCheckPolicy policy) { ArvGc *genicam; g_return_if_fail (ARV_IS_DEVICE (device)); genicam = arv_device_get_genicam (device); arv_gc_set_range_check_policy (genicam, policy); } /** * arv_device_set_access_check_policy: * @device: a #ArvDevice * @policy: access check policy * * Sets the feature access check policy. When enabled, before being accessed, the actual read/write access of register * is checked using AccessMode properties. On some devices, it helps to avoid forbidden writes to registers that may put * the device in a bad state. * * Access check is disabled by default. * * Since: 0.8.22 */ void arv_device_set_access_check_policy (ArvDevice *device, ArvAccessCheckPolicy policy) { ArvGc *genicam; g_return_if_fail (ARV_IS_DEVICE (device)); genicam = arv_device_get_genicam (device); arv_gc_set_access_check_policy (genicam, policy); } void arv_device_emit_control_lost_signal (ArvDevice *device) { g_return_if_fail (ARV_IS_DEVICE (device)); g_signal_emit (device, arv_device_signals[ARV_DEVICE_SIGNAL_CONTROL_LOST], 0); } void arv_device_take_init_error (ArvDevice *device, GError *error) { ArvDevicePrivate *priv = arv_device_get_instance_private (device); g_return_if_fail (ARV_IS_DEVICE (device)); g_clear_error (&priv->init_error); priv->init_error = error; } static void arv_device_init (ArvDevice *device) { } static void arv_device_finalize (GObject *object) { ArvDevicePrivate *priv = arv_device_get_instance_private (ARV_DEVICE (object)); g_clear_error (&priv->init_error); G_OBJECT_CLASS (arv_device_parent_class)->finalize (object); } static void arv_device_class_init (ArvDeviceClass *device_class) { GObjectClass *object_class = G_OBJECT_CLASS (device_class); object_class->finalize = arv_device_finalize; /** * ArvDevice::control-lost: * @device:a #ArvDevice * * Signal that the control of the device is lost. * * This signal may be emited from a thread different than the main one, * so please take care to shared data access from the callback. * * Since: 0.2.0 */ arv_device_signals[ARV_DEVICE_SIGNAL_CONTROL_LOST] = g_signal_new ("control-lost", G_TYPE_FROM_CLASS (device_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ArvDeviceClass, control_lost), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); } static gboolean arv_device_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { ArvDevicePrivate *priv = arv_device_get_instance_private (ARV_DEVICE (initable)); g_return_val_if_fail (ARV_IS_DEVICE (initable), FALSE); if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialization not supported"); return FALSE; } if (priv->init_error) { if (error != NULL) *error = g_error_copy (priv->init_error); return FALSE; } return TRUE; } static void arv_device_initable_iface_init (GInitableIface *iface) { iface->init = arv_device_initable_init; } aravis-0.8.34/src/arvdevice.h000066400000000000000000000207501475431451200160150ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_DEVICE_H #define ARV_DEVICE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include #include G_BEGIN_DECLS #define ARV_DEVICE_ERROR arv_device_error_quark() ARV_API GQuark arv_device_error_quark (void); /** * ArvDeviceError: * @ARV_DEVICE_ERROR_WRONG_FEATURE: Wrong feature type * @ARV_DEVICE_ERROR_FEATURE_NOT_FOUND: Feature node not found * @ARV_DEVICE_ERROR_NOT_CONNECTED: Device is not connected * @ARV_DEVICE_ERROR_PROTOCOL_ERROR: Protocol error * @ARV_DEVICE_ERROR_TRANSFER_ERROR: Transfer error * @ARV_DEVICE_ERROR_TIMEOUT: Timeout detected * @ARV_DEVICE_ERROR_NOT_FOUND: Device not found * @ARV_DEVICE_ERROR_INVALID_PARAMETER: Invalid construction parameter * @ARV_DEVICE_ERROR_GENICAM_NOT_FOUND: Missing Genicam data * @ARV_DEVICE_ERROR_NO_STREAM_CHANNEL: No stream channel found * @ARV_DEVICE_ERROR_NOT_CONTROLLER: Controller privilege required * @ARV_DEVICE_ERROR_UNKNOWN: Unknown error */ typedef enum { ARV_DEVICE_ERROR_WRONG_FEATURE, ARV_DEVICE_ERROR_FEATURE_NOT_FOUND, ARV_DEVICE_ERROR_NOT_CONNECTED, ARV_DEVICE_ERROR_PROTOCOL_ERROR, ARV_DEVICE_ERROR_TRANSFER_ERROR, ARV_DEVICE_ERROR_TIMEOUT, ARV_DEVICE_ERROR_NOT_FOUND, ARV_DEVICE_ERROR_INVALID_PARAMETER, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, ARV_DEVICE_ERROR_NO_STREAM_CHANNEL, ARV_DEVICE_ERROR_NOT_CONTROLLER, ARV_DEVICE_ERROR_UNKNOWN, ARV_DEVICE_ERROR_PROTOCOL_ERROR_NOT_IMPLEMENTED, ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_PARAMETER, ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_ADDRESS, ARV_DEVICE_ERROR_PROTOCOL_ERROR_WRITE_PROTECT, ARV_DEVICE_ERROR_PROTOCOL_ERROR_BAD_ALIGNMENT, ARV_DEVICE_ERROR_PROTOCOL_ERROR_ACCESS_DENIED, ARV_DEVICE_ERROR_PROTOCOL_ERROR_BUSY } ArvDeviceError; #define ARV_TYPE_DEVICE (arv_device_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDevice, arv_device, ARV, DEVICE, GObject) struct _ArvDeviceClass { GObjectClass parent_class; ArvStream * (*create_stream) (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error); const char * (*get_genicam_xml) (ArvDevice *device, size_t *size); ArvGc * (*get_genicam) (ArvDevice *device); gboolean (*read_memory) (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error); gboolean (*write_memory) (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error); gboolean (*read_register) (ArvDevice *device, guint64 address, guint32 *value, GError **error); gboolean (*write_register) (ArvDevice *device, guint64 address, guint32 value, GError **error); /* signals */ void (*control_lost) (ArvDevice *device); }; ARV_API ArvStream * arv_device_create_stream (ArvDevice *device, ArvStreamCallback callback, void *user_data, GError **error); ARV_API ArvStream * arv_device_create_stream_full (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error); ARV_API gboolean arv_device_read_memory (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error); ARV_API gboolean arv_device_write_memory (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error); ARV_API gboolean arv_device_read_register (ArvDevice *device, guint64 address, guint32 *value, GError **error); ARV_API gboolean arv_device_write_register (ArvDevice *device, guint64 address, guint32 value, GError **error); ARV_API const char * arv_device_get_genicam_xml (ArvDevice *device, size_t *size); ARV_API ArvGc * arv_device_get_genicam (ArvDevice *device); ARV_API gboolean arv_device_is_feature_available (ArvDevice *device, const char *feature, GError **error); ARV_API gboolean arv_device_is_feature_implemented (ArvDevice *device, const char *feature, GError **error); ARV_API ArvGcNode * arv_device_get_feature (ArvDevice *device, const char *feature); ARV_API ArvGcAccessMode arv_device_get_feature_access_mode (ArvDevice *device, const char *feature); ARV_API ArvGcRepresentation arv_device_get_feature_representation (ArvDevice *device, const char *feature); ARV_API ArvChunkParser *arv_device_create_chunk_parser (ArvDevice *device); ARV_API void arv_device_execute_command (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_get_feature_value (ArvDevice *device, const char *feature, GValue *value, GError **error); ARV_API void arv_device_set_feature_value (ArvDevice *device, const char *feature, const GValue *value, GError **error); ARV_API void arv_device_set_boolean_feature_value (ArvDevice *device, const char *feature, gboolean value, GError **error); ARV_API gboolean arv_device_get_boolean_feature_value (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_get_boolean_feature_value_gi (ArvDevice *device, const char *feature, gboolean *value, GError **error); ARV_API void arv_device_set_string_feature_value (ArvDevice *device, const char *feature, const char *value, GError **error); ARV_API const char * arv_device_get_string_feature_value (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_set_integer_feature_value (ArvDevice *device, const char *feature, gint64 value, GError **error); ARV_API gint64 arv_device_get_integer_feature_value (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_get_integer_feature_bounds (ArvDevice *device, const char *feature, gint64 *min, gint64 *max, GError **error); ARV_API gint64 arv_device_get_integer_feature_increment(ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_set_float_feature_value (ArvDevice *device, const char *feature, double value, GError **error); ARV_API double arv_device_get_float_feature_value (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_get_float_feature_bounds (ArvDevice *device, const char *feature, double *min, double *max, GError **error); ARV_API double arv_device_get_float_feature_increment (ArvDevice *device, const char *feature, GError **error); ARV_API void arv_device_set_register_feature_value (ArvDevice *device, const char *feature, guint64 length, void* value, GError **error); ARV_API void * arv_device_dup_register_feature_value (ArvDevice *device, const char *feature, guint64 *length, GError **error); ARV_API gint64 * arv_device_dup_available_enumeration_feature_values (ArvDevice *device, const char *feature, guint *n_values, GError **error); ARV_API const char ** arv_device_dup_available_enumeration_feature_values_as_strings (ArvDevice *device, const char *feature, guint *n_values, GError **error); ARV_API const char ** arv_device_dup_available_enumeration_feature_values_as_display_names (ArvDevice *device, const char *feature, guint *n_values, GError **error); ARV_API gboolean arv_device_is_enumeration_entry_available (ArvDevice *device, const char *feature, const char *entry, GError **error); ARV_API gboolean arv_device_set_features_from_string (ArvDevice *device, const char *string, GError **error); ARV_API void arv_device_set_register_cache_policy (ArvDevice *device, ArvRegisterCachePolicy policy); ARV_API void arv_device_set_range_check_policy (ArvDevice *device, ArvRangeCheckPolicy policy); ARV_API void arv_device_set_access_check_policy (ArvDevice *device, ArvAccessCheckPolicy policy); G_END_DECLS #endif aravis-0.8.34/src/arvdeviceprivate.h000066400000000000000000000023521475431451200174060ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_DEVICE_PRIVATE_H #define ARV_DEVICE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS void arv_device_emit_control_lost_signal (ArvDevice *device); void arv_device_take_init_error (ArvDevice *device, GError *error); G_END_DECLS #endif aravis-0.8.34/src/arvdomcharacterdata.c000066400000000000000000000062551475431451200200430ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomcharacterdata * @short_description: Base class for DOM character data nodes */ #include #include #include typedef struct { char *data; } ArvDomCharacterDataPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvDomCharacterData, arv_dom_character_data, ARV_TYPE_DOM_NODE, G_ADD_PRIVATE (ArvDomCharacterData)) /* ArvDomNode implementation */ static const char * arv_dom_character_data_get_node_value (ArvDomNode* self) { return arv_dom_character_data_get_data (ARV_DOM_CHARACTER_DATA (self)); } static void arv_dom_character_data_set_node_value (ArvDomNode* self, const char *value) { arv_dom_character_data_set_data (ARV_DOM_CHARACTER_DATA (self), value); } /* ArvDomCharacterData implementation */ const char * arv_dom_character_data_get_data (ArvDomCharacterData* self) { ArvDomCharacterDataPrivate *priv = arv_dom_character_data_get_instance_private (ARV_DOM_CHARACTER_DATA (self)); g_return_val_if_fail (ARV_IS_DOM_CHARACTER_DATA (self), NULL); return priv->data; } void arv_dom_character_data_set_data (ArvDomCharacterData* self, const char * value) { ArvDomCharacterDataPrivate *priv = arv_dom_character_data_get_instance_private (ARV_DOM_CHARACTER_DATA (self)); g_return_if_fail (ARV_IS_DOM_CHARACTER_DATA (self)); g_return_if_fail (value != NULL); g_free (priv->data); priv->data = g_strdup (value); arv_debug_dom ("[ArvDomCharacterData::set_data] Value = '%s'", value); arv_dom_node_changed (ARV_DOM_NODE (self)); } static void arv_dom_character_data_init (ArvDomCharacterData *character_data) { } static void arv_dom_character_data_finalize (GObject *self) { ArvDomCharacterDataPrivate *priv = arv_dom_character_data_get_instance_private (ARV_DOM_CHARACTER_DATA (self)); g_free (priv->data); G_OBJECT_CLASS (arv_dom_character_data_parent_class)->finalize (self); } /* ArvDomCharacterData class */ static void arv_dom_character_data_class_init (ArvDomCharacterDataClass *character_data_class) { GObjectClass *object_class = G_OBJECT_CLASS (character_data_class); ArvDomNodeClass *node_class = ARV_DOM_NODE_CLASS (character_data_class); object_class->finalize = arv_dom_character_data_finalize; node_class->set_node_value = arv_dom_character_data_set_node_value; node_class->get_node_value = arv_dom_character_data_get_node_value; } aravis-0.8.34/src/arvdomcharacterdata.h000066400000000000000000000031071475431451200200410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_CHARACTER_DATA_H #define ARV_DOM_CHARACTER_DATA_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_CHARACTER_DATA (arv_dom_character_data_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomCharacterData, arv_dom_character_data, ARV, DOM_CHARACTER_DATA, ArvDomNode) struct _ArvDomCharacterDataClass { ArvDomNodeClass parent_class; }; ARV_API const char * arv_dom_character_data_get_data (ArvDomCharacterData *self); ARV_API void arv_dom_character_data_set_data (ArvDomCharacterData *self, const char *value); G_END_DECLS #endif aravis-0.8.34/src/arvdomdocument.c000066400000000000000000000136501475431451200170700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomdocument * @short_description: Base class for DOM document nodes */ #include #include #include #include #include #include #include typedef struct { char * url; } ArvDomDocumentPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvDomDocument, arv_dom_document, ARV_TYPE_DOM_NODE, G_ADD_PRIVATE (ArvDomDocument)) /* ArvDomNode implementation */ static const char * arv_dom_document_get_node_name (ArvDomNode *node) { return "#document"; } static ArvDomNodeType arv_dom_document_get_node_type (ArvDomNode *node) { return ARV_DOM_NODE_TYPE_DOCUMENT_NODE; } /* ArvDomDocument implementation */ /** * arv_dom_document_get_document_element: * @self: a #ArvDomDocument * * Returns: (transfer none): the top element of @self. */ ArvDomElement * arv_dom_document_get_document_element (ArvDomDocument *self) { g_return_val_if_fail (ARV_IS_DOM_DOCUMENT (self), NULL); return ARV_DOM_ELEMENT (arv_dom_node_get_first_child (ARV_DOM_NODE (self))); } /** * arv_dom_document_create_element: * @self: a #ArvDomDocument * @tag_name: node name of the element to create * * Create a new element. * * Returns: (transfer full): a new orphan #ArvDomElement, NULL on error. */ ArvDomElement * arv_dom_document_create_element (ArvDomDocument *self, const char *tag_name) { ArvDomDocumentClass *document_class; g_return_val_if_fail (ARV_IS_DOM_DOCUMENT (self), NULL); document_class = ARV_DOM_DOCUMENT_GET_CLASS (self); if (document_class->create_element != NULL) return document_class->create_element (self, tag_name); return NULL; } static ArvDomText * arv_dom_document_create_text_node_base (ArvDomDocument *document, const char *data) { return ARV_DOM_TEXT (arv_dom_text_new (data)); } /** * arv_dom_document_create_text_node: * @self: a #ArvDomDocument * @data: initial content * * Create a new text element. * * Returns: (transfer full): a new orphan #ArvDomText, NULL on error. */ ArvDomText * arv_dom_document_create_text_node (ArvDomDocument *self, const char *data) { g_return_val_if_fail (ARV_IS_DOM_DOCUMENT (self), NULL); return ARV_DOM_DOCUMENT_GET_CLASS (self)->create_text_node (self, data); } const char * arv_dom_document_get_url (ArvDomDocument *self) { ArvDomDocumentPrivate *priv = arv_dom_document_get_instance_private (ARV_DOM_DOCUMENT (self)); g_return_val_if_fail (ARV_IS_DOM_DOCUMENT (self), NULL); return priv->url; } void arv_dom_document_set_path (ArvDomDocument *self, const char *path) { ArvDomDocumentPrivate *priv = arv_dom_document_get_instance_private (ARV_DOM_DOCUMENT (self)); g_return_if_fail (ARV_IS_DOM_DOCUMENT (self)); g_free (priv->url); if (path == NULL) { priv->url = NULL; return; } priv->url = arv_str_to_uri (path); } void arv_dom_document_set_url (ArvDomDocument *self, const char *url) { ArvDomDocumentPrivate *priv = arv_dom_document_get_instance_private (ARV_DOM_DOCUMENT (self)); g_return_if_fail (ARV_IS_DOM_DOCUMENT (self)); g_return_if_fail (url == NULL || arv_str_is_uri (url)); g_free (priv->url); priv->url = g_strdup (url); } /** * arv_dom_document_get_href_data: * @self: a #ArvDomDocument * @href: document reference * @size: data size placeholder * * Load the content referenced by @href. * * Returns: (transfer full): a newly allocated data buffer. */ void * arv_dom_document_get_href_data (ArvDomDocument *self, const char *href, gsize *size) { ArvDomDocumentPrivate *priv = arv_dom_document_get_instance_private (ARV_DOM_DOCUMENT (self)); GFile *file; char *data = NULL; g_return_val_if_fail (ARV_IS_DOM_DOCUMENT (self), NULL); g_return_val_if_fail (href != NULL, NULL); if (strncmp (href, "data:", 5) == 0) { while (*href != '\0' && *href != ',') href++; return g_base64_decode (href, size); } file = g_file_new_for_uri (href); if (!g_file_load_contents (file, NULL, &data, size, NULL, NULL) && priv->url != NULL) { GFile *document_file; GFile *parent_file; g_object_unref (file); document_file = g_file_new_for_uri (priv->url); parent_file = g_file_get_parent (document_file); file = g_file_resolve_relative_path (parent_file, href); g_object_unref (document_file); g_object_unref (parent_file); g_file_load_contents (file, NULL, &data, size, NULL, NULL); } g_object_unref (file); return data; } static void arv_dom_document_init (ArvDomDocument *document) { } static void arv_dom_document_finalize (GObject *self) { ArvDomDocumentPrivate *priv = arv_dom_document_get_instance_private (ARV_DOM_DOCUMENT (self)); g_free (priv->url); G_OBJECT_CLASS (arv_dom_document_parent_class)->finalize (self); } /* ArvDomDocument class */ static void arv_dom_document_class_init (ArvDomDocumentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ArvDomNodeClass *node_class = ARV_DOM_NODE_CLASS (klass); object_class->finalize = arv_dom_document_finalize; node_class->get_node_name = arv_dom_document_get_node_name; node_class->get_node_type = arv_dom_document_get_node_type; klass->create_text_node = arv_dom_document_create_text_node_base; } aravis-0.8.34/src/arvdomdocument.h000066400000000000000000000043241475431451200170730ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_DOCUMENT_H #define ARV_DOM_DOCUMENT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_DOCUMENT (arv_dom_document_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomDocument, arv_dom_document, ARV, DOM_DOCUMENT, ArvDomNode) struct _ArvDomDocumentClass { ArvDomNodeClass parent_class; ArvDomElement * (*get_document_element) (ArvDomDocument* self); ArvDomElement * (*create_element) (ArvDomDocument* self, const char *tag_name); ArvDomText * (*create_text_node) (ArvDomDocument* self, const char *data); }; ARV_API ArvDomElement* arv_dom_document_get_document_element (ArvDomDocument *self); ARV_API ArvDomElement* arv_dom_document_create_element (ArvDomDocument *self, const char *tag_name); ARV_API ArvDomText* arv_dom_document_create_text_node (ArvDomDocument *self, const char *data); ARV_API const char * arv_dom_document_get_url (ArvDomDocument *self); ARV_API void arv_dom_document_set_url (ArvDomDocument *self, const char *url); ARV_API void arv_dom_document_set_path (ArvDomDocument *self, const char *path); ARV_API void * arv_dom_document_get_href_data (ArvDomDocument *self, const char *href, gsize *size); G_END_DECLS #endif aravis-0.8.34/src/arvdomdocumentfragment.c000066400000000000000000000042201475431451200206050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomdocumentfragment * @short_description: Base class for DOM document fragments */ #include #include G_DEFINE_ABSTRACT_TYPE (ArvDomDocumentFragment, arv_dom_document_fragment, ARV_TYPE_DOM_NODE) /* ArvDomNode implementation */ static const char * arv_dom_document_fragment_get_node_name (ArvDomNode *node) { return "#document-fragment"; } static const char * arv_dom_document_fragment_get_node_value (ArvDomNode *node) { return NULL; } static ArvDomNodeType arv_dom_document_fragment_get_node_type (ArvDomNode *node) { return ARV_DOM_NODE_TYPE_DOCUMENT_FRAGMENT_NODE; } /* ArvDomDocumentFragment implementation */ ArvDomDocumentFragment * arv_dom_document_fragment_new (void) { return g_object_new (ARV_TYPE_DOM_DOCUMENT_FRAGMENT, NULL); } static void arv_dom_document_fragment_init (ArvDomDocumentFragment *document_fragment) { } /* ArvDomDocumentFragment class */ static void arv_dom_document_fragment_class_init (ArvDomDocumentFragmentClass *klass) { ArvDomNodeClass *node_class = ARV_DOM_NODE_CLASS (klass); node_class->get_node_name = arv_dom_document_fragment_get_node_name; node_class->get_node_value = arv_dom_document_fragment_get_node_value; node_class->get_node_type = arv_dom_document_fragment_get_node_type; } aravis-0.8.34/src/arvdomdocumentfragment.h000066400000000000000000000030121475431451200206100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_DOCUMENT_FRAGMENT_H #define ARV_DOM_DOCUMENT_FRAGMENT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_DOCUMENT_FRAGMENT (arv_dom_document_fragment_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomDocumentFragment, arv_dom_document_fragment, ARV, DOM_DOCUMENT_FRAGMENT, ArvDomNode) struct _ArvDomDocumentFragmentClass { ArvDomNodeClass parent_class; }; ARV_API ArvDomDocumentFragment * arv_dom_document_fragment_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvdomelement.c000066400000000000000000000046641475431451200167100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomelement * @short_description: Base class for DOM element nodes */ #include #include G_DEFINE_ABSTRACT_TYPE (ArvDomElement, arv_dom_element, ARV_TYPE_DOM_NODE) /* ArvDomNode implementation */ static const char * arv_dom_element_get_node_value (ArvDomNode *node) { return NULL; } static ArvDomNodeType arv_dom_element_get_node_type (ArvDomNode *node) { return ARV_DOM_NODE_TYPE_ELEMENT_NODE; } /* ArvDomElement implementation */ const char * arv_dom_element_get_attribute (ArvDomElement* self, const char* name) { g_return_val_if_fail (ARV_IS_DOM_ELEMENT (self), NULL); g_return_val_if_fail (name != NULL, NULL); return ARV_DOM_ELEMENT_GET_CLASS (self)->get_attribute (self, name); } void arv_dom_element_set_attribute (ArvDomElement* self, const char* name, const char* attribute_value) { g_return_if_fail (ARV_IS_DOM_ELEMENT (self)); g_return_if_fail (name != NULL); ARV_DOM_ELEMENT_GET_CLASS (self)->set_attribute (self, name, attribute_value); arv_dom_node_changed (ARV_DOM_NODE (self)); } const char * arv_dom_element_get_tag_name (ArvDomElement *self) { g_return_val_if_fail (ARV_IS_DOM_ELEMENT (self), NULL); return arv_dom_node_get_node_name (ARV_DOM_NODE (self)); } static void arv_dom_element_init (ArvDomElement *element) { } /* ArvDomElement class */ static void arv_dom_element_class_init (ArvDomElementClass *klass) { ArvDomNodeClass *node_class = ARV_DOM_NODE_CLASS (klass); node_class->get_node_value = arv_dom_element_get_node_value; node_class->get_node_type = arv_dom_element_get_node_type; } aravis-0.8.34/src/arvdomelement.h000066400000000000000000000034221475431451200167040ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_ELEMENT_H #define ARV_DOM_ELEMENT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_ELEMENT (arv_dom_element_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomElement, arv_dom_element, ARV, DOM_ELEMENT, ArvDomNode) struct _ArvDomElementClass { ArvDomNodeClass parent_class; const char* (*get_attribute) (ArvDomElement *self, const char *name); void (*set_attribute) (ArvDomElement *self, const char *name, const char *attribute_value); }; ARV_API const char * arv_dom_element_get_tag_name (ArvDomElement *self); ARV_API const char* arv_dom_element_get_attribute (ArvDomElement *self, const char *name); ARV_API void arv_dom_element_set_attribute (ArvDomElement *self, const char *name, const char *attribute_value); G_END_DECLS #endif aravis-0.8.34/src/arvdomimplementation.c000066400000000000000000000046311475431451200202760ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #include #include #include #include static GHashTable *document_types = NULL; void arv_dom_implementation_add_document_type (const char *qualified_name, GType document_type) { GType *document_type_ptr; if (document_types == NULL) document_types = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); document_type_ptr = g_new (GType, 1); *document_type_ptr = document_type; g_hash_table_insert (document_types, g_strdup (qualified_name), document_type_ptr); } /** * arv_dom_implementation_create_document: * @namespace_uri: namespace URI * @qualified_name: qualified name * * Create a new DOM document. Currently, only @qualified_name is used. * * Returns: (transfer full): a new #ArvDomDocument, NULL on error: */ ArvDomDocument * arv_dom_implementation_create_document (const char *namespace_uri, const char *qualified_name) { GType *document_type; g_return_val_if_fail (qualified_name != NULL, NULL); if (document_types == NULL) { arv_dom_implementation_add_document_type ("RegisterDescription", ARV_TYPE_GC); } document_type = g_hash_table_lookup (document_types, qualified_name); if (document_type == NULL) { arv_info_dom ("[ArvDomImplementation::create_document] Unknown document type (%s)", qualified_name); return NULL; } return g_object_new (*document_type, NULL); } void arv_dom_implementation_cleanup (void) { if (document_types == NULL) return; g_hash_table_unref (document_types); document_types = NULL; } aravis-0.8.34/src/arvdomimplementation.h000066400000000000000000000030241475431451200202760ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_IMPLEMENTATION_H #define ARV_DOM_IMPLEMENTATION_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS typedef ArvDomDocument * (*ArvDomDocumentCreateFunction) (void); ARV_API ArvDomDocument * arv_dom_implementation_create_document (const char *namespace_uri, const char *qualified_name); ARV_API void arv_dom_implementation_add_document_type (const char *qualified_name, GType document_type); ARV_API void arv_dom_implementation_cleanup (void); G_END_DECLS #endif aravis-0.8.34/src/arvdomnamednodemap.c000066400000000000000000000057771475431451200177150ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #include #include G_DEFINE_ABSTRACT_TYPE (ArvDomNamedNodeMap, arv_dom_named_node_map, G_TYPE_OBJECT) /* ArvDomNamedNodeMap implementation */ /** * arv_dom_named_node_map_get_named_item: * @map: a #ArvDomNamedNodeMap * @name: name of the element to look for. * * Returns: (transfer none): a #ArvDomElement. */ ArvDomNode * arv_dom_named_node_map_get_named_item (ArvDomNamedNodeMap *map, const char *name) { g_return_val_if_fail (ARV_IS_DOM_NAMED_NODE_MAP (map), NULL); return ARV_DOM_NAMED_NODE_MAP_GET_CLASS (map)->get (map, name); } /** * arv_dom_named_node_map_set_named_item: * @map: a #ArvDomNamedNodeMap * @item: a node to insert * * Returns: (transfer none): same as @node on success. */ ArvDomNode * arv_dom_named_node_map_set_named_item (ArvDomNamedNodeMap *map, ArvDomNode *item) { g_return_val_if_fail (ARV_IS_DOM_NAMED_NODE_MAP (map), NULL); return ARV_DOM_NAMED_NODE_MAP_GET_CLASS (map)->set (map, item); } /** * arv_dom_named_node_map_remove_named_item: * @map: a #ArvDomNamedNodeMap * @name: name of the node to remove * * Returns: (transfer none): the removed node. */ ArvDomNode * arv_dom_named_node_map_remove_named_item (ArvDomNamedNodeMap *map, const char *name) { g_return_val_if_fail (ARV_IS_DOM_NAMED_NODE_MAP (map), NULL); return ARV_DOM_NAMED_NODE_MAP_GET_CLASS (map)->remove (map, name); } /** * arv_dom_named_node_map_get_item: * @map: a #ArvDomNamedNodeMap * @index: an index * * Returns: (transfer none): the @ArvDomNode corresponding to @index. */ ArvDomNode * arv_dom_named_node_map_get_item (ArvDomNamedNodeMap *map, unsigned int index) { g_return_val_if_fail (ARV_IS_DOM_NAMED_NODE_MAP (map), NULL); return ARV_DOM_NAMED_NODE_MAP_GET_CLASS (map)->get_item (map, index); } unsigned int arv_dom_named_node_map_get_length (ArvDomNamedNodeMap *map) { g_return_val_if_fail (ARV_IS_DOM_NAMED_NODE_MAP (map), 0); return ARV_DOM_NAMED_NODE_MAP_GET_CLASS (map)->get_length (map); } static void arv_dom_named_node_map_init (ArvDomNamedNodeMap *map) { } /* ArvDomNamedNodeMap class */ static void arv_dom_named_node_map_class_init (ArvDomNamedNodeMapClass *klass) { } aravis-0.8.34/src/arvdomnamednodemap.h000066400000000000000000000043321475431451200177040ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_NAMED_NODE_MAP_H #define ARV_DOM_NAMED_NODE_MAP_H #include #include #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif G_BEGIN_DECLS #define ARV_TYPE_DOM_NAMED_NODE_MAP (arv_dom_named_node_map_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomNamedNodeMap, arv_dom_named_node_map, ARV, DOM_NAMED_NODE_MAP, GObject) struct _ArvDomNamedNodeMapClass { GObjectClass parent_class; ArvDomNode * (*get) (ArvDomNamedNodeMap *map, const char *name); ArvDomNode * (*set) (ArvDomNamedNodeMap *map, ArvDomNode *node); ArvDomNode * (*remove) (ArvDomNamedNodeMap *map, const char *name); ArvDomNode * (*get_item) (ArvDomNamedNodeMap *map, unsigned int index); unsigned int (*get_length) (ArvDomNamedNodeMap *map); }; ARV_API ArvDomNode * arv_dom_named_node_map_get_named_item (ArvDomNamedNodeMap *map, const char *name); ARV_API ArvDomNode * arv_dom_named_node_map_set_named_item (ArvDomNamedNodeMap *map, ArvDomNode *item); ARV_API ArvDomNode * arv_dom_named_node_map_remove_named_item (ArvDomNamedNodeMap *map, const char *name); ARV_API ArvDomNode * arv_dom_named_node_map_get_item (ArvDomNamedNodeMap *map, unsigned int index); ARV_API unsigned int arv_dom_named_node_map_get_length (ArvDomNamedNodeMap *map); G_END_DECLS #endif aravis-0.8.34/src/arvdomnode.c000066400000000000000000000367621475431451200162100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomnode * @short_description: Base class for DOM nodes */ #include #include #include #include #include #include #include typedef struct { ArvDomNode *next_sibling; ArvDomNode *previous_sibling; ArvDomNode *parent_node; ArvDomNode *first_child; ArvDomNode *last_child; } ArvDomNodePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ArvDomNode, arv_dom_node, G_TYPE_OBJECT) /* ArvDomNode implementation */ /** * arv_dom_node_get_node_name: * @self: a #ArvDomNode * * Gets the node name. * * Return value: the node name. */ const char* arv_dom_node_get_node_name (ArvDomNode* self) { ArvDomNodeClass *node_class = ARV_DOM_NODE_GET_CLASS (self); g_return_val_if_fail (node_class != NULL, NULL); if (node_class->get_node_name) return node_class->get_node_name (self); return NULL; } /** * arv_dom_node_get_node_value: * @self: a #ArvDomNode * * Gets the node value. * * Return value: the node value. */ const char* arv_dom_node_get_node_value (ArvDomNode* self) { ArvDomNodeClass *node_class = ARV_DOM_NODE_GET_CLASS (self); g_return_val_if_fail (node_class != NULL, NULL); if (node_class->get_node_value) return node_class->get_node_value (self); return NULL; } void arv_dom_node_set_node_value (ArvDomNode* self, const char* new_value) { ArvDomNodeClass *node_class = ARV_DOM_NODE_GET_CLASS (self); g_return_if_fail (node_class != NULL); g_return_if_fail (new_value != NULL); if (node_class->set_node_value) node_class->set_node_value (self, new_value); } ArvDomNodeType arv_dom_node_get_node_type (ArvDomNode* self) { ArvDomNodeClass *node_class = ARV_DOM_NODE_GET_CLASS (self); g_return_val_if_fail (node_class != NULL, 0); if (node_class->get_node_type) return node_class->get_node_type (self); return 0; } /** * arv_dom_node_get_parent_node: * @self: a #ArvDomNode * * Get the parent node of @self. * * Returns: (transfer none): @self parent. */ ArvDomNode* arv_dom_node_get_parent_node (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); return priv->parent_node; } /** * arv_dom_node_get_child_nodes: * @self: a #ArvDomNode * * Returns: (transfer none): a #ArvDomNodeList, NULL on error. */ ArvDomNodeList* arv_dom_node_get_child_nodes (ArvDomNode* self) { ArvDomNodeList *list; g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); list = g_object_get_data (G_OBJECT (self), "child-nodes"); if (list == NULL) { list = arv_dom_node_child_list_new (self); g_object_set_data_full (G_OBJECT (self), "child-nodes", list, g_object_unref); } return list; } /** * arv_dom_node_get_first_child: * @self: a #ArvDomNode * * Returns: (transfer none): @self first child. */ ArvDomNode* arv_dom_node_get_first_child (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); return priv->first_child; } /** * arv_dom_node_get_last_child: * @self: a #ArvDomNode * * Returns: (transfer none): @self last child. */ ArvDomNode* arv_dom_node_get_last_child (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); return priv->last_child; } /** * arv_dom_node_get_previous_sibling: * @self: a #ArvDomNode * * Returns: (transfer none): @self previous sibling. */ ArvDomNode* arv_dom_node_get_previous_sibling (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); return priv->previous_sibling; } /** * arv_dom_node_get_next_sibling: * @self: a #ArvDomNode * * Returns: (transfer none): @self next sibling. */ ArvDomNode* arv_dom_node_get_next_sibling (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); return priv->next_sibling; } /*ArvDomNamedNodeMap**/ /*arv_dom_node_get_attributes (ArvDomNode* self)*/ /*{*/ /* return ARV_DOM_NODE_GET_CLASS (self)->get_attributes (self);*/ /*}*/ /** * arv_dom_node_get_owner_document: * @self: a #ArvDomNode * * Returns: (transfer none): @self owner document. */ ArvDomDocument* arv_dom_node_get_owner_document (ArvDomNode* self) { ArvDomNode *parent; g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); for (parent = self; parent != NULL && !ARV_IS_DOM_DOCUMENT (parent); parent = arv_dom_node_get_parent_node (parent)); return ARV_DOM_DOCUMENT (parent); } /** * arv_dom_node_insert_before: * @self: a #ArvDomNode * @new_child: (transfer full): node to insert * @ref_child: (transfer none): reference node, i.e., the node before which the new node must be inserted. * * Inserts the node @new_child before the existing child node @ref_child. If * @ref_child is null, insert @new_child at the end of the list of children. * If the @new_child is already in the tree, it is first removed. * * Returns: (transfer none): the inserted node. */ /* TODO: * If @new_child is a #LsmDocumentFragment object, all of its children are inserted, * in the same order, before @ref_child. * Check if new_child is an ancestor of self. */ ArvDomNode* arv_dom_node_insert_before (ArvDomNode* self, ArvDomNode* new_child, ArvDomNode* ref_child) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); ArvDomNodePrivate *new_child_priv = arv_dom_node_get_instance_private (new_child); ArvDomNodePrivate *ref_child_priv = arv_dom_node_get_instance_private (ref_child); ArvDomNodeClass *node_class; if (ref_child == NULL) arv_dom_node_append_child (self, new_child); g_return_val_if_fail (ARV_IS_DOM_NODE (new_child), NULL); if (new_child_priv->parent_node != NULL) arv_dom_node_remove_child (self, new_child); if (!ARV_IS_DOM_NODE (self)) { g_critical ("%s: self is not a ArvDomNode", G_STRFUNC); g_object_unref (new_child); return NULL; } if (!ARV_IS_DOM_NODE (ref_child)) { g_critical ("%s: ref_child is not a ArvDomNode", G_STRFUNC); g_object_unref (new_child); return NULL; } if (ref_child_priv->parent_node != self) { arv_info_dom ("[ArvDomNode::insert_before] Ref child '%s' doesn't belong to '%s'", arv_dom_node_get_node_name (ref_child), arv_dom_node_get_node_name (self)); g_object_unref (new_child); return NULL; } if (!ARV_DOM_NODE_GET_CLASS (self)->can_append_child (self, new_child)) { arv_debug_dom ("[ArvDomNode::insert_before] Can't append '%s' to '%s'", arv_dom_node_get_node_name (new_child), arv_dom_node_get_node_name (self)); g_object_unref (new_child); return NULL; } new_child_priv->parent_node = self; new_child_priv->next_sibling = ref_child; new_child_priv->previous_sibling = ref_child_priv->previous_sibling; if (ref_child_priv->previous_sibling == NULL) priv->first_child = new_child; else { ArvDomNodePrivate *previous_sibling_priv = arv_dom_node_get_instance_private (ref_child_priv->previous_sibling); previous_sibling_priv->next_sibling = new_child; } ref_child_priv->previous_sibling = new_child; node_class = ARV_DOM_NODE_GET_CLASS (self); if (node_class->post_new_child) node_class->post_new_child (self, new_child); arv_dom_node_changed (self); return new_child; } /** * arv_dom_node_replace_child: * @self: a #ArvDomNode * @new_child: (transfer full): a replacement node * @old_child: (transfer none): node to replace * * Replaces the child node @old_child with @new_child in the list of children, * and returns the @old_child node. * If the @new_child is already in the tree, it is first removed. * * Returns: (transfer full): the replaced node. */ /* TODO: * Check if new_child is an ancestor of self. */ ArvDomNode* arv_dom_node_replace_child (ArvDomNode* self, ArvDomNode* new_child, ArvDomNode* old_child) { ArvDomNodePrivate *new_child_priv = arv_dom_node_get_instance_private (new_child); ArvDomNodePrivate *old_child_priv = arv_dom_node_get_instance_private (old_child); ArvDomNode *next_sibling; ArvDomNode *node; if (new_child == NULL) return arv_dom_node_remove_child (self, old_child); if (!ARV_IS_DOM_NODE (new_child)) { g_critical ("%s: new_child is not a ArvDomNode", G_STRFUNC); if (ARV_IS_DOM_NODE (old_child)) g_object_unref (old_child); return NULL; } if (new_child_priv->parent_node != NULL) arv_dom_node_remove_child (self, new_child); if (old_child == NULL) { arv_info_dom ("[ArvDomNode::replace_child] old_child == NULL)"); g_object_unref (new_child); return NULL; } if (!ARV_IS_DOM_NODE (old_child)) { g_critical ("%s: old_child is not a ArvDomNode", G_STRFUNC); g_object_unref (new_child); return NULL; } if (!ARV_IS_DOM_NODE (self)) { g_critical ("%s: self is not a ArvDomNode", G_STRFUNC); g_object_unref (new_child); g_object_unref (old_child); return NULL; } if (old_child_priv->parent_node != self) { g_object_unref (new_child); g_object_unref (old_child); return NULL; } next_sibling = old_child_priv->next_sibling; node = arv_dom_node_remove_child (self, old_child); if (node != old_child) { g_object_unref (new_child); g_object_unref (old_child); return NULL; } if (next_sibling == NULL) arv_dom_node_append_child (self, new_child); else arv_dom_node_insert_before (self, new_child, next_sibling); return old_child; } /** * arv_dom_node_remove_child: * @self: a #ArvDomNode * @old_child: (transfer none): node to remove. * * Removes the child node indicated by @old_child from the list of children, and returns it. * * Returns: (transfer full): the removed node. */ ArvDomNode* arv_dom_node_remove_child (ArvDomNode* self, ArvDomNode* old_child) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); ArvDomNodePrivate *old_child_priv = arv_dom_node_get_instance_private (old_child); ArvDomNode *node; ArvDomNodeClass *node_class; g_return_val_if_fail (ARV_IS_DOM_NODE (self), NULL); if (old_child == NULL) return NULL; g_return_val_if_fail (ARV_IS_DOM_NODE (old_child), NULL); for (node = priv->first_child; node != NULL && node != old_child; node = arv_dom_node_get_next_sibling (node)); if (node == NULL) return NULL; node_class = ARV_DOM_NODE_GET_CLASS (self); if (node_class->pre_remove_child) node_class->pre_remove_child (self, old_child); if (priv->first_child == old_child) priv->first_child = old_child_priv->next_sibling; if (priv->last_child == old_child) priv->last_child = old_child_priv->previous_sibling; if (old_child_priv->next_sibling != NULL) { ArvDomNodePrivate *next_sibling_priv = arv_dom_node_get_instance_private (old_child_priv->next_sibling); next_sibling_priv->previous_sibling = old_child_priv->previous_sibling; } if (old_child_priv->previous_sibling != NULL) { ArvDomNodePrivate *previous_sibling_priv = arv_dom_node_get_instance_private (old_child_priv->previous_sibling); previous_sibling_priv->next_sibling = old_child_priv->next_sibling; } old_child_priv->parent_node = NULL; old_child_priv->next_sibling = NULL; old_child_priv->previous_sibling = NULL; arv_dom_node_changed (self); return old_child; } /** * arv_dom_node_append_child: * @self: a #ArvDomNode * @new_child: (transfer full): node to append * * Adds the node @new_child to the end of the list of children of this node. * If the @new_child is already in the tree, it is first removed. * * Returns: (transfer none): the added node. */ ArvDomNode * arv_dom_node_append_child (ArvDomNode* self, ArvDomNode* new_child) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); ArvDomNodePrivate *new_child_priv = arv_dom_node_get_instance_private (new_child); ArvDomNodeClass *node_class; if (new_child == NULL) return NULL; g_return_val_if_fail (ARV_IS_DOM_NODE (new_child), NULL); if (!ARV_IS_DOM_NODE (self)) { g_critical ("%s: self is not a ArvDomNode", G_STRFUNC); g_object_unref (new_child); return NULL; } if (new_child_priv->parent_node != NULL) arv_dom_node_remove_child (self, new_child); if (!ARV_DOM_NODE_GET_CLASS (self)->can_append_child (self, new_child)) { arv_debug_dom ("[ArvDomNode::append_child] Can't append '%s' to '%s'", arv_dom_node_get_node_name (new_child), arv_dom_node_get_node_name (self)); g_object_unref (new_child); return NULL; } if (priv->first_child == NULL) priv->first_child = new_child; if (priv->last_child != NULL) { ArvDomNodePrivate *last_child_priv = arv_dom_node_get_instance_private (priv->last_child); last_child_priv->next_sibling = new_child; } new_child_priv->parent_node = self; new_child_priv->next_sibling = NULL; new_child_priv->previous_sibling = priv->last_child; priv->last_child = new_child; node_class = ARV_DOM_NODE_GET_CLASS (self); if (node_class->post_new_child) node_class->post_new_child (self, new_child); arv_dom_node_changed (self); return new_child; } static gboolean arv_dom_node_can_append_child_default (ArvDomNode *self, ArvDomNode* new_child) { return FALSE; } void arv_dom_node_changed (ArvDomNode *self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); ArvDomNode *parent_node; ArvDomNode *child_node; ArvDomNodeClass *node_class; g_return_if_fail (ARV_IS_DOM_NODE (self)); node_class = ARV_DOM_NODE_GET_CLASS (self); if (node_class->changed) node_class->changed (self); child_node = self; for (parent_node = priv->parent_node; parent_node != NULL; parent_node = arv_dom_node_get_parent_node (parent_node)) { node_class = ARV_DOM_NODE_GET_CLASS (parent_node); if (node_class->child_changed == NULL || !node_class->child_changed (parent_node, child_node)) break; child_node = parent_node; } } gboolean arv_dom_node_has_child_nodes (ArvDomNode* self) { ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_DOM_NODE (self), FALSE); return priv->first_child != NULL; } static void arv_dom_node_init (ArvDomNode *node) { } static void arv_dom_node_finalize (GObject *object) { ArvDomNode *node = ARV_DOM_NODE (object); ArvDomNodePrivate *priv = arv_dom_node_get_instance_private (node); ArvDomNode *child, *next_child; child = priv->first_child; while (child != NULL) { next_child = arv_dom_node_get_next_sibling (child); g_object_unref (child); child = next_child; } G_OBJECT_CLASS (arv_dom_node_parent_class)->finalize (object); } /* ArvDomNode class */ static void arv_dom_node_class_init (ArvDomNodeClass *node_class) { GObjectClass *object_class = G_OBJECT_CLASS (node_class); object_class->finalize = arv_dom_node_finalize; node_class->can_append_child = arv_dom_node_can_append_child_default; } aravis-0.8.34/src/arvdomnode.h000066400000000000000000000075431475431451200162100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_NODE_H #define ARV_DOM_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS typedef enum { ARV_DOM_NODE_TYPE_ELEMENT_NODE = 1, ARV_DOM_NODE_TYPE_ATTRIBUTE_NODE, ARV_DOM_NODE_TYPE_TEXT_NODE, ARV_DOM_NODE_TYPE_CDATA_SECTION_NODE, ARV_DOM_NODE_TYPE_ENTITY_REFERENCE_NODE, ARV_DOM_NODE_TYPE_ENTITY_NODE, ARV_DOM_NODE_TYPE_PROCESSING_INSTRUCTION_NODE, ARV_DOM_NODE_TYPE_COMMENT_NODE, ARV_DOM_NODE_TYPE_DOCUMENT_NODE, ARV_DOM_NODE_TYPE_DOCUMENT_TYPE_NODE, ARV_DOM_NODE_TYPE_DOCUMENT_FRAGMENT_NODE, ARV_DOM_NODE_TYPE_NOTATION_NODE } ArvDomNodeType; #define ARV_TYPE_DOM_NODE (arv_dom_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomNode, arv_dom_node, ARV, DOM_NODE, GObject) struct _ArvDomNodeClass { GObjectClass parent_class; /* DOM node virtuals */ const char* (*get_node_name) (ArvDomNode* self); const char* (*get_node_value) (ArvDomNode* self); void (*set_node_value) (ArvDomNode* self, const char* new_value); ArvDomNodeType (*get_node_type) (ArvDomNode* self); /* Validation virtuals */ gboolean (*can_append_child) (ArvDomNode *self, ArvDomNode *new_child); /* Implementation virtuals */ void (*post_new_child) (ArvDomNode *parent, ArvDomNode *child); void (*pre_remove_child) (ArvDomNode *parent, ArvDomNode *child); void (*changed) (ArvDomNode *self); gboolean (*child_changed) (ArvDomNode *self, ArvDomNode *child); }; ARV_API const char * arv_dom_node_get_node_name (ArvDomNode *self); ARV_API const char * arv_dom_node_get_node_value (ArvDomNode *self); ARV_API void arv_dom_node_set_node_value (ArvDomNode *self, const char *new_value); ARV_API ArvDomNodeType arv_dom_node_get_node_type (ArvDomNode *self); ARV_API ArvDomNode * arv_dom_node_get_parent_node (ArvDomNode *self); ARV_API ArvDomNodeList * arv_dom_node_get_child_nodes (ArvDomNode *self); ARV_API ArvDomNode * arv_dom_node_get_first_child (ArvDomNode *self); ARV_API ArvDomNode * arv_dom_node_get_last_child (ArvDomNode *self); ARV_API ArvDomNode * arv_dom_node_get_previous_sibling (ArvDomNode *self); ARV_API ArvDomNode * arv_dom_node_get_next_sibling (ArvDomNode *self); #if 0 ARV_API ArvDomNamedNodeMap * arv_dom_node_get_attributes (ArvDomNode *self); #endif ARV_API ArvDomNode * arv_dom_node_insert_before (ArvDomNode *self, ArvDomNode *new_child, ArvDomNode *ref_child); ARV_API ArvDomNode * arv_dom_node_replace_child (ArvDomNode *self, ArvDomNode *new_child, ArvDomNode *old_child); ARV_API ArvDomNode * arv_dom_node_append_child (ArvDomNode *self, ArvDomNode *new_child); ARV_API ArvDomNode * arv_dom_node_remove_child (ArvDomNode *self, ArvDomNode *old_child); ARV_API gboolean arv_dom_node_has_child_nodes (ArvDomNode *self); ARV_API void arv_dom_node_changed (ArvDomNode *self); ARV_API ArvDomDocument * arv_dom_node_get_owner_document (ArvDomNode *self); G_END_DECLS #endif aravis-0.8.34/src/arvdomnodechildlist.c000066400000000000000000000070361475431451200201000ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #include #include #include /** * SECTION:arvdomnodechildlist * @short_description: Class for DOM node child lists */ /* ArvDomNodeChildList */ struct _ArvDomNodeChildList { ArvDomNodeList base; ArvDomNode *parent_node; }; struct _ArvDomNodeChildListClass { ArvDomNodeListClass parent_class; }; G_DEFINE_TYPE (ArvDomNodeChildList, arv_dom_node_child_list, ARV_TYPE_DOM_NODE_LIST) static void arv_dom_node_child_list_weak_notify_cb (void *user_data, GObject *object) { ArvDomNodeChildList *list = user_data; list->parent_node = NULL; } static ArvDomNode * arv_dom_node_child_list_get_item (ArvDomNodeList *list, unsigned int index) { ArvDomNodeChildList *child_list = ARV_DOM_NODE_CHILD_LIST (list); ArvDomNode *iter; unsigned int i = 0; if (child_list->parent_node == NULL) return NULL; for (iter = arv_dom_node_get_first_child (child_list->parent_node); iter != NULL; iter = arv_dom_node_get_next_sibling (iter)) { if (i == index) return iter; i++; } return NULL; } static unsigned int arv_dom_node_child_list_get_length (ArvDomNodeList *list) { ArvDomNodeChildList *child_list = ARV_DOM_NODE_CHILD_LIST (list); ArvDomNode *iter; unsigned int length = 0; if (child_list->parent_node == NULL) return 0; for (iter = arv_dom_node_get_first_child (child_list->parent_node); iter != NULL; iter = arv_dom_node_get_next_sibling (iter)) length++; return length; } ArvDomNodeList * arv_dom_node_child_list_new (ArvDomNode *parent_node) { ArvDomNodeChildList *list; g_return_val_if_fail (ARV_IS_DOM_NODE (parent_node), NULL); list = g_object_new (ARV_TYPE_DOM_NODE_CHILD_LIST, NULL); list->parent_node = parent_node; g_object_weak_ref (G_OBJECT (parent_node), arv_dom_node_child_list_weak_notify_cb, list); return ARV_DOM_NODE_LIST (list); } static void arv_dom_node_child_list_init (ArvDomNodeChildList *list) { } static void arv_dom_node_child_list_finalize (GObject *object) { ArvDomNodeChildList *list = ARV_DOM_NODE_CHILD_LIST (object); if (list->parent_node != NULL) { g_object_weak_unref (G_OBJECT (list->parent_node), arv_dom_node_child_list_weak_notify_cb, list); list->parent_node = NULL; } G_OBJECT_CLASS (arv_dom_node_child_list_parent_class)->finalize (object); } static void arv_dom_node_child_list_class_init (ArvDomNodeChildListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ArvDomNodeListClass *node_list_class = ARV_DOM_NODE_LIST_CLASS (klass); object_class->finalize = arv_dom_node_child_list_finalize; node_list_class->get_item = arv_dom_node_child_list_get_item; node_list_class->get_length = arv_dom_node_child_list_get_length; } aravis-0.8.34/src/arvdomnodechildlist.h000066400000000000000000000026361475431451200201060ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_NODE_CHILD_LIST_H #define ARV_DOM_NODE_CHILD_LIST_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_NODE_CHILD_LIST (arv_dom_node_child_list_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvDomNodeChildList, arv_dom_node_child_list, ARV, DOM_NODE_CHILD_LIST, ArvDomNodeList) ARV_API ArvDomNodeList * arv_dom_node_child_list_new (ArvDomNode *parent_node); G_END_DECLS #endif aravis-0.8.34/src/arvdomnodelist.c000066400000000000000000000034451475431451200170740ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #include #include G_DEFINE_ABSTRACT_TYPE (ArvDomNodeList, arv_dom_node_list, G_TYPE_OBJECT) /* ArvDomNodeList implementation */ /** * arv_dom_node_list_get_item: * @list: a #ArvDomNodeList * @index: item index * * Get one of the item of @list. * * Returns: (transfer none): item corresponding to index, NULL on error. */ ArvDomNode * arv_dom_node_list_get_item (ArvDomNodeList *list, unsigned int index) { g_return_val_if_fail (ARV_IS_DOM_NODE_LIST (list), NULL); return ARV_DOM_NODE_LIST_GET_CLASS (list)->get_item (list, index); } unsigned int arv_dom_node_list_get_length (ArvDomNodeList *list) { g_return_val_if_fail (ARV_IS_DOM_NODE_LIST (list), 0); return ARV_DOM_NODE_LIST_GET_CLASS (list)->get_length (list); } static void arv_dom_node_list_init (ArvDomNodeList *list) { } /* ArvDomNodeList class */ static void arv_dom_node_list_class_init (ArvDomNodeListClass *klass) { } aravis-0.8.34/src/arvdomnodelist.h000066400000000000000000000032141475431451200170730ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_NODE_LIST_H #define ARV_DOM_NODE_LIST_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_NODE_LIST (arv_dom_node_list_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomNodeList, arv_dom_node_list, ARV, DOM_NODE_LIST, GObject) struct _ArvDomNodeListClass { GObjectClass parent_class; ArvDomNode * (*get_item) (ArvDomNodeList *list, unsigned int index); unsigned int (*get_length) (ArvDomNodeList *list); }; ARV_API ArvDomNode * arv_dom_node_list_get_item (ArvDomNodeList *list, unsigned int index); ARV_API unsigned int arv_dom_node_list_get_length (ArvDomNodeList *list); G_END_DECLS #endif aravis-0.8.34/src/arvdomparser.c000066400000000000000000000233651475431451200165520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #include #include #include #include #include #include #include #include #include typedef enum { STATE } ArvDomSaxParserStateEnum; typedef struct { ArvDomSaxParserStateEnum state; ArvDomDocument *document; ArvDomNode *current_node; gboolean is_error; int error_depth; GHashTable *entities; } ArvDomSaxParserState; static void _free_entity (void *data) { xmlEntity *entity = data; xmlFree ((xmlChar *) entity->name); xmlFree ((xmlChar *) entity->ExternalID); xmlFree ((xmlChar *) entity->SystemID); xmlFree (entity->content); xmlFree (entity->orig); g_free (entity); } static void arv_dom_parser_start_document (void *user_data) { ArvDomSaxParserState *state = user_data; state->state = STATE; state->is_error = FALSE; state->error_depth = 0; state->entities = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, _free_entity); } static void arv_dom_parser_end_document (void *user_data) { ArvDomSaxParserState *state = user_data; g_hash_table_unref (state->entities); } static void arv_dom_parser_start_element(void *user_data, const xmlChar *name, const xmlChar **attrs) { ArvDomSaxParserState *state = user_data; ArvDomNode *node; int i; if (state->is_error) { state->error_depth++; return; } if (state->document == NULL) { state->document = arv_dom_implementation_create_document (NULL, (char *) name); state->current_node = ARV_DOM_NODE (state->document); g_return_if_fail (ARV_IS_DOM_DOCUMENT (state->document)); } node = ARV_DOM_NODE (arv_dom_document_create_element (ARV_DOM_DOCUMENT (state->document), (char *) name)); if (ARV_IS_DOM_NODE (node) && arv_dom_node_append_child (state->current_node, node) != NULL) { if (attrs != NULL) for (i = 0; attrs[i] != NULL && attrs[i+1] != NULL; i += 2) arv_dom_element_set_attribute (ARV_DOM_ELEMENT (node), (char *) attrs[i], (char *) attrs[i+1]); state->current_node = node; state->is_error = FALSE; state->error_depth = 0; } else { state->is_error = TRUE; state->error_depth = 1; } } static void arv_dom_parser_end_element (void *user_data, const xmlChar *name) { ArvDomSaxParserState *state = user_data; if (state->is_error) { state->error_depth--; if (state->error_depth > 0) { return; } state->is_error = FALSE; return; } state->current_node = arv_dom_node_get_parent_node (state->current_node); } static void arv_dom_parser_characters (void *user_data, const xmlChar *ch, int len) { ArvDomSaxParserState *state = user_data; if (!state->is_error) { ArvDomNode *node; char *text; text = g_strndup ((char *) ch, len); node = ARV_DOM_NODE (arv_dom_document_create_text_node (ARV_DOM_DOCUMENT (state->document), text)); arv_dom_node_append_child (state->current_node, node); g_free (text); } } static void arv_dom_parser_warning (void *user_data, const char *msg, ...) G_GNUC_PRINTF(2,3); static void arv_dom_parser_error (void *user_data, const char *msg, ...) G_GNUC_PRINTF(2,3); static void arv_dom_parser_fatal_error (void *user_data, const char *msg, ...) G_GNUC_PRINTF(2,3); static void arv_dom_parser_warning (void *user_data, const char *msg, ...) { va_list args; char *message; va_start(args, msg); message = g_strdup_vprintf (msg, args); arv_warning (ARV_DEBUG_CATEGORY_DOM, "[DomParser::parse] %s", message); g_free (message); va_end(args); } static void arv_dom_parser_error (void *user_data, const char *msg, ...) { va_list args; char *message; va_start(args, msg); message = g_strdup_vprintf (msg, args); arv_warning (ARV_DEBUG_CATEGORY_DOM, "[DomParser::parse] %s", message); g_free (message); va_end(args); } static void arv_dom_parser_fatal_error (void *user_data, const char *msg, ...) { va_list args; char *message; va_start(args, msg); message = g_strdup_vprintf (msg, args); arv_warning (ARV_DEBUG_CATEGORY_DOM, "[DomParser::parse] %s", message); g_free (message); va_end(args); } static xmlSAXHandler sax_handler = { .warning = arv_dom_parser_warning, .error = arv_dom_parser_error, .fatalError = arv_dom_parser_fatal_error, .startDocument = arv_dom_parser_start_document, .endDocument = arv_dom_parser_end_document, .startElement = arv_dom_parser_start_element, .endElement = arv_dom_parser_end_element, .characters = arv_dom_parser_characters }; static GQuark arv_dom_document_error_quark (void) { return g_quark_from_static_string ("arv-dom-error-quark"); } #define ARV_DOM_DOCUMENT_ERROR arv_dom_document_error_quark () typedef enum { ARV_DOM_DOCUMENT_ERROR_INVALID_XML } ArvDomDocumentError; #if LIBXML_VERSION >= 21100 static ArvDomDocument * _parse_memory (ArvDomDocument *document, ArvDomNode *node, const void *buffer, int size, GError **error) { static ArvDomSaxParserState state; xmlParserCtxt *xml_parser_ctxt; state.document = document; if (node != NULL) state.current_node = node; else state.current_node = ARV_DOM_NODE (document); if (size < 0) size = strlen (buffer); xml_parser_ctxt = xmlNewSAXParserCtxt (&sax_handler, &state); if (xml_parser_ctxt == NULL) { g_set_error (error, ARV_DOM_DOCUMENT_ERROR, ARV_DOM_DOCUMENT_ERROR_INVALID_XML, "Failed to create parser context"); return NULL; } xmlCtxtReadMemory (xml_parser_ctxt, buffer, size, NULL, NULL, 0); if (!xml_parser_ctxt->wellFormed) { if (state.document != NULL) g_object_unref (state.document); state.document = NULL; arv_warning_dom ("[DomParser::parse] Invalid document"); g_set_error (error, ARV_DOM_DOCUMENT_ERROR, ARV_DOM_DOCUMENT_ERROR_INVALID_XML, "Invalid document"); } xmlFreeParserCtxt(xml_parser_ctxt); return state.document; } #else static ArvDomDocument * _parse_memory (ArvDomDocument *document, ArvDomNode *node, const void *buffer, int size, GError **error) { static ArvDomSaxParserState state; state.document = document; if (node != NULL) state.current_node = node; else state.current_node = ARV_DOM_NODE (document); if (size < 0) size = strlen (buffer); if (xmlSAXUserParseMemory (&sax_handler, &state, buffer, size) < 0) { if (state.document != NULL) g_object_unref (state.document); state.document = NULL; arv_warning_dom ("[ArvDomParser::from_memory] Invalid document"); g_set_error (error, ARV_DOM_DOCUMENT_ERROR, ARV_DOM_DOCUMENT_ERROR_INVALID_XML, "Invalid document"); } return state.document; } #endif /** * arv_dom_document_append_from_memory: * @document: a #ArvDomDocument * @node: a #ArvDomNode * @buffer: a memory buffer holding xml data * @size: size of the xml data, in bytes * @error: an error placeholder * * Append a chunk of xml tree to an existing document. The resulting nodes will be appended to * @node, or to @document if @node == NULL. * * Size set to a negative value indicated an unknow xml data size. */ void arv_dom_document_append_from_memory (ArvDomDocument *document, ArvDomNode *node, const void *buffer, int size, GError **error) { g_return_if_fail (ARV_IS_DOM_DOCUMENT (document)); g_return_if_fail (ARV_IS_DOM_NODE (node) || node == NULL); g_return_if_fail (buffer != NULL); _parse_memory (document, node, buffer, size, error); } ArvDomDocument * arv_dom_document_new_from_memory (const void *buffer, int size, GError **error) { g_return_val_if_fail (buffer != NULL, NULL); return _parse_memory (NULL, NULL, buffer, size, error); } static ArvDomDocument * arv_dom_document_new_from_file (GFile *file, GError **error) { ArvDomDocument *document; gsize size = 0; char *contents = NULL; if (!g_file_load_contents (file, NULL, &contents, &size, NULL, error)) return NULL; document = arv_dom_document_new_from_memory (contents, size, error); g_free (contents); return document; } ArvDomDocument * arv_dom_document_new_from_path (const char *path, GError **error) { ArvDomDocument *document; GFile *file; g_return_val_if_fail (path != NULL, NULL); file = g_file_new_for_path (path); document = arv_dom_document_new_from_file (file, error); g_object_unref (file); if (document != NULL) arv_dom_document_set_path (document, path); return document; } ArvDomDocument * arv_dom_document_new_from_url (const char *url, GError **error) { ArvDomDocument *document; GFile *file; g_return_val_if_fail (url != NULL, NULL); file = g_file_new_for_uri (url); document = arv_dom_document_new_from_file (file, error); g_object_unref (file); if (document != NULL) arv_dom_document_set_url (document, url); return document; } aravis-0.8.34/src/arvdomparser.h000066400000000000000000000031121475431451200165430ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_PARSER_H #define ARV_DOM_PARSER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS ARV_API void arv_dom_document_append_from_memory (ArvDomDocument *document, ArvDomNode *node, const void *buffer, int size, GError **error); ARV_API ArvDomDocument * arv_dom_document_new_from_memory (const void *buffer, int size, GError **error); ARV_API ArvDomDocument * arv_dom_document_new_from_path (const char *path, GError **error); ARV_API ArvDomDocument * arv_dom_document_new_from_url (const char *url, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvdomtext.c000066400000000000000000000040271475431451200162340ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION:arvdomtext * @short_description: Base class for DOM text nodes */ #include G_DEFINE_TYPE (ArvDomText, arv_dom_text, ARV_TYPE_DOM_CHARACTER_DATA) /* ArvDomNode implementation */ static const char * arv_dom_text_get_node_name (ArvDomNode *node) { return "#text"; } static const char * arv_dom_text_get_node_value (ArvDomNode *node) { return arv_dom_character_data_get_data (ARV_DOM_CHARACTER_DATA (node)); } static ArvDomNodeType arv_dom_text_get_node_type (ArvDomNode *node) { return ARV_DOM_NODE_TYPE_TEXT_NODE; } /* ArvDomText implementation */ ArvDomNode * arv_dom_text_new (const char *data) { ArvDomNode *node; node = g_object_new (ARV_TYPE_DOM_TEXT, NULL); arv_dom_character_data_set_data (ARV_DOM_CHARACTER_DATA (node), data); return node; } static void arv_dom_text_init (ArvDomText *text_node) { } /* ArvDomText class */ static void arv_dom_text_class_init (ArvDomTextClass *klass) { ArvDomNodeClass *node_class = ARV_DOM_NODE_CLASS (klass); node_class->get_node_name = arv_dom_text_get_node_name; node_class->get_node_value = arv_dom_text_get_node_value; node_class->get_node_type = arv_dom_text_get_node_type; } aravis-0.8.34/src/arvdomtext.h000066400000000000000000000026371475431451200162460ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_DOM_TEXT_H #define ARV_DOM_TEXT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_DOM_TEXT (arv_dom_text_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvDomText, arv_dom_text, ARV, DOM_TEXT, ArvDomCharacterData) struct _ArvDomTextClass { ArvDomCharacterDataClass parent_class; }; ARV_API ArvDomNode * arv_dom_text_new (const char *data); G_END_DECLS #endif aravis-0.8.34/src/arvenums.c000066400000000000000000000045641475431451200157050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include static unsigned int _from_string (const char *string, const char **strings, unsigned int n_strings) { unsigned int i; if (string == NULL) return 0; for (i = 0; i < n_strings; i++) if (g_strcmp0 (string, strings[i]) == 0) return i; return 0; } static const char *arv_auto_strings[] = { "Off", "Once", "Continuous" }; const char * arv_auto_to_string (ArvAuto value) { return arv_auto_strings[CLAMP (value, 0, ARV_AUTO_CONTINUOUS)]; } ArvAuto arv_auto_from_string (const char *string) { return _from_string (string, arv_auto_strings, G_N_ELEMENTS (arv_auto_strings)); } static const char *arv_acquisition_mode_strings[] = { "Continuous", "SingleFrame", "MultiFrame" }; const char * arv_acquisition_mode_to_string (ArvAcquisitionMode value) { return arv_acquisition_mode_strings[CLAMP (value, 0, ARV_ACQUISITION_MODE_MULTI_FRAME)]; } ArvAcquisitionMode arv_acquisition_mode_from_string (const char *string) { return _from_string (string, arv_acquisition_mode_strings, G_N_ELEMENTS (arv_acquisition_mode_strings)); } static const char *arv_exposure_mode_strings[] = { "Off", "Timed", "TriggerWidth", "TriggerControlled" }; const char * arv_exposure_mode_to_string (ArvExposureMode value) { return arv_exposure_mode_strings[CLAMP (value, 0, ARV_EXPOSURE_MODE_TRIGGER_CONTROLLED)]; } ArvExposureMode arv_exposure_mode_from_string (const char *string) { return _from_string (string, arv_exposure_mode_strings, G_N_ELEMENTS (arv_exposure_mode_strings)); } aravis-0.8.34/src/arvenums.h000066400000000000000000000302321475431451200157010ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_ENUMS_H #define ARV_ENUMS_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * ArvAuto: * @ARV_AUTO_OFF: manual setting * @ARV_AUTO_ONCE: automatic setting done once, then returns to manual * @ARV_AUTO_CONTINUOUS: setting is adjusted continuously */ typedef enum { ARV_AUTO_OFF, ARV_AUTO_ONCE, ARV_AUTO_CONTINUOUS } ArvAuto; ARV_API const char * arv_auto_to_string (ArvAuto value); ARV_API ArvAuto arv_auto_from_string (const char *string); /** * ArvAcquisitionMode: * @ARV_ACQUISITION_MODE_CONTINUOUS: frames are captured continuously until stopped with the AcquisitionStop command. * @ARV_ACQUISITION_MODE_SINGLE_FRAME: only one frame will be acquired * @ARV_ACQUISITION_MODE_MULTI_FRAME: the number of frames specified by AcquisitionFrameCount is captured. */ typedef enum { ARV_ACQUISITION_MODE_CONTINUOUS, ARV_ACQUISITION_MODE_SINGLE_FRAME, ARV_ACQUISITION_MODE_MULTI_FRAME } ArvAcquisitionMode; ARV_API const char * arv_acquisition_mode_to_string (ArvAcquisitionMode value); ARV_API ArvAcquisitionMode arv_acquisition_mode_from_string (const char *string); /** * ArvExposureMode: * @ARV_EXPOSURE_MODE_OFF: disables the Exposure and let the shutter open. * @ARV_EXPOSURE_MODE_TIMED: timed exposure. The exposure duration time is set using the ExposureTime or ExposureAuto * features and the exposure starts with the FrameStart or LineStart. * @ARV_EXPOSURE_MODE_TRIGGER_WIDTH: uses the width of the current Frame or Line trigger signal(s) pulse to control the * exposure duration. Note that if the Frame or Line TriggerActivation is RisingEdge or LevelHigh, the exposure duration * will be the time the trigger stays High. If TriggerActivation is FallingEdge or LevelLow, the exposure time will last * as long as the trigger stays Low. * @ARV_EXPOSURE_MODE_TRIGGER_CONTROLLED: uses one or more trigger signal(s) to control the exposure duration * independently from the current Frame or Line triggers. See ExposureStart, ExposureEnd and ExposureActive of * the TriggerSelector feature. */ typedef enum { ARV_EXPOSURE_MODE_OFF, ARV_EXPOSURE_MODE_TIMED, ARV_EXPOSURE_MODE_TRIGGER_WIDTH, ARV_EXPOSURE_MODE_TRIGGER_CONTROLLED } ArvExposureMode; ARV_API const char * arv_exposure_mode_to_string (ArvExposureMode value); ARV_API ArvExposureMode arv_exposure_mode_from_string (const char *string); /** * ArvUvUsbMode: * @ARV_UV_USB_MODE_SYNC: utilize libusb synchronous device I/O API * @ARV_UV_USB_MODE_ASYNC: utilize libusb asynchronous device I/O API * @ARV_UV_USB_MODE_DEFAULT: default usb mode */ typedef enum { ARV_UV_USB_MODE_SYNC, ARV_UV_USB_MODE_ASYNC, ARV_UV_USB_MODE_DEFAULT = ARV_UV_USB_MODE_ASYNC } ArvUvUsbMode; /** * ArvPixelFormat: * * A datatype to hold a pixel format. */ typedef guint32 ArvPixelFormat; /** * ARV_TYPE_PIXEL_FORMAT: * * The #GType of a #ArvPixelFormat. */ #define ARV_TYPE_PIXEL_FORMAT G_TYPE_UINT32 #define ARV_PIXEL_FORMAT_BIT_PER_PIXEL(pixel_format) (((pixel_format) >> 16) & 0xff) /* Grey pixel formats */ #define ARV_PIXEL_FORMAT_MONO_8 ((ArvPixelFormat) 0x01080001u) #define ARV_PIXEL_FORMAT_MONO_8_SIGNED ((ArvPixelFormat) 0x01080002u) #define ARV_PIXEL_FORMAT_MONO_10 ((ArvPixelFormat) 0x01100003u) #define ARV_PIXEL_FORMAT_MONO_10_PACKED ((ArvPixelFormat) 0x010c0004u) #define ARV_PIXEL_FORMAT_MONO_12 ((ArvPixelFormat) 0x01100005u) #define ARV_PIXEL_FORMAT_MONO_12_PACKED ((ArvPixelFormat) 0x010c0006u) #define ARV_PIXEL_FORMAT_MONO_14 ((ArvPixelFormat) 0x01100025u) #define ARV_PIXEL_FORMAT_MONO_16 ((ArvPixelFormat) 0x01100007u) #define ARV_PIXEL_FORMAT_BAYER_GR_8 ((ArvPixelFormat) 0x01080008u) #define ARV_PIXEL_FORMAT_BAYER_RG_8 ((ArvPixelFormat) 0x01080009u) #define ARV_PIXEL_FORMAT_BAYER_GB_8 ((ArvPixelFormat) 0x0108000au) #define ARV_PIXEL_FORMAT_BAYER_BG_8 ((ArvPixelFormat) 0x0108000bu) #define ARV_PIXEL_FORMAT_BAYER_GR_10 ((ArvPixelFormat) 0x0110000cu) #define ARV_PIXEL_FORMAT_BAYER_RG_10 ((ArvPixelFormat) 0x0110000du) #define ARV_PIXEL_FORMAT_BAYER_GB_10 ((ArvPixelFormat) 0x0110000eu) #define ARV_PIXEL_FORMAT_BAYER_BG_10 ((ArvPixelFormat) 0x0110000fu) #define ARV_PIXEL_FORMAT_BAYER_GR_12 ((ArvPixelFormat) 0x01100010u) #define ARV_PIXEL_FORMAT_BAYER_RG_12 ((ArvPixelFormat) 0x01100011u) #define ARV_PIXEL_FORMAT_BAYER_GB_12 ((ArvPixelFormat) 0x01100012u) #define ARV_PIXEL_FORMAT_BAYER_BG_12 ((ArvPixelFormat) 0x01100013u) #define ARV_PIXEL_FORMAT_BAYER_GR_16 ((ArvPixelFormat) 0x0110002eu) #define ARV_PIXEL_FORMAT_BAYER_RG_16 ((ArvPixelFormat) 0x0110002fu) #define ARV_PIXEL_FORMAT_BAYER_GB_16 ((ArvPixelFormat) 0x01100030u) #define ARV_PIXEL_FORMAT_BAYER_BG_16 ((ArvPixelFormat) 0x01100031u) #define ARV_PIXEL_FORMAT_BAYER_BG_10P ((ArvPixelFormat) 0x010a0052u) #define ARV_PIXEL_FORMAT_BAYER_GB_10P ((ArvPixelFormat) 0x010a0054u) #define ARV_PIXEL_FORMAT_BAYER_GR_10P ((ArvPixelFormat) 0x010a0056u) #define ARV_PIXEL_FORMAT_BAYER_RG_10P ((ArvPixelFormat) 0x010a0058u) #define ARV_PIXEL_FORMAT_BAYER_BG_12P ((ArvPixelFormat) 0x010c0053u) #define ARV_PIXEL_FORMAT_BAYER_GB_12P ((ArvPixelFormat) 0x010c0055u) #define ARV_PIXEL_FORMAT_BAYER_GR_12P ((ArvPixelFormat) 0x010c0057u) #define ARV_PIXEL_FORMAT_BAYER_RG_12P ((ArvPixelFormat) 0x010c0059u) #define ARV_PIXEL_FORMAT_BAYER_GR_12_PACKED ((ArvPixelFormat) 0x010c002au) #define ARV_PIXEL_FORMAT_BAYER_RG_12_PACKED ((ArvPixelFormat) 0x010c002bu) #define ARV_PIXEL_FORMAT_BAYER_GB_12_PACKED ((ArvPixelFormat) 0x010c002cu) #define ARV_PIXEL_FORMAT_BAYER_BG_12_PACKED ((ArvPixelFormat) 0x010c002du) #define ARV_PIXEL_FORMAT_BAYER_GR_10_PACKED ((ArvPixelFormat) 0x010c0026u) #define ARV_PIXEL_FORMAT_BAYER_RG_10_PACKED ((ArvPixelFormat) 0x010c0027u) #define ARV_PIXEL_FORMAT_BAYER_GB_10_PACKED ((ArvPixelFormat) 0x010c0028u) #define ARV_PIXEL_FORMAT_BAYER_BG_10_PACKED ((ArvPixelFormat) 0x010c0029u) #define ARV_PIXEL_FORMAT_COORD3D_ABC_8 ((ArvPixelFormat) 0x021800B2u) #define ARV_PIXEL_FORMAT_COORD3D_ABC_8_PLANAR ((ArvPixelFormat) 0x021800B3u) #define ARV_PIXEL_FORMAT_COORD3D_ABC_10P ((ArvPixelFormat) 0x021E00DBu) #define ARV_PIXEL_FORMAT_COORD3D_ABC_10P_PLANAR ((ArvPixelFormat) 0x021E00DCu) #define ARV_PIXEL_FORMAT_COORD3D_ABC_12P ((ArvPixelFormat) 0x022400DEu) #define ARV_PIXEL_FORMAT_COORD3D_ABC_12P_PLANAR ((ArvPixelFormat) 0x022400DFu) #define ARV_PIXEL_FORMAT_COORD3D_ABC_16 ((ArvPixelFormat) 0x023000B9u) #define ARV_PIXEL_FORMAT_COORD3D_ABC_16_PLANAR ((ArvPixelFormat) 0x023000BAu) #define ARV_PIXEL_FORMAT_COORD3D_ABC_32F ((ArvPixelFormat) 0x026000C0u) #define ARV_PIXEL_FORMAT_COORD3D_ABC_32F_PLANAR ((ArvPixelFormat) 0x026000C1u) #define ARV_PIXEL_FORMAT_COORD3D_AC_8 ((ArvPixelFormat) 0x021000B4u) #define ARV_PIXEL_FORMAT_COORD3D_AC_8_PLANAR ((ArvPixelFormat) 0x021000B5u) #define ARV_PIXEL_FORMAT_COORD3D_AC_10P ((ArvPixelFormat) 0x021400F0u) #define ARV_PIXEL_FORMAT_COORD3D_AC_10P_PLANAR ((ArvPixelFormat) 0x021400F1u) #define ARV_PIXEL_FORMAT_COORD3D_AC_12P ((ArvPixelFormat) 0x021800F2u) #define ARV_PIXEL_FORMAT_COORD3D_AC_12P_PLANAR ((ArvPixelFormat) 0x021800F3u) #define ARV_PIXEL_FORMAT_COORD3D_AC_16 ((ArvPixelFormat) 0x022000BBu) #define ARV_PIXEL_FORMAT_COORD3D_AC_16_PLANAR ((ArvPixelFormat) 0x022000BCu) #define ARV_PIXEL_FORMAT_COORD3D_AC_32F ((ArvPixelFormat) 0x024000C2u) #define ARV_PIXEL_FORMAT_COORD3D_AC_32F_PLANAR ((ArvPixelFormat) 0x024000C3u) #define ARV_PIXEL_FORMAT_COORD3D_A_8 ((ArvPixelFormat) 0x010800AFu) #define ARV_PIXEL_FORMAT_COORD3D_A_10P ((ArvPixelFormat) 0x010A00D5u) #define ARV_PIXEL_FORMAT_COORD3D_A_12P ((ArvPixelFormat) 0x010C00D8u) #define ARV_PIXEL_FORMAT_COORD3D_A_16 ((ArvPixelFormat) 0x011000B6u) #define ARV_PIXEL_FORMAT_COORD3D_A_32F ((ArvPixelFormat) 0x012000BDu) #define ARV_PIXEL_FORMAT_COORD3D_B_8 ((ArvPixelFormat) 0x010800B0u) #define ARV_PIXEL_FORMAT_COORD3D_B_10P ((ArvPixelFormat) 0x010A00D6u) #define ARV_PIXEL_FORMAT_COORD3D_B_12P ((ArvPixelFormat) 0x010C00D9u) #define ARV_PIXEL_FORMAT_COORD3D_B_16 ((ArvPixelFormat) 0x011000B7u) #define ARV_PIXEL_FORMAT_COORD3D_B_32F ((ArvPixelFormat) 0x012000BEu) #define ARV_PIXEL_FORMAT_COORD3D_C_8 ((ArvPixelFormat) 0x010800B1u) #define ARV_PIXEL_FORMAT_COORD3D_C_10P ((ArvPixelFormat) 0x010A00D7u) #define ARV_PIXEL_FORMAT_COORD3D_C_12P ((ArvPixelFormat) 0x010C00DAu) #define ARV_PIXEL_FORMAT_COORD3D_C_16 ((ArvPixelFormat) 0x011000B8u) #define ARV_PIXEL_FORMAT_COORD3D_C_32F ((ArvPixelFormat) 0x012000BFu) #define ARV_PIXEL_FORMAT_DATA_8 ((ArvPixelFormat) 0x01080116u) #define ARV_PIXEL_FORMAT_DATA_8S ((ArvPixelFormat) 0x01080117u) #define ARV_PIXEL_FORMAT_DATA_16 ((ArvPixelFormat) 0x01100118u) #define ARV_PIXEL_FORMAT_DATA_16S ((ArvPixelFormat) 0x01100119u) #define ARV_PIXEL_FORMAT_DATA_32 ((ArvPixelFormat) 0x0120011Au) #define ARV_PIXEL_FORMAT_DATA_32F ((ArvPixelFormat) 0x0120011Cu) #define ARV_PIXEL_FORMAT_DATA_32S ((ArvPixelFormat) 0x0120011Bu) #define ARV_PIXEL_FORMAT_DATA_64 ((ArvPixelFormat) 0x0140011Du) #define ARV_PIXEL_FORMAT_DATA_64F ((ArvPixelFormat) 0x0140011Fu) #define ARV_PIXEL_FORMAT_DATA_64S ((ArvPixelFormat) 0x0140011Eu) /* Color pixel formats */ #define ARV_PIXEL_FORMAT_RGB_8_PACKED ((ArvPixelFormat) 0x02180014u) #define ARV_PIXEL_FORMAT_BGR_8_PACKED ((ArvPixelFormat) 0x02180015u) #define ARV_PIXEL_FORMAT_RGBA_8_PACKED ((ArvPixelFormat) 0x02200016u) #define ARV_PIXEL_FORMAT_BGRA_8_PACKED ((ArvPixelFormat) 0x02200017u) #define ARV_PIXEL_FORMAT_RGB_10_PACKED ((ArvPixelFormat) 0x02300018u) #define ARV_PIXEL_FORMAT_BGR_10_PACKED ((ArvPixelFormat) 0x02300019u) #define ARV_PIXEL_FORMAT_RGB_12_PACKED ((ArvPixelFormat) 0x0230001au) #define ARV_PIXEL_FORMAT_BGR_12_PACKED ((ArvPixelFormat) 0x0230001bu) #define ARV_PIXEL_FORMAT_YUV_411_PACKED ((ArvPixelFormat) 0x020c001eu) #define ARV_PIXEL_FORMAT_YUV_422_PACKED ((ArvPixelFormat) 0x0210001fu) #define ARV_PIXEL_FORMAT_YUV_444_PACKED ((ArvPixelFormat) 0x02180020u) #define ARV_PIXEL_FORMAT_RGB_8_PLANAR ((ArvPixelFormat) 0x02180021u) #define ARV_PIXEL_FORMAT_RGB_10_PLANAR ((ArvPixelFormat) 0x02300022u) #define ARV_PIXEL_FORMAT_RGB_12_PLANAR ((ArvPixelFormat) 0x02300023u) #define ARV_PIXEL_FORMAT_RGB_16_PLANAR ((ArvPixelFormat) 0x02300024u) #define ARV_PIXEL_FORMAT_YUV_422_YUYV_PACKED ((ArvPixelFormat) 0x02100032u) /* Custom */ /** * ARV_PIXEL_FORMAT_CUSTOM_BAYER_GR_12_PACKED: (type ArvPixelFormat) */ #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_GR_12_PACKED ((ArvPixelFormat) 0x810c0001u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_RG_12_PACKED ((ArvPixelFormat) 0x810c0002u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_GB_12_PACKED ((ArvPixelFormat) 0x810c0003u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_BG_12_PACKED ((ArvPixelFormat) 0x810c0004u) #define ARV_PIXEL_FORMAT_CUSTOM_YUV_422_YUYV_PACKED ((ArvPixelFormat) 0x82100005u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_GR_16 ((ArvPixelFormat) 0x81100006u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_RG_16 ((ArvPixelFormat) 0x81100007u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_GB_16 ((ArvPixelFormat) 0x81100008u) #define ARV_PIXEL_FORMAT_CUSTOM_BAYER_BG_16 ((ArvPixelFormat) 0x81100009u) G_END_DECLS #endif aravis-0.8.34/src/arvevaluator.c000066400000000000000000001525631475431451200165630ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvevaluator * @short_description: A math expression evaluator with Genicam syntax */ #include #include #include #include #include #include #define ARV_EVALUATOR_STACK_SIZE 128 typedef enum { ARV_EVALUATOR_STATUS_SUCCESS, ARV_EVALUATOR_STATUS_NOT_PARSED, ARV_EVALUATOR_STATUS_EMPTY_EXPRESSION, ARV_EVALUATOR_STATUS_PARENTHESES_MISMATCH, ARV_EVALUATOR_STATUS_SYNTAX_ERROR, ARV_EVALUATOR_STATUS_UNKNOWN_OPERATOR, ARV_EVALUATOR_STATUS_UNKNOWN_VARIABLE, ARV_EVALUATOR_STATUS_UNKNOWN_SUB_EXPRESSION, ARV_EVALUATOR_STATUS_UNKNOWN_CONSTANT, ARV_EVALUATOR_STATUS_MISSING_ARGUMENTS, ARV_EVALUATOR_STATUS_REMAINING_OPERANDS, ARV_EVALUATOR_STATUS_DIVISION_BY_ZERO, ARV_EVALUATOR_STATUS_STACK_OVERFLOW, ARV_EVALUATOR_STATUS_INVALID_DOUBLE_FUNCTION, ARV_EVALUATOR_STATUS_FORBIDDEN_RECUSRION } ArvEvaluatorStatus; typedef struct { char *expression; GSList *rpn_stack; ArvEvaluatorStatus parsing_status; GHashTable *variables; GHashTable *sub_expressions; GHashTable *constants; } ArvEvaluatorPrivate; struct _ArvEvaluator { GObject object; ArvEvaluatorPrivate *priv; }; struct _ArvEvaluatorClass { GObjectClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvEvaluator, arv_evaluator, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvEvaluator)) static const char *arv_evaluator_status_strings[] = { "success", "not parsed", "empty expression", "parentheses mismatch", "syntax error", "unknown operator", "unknown variable", "unknown subexpression", "unknown constant", "missing arguments", "remaining operands", "division by zero", "stack overflow", "invalid double function", "forbidden recursion" }; typedef enum { ARV_EVALUATOR_TOKEN_UNKNOWN, ARV_EVALUATOR_TOKEN_COMMA, ARV_EVALUATOR_TOKEN_TERNARY_QUESTION_MARK, ARV_EVALUATOR_TOKEN_TERNARY_COLON, ARV_EVALUATOR_TOKEN_LOGICAL_OR, ARV_EVALUATOR_TOKEN_LOGICAL_AND, ARV_EVALUATOR_TOKEN_BITWISE_OR, ARV_EVALUATOR_TOKEN_BITWISE_XOR, ARV_EVALUATOR_TOKEN_BITWISE_AND, ARV_EVALUATOR_TOKEN_EQUAL, ARV_EVALUATOR_TOKEN_NOT_EQUAL, ARV_EVALUATOR_TOKEN_LESS_OR_EQUAL, ARV_EVALUATOR_TOKEN_GREATER_OR_EQUAL, ARV_EVALUATOR_TOKEN_LESS, ARV_EVALUATOR_TOKEN_GREATER, ARV_EVALUATOR_TOKEN_SHIFT_RIGHT, ARV_EVALUATOR_TOKEN_SHIFT_LEFT, ARV_EVALUATOR_TOKEN_SUBSTRACTION, ARV_EVALUATOR_TOKEN_ADDITION, ARV_EVALUATOR_TOKEN_REMAINDER, ARV_EVALUATOR_TOKEN_DIVISION, ARV_EVALUATOR_TOKEN_MULTIPLICATION, ARV_EVALUATOR_TOKEN_POWER, ARV_EVALUATOR_TOKEN_MINUS, ARV_EVALUATOR_TOKEN_PLUS, ARV_EVALUATOR_TOKEN_BITWISE_NOT, ARV_EVALUATOR_TOKEN_FUNCTION_SIN, ARV_EVALUATOR_TOKEN_FUNCTION_COS, ARV_EVALUATOR_TOKEN_FUNCTION_SGN, ARV_EVALUATOR_TOKEN_FUNCTION_NEG, ARV_EVALUATOR_TOKEN_FUNCTION_ATAN, ARV_EVALUATOR_TOKEN_FUNCTION_TAN, ARV_EVALUATOR_TOKEN_FUNCTION_ABS, ARV_EVALUATOR_TOKEN_FUNCTION_EXP, ARV_EVALUATOR_TOKEN_FUNCTION_LN, ARV_EVALUATOR_TOKEN_FUNCTION_LG, ARV_EVALUATOR_TOKEN_FUNCTION_SQRT, ARV_EVALUATOR_TOKEN_FUNCTION_TRUNC, ARV_EVALUATOR_TOKEN_FUNCTION_ROUND, ARV_EVALUATOR_TOKEN_FUNCTION_FLOOR, ARV_EVALUATOR_TOKEN_FUNCTION_CEIL, ARV_EVALUATOR_TOKEN_FUNCTION_ASIN, ARV_EVALUATOR_TOKEN_FUNCTION_ACOS, ARV_EVALUATOR_TOKEN_RIGHT_PARENTHESIS, ARV_EVALUATOR_TOKEN_LEFT_PARENTHESIS, ARV_EVALUATOR_TOKEN_CONSTANT_INT64, ARV_EVALUATOR_TOKEN_CONSTANT_DOUBLE, ARV_EVALUATOR_TOKEN_VARIABLE } ArvEvaluatorTokenId; typedef enum { ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT } ArvEvaluatorTokenAssociativity; typedef struct { const char * tag; int precedence; gboolean double_only; int n_args; ArvEvaluatorTokenAssociativity associativity; } ArvEvaluatorTokenInfos; static ArvEvaluatorTokenInfos arv_evaluator_token_infos[] = { {"", 0, FALSE, 1, 0}, /* UNKNOWN */ {",", 0, FALSE, 0, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* COMMA */ {"?", 5, FALSE, 3, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* TERNARY_QUESTION_MARK */ {":", 5, FALSE, 1, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* TERNARY_COLON */ {"||", 10, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* LOGICAL_OR */ {"&&", 20, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* LOGICAL_AND */ {"|", 40, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* BITWISE_OR */ {"^", 50, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* BITWISE_XOR */ {"&", 60, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* BITWISE_AND */ {"=", 70, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* EQUAL, */ {"<>", 70, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* NOT_EQUAL */ {"<=", 80, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* LESS_OR_EQUAL */ {">=", 80, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* GREATER_OR_EQUAL */ {"<", 80, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* LESS */ {">", 80, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* GREATER */ {">>", 90, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* SHIFT_RIGHT */ {"<<", 90, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* SHIFT_LEFT */ {"-", 100, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* SUBSTRACTION */ {"+", 100, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* ADDITION */ {"%", 110, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* REMAINDER */ {"/", 110, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* DIVISION */ {"*", 110, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT}, /* MULTIPLICATION */ {"**", 120, FALSE, 2, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* POWER */ {"minus", 130, FALSE, 1, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* MINUS */ {"plus", 130, FALSE, 1, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* PLUS */ {"~", 130, FALSE, 1, ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT}, /* BITWISE_NOT */ {"sin", 200, TRUE, 1, 0}, /* FUNCTION_SIN */ {"cos", 200, TRUE, 1, 0}, /* FUNCTION_COS */ {"sgn", 200, FALSE, 1, 0}, /* FUNCTION_SGN */ {"neg", 200, FALSE, 1, 0}, /* FUNCTION_NEG */ {"atan", 200, TRUE, 1, 0}, /* FUNCTION_ATAN */ {"tan" , 200, TRUE, 1, 0}, /* FUNCTION_TAN */ {"abs" , 200, TRUE, 1, 0}, /* FUNCTION_ABS */ {"exp" , 200, TRUE, 1, 0}, /* FUNCTION_EXP */ {"ln", 200, TRUE, 1, 0}, /* FUNCTION_LN */ {"lg", 200, TRUE, 1, 0}, /* FUNCTION_LG */ {"sqrt", 200, TRUE, 1, 0}, /* FUNCTION_SQRT */ {"trunc", 200, TRUE, 1, 0}, /* FUNCTION_TRUNC */ {"round", 200, TRUE, 1, 0}, /* FUNCTION_ROUND */ {"floor", 200, TRUE, 1, 0}, /* FUNCTION_FLOOR */ {"ceil", 200, TRUE, 1, 0}, /* FUNCTION_CEIL */ {"asin", 200, TRUE, 1, 0}, /* FUNCTION_ASIN */ {"acos", 200, TRUE, 1, 0}, /* FUNCTION_ACOS */ {")", 990, FALSE, 0, 0}, /* RIGHT_PARENTHESIS */ {"(", -1, FALSE, 0, 0}, /* LEFT_PARENTHESIS */ {"int64", 200, FALSE, 0, 0}, /* CONSTANT_INT64 */ {"double", 200, FALSE, 0, 0}, /* CONSTANT_DOUBLE */ {"var", 200, FALSE, 0, 0}, /* VARIABLE */ }; typedef struct { ArvEvaluatorTokenId token_id; gint32 parenthesis_level; union { double v_double; gint64 v_int64; char * name; } data; } ArvEvaluatorToken; typedef struct { gint32 parenthesis_level; ArvValue value; } ArvEvaluatorValuesStackItem; static ArvEvaluatorToken * arv_evaluator_token_new (ArvEvaluatorTokenId token_id) { ArvEvaluatorToken *token = g_new0 (ArvEvaluatorToken, 1); token->token_id = token_id; return token; } static ArvEvaluatorToken * arv_evaluator_token_new_double (double v_double) { ArvEvaluatorToken *token = arv_evaluator_token_new (ARV_EVALUATOR_TOKEN_CONSTANT_DOUBLE); token->data.v_double = v_double; return token; } static ArvEvaluatorToken * arv_evaluator_token_new_int64 (double v_int64) { ArvEvaluatorToken *token = arv_evaluator_token_new (ARV_EVALUATOR_TOKEN_CONSTANT_INT64); token->data.v_int64 = v_int64; return token; } static ArvEvaluatorToken * arv_evaluator_token_new_variable (const char *name) { ArvEvaluatorToken *token = arv_evaluator_token_new (ARV_EVALUATOR_TOKEN_VARIABLE); token->data.name = g_strdup (name); return token; } static void arv_evaluator_token_free (ArvEvaluatorToken *token) { if (token == NULL) return; if (token->token_id == ARV_EVALUATOR_TOKEN_VARIABLE) g_free (token->data.name); g_free (token); } static void arv_evaluator_token_debug (ArvEvaluatorToken *token, GHashTable *variables) { ArvValue *value; g_return_if_fail (token != NULL); switch (token->token_id) { case ARV_EVALUATOR_TOKEN_VARIABLE: value = g_hash_table_lookup (variables, token->data.name); if (value != NULL && arv_value_holds_double (value)) arv_debug_evaluator ("(var) %s = %g (double)", token->data.name, arv_value_get_double (value)); else if (value != NULL && arv_value_holds_int64 (value)) arv_debug_evaluator ("(var) %s = 0x%016" G_GINT64_MODIFIER "x %" G_GINT64_FORMAT" (int64)", token->data.name, arv_value_get_int64 (value), arv_value_get_int64 (value)); else arv_debug_evaluator ("(var) %s not found", token->data.name); break; case ARV_EVALUATOR_TOKEN_CONSTANT_INT64: arv_debug_evaluator ("(int64) %" G_GINT64_FORMAT, token->data.v_int64); break; case ARV_EVALUATOR_TOKEN_CONSTANT_DOUBLE: arv_debug_evaluator ("(double) %g", token->data.v_double); break; default: arv_debug_evaluator ("(operator) %s", arv_evaluator_token_infos[token->token_id].tag); } } static gboolean arv_evaluator_token_is_variable (ArvEvaluatorToken *token) { return (token != NULL && token->token_id == ARV_EVALUATOR_TOKEN_VARIABLE); } static gboolean arv_evaluator_token_is_operand (ArvEvaluatorToken *token) { return (token != NULL && token->token_id > ARV_EVALUATOR_TOKEN_LEFT_PARENTHESIS); } static gboolean arv_evaluator_token_is_operator (ArvEvaluatorToken *token) { return (token != NULL && token->token_id > ARV_EVALUATOR_TOKEN_UNKNOWN && token->token_id < ARV_EVALUATOR_TOKEN_RIGHT_PARENTHESIS); } static gboolean arv_evaluator_token_is_comma (ArvEvaluatorToken *token) { return (token != NULL && token->token_id == ARV_EVALUATOR_TOKEN_COMMA); } static gboolean arv_evaluator_token_is_left_parenthesis (ArvEvaluatorToken *token) { return (token != NULL && token->token_id == ARV_EVALUATOR_TOKEN_LEFT_PARENTHESIS); } static gboolean arv_evaluator_token_is_right_parenthesis (ArvEvaluatorToken *token) { return (token != NULL && token->token_id == ARV_EVALUATOR_TOKEN_RIGHT_PARENTHESIS); } static gboolean arv_evaluator_token_compare_precedence (ArvEvaluatorToken *a, ArvEvaluatorToken *b) { gint a_precedence; gint b_precedence; ArvEvaluatorTokenAssociativity a_associativity; if (a == NULL || b == NULL || a->token_id >= G_N_ELEMENTS (arv_evaluator_token_infos) || b->token_id >= G_N_ELEMENTS (arv_evaluator_token_infos)) return FALSE; a_precedence = arv_evaluator_token_infos[a->token_id].precedence; b_precedence = arv_evaluator_token_infos[b->token_id].precedence; a_associativity = arv_evaluator_token_infos[a->token_id].associativity; return (((a_precedence <= b_precedence) && a_associativity == ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_LEFT_TO_RIGHT) || ((a_precedence < b_precedence) && a_associativity == ARV_EVALUATOR_TOKEN_ASSOCIATIVITY_RIGHT_TO_LEFT)); } static ArvEvaluatorToken * arv_get_next_token (char **expression, gboolean previous_token_was_operand, gboolean previous_token_was_right_parenthesis) { ArvEvaluatorToken *token = NULL; ArvEvaluatorTokenId token_id = ARV_EVALUATOR_TOKEN_UNKNOWN; g_return_val_if_fail (expression != NULL && *expression != NULL, NULL); arv_str_skip_spaces (expression); if (**expression == '\0') return NULL; if (g_ascii_isdigit (**expression)) { char *end; gint64 v_int64; double v_double; ptrdiff_t length_int64; ptrdiff_t length_double; v_int64 = g_ascii_strtoll (*expression, &end, 0); length_int64 = end - *expression; end = *expression; arv_str_parse_double (&end, &v_double); length_double = end - *expression; if (length_double > 0 || length_int64 > 0) { if (length_double > length_int64) { token = arv_evaluator_token_new_double (v_double); *expression += length_double; } else { token = arv_evaluator_token_new_int64 (v_int64); *expression += length_int64; } } } else if (g_ascii_isalpha (**expression) || **expression=='_' || **expression=='.') { char *end = *expression; ptrdiff_t token_length; while (g_ascii_isalnum (*end) || *end == '_' || *end == '.') end++; token_length = end - *expression; if (token_length == 2) { if (g_ascii_strncasecmp ("ln", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_LN; else if (g_ascii_strncasecmp ("lg", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_LG; } else if (token_length == 3) { if (g_ascii_strncasecmp ("sin", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_SIN; else if (g_ascii_strncasecmp ("cos", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_COS; else if (g_ascii_strncasecmp ("sgn", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_SGN; else if (g_ascii_strncasecmp ("neg", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_NEG; else if (g_ascii_strncasecmp ("tan", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_TAN; else if (g_ascii_strncasecmp ("abs", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_ABS; else if (g_ascii_strncasecmp ("exp", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_EXP; } else if (token_length == 4) { if (g_ascii_strncasecmp ("atan", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_ATAN; else if (g_ascii_strncasecmp ("sqrt", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_SQRT; else if (g_ascii_strncasecmp ("ceil", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_CEIL; else if (g_ascii_strncasecmp ("asin", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_ASIN; else if (g_ascii_strncasecmp ("acos", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_ACOS; } else if (token_length == 5) { if (g_ascii_strncasecmp ("trunc", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_TRUNC; else if (g_ascii_strncasecmp ("round", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_ROUND; else if (g_ascii_strncasecmp ("floor", *expression, token_length) == 0) token_id = ARV_EVALUATOR_TOKEN_FUNCTION_FLOOR; } if (token_id != ARV_EVALUATOR_TOKEN_UNKNOWN) token = arv_evaluator_token_new (token_id); else { char *name = g_strndup (*expression, token_length); token = arv_evaluator_token_new_variable (name); g_free (name); } *expression = end; } else { switch (**expression) { case '(': token_id = ARV_EVALUATOR_TOKEN_LEFT_PARENTHESIS; break; case ')': token_id = ARV_EVALUATOR_TOKEN_RIGHT_PARENTHESIS; break; case ',': token_id = ARV_EVALUATOR_TOKEN_COMMA; break; case '?': token_id = ARV_EVALUATOR_TOKEN_TERNARY_QUESTION_MARK; break; case ':': token_id = ARV_EVALUATOR_TOKEN_TERNARY_COLON; break; case '+': if (previous_token_was_operand || previous_token_was_right_parenthesis) token_id = ARV_EVALUATOR_TOKEN_ADDITION; else token_id = ARV_EVALUATOR_TOKEN_PLUS; break; case '-': if (previous_token_was_operand || previous_token_was_right_parenthesis) token_id = ARV_EVALUATOR_TOKEN_SUBSTRACTION; else token_id = ARV_EVALUATOR_TOKEN_MINUS; break; case '*': if ((*expression)[1] == '*') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_POWER; } else token_id = ARV_EVALUATOR_TOKEN_MULTIPLICATION; break; case '/': token_id = ARV_EVALUATOR_TOKEN_DIVISION; break; case '%': token_id = ARV_EVALUATOR_TOKEN_REMAINDER; break; case '&': if ((*expression)[1] == '&') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_LOGICAL_AND; } else token_id = ARV_EVALUATOR_TOKEN_BITWISE_AND; break; case '|': if ((*expression)[1] == '|') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_LOGICAL_OR; } else token_id = ARV_EVALUATOR_TOKEN_BITWISE_OR; break; case '^': token_id = ARV_EVALUATOR_TOKEN_BITWISE_XOR; break; case '~': token_id = ARV_EVALUATOR_TOKEN_BITWISE_NOT; break; case '<': if ((*expression)[1] == '>') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_NOT_EQUAL; } else if ((*expression)[1] == '<') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_SHIFT_LEFT; } else if ((*expression)[1] == '=') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_LESS_OR_EQUAL; } else token_id = ARV_EVALUATOR_TOKEN_LESS; break; case '>': if ((*expression)[1] == '>') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_SHIFT_RIGHT; } else if ((*expression)[1] == '=') { (*expression)++; token_id = ARV_EVALUATOR_TOKEN_GREATER_OR_EQUAL; } else token_id = ARV_EVALUATOR_TOKEN_GREATER; break; case '=': token_id = ARV_EVALUATOR_TOKEN_EQUAL; break; } if (token_id != ARV_EVALUATOR_TOKEN_UNKNOWN) { (*expression)++; token = arv_evaluator_token_new (token_id); } } return token; } static double round_with_precision (double x, gint64 precision) { double pow_multiplier = pow(10, precision); return (round (x*pow_multiplier))/pow_multiplier; } static int get_arguments_count(ArvEvaluatorValuesStackItem * stack, int current_index) { int arguments_count = 0; int index = current_index; int parenthesis_level = stack[index].parenthesis_level; while ((index>=0) && (stack[index].parenthesis_level>=parenthesis_level)){ index--; arguments_count++; } return arguments_count; } static ArvEvaluatorStatus evaluate (GSList *token_stack, GHashTable *variables, gint64 *v_int64, double *v_double) { ArvEvaluatorToken *token; ArvEvaluatorStatus status; GSList *iter; ArvEvaluatorValuesStackItem stack[ARV_EVALUATOR_STACK_SIZE]; ArvValue *value; int index = -1; gboolean integer_mode; g_assert (v_int64 != NULL || v_double != NULL); integer_mode = v_int64 != NULL; for (iter = token_stack; iter != NULL; iter = iter->next) { int actual_arguments_count; token = iter->data; if (index < (arv_evaluator_token_infos[token->token_id].n_args - 1)) { status = ARV_EVALUATOR_STATUS_MISSING_ARGUMENTS; goto CLEANUP; } if (arv_evaluator_token_infos[token->token_id].double_only && integer_mode) { status = ARV_EVALUATOR_STATUS_INVALID_DOUBLE_FUNCTION; goto CLEANUP; } if (index >= ARV_EVALUATOR_STACK_SIZE - 1) { status = ARV_EVALUATOR_STATUS_STACK_OVERFLOW; goto CLEANUP; } arv_evaluator_token_debug (token, variables); actual_arguments_count = arv_evaluator_token_infos[token->token_id].n_args; switch (token->token_id) { case ARV_EVALUATOR_TOKEN_LOGICAL_AND: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) && arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_LOGICAL_OR: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) || arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_BITWISE_NOT: arv_value_set_int64 (&stack[index].value, ~arv_value_get_int64 (&stack[index].value)); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_BITWISE_AND: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) & arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_BITWISE_OR: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) | arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_BITWISE_XOR: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) ^ arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_EQUAL: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) == arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) == arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_NOT_EQUAL: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) != arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) != arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_LESS_OR_EQUAL: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) <= arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) <= arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_GREATER_OR_EQUAL: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) >= arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) >= arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_LESS: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) < arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) < arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_GREATER: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) > arv_value_get_int64 (&stack[index].value)); else arv_value_set_int64 (&stack[index - 1].value, arv_value_get_double (&stack[index-1].value) > arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_SHIFT_RIGHT: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) >> arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_SHIFT_LEFT: arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) << arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_SUBSTRACTION: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) - arv_value_get_int64 (&stack[index].value)); else arv_value_set_double (&stack[index-1].value, arv_value_get_double (&stack[index-1].value) - arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_ADDITION: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) + arv_value_get_int64 (&stack[index].value)); else arv_value_set_double (&stack[index-1].value, arv_value_get_double (&stack[index-1].value) + arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_REMAINDER: if (arv_value_get_int64 (&stack[index].value) == 0) { status = ARV_EVALUATOR_STATUS_DIVISION_BY_ZERO; goto CLEANUP; } arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) % arv_value_get_int64 (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_DIVISION: if (integer_mode) { if (arv_value_get_int64 (&stack[index].value) == 0) { status = ARV_EVALUATOR_STATUS_DIVISION_BY_ZERO; goto CLEANUP; } arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) / arv_value_get_int64 (&stack[index].value)); } else { if (arv_value_get_double (&stack[index].value) == 0.0) { status = ARV_EVALUATOR_STATUS_DIVISION_BY_ZERO; goto CLEANUP; } arv_value_set_double (&stack[index-1].value, arv_value_get_double (&stack[index-1].value) / arv_value_get_double (&stack[index].value)); } stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_MULTIPLICATION: if (integer_mode || (arv_value_holds_int64 (&stack[index-1].value) && arv_value_holds_int64 (&stack[index].value))) arv_value_set_int64 (&stack[index-1].value, arv_value_get_int64 (&stack[index-1].value) * arv_value_get_int64 (&stack[index].value)); else arv_value_set_double (&stack[index-1].value, arv_value_get_double (&stack[index-1].value) * arv_value_get_double (&stack[index].value)); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_POWER: if (integer_mode) arv_value_set_int64 (&stack[index-1].value, pow (arv_value_get_int64(&stack[index-1].value), arv_value_get_int64(&stack[index].value))); else arv_value_set_double (&stack[index-1].value, pow (arv_value_get_double(&stack[index-1].value), arv_value_get_double(&stack[index].value))); stack[index-1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_MINUS: if (integer_mode || arv_value_holds_int64 (&stack[index].value)) arv_value_set_int64 (&stack[index].value, -arv_value_get_int64 (&stack[index].value)); else arv_value_set_double (&stack[index].value, -arv_value_get_double (&stack[index].value)); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_PLUS: break; case ARV_EVALUATOR_TOKEN_FUNCTION_SIN: arv_value_set_double (&stack[index].value, sin (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_COS: arv_value_set_double (&stack[index].value, cos (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_SGN: if (integer_mode || arv_value_holds_int64 (&stack[index].value)) { gint64 int_value = arv_value_get_int64 (&stack[index].value); if (int_value < 0) arv_value_set_int64 (&stack[index].value, -1); else if (int_value > 0) arv_value_set_int64 (&stack[index].value, 1); else arv_value_set_int64 (&stack[index].value, 0); } else { double dbl_value = arv_value_get_double (&stack[index].value); if (dbl_value < 0.0) arv_value_set_int64 (&stack[index].value, -1); else if (dbl_value > 0.0) arv_value_set_int64 (&stack[index].value, 1); else arv_value_set_int64 (&stack[index].value, 0); } stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_NEG: if (integer_mode || arv_value_holds_int64 (&stack[index].value)) arv_value_set_int64 (&stack[index].value, -arv_value_get_int64 (&stack[index].value)); else arv_value_set_double (&stack[index].value, -arv_value_get_double (&stack[index].value)); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_ATAN: arv_value_set_double (&stack[index].value, atan (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_TAN: arv_value_set_double (&stack[index].value, tan (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_ABS: if (arv_value_holds_double (&stack[index].value)) arv_value_set_double (&stack[index].value, fabs (arv_value_get_double (&stack[index].value))); else arv_value_set_int64 (&stack[index].value, llabs (arv_value_get_int64 (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_EXP: arv_value_set_double (&stack[index].value, exp (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_LN: arv_value_set_double (&stack[index].value, log (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_LG: arv_value_set_double (&stack[index].value, log10 (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_SQRT: arv_value_set_double (&stack[index].value, sqrt (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_TRUNC: if (arv_value_get_double (&stack[index].value) > 0.0) arv_value_set_double (&stack[index].value, floor (arv_value_get_double (&stack[index].value))); else arv_value_set_double (&stack[index].value, ceil (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_ROUND: actual_arguments_count = get_arguments_count(stack, index); if (actual_arguments_count==1) { arv_value_set_double(&stack[index].value, round(arv_value_get_double(&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; } else if (actual_arguments_count==2) { arv_value_set_double(&stack[index - 1].value, round_with_precision(arv_value_get_double(&stack[index - 1].value), arv_value_get_int64(&stack[index].value))); stack[index - 1].parenthesis_level = token->parenthesis_level; } else { if (actual_arguments_count<1) { status = ARV_EVALUATOR_STATUS_MISSING_ARGUMENTS; } else { status = ARV_EVALUATOR_STATUS_REMAINING_OPERANDS; } goto CLEANUP; } break; case ARV_EVALUATOR_TOKEN_FUNCTION_FLOOR: arv_value_set_double (&stack[index].value, floor (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_CEIL: arv_value_set_double (&stack[index].value, ceil (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_ASIN: arv_value_set_double (&stack[index].value, asin (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_FUNCTION_ACOS: arv_value_set_double (&stack[index].value, acos (arv_value_get_double (&stack[index].value))); stack[index].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_CONSTANT_INT64: arv_value_set_int64 (&stack[index+1].value, token->data.v_int64); stack[index+1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_CONSTANT_DOUBLE: if (integer_mode) arv_value_set_int64 (&stack[index+1].value, token->data.v_double); else arv_value_set_double (&stack[index+1].value, token->data.v_double); stack[index+1].parenthesis_level = token->parenthesis_level; break; case ARV_EVALUATOR_TOKEN_VARIABLE: value = g_hash_table_lookup (variables, token->data.name); if (value != NULL) { arv_value_copy (&stack[index+1].value, value); stack[index+1].parenthesis_level = token->parenthesis_level; } else { status = ARV_EVALUATOR_STATUS_UNKNOWN_VARIABLE; goto CLEANUP; } break; case ARV_EVALUATOR_TOKEN_TERNARY_COLON: break; case ARV_EVALUATOR_TOKEN_TERNARY_QUESTION_MARK: if (arv_value_get_int64 (&stack[index-2].value) != 0) { arv_value_copy(&stack[index - 2].value, &stack[index - 1].value); stack[index - 2].parenthesis_level = stack[index - 1].parenthesis_level; } else { arv_value_copy(&stack[index - 2].value, &stack[index].value); stack[index - 2].parenthesis_level = stack[index].parenthesis_level; } break; default: status = ARV_EVALUATOR_STATUS_UNKNOWN_OPERATOR; goto CLEANUP; break; } index = index - actual_arguments_count + 1; } if (index != 0) { status = ARV_EVALUATOR_STATUS_REMAINING_OPERANDS; goto CLEANUP; } if (v_double != NULL) *v_double = arv_value_get_double (&stack[0].value); if (v_int64 != NULL) *v_int64 = arv_value_get_int64 (&stack[0].value); if (arv_value_holds_int64 (&stack[0].value)) arv_debug_evaluator ("[Evaluator::evaluate] Result = (int64) %" G_GINT64_FORMAT, arv_value_get_int64 (&stack[0].value)); else arv_debug_evaluator ("[Evaluator::evaluate] Result = (double) %g", arv_value_get_double (&stack[0].value)); return ARV_EVALUATOR_STATUS_SUCCESS; CLEANUP: if (v_double != NULL) *v_double = 0.0; if (v_int64 != NULL) *v_int64 = 0.0; return status; } typedef struct { int count; GSList *token_stack; GSList *operator_stack; GSList *garbage_stack; gboolean in_sub_expression; gboolean previous_token_was_operand; gboolean previous_token_was_right_parenthesis; } ArvEvaluatorParserState; static ArvEvaluatorStatus parse_to_stacks (ArvEvaluator *evaluator, char *expression, ArvEvaluatorParserState *state) { ArvEvaluatorToken *token; ArvEvaluatorStatus status; gboolean token_found; gint32 current_parenthesis_level = 0; if (expression == NULL) return ARV_EVALUATOR_STATUS_EMPTY_EXPRESSION; /* Dijkstra's "shunting yard" algorithm */ /* http://en.wikipedia.org/wiki/Shunting-yard_algorithm */ do { token = arv_get_next_token (&expression, state->previous_token_was_operand, state->previous_token_was_right_parenthesis); if (token != NULL) { token_found = TRUE; token->parenthesis_level = current_parenthesis_level; state->previous_token_was_operand = arv_evaluator_token_is_operand (token); state->previous_token_was_right_parenthesis = arv_evaluator_token_is_right_parenthesis (token); if (arv_evaluator_token_is_variable (token)) { if (g_hash_table_lookup_extended (evaluator->priv->constants, token->data.name, NULL, NULL)) { const char *constant; constant = g_hash_table_lookup (evaluator->priv->constants, token->data.name); if (constant != NULL) { arv_evaluator_token_free (token); token = arv_get_next_token ((char **) &constant, FALSE, FALSE); if (token != NULL) state->token_stack = g_slist_prepend (state->token_stack, token); } else { status = ARV_EVALUATOR_STATUS_UNKNOWN_CONSTANT; goto CLEANUP; } } else if (g_hash_table_lookup_extended (evaluator->priv->sub_expressions, token->data.name, NULL, NULL)) { const char *sub_expression; sub_expression = g_hash_table_lookup (evaluator->priv->sub_expressions, token->data.name); if (sub_expression != NULL) { char *string; if (state->in_sub_expression) { status = ARV_EVALUATOR_STATUS_FORBIDDEN_RECUSRION; goto CLEANUP; } string = g_strdup_printf ("(%s)", sub_expression); state->in_sub_expression = TRUE; status = parse_to_stacks (evaluator, string, state); state->in_sub_expression = FALSE; g_free (string); if (status != ARV_EVALUATOR_STATUS_SUCCESS) { goto CLEANUP; } arv_evaluator_token_free (token); token = NULL; } else { status = ARV_EVALUATOR_STATUS_UNKNOWN_SUB_EXPRESSION; goto CLEANUP; } } else { state->token_stack = g_slist_prepend (state->token_stack, token); } } else if (arv_evaluator_token_is_operand (token)) { state->token_stack = g_slist_prepend (state->token_stack, token); } else if (arv_evaluator_token_is_comma (token)) { while (state->operator_stack != NULL && !arv_evaluator_token_is_left_parenthesis (state->operator_stack->data)) { state->token_stack = g_slist_prepend (state->token_stack, state->operator_stack->data); state->operator_stack = g_slist_delete_link (state->operator_stack, state->operator_stack); } if (state->operator_stack == NULL || !arv_evaluator_token_is_left_parenthesis (state->operator_stack->data)) { status = ARV_EVALUATOR_STATUS_PARENTHESES_MISMATCH; goto CLEANUP; } state->garbage_stack = g_slist_prepend (state->garbage_stack, token); } else if (arv_evaluator_token_is_operator (token)) { while (state->operator_stack != NULL && arv_evaluator_token_compare_precedence (token, state->operator_stack->data)) { state->token_stack = g_slist_prepend (state->token_stack, state->operator_stack->data); state->operator_stack = g_slist_delete_link (state->operator_stack, state->operator_stack); } state->operator_stack = g_slist_prepend (state->operator_stack, token); } else if (arv_evaluator_token_is_left_parenthesis (token)) { current_parenthesis_level+=1; state->operator_stack = g_slist_prepend (state->operator_stack, token); } else if (arv_evaluator_token_is_right_parenthesis (token)) { current_parenthesis_level-=1; while (state->operator_stack != NULL && !arv_evaluator_token_is_left_parenthesis (state->operator_stack->data)) { state->token_stack = g_slist_prepend (state->token_stack, state->operator_stack->data); state->operator_stack = g_slist_delete_link (state->operator_stack, state->operator_stack); } if (state->operator_stack == NULL) { status = ARV_EVALUATOR_STATUS_PARENTHESES_MISMATCH; goto CLEANUP; } state->garbage_stack = g_slist_prepend (state->garbage_stack, token); state->garbage_stack = g_slist_prepend (state->garbage_stack, state->operator_stack->data); state->operator_stack = g_slist_delete_link (state->operator_stack, state->operator_stack); } else { status = ARV_EVALUATOR_STATUS_SYNTAX_ERROR; goto CLEANUP; } (state->count)++; } else if (*expression != '\0') { status = ARV_EVALUATOR_STATUS_SYNTAX_ERROR; goto CLEANUP; } else { token_found = FALSE; } } while (token_found); return ARV_EVALUATOR_STATUS_SUCCESS; CLEANUP: if (token != NULL) arv_evaluator_token_free (token); return status; } static void free_rpn_stack (ArvEvaluator *evaluator) { GSList *iter; for (iter = evaluator->priv->rpn_stack; iter != NULL; iter = iter->next) arv_evaluator_token_free (iter->data); g_slist_free (evaluator->priv->rpn_stack); evaluator->priv->rpn_stack = NULL; } static ArvEvaluatorStatus parse_expression (ArvEvaluator *evaluator) { ArvEvaluatorParserState state; ArvEvaluatorStatus status; GSList *iter; int count; state.count =0; state.previous_token_was_operand = FALSE; state.previous_token_was_right_parenthesis = FALSE; state.token_stack = NULL; state.operator_stack = NULL; state.garbage_stack = NULL; state.in_sub_expression = FALSE; free_rpn_stack (evaluator); arv_debug_evaluator ("[Evaluator::parse_expression] %s", evaluator->priv->expression); status = parse_to_stacks (evaluator, evaluator->priv->expression, &state); if (status != ARV_EVALUATOR_STATUS_SUCCESS) goto CLEANUP; arv_debug_evaluator ("[Evaluator::parse_expression] Found %d items in expression", state.count); while (state.operator_stack != NULL) { if (arv_evaluator_token_is_left_parenthesis (state.operator_stack->data)) { status = ARV_EVALUATOR_STATUS_PARENTHESES_MISMATCH; goto CLEANUP; } state.token_stack = g_slist_prepend (state.token_stack, state.operator_stack->data); state.operator_stack = g_slist_delete_link (state.operator_stack, state.operator_stack); } evaluator->priv->rpn_stack = g_slist_reverse (state.token_stack); for (iter = state.garbage_stack, count = 0; iter != NULL; iter = iter->next, count++) arv_evaluator_token_free (iter->data); g_slist_free (state.garbage_stack); arv_debug_evaluator ("[Evaluator::parse_expression] %d items in garbage list", count); arv_debug_evaluator ("[Evaluator::parse_expression] %d items in token list", g_slist_length (evaluator->priv->rpn_stack)); return evaluator->priv->rpn_stack == NULL ? ARV_EVALUATOR_STATUS_EMPTY_EXPRESSION : ARV_EVALUATOR_STATUS_SUCCESS; CLEANUP: for (iter = state.garbage_stack; iter != NULL; iter = iter->next) arv_evaluator_token_free (iter->data); g_slist_free (state.garbage_stack); for (iter = state.token_stack; iter != NULL; iter = iter->next) arv_evaluator_token_free (iter->data); g_slist_free (state.token_stack); for (iter = state.operator_stack; iter != NULL; iter = iter->next) arv_evaluator_token_free (iter->data); g_slist_free (state.operator_stack); return status; } static void arv_evaluator_set_error (GError **error, ArvEvaluatorStatus status) { g_set_error (error, g_quark_from_string ("Aravis"), status, "Parsing error (%s)", arv_evaluator_status_strings [MIN (status, G_N_ELEMENTS (arv_evaluator_status_strings)-1)]); arv_warning_evaluator ("[Evaluator::set_error] Error '%s'", arv_evaluator_status_strings [MIN (status, G_N_ELEMENTS (arv_evaluator_status_strings)-1)]); } double arv_evaluator_evaluate_as_double (ArvEvaluator *evaluator, GError **error) { ArvEvaluatorStatus status; double value; g_return_val_if_fail (ARV_IS_EVALUATOR (evaluator), 0.0); arv_debug_evaluator ("[Evaluator::evaluate_as_double] Expression = '%s'", evaluator->priv->expression); if (evaluator->priv->parsing_status == ARV_EVALUATOR_STATUS_NOT_PARSED) { evaluator->priv->parsing_status = parse_expression (evaluator); arv_debug_evaluator ("[Evaluator::evaluate_as_double] Parsing status = %d", evaluator->priv->parsing_status); } if (evaluator->priv->parsing_status != ARV_EVALUATOR_STATUS_SUCCESS) { arv_evaluator_set_error (error, evaluator->priv->parsing_status); return 0.0; } status = evaluate (evaluator->priv->rpn_stack, evaluator->priv->variables, NULL, &value); if (status != ARV_EVALUATOR_STATUS_SUCCESS) { arv_evaluator_set_error (error, status); return 0.0; } return value; } gint64 arv_evaluator_evaluate_as_int64 (ArvEvaluator *evaluator, GError **error) { ArvEvaluatorStatus status; gint64 value; g_return_val_if_fail (ARV_IS_EVALUATOR (evaluator), 0.0); arv_debug_evaluator ("[Evaluator::evaluate_as_int64] Expression = '%s'", evaluator->priv->expression); if (evaluator->priv->parsing_status == ARV_EVALUATOR_STATUS_NOT_PARSED) { evaluator->priv->parsing_status = parse_expression (evaluator); arv_debug_evaluator ("[Evaluator::evaluate_as_int64] Parsing status = %d", evaluator->priv->parsing_status); } if (evaluator->priv->parsing_status != ARV_EVALUATOR_STATUS_SUCCESS) { arv_evaluator_set_error (error, evaluator->priv->parsing_status); return 0.0; } status = evaluate (evaluator->priv->rpn_stack, evaluator->priv->variables, &value, NULL); if (status != ARV_EVALUATOR_STATUS_SUCCESS) { arv_evaluator_set_error (error, status); return 0.0; } return value; } void arv_evaluator_set_expression (ArvEvaluator *evaluator, const char *expression) { g_return_if_fail (ARV_IS_EVALUATOR (evaluator)); if (g_strcmp0 (expression, evaluator->priv->expression) == 0) return; g_free (evaluator->priv->expression); evaluator->priv->expression = NULL; if (expression == NULL) { evaluator->priv->parsing_status = ARV_EVALUATOR_STATUS_EMPTY_EXPRESSION; return; } evaluator->priv->parsing_status = ARV_EVALUATOR_STATUS_NOT_PARSED; evaluator->priv->expression = g_strdup (expression); } const char * arv_evaluator_get_expression (ArvEvaluator *evaluator) { g_return_val_if_fail (ARV_IS_EVALUATOR (evaluator), NULL); return evaluator->priv->expression; } /** * arv_evaluator_set_sub_expression: * @evaluator:a #ArvEvaluator * @name: sub-expression name * @expression: (allow-none): sub-pexression formula * * Assign a formula to a sub-expression. If @expression == %NULL, the sub-expression previously assigned to @name will be removed. * A sub-expression may not reference another sub-expression. * * Since: 0.6.0 */ void arv_evaluator_set_sub_expression (ArvEvaluator *evaluator, const char *name, const char *expression) { const char *old_expression; g_return_if_fail (ARV_IS_EVALUATOR (evaluator)); if (name == NULL) return; old_expression = g_hash_table_lookup (evaluator->priv->sub_expressions, name); if (old_expression != NULL && g_strcmp0 (old_expression, expression) == 0) return; if (expression != NULL) g_hash_table_replace (evaluator->priv->sub_expressions, g_strdup (name), g_strdup (expression)); else g_hash_table_remove (evaluator->priv->sub_expressions, name); evaluator->priv->parsing_status = ARV_EVALUATOR_STATUS_NOT_PARSED; arv_debug_evaluator ("[Evaluator::set_sub_expression] %s = %s", name, expression); } /** * arv_evaluator_get_sub_expression: * @evaluator: a #ArvEvaluator * @name: sub-expression name * * Returns: The formula of the sub-expression corresponding to @name, %NULL if not defined. * * Since: 0.6.0 */ const char * arv_evaluator_get_sub_expression (ArvEvaluator *evaluator, const char *name) { g_return_val_if_fail (ARV_IS_EVALUATOR (evaluator), NULL); if (name == NULL) return NULL; return g_hash_table_lookup (evaluator->priv->sub_expressions, name); } /** * arv_evaluator_set_constant: * @evaluator:a #ArvEvaluator * @name: constant name * @constant: (allow-none): constant as a string * * Assign a string to a constant. If @constant == %NULL, the constant previously assigned to @name will be removed. * * Since: 0.6.0 */ void arv_evaluator_set_constant (ArvEvaluator *evaluator, const char *name, const char *constant) { const char *old_constant; g_return_if_fail (ARV_IS_EVALUATOR (evaluator)); if (name == NULL) return; old_constant = g_hash_table_lookup (evaluator->priv->constants, name); if (old_constant != NULL && g_strcmp0 (old_constant, constant) == 0) return; if (constant != NULL) g_hash_table_replace (evaluator->priv->constants, g_strdup (name), g_strdup (constant)); else g_hash_table_remove (evaluator->priv->constants, name); evaluator->priv->parsing_status = ARV_EVALUATOR_STATUS_NOT_PARSED; arv_debug_evaluator ("[Evaluator::set_constant] %s = %s", name, constant); } /** * arv_evaluator_get_constant: * @evaluator: a #ArvEvaluator * @name: constant name * * Returns: The formula of the constant corresponding to @name, %NULL if not defined. * * Since: 0.6.0 */ const char * arv_evaluator_get_constant (ArvEvaluator *evaluator, const char *name) { g_return_val_if_fail (ARV_IS_EVALUATOR (evaluator), NULL); if (name == NULL) return NULL; return g_hash_table_lookup (evaluator->priv->constants, name); } void arv_evaluator_set_double_variable (ArvEvaluator *evaluator, const char *name, double v_double) { ArvValue *old_value; g_return_if_fail (ARV_IS_EVALUATOR (evaluator)); g_return_if_fail (name != NULL); old_value = g_hash_table_lookup (evaluator->priv->variables, name); if (old_value != NULL && (arv_value_get_double (old_value) == v_double)) return; g_hash_table_replace (evaluator->priv->variables, g_strdup (name), arv_value_new_double (v_double)); arv_debug_evaluator ("[Evaluator::set_double_variable] %s = %g", name, v_double); } void arv_evaluator_set_int64_variable (ArvEvaluator *evaluator, const char *name, gint64 v_int64) { ArvValue *old_value; g_return_if_fail (ARV_IS_EVALUATOR (evaluator)); g_return_if_fail (name != NULL); old_value = g_hash_table_lookup (evaluator->priv->variables, name); if (old_value != NULL && (arv_value_get_int64 (old_value) == v_int64)) return; g_hash_table_replace (evaluator->priv->variables, g_strdup (name), arv_value_new_int64 (v_int64)); arv_debug_evaluator ("[Evaluator::set_int64_variable] %s = %" G_GINT64_FORMAT, name, v_int64); } /** * arv_evaluator_new: * @expression: (allow-none): an evaluator expression * * Creates a new #ArvEvaluator object. The syntax is described in the genicam standard specification. * * Return value: a new #ArvEvaluator object. */ ArvEvaluator * arv_evaluator_new (const char *expression) { ArvEvaluator *evaluator; evaluator = g_object_new (ARV_TYPE_EVALUATOR, NULL); arv_evaluator_set_expression (evaluator, expression); return evaluator; } static void arv_evaluator_init (ArvEvaluator *evaluator) { evaluator->priv = arv_evaluator_get_instance_private (evaluator); evaluator->priv->expression = NULL; evaluator->priv->rpn_stack = NULL; evaluator->priv->variables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) arv_value_free); evaluator->priv->sub_expressions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); evaluator->priv->constants = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); arv_evaluator_set_double_variable (evaluator, "PI", G_PI); arv_evaluator_set_double_variable (evaluator, "E", G_E); } static void arv_evaluator_finalize (GObject *object) { ArvEvaluator *evaluator = ARV_EVALUATOR (object); arv_evaluator_set_expression (evaluator, NULL); g_hash_table_unref (evaluator->priv->variables); g_hash_table_unref (evaluator->priv->sub_expressions); g_hash_table_unref (evaluator->priv->constants); free_rpn_stack (evaluator); G_OBJECT_CLASS (arv_evaluator_parent_class)->finalize (object); } static void arv_evaluator_class_init (ArvEvaluatorClass *evaluator_class) { GObjectClass *object_class = G_OBJECT_CLASS (evaluator_class); object_class->finalize = arv_evaluator_finalize; } aravis-0.8.34/src/arvevaluator.h000066400000000000000000000044661475431451200165660ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_EVALUATOR_H #define ARV_EVALUATOR_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_EVALUATOR (arv_evaluator_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvEvaluator, arv_evaluator, ARV, EVALUATOR, GObject) ARV_API ArvEvaluator * arv_evaluator_new (const char *expression); ARV_API void arv_evaluator_set_expression (ArvEvaluator *evaluator, const char *expression); ARV_API const char * arv_evaluator_get_expression (ArvEvaluator *evaluator); ARV_API void arv_evaluator_set_sub_expression (ArvEvaluator *evaluator, const char *name, const char *expression); ARV_API const char * arv_evaluator_get_sub_expression (ArvEvaluator *evaluator, const char *name); ARV_API void arv_evaluator_set_constant (ArvEvaluator *evaluator, const char *name, const char *constant); ARV_API const char * arv_evaluator_get_constant (ArvEvaluator *evaluator, const char *name); ARV_API double arv_evaluator_evaluate_as_double (ArvEvaluator *evaluator, GError **error); ARV_API gint64 arv_evaluator_evaluate_as_int64 (ArvEvaluator *evaluator, GError **error); ARV_API void arv_evaluator_set_double_variable (ArvEvaluator *evaluator, const char *name, double v_double); ARV_API void arv_evaluator_set_int64_variable (ArvEvaluator *evaluator, const char *name, gint64 v_int64); G_END_DECLS #endif aravis-0.8.34/src/arvfakecamera.c000066400000000000000000000771031475431451200166340ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvfakecamera * @short_description: Fake camera internals * * #ArvFakeCamera is a class that simulate a real camera, which provides * methods for the implementation of #ArvFakeDevice and #ArvFakeStream. * * arv-fake-gv-camera is a GV camera simulator based on this class. */ #include #include #include #include #include #include #include #include #include #include static char *arv_fake_camera_genicam_filename = NULL; /* * arv_set_fake_camera_genicam_filename: * @filename: path to genicam file * * Sets name of genicam file. This needs to be called prior to * instantiation of the fake camera. This function is not thread safe. */ void arv_set_fake_camera_genicam_filename (const char *filename) { g_clear_pointer (&arv_fake_camera_genicam_filename, g_free); arv_fake_camera_genicam_filename = g_strdup (filename); } static const char * arv_get_fake_camera_genicam_filename (void) { return arv_fake_camera_genicam_filename; } typedef struct { void *memory; char *genicam_xml; size_t genicam_xml_size; char *genicam_xml_url; guint32 frame_id; double trigger_frequency; GMutex fill_pattern_mutex; ArvFakeCameraFillPattern fill_pattern_callback; void *fill_pattern_data; } ArvFakeCameraPrivate; struct _ArvFakeCamera { GObject object; ArvFakeCameraPrivate *priv; }; struct _ArvFakeCameraClass { GObjectClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvFakeCamera, arv_fake_camera, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvFakeCamera)) /* ArvFakeCamera implementation */ gboolean arv_fake_camera_read_memory (ArvFakeCamera *camera, guint32 address, guint32 size, void *buffer) { guint32 read_size; g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), FALSE); g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (size > 0, FALSE); if (address < ARV_FAKE_CAMERA_MEMORY_SIZE) { read_size = MIN (address + size, ARV_FAKE_CAMERA_MEMORY_SIZE) - address; memcpy (buffer, ((char *) camera->priv->memory) + address, read_size); if (read_size == size) return TRUE; size = size - read_size; address = ARV_FAKE_CAMERA_MEMORY_SIZE; buffer = ((char *) buffer) + read_size; } address -= ARV_FAKE_CAMERA_MEMORY_SIZE; read_size = MIN (address + size, camera->priv->genicam_xml_size) - address; memcpy (buffer, ((char *) camera->priv->genicam_xml) + address, read_size); if (read_size < size) memset (((char *) buffer) + read_size, 0, size - read_size); return TRUE; } gboolean arv_fake_camera_write_memory (ArvFakeCamera *camera, guint32 address, guint32 size, const void *buffer) { g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), FALSE); g_return_val_if_fail (address + size < ARV_FAKE_CAMERA_MEMORY_SIZE + camera->priv->genicam_xml_size, FALSE); g_return_val_if_fail (buffer != NULL, FALSE); g_return_val_if_fail (size > 0, FALSE); /* genicam_data are read only */ if (address + size > ARV_FAKE_CAMERA_MEMORY_SIZE) return FALSE; memcpy (((char *) camera->priv->memory) + address, buffer, size); return TRUE; } /** * arv_fake_camera_read_register: * @camera: a #ArvFakeCamera * @address: the register address * @value: (out): the register value * * Return value: true if the read succeeded, false otherwise */ gboolean arv_fake_camera_read_register (ArvFakeCamera *camera, guint32 address, guint32 *value) { gboolean success; guint32 be_value = 0; g_return_val_if_fail (value != NULL, FALSE); success = arv_fake_camera_read_memory (camera, address, sizeof (*value), &be_value); *value = GUINT32_FROM_BE (be_value); return success; } gboolean arv_fake_camera_write_register (ArvFakeCamera *camera, guint32 address, guint32 value) { guint32 be_value = GUINT32_TO_BE (value); return arv_fake_camera_write_memory (camera, address, sizeof (value), &be_value); } static guint32 _get_register (ArvFakeCamera *camera, guint32 address) { guint32 value; if (address + sizeof (guint32) > ARV_FAKE_CAMERA_MEMORY_SIZE) return 0; value = *((guint32 *) (((char *)(camera->priv->memory) + address))); return GUINT32_FROM_BE (value); } size_t arv_fake_camera_get_payload (ArvFakeCamera *camera) { guint32 width, height, pixel_format; g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), 0); width = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_WIDTH); height = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_HEIGHT); pixel_format = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_PIXEL_FORMAT); return width * height * ARV_PIXEL_FORMAT_BIT_PER_PIXEL(pixel_format)/8; } /** * arv_fake_camera_get_sleep_time_for_next_frame: * @camera: a #ArvFakeCamera * @next_timestamp_us: (out) (optional): the timestamp for the next frame in microseconds * * Return value: the sleep time for the next frame */ guint64 arv_fake_camera_get_sleep_time_for_next_frame (ArvFakeCamera *camera, guint64 *next_timestamp_us) { guint64 time_us; guint64 sleep_time_us; guint64 frame_period_time_us; g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), 0); if (_get_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE) == 1) frame_period_time_us = 1000000L / camera->priv->trigger_frequency; else frame_period_time_us = (guint64) _get_register (camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION_FRAME_PERIOD_US); if (frame_period_time_us == 0) { arv_warning_misc ("Invalid zero frame period, defaulting to 1 second"); frame_period_time_us = 1000000L; } time_us = g_get_real_time (); sleep_time_us = frame_period_time_us - (time_us % frame_period_time_us); if (next_timestamp_us != NULL) *next_timestamp_us = time_us + sleep_time_us; return sleep_time_us; } void arv_fake_camera_wait_for_next_frame (ArvFakeCamera *camera) { g_return_if_fail (ARV_IS_FAKE_CAMERA (camera)); g_usleep (arv_fake_camera_get_sleep_time_for_next_frame (camera, NULL)); } static struct { unsigned char r,g,b; } jet_colormap [] = { {0 , 0 , 132}, {0 , 0 , 136}, {0 , 0 , 140}, {0 , 0 , 144}, {0 , 0 , 148}, {0 , 0 , 152}, {0 , 0 , 156}, {0 , 0 , 160}, {0 , 0 , 164}, {0 , 0 , 168}, {0 , 0 , 172}, {0 , 0 , 176}, {0 , 0 , 180}, {0 , 0 , 184}, {0 , 0 , 188}, {0 , 0 , 192}, {0 , 0 , 196}, {0 , 0 , 200}, {0 , 0 , 204}, {0 , 0 , 208}, {0 , 0 , 212}, {0 , 0 , 216}, {0 , 0 , 220}, {0 , 0 , 224}, {0 , 0 , 228}, {0 , 0 , 232}, {0 , 0 , 236}, {0 , 0 , 240}, {0 , 0 , 244}, {0 , 0 , 248}, {0 , 0 , 252}, {0 , 0 , 255}, {0 , 4 , 255}, {0 , 8 , 255}, {0 , 12 , 255}, {0 , 16 , 255}, {0 , 20 , 255}, {0 , 24 , 255}, {0 , 28 , 255}, {0 , 32 , 255}, {0 , 36 , 255}, {0 , 40 , 255}, {0 , 44 , 255}, {0 , 48 , 255}, {0 , 52 , 255}, {0 , 56 , 255}, {0 , 60 , 255}, {0 , 64 , 255}, {0 , 68 , 255}, {0 , 72 , 255}, {0 , 76 , 255}, {0 , 80 , 255}, {0 , 84 , 255}, {0 , 88 , 255}, {0 , 92 , 255}, {0 , 96 , 255}, {0 , 100 , 255}, {0 , 104 , 255}, {0 , 108 , 255}, {0 , 112 , 255}, {0 , 116 , 255}, {0 , 120 , 255}, {0 , 124 , 255}, {0 , 128 , 255}, {0 , 132 , 255}, {0 , 136 , 255}, {0 , 140 , 255}, {0 , 144 , 255}, {0 , 148 , 255}, {0 , 152 , 255}, {0 , 156 , 255}, {0 , 160 , 255}, {0 , 164 , 255}, {0 , 168 , 255}, {0 , 172 , 255}, {0 , 176 , 255}, {0 , 180 , 255}, {0 , 184 , 255}, {0 , 188 , 255}, {0 , 192 , 255}, {0 , 196 , 255}, {0 , 200 , 255}, {0 , 204 , 255}, {0 , 208 , 255}, {0 , 212 , 255}, {0 , 216 , 255}, {0 , 220 , 255}, {0 , 224 , 255}, {0 , 228 , 255}, {0 , 232 , 255}, {0 , 236 , 255}, {0 , 240 , 255}, {0 , 244 , 255}, {0 , 248 , 255}, {0 , 252 , 255}, {0 , 255 , 255}, {4 , 255 , 252}, {8 , 255 , 248}, {12 , 255 , 244}, {16 , 255 , 240}, {20 , 255 , 236}, {24 , 255 , 232}, {28 , 255 , 228}, {32 , 255 , 224}, {36 , 255 , 220}, {40 , 255 , 216}, {44 , 255 , 212}, {48 , 255 , 208}, {52 , 255 , 204}, {56 , 255 , 200}, {60 , 255 , 196}, {64 , 255 , 192}, {68 , 255 , 188}, {72 , 255 , 184}, {76 , 255 , 180}, {80 , 255 , 176}, {84 , 255 , 172}, {88 , 255 , 168}, {92 , 255 , 164}, {96 , 255 , 160}, {100, 255, 156}, {104, 255, 152}, {108, 255, 148}, {112, 255, 144}, {116, 255, 140}, {120, 255, 136}, {124, 255, 132}, {128, 255, 128}, {132, 255, 124}, {136, 255, 120}, {140, 255, 116}, {144, 255, 112}, {148, 255, 108}, {152, 255, 104}, {156, 255, 100}, {160, 255, 96}, {164, 255, 92}, {168, 255, 88}, {172, 255, 84}, {176, 255, 80}, {180, 255, 76}, {184, 255, 72}, {188, 255, 68}, {192, 255, 64}, {196, 255, 60}, {200, 255, 56}, {204, 255, 52}, {208, 255, 48}, {212, 255, 44}, {216, 255, 40}, {220, 255, 36}, {224, 255, 32}, {228, 255, 28}, {232, 255, 24}, {236, 255, 20}, {240, 255, 16}, {244, 255, 12}, {248, 255, 8}, {252, 255, 4}, {255, 255, 0}, {255, 252, 0}, {255, 248, 0}, {255, 244, 0}, {255, 240, 0}, {255, 236, 0}, {255, 232, 0}, {255, 228, 0}, {255, 224, 0}, {255, 220, 0}, {255, 216, 0}, {255, 212, 0}, {255, 208, 0}, {255, 204, 0}, {255, 200, 0}, {255, 196, 0}, {255, 192, 0}, {255, 188, 0}, {255, 184, 0}, {255, 180, 0}, {255, 176, 0}, {255, 172, 0}, {255, 168, 0}, {255, 164, 0}, {255, 160, 0}, {255, 156, 0}, {255, 152, 0}, {255, 148, 0}, {255, 144, 0}, {255, 140, 0}, {255, 136, 0}, {255, 132, 0}, {255, 128, 0}, {255, 124, 0}, {255, 120, 0}, {255, 116, 0}, {255, 112, 0}, {255, 108, 0}, {255, 104, 0}, {255, 100, 0}, {255, 96, 0}, {255, 92, 0}, {255, 88, 0}, {255, 84, 0}, {255, 80, 0}, {255, 76, 0}, {255, 72, 0}, {255, 68, 0}, {255, 64, 0}, {255, 60, 0}, {255, 56, 0}, {255, 52, 0}, {255, 48, 0}, {255, 44, 0}, {255, 40, 0}, {255, 36, 0}, {255, 32, 0}, {255, 28, 0}, {255, 24, 0}, {255, 20, 0}, {255, 16, 0}, {255, 12, 0}, {255, 8, 0}, {255, 4, 0}, {255, 0, 0}, {252, 0, 0}, {248, 0, 0}, {244, 0, 0}, {240, 0, 0}, {236, 0, 0}, {232, 0, 0}, {228, 0, 0}, {224, 0, 0}, {220, 0, 0}, {216, 0, 0}, {212, 0, 0}, {208, 0, 0}, {204, 0, 0}, {200, 0, 0}, {196, 0, 0}, {192, 0, 0}, {188, 0, 0}, {184, 0, 0}, {180, 0, 0}, {176, 0, 0}, {172, 0, 0}, {168, 0, 0}, {164, 0, 0}, {160, 0, 0}, {156, 0, 0}, {152, 0, 0}, {148, 0, 0}, {144, 0, 0}, {140, 0, 0}, {136, 0, 0}, {132, 0, 0}, {128, 0, 0}, }; static void arv_fake_camera_diagonal_ramp (ArvBuffer *buffer, void *fill_pattern_data, guint32 exposure_time_us, guint32 gain, ArvPixelFormat pixel_format) { double pixel_value; double scale; guint32 x, y; guint32 width; guint32 height; g_return_if_fail (buffer != NULL); g_return_if_fail (buffer->priv->n_parts == 1); width = buffer->priv->parts[0].width; height = buffer->priv->parts[0].height; scale = 1.0 + gain + log10 ((double) exposure_time_us / 10000.0); switch (pixel_format) { case ARV_PIXEL_FORMAT_MONO_8: if (height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned char *pixel = &buffer->priv->data [y * width + x]; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; *pixel = CLAMP (pixel_value, 0, 255); } } buffer->priv->received_size = height * width; } break; case ARV_PIXEL_FORMAT_MONO_16: if (2 * height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned short *pixel = (unsigned short *)&buffer->priv->data [2*y * width + 2*x]; pixel_value = (256*x + 256*buffer->priv->frame_id + 256*y) % 65535; pixel_value *= scale; *pixel = CLAMP (pixel_value, 0, 65535); } } buffer->priv->received_size = 2 * height * width; } break; case ARV_PIXEL_FORMAT_BAYER_BG_8: if (height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned int index; unsigned char *pixel; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; index = CLAMP (pixel_value, 0, 255); // BG // GR pixel = &buffer->priv->data [y * width + x]; if (x & 1) { if (y & 1) *pixel = jet_colormap [index].b; else *pixel = jet_colormap [index].g; } else { if (y & 1) *pixel = jet_colormap [index].g; else *pixel = jet_colormap [index].r; } } } buffer->priv->received_size = height * width; } break; case ARV_PIXEL_FORMAT_BAYER_GB_8: if (height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned int index; unsigned char *pixel; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; index = CLAMP (pixel_value, 0, 255); // GB // RG pixel = &buffer->priv->data [y * width + x]; if (x & 1) { if (y & 1) *pixel = jet_colormap [index].g; else *pixel = jet_colormap [index].b; } else { if (y & 1) *pixel = jet_colormap [index].r; else *pixel = jet_colormap [index].g; } } } buffer->priv->received_size = height * width; } break; case ARV_PIXEL_FORMAT_BAYER_GR_8: if (height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned int index; unsigned char *pixel; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; index = CLAMP (pixel_value, 0, 255); // GR // BG pixel = &buffer->priv->data [y * width + x]; if (x & 1) { if (y & 1) *pixel = jet_colormap [index].g; else *pixel = jet_colormap [index].r; } else { if (y & 1) *pixel = jet_colormap [index].b; else *pixel = jet_colormap [index].g; } } } buffer->priv->received_size = height * width; } break; case ARV_PIXEL_FORMAT_BAYER_RG_8: if (height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned int index; unsigned char *pixel; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; index = CLAMP (pixel_value, 0, 255); // RG // GB pixel = &buffer->priv->data [y * width + x]; if (x & 1) { if (y & 1) *pixel = jet_colormap [index].r; else *pixel = jet_colormap [index].g; } else { if (y & 1) *pixel = jet_colormap [index].g; else *pixel = jet_colormap [index].b; } } } buffer->priv->received_size = height * width; } break; case ARV_PIXEL_FORMAT_RGB_8_PACKED: if (3 * height * width <= buffer->priv->allocated_size) { for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { unsigned char *pixel = &buffer->priv->data [3 * (y * width + x)]; unsigned int index; pixel_value = (x + buffer->priv->frame_id + y) % 255; pixel_value *= scale; index = CLAMP (pixel_value, 0, 255); pixel[0] = jet_colormap [index].r; pixel[1] = jet_colormap [index].g; pixel[2] = jet_colormap [index].b; } } buffer->priv->received_size = 3 * height * width; } break; default: g_critical ("Unsupported pixel format"); break; } } /** * arv_fake_camera_set_fill_pattern: * @camera: a #ArvFakeCamera * @fill_pattern_callback: (scope call) (closure fill_pattern_data) : callback for image filling * @fill_pattern_data: image filling user data * * Sets the fill pattern callback for custom test images. */ void arv_fake_camera_set_fill_pattern (ArvFakeCamera *camera, ArvFakeCameraFillPattern fill_pattern_callback, void *fill_pattern_data) { g_return_if_fail (ARV_IS_FAKE_CAMERA (camera)); g_mutex_lock (&camera->priv->fill_pattern_mutex); if (fill_pattern_callback != NULL) { camera->priv->fill_pattern_callback = fill_pattern_callback; camera->priv->fill_pattern_data = fill_pattern_data; } else { camera->priv->fill_pattern_callback = arv_fake_camera_diagonal_ramp; camera->priv->fill_pattern_data = NULL; } g_mutex_unlock (&camera->priv->fill_pattern_mutex); } /** * arv_fake_camera_fill_buffer: * @camera: a #ArvFakeCamera * @buffer: the #ArvBuffer to fill * @packet_size: (out) (optional): the packet size * * Fill a buffer with data from the fake camera. */ void arv_fake_camera_fill_buffer (ArvFakeCamera *camera, ArvBuffer *buffer, guint32 *packet_size) { guint32 width; guint32 height; guint32 exposure_time_us; guint32 gain; guint32 pixel_format; size_t payload; if (camera == NULL || buffer == NULL) return; arv_buffer_set_n_parts(buffer, 1); width = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_WIDTH); height = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_HEIGHT); payload = arv_fake_camera_get_payload (camera); if (buffer->priv->allocated_size < payload) { buffer->priv->status = ARV_BUFFER_STATUS_SIZE_MISMATCH; return; } /* frame id is a 16 bit value, 0 is invalid */ camera->priv->frame_id = (camera->priv->frame_id + 1) % 65536; if (camera->priv->frame_id == 0) camera->priv->frame_id = 1; buffer->priv->payload_type = ARV_BUFFER_PAYLOAD_TYPE_IMAGE; buffer->priv->chunk_endianness = G_BIG_ENDIAN; buffer->priv->status = ARV_BUFFER_STATUS_SUCCESS; buffer->priv->timestamp_ns = g_get_real_time () * 1000; buffer->priv->system_timestamp_ns = buffer->priv->timestamp_ns; buffer->priv->frame_id = camera->priv->frame_id; buffer->priv->parts[0].data_offset = 0; buffer->priv->parts[0].component_id = 0; buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; buffer->priv->parts[0].pixel_format = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_PIXEL_FORMAT); buffer->priv->parts[0].width = width; buffer->priv->parts[0].height = height; buffer->priv->parts[0].x_offset = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_X_OFFSET); buffer->priv->parts[0].y_offset = _get_register (camera, ARV_FAKE_CAMERA_REGISTER_Y_OFFSET); buffer->priv->parts[0].x_padding = 0; buffer->priv->parts[0].y_padding = 0; g_mutex_lock (&camera->priv->fill_pattern_mutex); arv_fake_camera_read_register (camera, ARV_FAKE_CAMERA_REGISTER_EXPOSURE_TIME_US, &exposure_time_us); arv_fake_camera_read_register (camera, ARV_FAKE_CAMERA_REGISTER_GAIN_RAW, &gain); arv_fake_camera_read_register (camera, ARV_FAKE_CAMERA_REGISTER_PIXEL_FORMAT, &pixel_format); camera->priv->fill_pattern_callback (buffer, camera->priv->fill_pattern_data, exposure_time_us, gain, pixel_format); g_mutex_unlock (&camera->priv->fill_pattern_mutex); buffer->priv->parts[0].size = buffer->priv->received_size; if (packet_size != NULL) *packet_size = (_get_register (camera, ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_OFFSET) >> ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_POS) & ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_MASK; } void arv_fake_camera_set_inet_address (ArvFakeCamera *camera, GInetAddress *address) { const guint8 *bytes; g_return_if_fail (ARV_IS_FAKE_CAMERA (camera)); g_return_if_fail (G_IS_INET_ADDRESS (address)); g_return_if_fail (g_inet_address_get_family (address) == G_SOCKET_FAMILY_IPV4); bytes = g_inet_address_to_bytes (address); arv_fake_camera_write_memory (camera, ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET, g_inet_address_get_native_size (address), (char *) bytes); } guint32 arv_fake_camera_get_acquisition_status (ArvFakeCamera *camera) { g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), 0); return _get_register (camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION); } /** * arv_fake_camera_get_stream_address: * @camera: a #ArvFakeCamera * * Return value: (transfer full): the data stream #GSocketAddress for this camera */ GSocketAddress * arv_fake_camera_get_stream_address (ArvFakeCamera *camera) { GSocketAddress *stream_socket_address; GInetAddress *inet_address; guint32 value; g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), NULL); arv_fake_camera_read_memory (camera, ARV_GVBS_STREAM_CHANNEL_0_IP_ADDRESS_OFFSET, sizeof (value), &value); inet_address = g_inet_address_new_from_bytes ((guint8 *) &value, G_SOCKET_FAMILY_IPV4); stream_socket_address = g_inet_socket_address_new (inet_address, _get_register (camera, ARV_GVBS_STREAM_CHANNEL_0_PORT_OFFSET)); g_object_unref (inet_address); return stream_socket_address; } void arv_fake_camera_set_trigger_frequency (ArvFakeCamera *camera, double frequency) { g_return_if_fail (ARV_IS_FAKE_CAMERA (camera)); g_return_if_fail (frequency > 0.0); camera->priv->trigger_frequency = frequency; } gboolean arv_fake_camera_check_and_acknowledge_software_trigger (ArvFakeCamera *camera) { g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), FALSE); if (_get_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOFTWARE) == 1) { arv_fake_camera_write_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOFTWARE, 0); return TRUE; } return FALSE; } gboolean arv_fake_camera_is_in_free_running_mode (ArvFakeCamera *camera) { g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), FALSE); if (_get_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE) == 0 && _get_register (camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION_FRAME_PERIOD_US) > 0) { return TRUE; } return FALSE; } gboolean arv_fake_camera_is_in_software_trigger_mode (ArvFakeCamera *camera) { g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), FALSE); if (_get_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE) == 1 && _get_register (camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOURCE) == 1) { return TRUE; } return FALSE; } guint32 arv_fake_camera_get_control_channel_privilege (ArvFakeCamera *camera) { guint32 value; arv_fake_camera_read_register (camera, ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, &value); return value; } void arv_fake_camera_set_control_channel_privilege (ArvFakeCamera *camera, guint32 privilege) { arv_fake_camera_write_register (camera, ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, privilege); } guint32 arv_fake_camera_get_heartbeat_timeout (ArvFakeCamera *camera) { guint32 value; arv_fake_camera_read_register (camera, ARV_GVBS_HEARTBEAT_TIMEOUT_OFFSET, &value); return value; } const char * arv_fake_camera_get_genicam_xml_url (ArvFakeCamera *camera){ g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), NULL); return camera->priv->genicam_xml_url; } /** * arv_fake_camera_get_genicam_xml: * @camera: a #ArvFakeCamera * @size: (out) (optional): the size of the returned XML string * * Return value: (transfer none): the genicam XML description of the camera */ const char * arv_fake_camera_get_genicam_xml (ArvFakeCamera *camera, size_t *size) { if (size != NULL) *size = 0; g_return_val_if_fail (ARV_IS_FAKE_CAMERA (camera), NULL); if (size != NULL) *size = camera->priv->genicam_xml_size; return camera->priv->genicam_xml; } /* GObject implemenation */ ArvFakeCamera * arv_fake_camera_new_full (const char *serial_number, const char *genicam_filename) { ArvFakeCamera *fake_camera; GError *error = NULL; char *filename; void *memory; g_return_val_if_fail (serial_number != NULL, NULL); g_return_val_if_fail (*serial_number != '\0', NULL); g_return_val_if_fail (strlen (serial_number) < ARV_GVBS_SERIAL_NUMBER_SIZE, NULL); fake_camera = g_object_new (ARV_TYPE_FAKE_CAMERA, NULL); memory = g_malloc0 (ARV_FAKE_CAMERA_MEMORY_SIZE); g_mutex_init (&fake_camera->priv->fill_pattern_mutex); fake_camera->priv->fill_pattern_callback = arv_fake_camera_diagonal_ramp; fake_camera->priv->fill_pattern_data = NULL; if (genicam_filename != NULL) filename = g_strdup (genicam_filename); else if (arv_get_fake_camera_genicam_filename () != NULL) filename = g_strdup (arv_get_fake_camera_genicam_filename ()); else filename = NULL; if (filename) { if (!g_file_get_contents (filename, &fake_camera->priv->genicam_xml, &fake_camera->priv->genicam_xml_size, &error)) { g_critical ("Failed to load genicam file '%s': %s", filename, error != NULL ? error->message : "Unknown reason"); g_clear_error (&error); fake_camera->priv->genicam_xml = NULL; fake_camera->priv->genicam_xml_size = 0; } } else { GBytes *bytes = g_resources_lookup_data("/org/aravis/arv-fake-camera.xml", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); if (error != NULL) { g_critical ("Failed to load embedded resource arv-fake-camera.xml: %s",error->message); g_clear_error (&error); } else { fake_camera->priv->genicam_xml = g_strndup (g_bytes_get_data(bytes,NULL), g_bytes_get_size(bytes)); fake_camera->priv->genicam_xml_size = g_bytes_get_size(bytes); } g_bytes_unref (bytes); } g_clear_pointer (&filename, g_free); fake_camera->priv->memory = memory; strcpy (((char *) memory) + ARV_GVBS_MANUFACTURER_NAME_OFFSET, "Aravis"); strcpy (((char *) memory) + ARV_GVBS_MODEL_NAME_OFFSET, "Fake"); strcpy (((char *) memory) + ARV_GVBS_MANUFACTURER_INFO_OFFSET, "none"); strcpy (((char *) memory) + ARV_GVBS_DEVICE_VERSION_OFFSET, ARAVIS_VERSION); strcpy (((char *) memory) + ARV_GVBS_SERIAL_NUMBER_OFFSET, serial_number); fake_camera->priv->genicam_xml_url = g_strdup_printf ("Local:///arv-fake-camera.xml;%x;%x", ARV_FAKE_CAMERA_MEMORY_SIZE, (unsigned int) fake_camera->priv->genicam_xml_size); strcpy (((char *) memory) + ARV_GVBS_XML_URL_0_OFFSET, fake_camera->priv->genicam_xml_url); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_SENSOR_WIDTH, ARV_FAKE_CAMERA_SENSOR_WIDTH); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_SENSOR_HEIGHT, ARV_FAKE_CAMERA_SENSOR_HEIGHT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_WIDTH, ARV_FAKE_CAMERA_WIDTH_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_HEIGHT, ARV_FAKE_CAMERA_HEIGHT_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_X_OFFSET, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_Y_OFFSET, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_BINNING_HORIZONTAL, ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_BINNING_VERTICAL, ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_PIXEL_FORMAT, ARV_PIXEL_FORMAT_MONO_8); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION_MODE, 1); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_ACQUISITION_FRAME_PERIOD_US, 1000000.0 / ARV_FAKE_CAMERA_ACQUISITION_FRAME_RATE_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_EXPOSURE_TIME_US, ARV_FAKE_CAMERA_EXPOSURE_TIME_US_DEFAULT); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOURCE, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_ACTIVATION, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOFTWARE, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_GAIN_RAW, 0); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_GAIN_MODE, 1); arv_fake_camera_write_register (fake_camera, ARV_GVBS_HEARTBEAT_TIMEOUT_OFFSET, 3000); arv_fake_camera_write_register (fake_camera, ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_HIGH_OFFSET, 0); arv_fake_camera_write_register (fake_camera, ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_LOW_OFFSET, 1000000000); arv_fake_camera_write_register (fake_camera, ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, 0); arv_fake_camera_write_register (fake_camera, ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_OFFSET, 1400); arv_fake_camera_write_register (fake_camera, ARV_GVBS_N_NETWORK_INTERFACES_OFFSET, 1); arv_fake_camera_write_register (fake_camera, ARV_GVBS_N_STREAM_CHANNELS_OFFSET, 1); arv_fake_camera_write_register (fake_camera, ARV_FAKE_CAMERA_REGISTER_TEST, ARV_FAKE_CAMERA_TEST_REGISTER_DEFAULT); return fake_camera; } ArvFakeCamera * arv_fake_camera_new (const char *serial_number) { return arv_fake_camera_new_full (serial_number, NULL); } static void arv_fake_camera_init (ArvFakeCamera *fake_camera) { fake_camera->priv = arv_fake_camera_get_instance_private (fake_camera); fake_camera->priv->trigger_frequency = 25.0; fake_camera->priv->frame_id = 65400; /* Trigger circular counter bugs sooner */ } static void arv_fake_camera_finalize (GObject *object) { ArvFakeCamera *fake_camera = ARV_FAKE_CAMERA (object); g_mutex_clear (&fake_camera->priv->fill_pattern_mutex); g_clear_pointer (&fake_camera->priv->memory, g_free); g_clear_pointer (&fake_camera->priv->genicam_xml, g_free); g_clear_pointer (&fake_camera->priv->genicam_xml_url, g_free); G_OBJECT_CLASS (arv_fake_camera_parent_class)->finalize (object); } static void arv_fake_camera_class_init (ArvFakeCameraClass *fake_camera_class) { GObjectClass *object_class = G_OBJECT_CLASS (fake_camera_class); object_class->finalize = arv_fake_camera_finalize; } ARV_DEFINE_DESTRUCTOR (arv_fake_camera_destructor) static void arv_fake_camera_destructor (void) { arv_set_fake_camera_genicam_filename (NULL); } aravis-0.8.34/src/arvfakecamera.h000066400000000000000000000131771475431451200166420ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_CAMERA_H #define ARV_FAKE_CAMERA_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS ARV_API void arv_set_fake_camera_genicam_filename (const char *filename); #define ARV_FAKE_CAMERA_MEMORY_SIZE 0x10000 /* To keep in sync with arv-fake-camera.xml */ /* Image format control */ #define ARV_FAKE_CAMERA_REGISTER_SENSOR_WIDTH 0x11c #define ARV_FAKE_CAMERA_REGISTER_SENSOR_HEIGHT 0x118 #define ARV_FAKE_CAMERA_REGISTER_WIDTH 0x100 #define ARV_FAKE_CAMERA_REGISTER_HEIGHT 0x104 #define ARV_FAKE_CAMERA_REGISTER_X_OFFSET 0x130 #define ARV_FAKE_CAMERA_REGISTER_Y_OFFSET 0x134 #define ARV_FAKE_CAMERA_REGISTER_BINNING_HORIZONTAL 0x108 #define ARV_FAKE_CAMERA_REGISTER_BINNING_VERTICAL 0x10c #define ARV_FAKE_CAMERA_REGISTER_PIXEL_FORMAT 0x128 #define ARV_FAKE_CAMERA_REGISTER_TEST 0x1f0 #define ARV_FAKE_CAMERA_SENSOR_WIDTH 2048 #define ARV_FAKE_CAMERA_SENSOR_HEIGHT 2048 #define ARV_FAKE_CAMERA_WIDTH_DEFAULT 512 #define ARV_FAKE_CAMERA_HEIGHT_DEFAULT 512 #define ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT 1 #define ARV_FAKE_CAMERA_BINNING_VERTICAL_DEFAULT 1 #define ARV_FAKE_CAMERA_PIXEL_FORMAT_DEFAULT ARV_PIXEL_FORMAT_MONO_8 #define ARV_FAKE_CAMERA_TEST_REGISTER_DEFAULT 0x12345678 /* Acquisition control */ #define ARV_FAKE_CAMERA_REGISTER_ACQUISITION_MODE 0x12c #define ARV_FAKE_CAMERA_REGISTER_ACQUISITION_FRAME_PERIOD_US 0x138 #define ARV_FAKE_CAMERA_REGISTER_FRAME_START_OFFSET 0x000 #define ARV_FAKE_CAMERA_REGISTER_ACQUISITION_START_OFFSET 0x020 #define ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE 0x300 #define ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOURCE 0x304 #define ARV_FAKE_CAMERA_REGISTER_TRIGGER_ACTIVATION 0x308 #define ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOFTWARE 0x30c #define ARV_FAKE_CAMERA_REGISTER_ACQUISITION 0x124 #define ARV_FAKE_CAMERA_REGISTER_EXPOSURE_TIME_US 0x120 #define ARV_FAKE_CAMERA_ACQUISITION_FRAME_RATE_DEFAULT 25.0 #define ARV_FAKE_CAMERA_EXPOSURE_TIME_US_DEFAULT 10000.0 /* Analog control */ #define ARV_FAKE_CAMERA_REGISTER_GAIN_RAW 0x110 #define ARV_FAKE_CAMERA_REGISTER_GAIN_MODE 0x114 #define ARV_TYPE_FAKE_CAMERA (arv_fake_camera_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvFakeCamera, arv_fake_camera, ARV, FAKE_CAMERA, GObject) typedef void (*ArvFakeCameraFillPattern) (ArvBuffer *buffer, void *fill_pattern_data, guint32 exposure_time_us, guint32 gain, ArvPixelFormat pixel_format); ARV_API ArvFakeCamera * arv_fake_camera_new (const char *serial_number); ARV_API ArvFakeCamera * arv_fake_camera_new_full (const char *serial_number, const char *genicam_filename); ARV_API gboolean arv_fake_camera_read_memory (ArvFakeCamera *camera, guint32 address, guint32 size, void *buffer); ARV_API gboolean arv_fake_camera_write_memory (ArvFakeCamera *camera, guint32 address, guint32 size, const void *buffer); ARV_API gboolean arv_fake_camera_read_register (ArvFakeCamera *camera, guint32 address, guint32 *value); ARV_API gboolean arv_fake_camera_write_register (ArvFakeCamera *camera, guint32 address, guint32 value); ARV_API size_t arv_fake_camera_get_payload (ArvFakeCamera *camera); ARV_API void arv_fake_camera_wait_for_next_frame (ArvFakeCamera *camera); ARV_API guint64 arv_fake_camera_get_sleep_time_for_next_frame (ArvFakeCamera *camera, guint64 *next_timestamp_us); ARV_API void arv_fake_camera_fill_buffer (ArvFakeCamera *camera, ArvBuffer *buffer, guint32 *packet_size); ARV_API guint32 arv_fake_camera_get_acquisition_status (ArvFakeCamera *camera); ARV_API GSocketAddress * arv_fake_camera_get_stream_address (ArvFakeCamera *camera); ARV_API void arv_fake_camera_set_inet_address (ArvFakeCamera *camera, GInetAddress *address); ARV_API guint32 arv_fake_camera_get_control_channel_privilege (ArvFakeCamera *camera); ARV_API void arv_fake_camera_set_control_channel_privilege (ArvFakeCamera *camera, guint32 privilege); ARV_API guint32 arv_fake_camera_get_heartbeat_timeout (ArvFakeCamera *camera); ARV_API void arv_fake_camera_set_fill_pattern (ArvFakeCamera *camera, ArvFakeCameraFillPattern fill_pattern_callback, void *fill_pattern_data); ARV_API void arv_fake_camera_set_trigger_frequency (ArvFakeCamera *camera, double frequency); ARV_API gboolean arv_fake_camera_is_in_free_running_mode (ArvFakeCamera *camera); ARV_API gboolean arv_fake_camera_is_in_software_trigger_mode (ArvFakeCamera *camera); ARV_API gboolean arv_fake_camera_check_and_acknowledge_software_trigger (ArvFakeCamera *camera); ARV_API const char * arv_fake_camera_get_genicam_xml (ArvFakeCamera *camera, size_t *size); ARV_API const char * arv_fake_camera_get_genicam_xml_url (ArvFakeCamera *camera); G_END_DECLS #endif aravis-0.8.34/src/arvfakedevice.c000066400000000000000000000166171475431451200166460ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvfakedevice * @short_description: Fake device */ #include #include #include #include #include #include #include enum { PROP_0, PROP_FAKE_DEVICE_SERIAL_NUMBER, }; typedef struct { char *serial_number; ArvFakeCamera *camera; ArvGc *genicam; } ArvFakeDevicePrivate; struct _ArvFakeDevice { ArvDevice device; }; struct _ArvFakeDeviceClass { ArvDeviceClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvFakeDevice, arv_fake_device, ARV_TYPE_DEVICE, G_ADD_PRIVATE (ArvFakeDevice)) /* ArvFakeDevice implemenation */ /* ArvDevice implemenation */ static ArvStream * arv_fake_device_create_stream (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error) { return arv_fake_stream_new (ARV_FAKE_DEVICE (device), callback, user_data, destroy, error); } static const char * arv_fake_device_get_genicam_xml (ArvDevice *device, size_t *size) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return arv_fake_camera_get_genicam_xml (priv->camera, size); } static ArvGc * arv_fake_device_get_genicam (ArvDevice *device) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return priv->genicam; } static gboolean arv_fake_device_read_memory (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return arv_fake_camera_read_memory (priv->camera, address, size, buffer); } static gboolean arv_fake_device_write_memory (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return arv_fake_camera_write_memory (priv->camera, address, size, buffer); } static gboolean arv_fake_device_read_register (ArvDevice *device, guint64 address, guint32 *value, GError **error) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return arv_fake_camera_read_register (priv->camera, address, value); } static gboolean arv_fake_device_write_register (ArvDevice *device, guint64 address, guint32 value, GError **error) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); return arv_fake_camera_write_register (priv->camera, address, value); } /** * arv_fake_device_get_fake_camera: * @device: a fake device * * Return value: (transfer none): the #ArvFakeCamera used by this device instance. * * Since: 0.8.0 */ ArvFakeCamera * arv_fake_device_get_fake_camera (ArvFakeDevice *device) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (device)); g_return_val_if_fail (ARV_IS_FAKE_DEVICE (device), NULL); return priv->camera; } /** * arv_fake_device_new: * @serial_number: fake device serial number * @error: a #GError placeholder, %NULL to ignore * * Returns: a newly created #ArvDevice simulating a real device * * Since: 0.8.0 */ ArvDevice * arv_fake_device_new (const char *serial_number, GError **error) { return g_initable_new (ARV_TYPE_FAKE_DEVICE, NULL, error, "serial-number", serial_number, NULL); } static void arv_fake_device_init (ArvFakeDevice *fake_device) { } static void arv_fake_device_finalize (GObject *object) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (object)); g_clear_pointer (&priv->serial_number, g_free); g_clear_object (&priv->genicam); g_clear_object (&priv->camera); G_OBJECT_CLASS (arv_fake_device_parent_class)->finalize (object); } static void arv_fake_device_constructed (GObject *self) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (self)); const char *genicam_xml; gsize genicam_xml_size; G_OBJECT_CLASS (arv_fake_device_parent_class)->constructed (self); if (priv->serial_number == NULL) { arv_device_take_init_error (ARV_DEVICE (self), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "Can't construct a fake device without a serial number")); return; } priv->camera = arv_fake_camera_new_full (priv->serial_number, NULL); genicam_xml = arv_fake_camera_get_genicam_xml (priv->camera, &genicam_xml_size); if (genicam_xml == NULL) { arv_device_take_init_error (ARV_DEVICE (self), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, "Genicam data not found")); return; } priv->genicam = arv_gc_new (ARV_DEVICE (self), genicam_xml, genicam_xml_size); if (!ARV_IS_GC (priv->genicam)) { arv_device_take_init_error (ARV_DEVICE (self), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, "Invalid Genicam data")); return; } arv_gc_set_default_gv_features(priv->genicam); arv_dom_document_set_url(ARV_DOM_DOCUMENT(priv->genicam), arv_fake_camera_get_genicam_xml_url(priv->camera)); } static void arv_fake_device_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec) { ArvFakeDevicePrivate *priv = arv_fake_device_get_instance_private (ARV_FAKE_DEVICE (self)); switch (prop_id) { case PROP_FAKE_DEVICE_SERIAL_NUMBER: g_free (priv->serial_number); priv->serial_number = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; } } static void arv_fake_device_class_init (ArvFakeDeviceClass *fake_device_class) { GObjectClass *object_class = G_OBJECT_CLASS (fake_device_class); ArvDeviceClass *device_class = ARV_DEVICE_CLASS (fake_device_class); object_class->finalize = arv_fake_device_finalize; object_class->constructed = arv_fake_device_constructed; object_class->set_property = arv_fake_device_set_property; device_class->create_stream = arv_fake_device_create_stream; device_class->get_genicam_xml = arv_fake_device_get_genicam_xml; device_class->get_genicam = arv_fake_device_get_genicam; device_class->read_memory = arv_fake_device_read_memory; device_class->write_memory = arv_fake_device_write_memory; device_class->read_register = arv_fake_device_read_register; device_class->write_register = arv_fake_device_write_register; g_object_class_install_property (object_class, PROP_FAKE_DEVICE_SERIAL_NUMBER, g_param_spec_string ("serial-number", "Serial number", "Fake device serial number", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } aravis-0.8.34/src/arvfakedevice.h000066400000000000000000000027271475431451200166500ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_DEVICE_H #define ARV_FAKE_DEVICE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_FAKE_DEVICE (arv_fake_device_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvFakeDevice, arv_fake_device, ARV, FAKE_DEVICE, ArvDevice) ARV_API ArvDevice * arv_fake_device_new (const char *serial_number, GError **error); ARV_API ArvFakeCamera * arv_fake_device_get_fake_camera (ArvFakeDevice *device); G_END_DECLS #endif aravis-0.8.34/src/arvfakedeviceprivate.h000066400000000000000000000021601475431451200202320ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_DEVICE_PRIVATE_H #define ARV_FAKE_DEVICE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS G_END_DECLS #endif aravis-0.8.34/src/arvfakegvcamera.c000066400000000000000000000072021475431451200171620ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static char *arv_option_interface_name = NULL; static char *arv_option_serial_number = NULL; static char *arv_option_genicam_file = NULL; static double arv_option_gvsp_lost_ratio = 0.0; static char *arv_option_debug_domains = NULL; static const GOptionEntry arv_option_entries[] = { { "interface", 'i', 0, G_OPTION_ARG_STRING, &arv_option_interface_name, "Listening interface name or address", "interface"}, { "serial", 's', 0, G_OPTION_ARG_STRING, &arv_option_serial_number, "Fake camera serial number", "serial_nbr"}, { "genicam", 'g', 0, G_OPTION_ARG_STRING, &arv_option_genicam_file, "XML Genicam file to use", "genicam_filename"}, { "gvsp-lost-ratio", 'r', 0, G_OPTION_ARG_DOUBLE, &arv_option_gvsp_lost_ratio, "GVSP lost packet ratio", "packet_per_thousand"}, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, NULL, "{[:][,...]|help}" }, { NULL } }; static const char description_content[] = "The genicam parameter is for debug purpose only. It is not possible to load\n" "any arbitrary genicam data, as the declared features must match the registers\n" "of the fake device.\n" "\n" "Examples:\n" "\n" "arv-fake-gv-camera-" ARAVIS_API_VERSION " -i eth0\n" "arv-fake-gv-camera-" ARAVIS_API_VERSION " -i 127.0.0.1\n" "arv-fake-gv-camera-" ARAVIS_API_VERSION " -s GV02 -d all\n"; int main (int argc, char **argv) { ArvGvFakeCamera *gv_camera; GOptionContext *context; GError *error = NULL; context = g_option_context_new (NULL); g_option_context_set_summary (context, "Fake GigEVision camera."); g_option_context_set_description (context, description_content); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); if (!arv_debug_enable (arv_option_debug_domains)) { if (g_strcmp0 (arv_option_debug_domains, "help") != 0) printf ("Invalid debug selection\n"); else arv_debug_print_infos (); return EXIT_FAILURE; } gv_camera = arv_gv_fake_camera_new_full (arv_option_interface_name, arv_option_serial_number, arv_option_genicam_file); g_object_set (gv_camera, "gvsp-lost-ratio", arv_option_gvsp_lost_ratio / 1000.0, NULL); signal (SIGINT, set_cancel); if (arv_gv_fake_camera_is_running (gv_camera)) while (!cancel) g_usleep (1000000); else printf ("Failed to start camera\n"); g_object_unref (gv_camera); return EXIT_SUCCESS; } aravis-0.8.34/src/arvfakeinterface.c000066400000000000000000000076421475431451200173450ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvfakeinterface * @short_description: Fake interface */ #include #include #include #include #include #include #define ARV_FAKE_DEVICE_ID "Fake_1" #define ARV_FAKE_PHYSICAL_ID "Fake_1" #define ARV_FAKE_ADDRESS "0.0.0.0" #define ARV_FAKE_VENDOR "Aravis" #define ARV_FAKE_MANUFACTURER_INFO "none" #define ARV_FAKE_MODEL "Fake" #define ARV_FAKE_SERIAL "1" struct _ArvFakeInterface { ArvInterface interface; }; struct _ArvFakeInterfaceClass { ArvInterfaceClass parent_class; }; G_DEFINE_TYPE (ArvFakeInterface, arv_fake_interface, ARV_TYPE_INTERFACE) static void arv_fake_interface_update_device_list (ArvInterface *interface, GArray *device_ids) { ArvInterfaceDeviceIds *ids; g_assert (device_ids->len == 0); ids = g_new0 (ArvInterfaceDeviceIds, 1); ids->device = g_strdup (ARV_FAKE_DEVICE_ID); ids->physical = g_strdup (ARV_FAKE_PHYSICAL_ID); ids->address = g_strdup (ARV_FAKE_ADDRESS); ids->vendor = g_strdup (ARV_FAKE_VENDOR); ids->manufacturer_info = g_strdup (ARV_FAKE_MANUFACTURER_INFO); ids->model = g_strdup (ARV_FAKE_MODEL); ids->serial_nbr = g_strdup (ARV_FAKE_SERIAL); g_array_append_val (device_ids, ids); } static ArvDevice * arv_fake_interface_open_device (ArvInterface *interface, const char *device_id, GError **error) { if (g_strcmp0 (device_id, ARV_FAKE_DEVICE_ID) == 0 || g_strcmp0 (device_id, ARV_FAKE_PHYSICAL_ID) == 0) return arv_fake_device_new ("1", error); return NULL; } static ArvInterface *arv_fake_interface = NULL; static GMutex arv_fake_interface_mutex; /** * arv_fake_interface_get_instance: * * Gets the unique instance of the fake interface. * * Returns: (transfer none): a #ArvInterface singleton. */ ArvInterface * arv_fake_interface_get_instance (void) { g_mutex_lock (&arv_fake_interface_mutex); if (arv_fake_interface == NULL) arv_fake_interface = g_object_new (ARV_TYPE_FAKE_INTERFACE, NULL); g_mutex_unlock (&arv_fake_interface_mutex); return ARV_INTERFACE (arv_fake_interface); } void arv_fake_interface_destroy_instance (void) { g_mutex_lock (&arv_fake_interface_mutex); if (arv_fake_interface != NULL) { g_object_unref (arv_fake_interface); arv_fake_interface = NULL; } g_mutex_unlock (&arv_fake_interface_mutex); } static void arv_fake_interface_init (ArvFakeInterface *fake_interface) { } static void arv_fake_interface_finalize (GObject *object) { G_OBJECT_CLASS (arv_fake_interface_parent_class)->finalize (object); } static void arv_fake_interface_class_init (ArvFakeInterfaceClass *fake_interface_class) { GObjectClass *object_class = G_OBJECT_CLASS (fake_interface_class); ArvInterfaceClass *interface_class = ARV_INTERFACE_CLASS (fake_interface_class); object_class->finalize = arv_fake_interface_finalize; interface_class->update_device_list = arv_fake_interface_update_device_list; interface_class->open_device = arv_fake_interface_open_device; interface_class->protocol = "Fake"; } aravis-0.8.34/src/arvfakeinterface.h000066400000000000000000000026071475431451200173460ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_INTERFACE_H #define ARV_FAKE_INTERFACE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_FAKE_INTERFACE (arv_fake_interface_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvFakeInterface, arv_fake_interface, ARV, FAKE_INTERFACE, ArvInterface) ARV_API ArvInterface * arv_fake_interface_get_instance (void); G_END_DECLS #endif aravis-0.8.34/src/arvfakeinterfaceprivate.h000066400000000000000000000024721475431451200207410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_INTERFACE_PRIVATE_H #define ARV_FAKE_INTERFACE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_FAKE_INTERFACE_DISCOVERY_TIMEOUT_MS 1000 #define ARV_FAKE_INTERFACE_SOCKET_BUFFER_SIZE 1024 G_END_DECLS void arv_fake_interface_destroy_instance (void); #endif aravis-0.8.34/src/arvfakestream.c000066400000000000000000000166371475431451200167040ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvfakestream * @short_description: Fake stream */ #include #include #include #include #include #include #include typedef struct { ArvStream *stream; ArvFakeCamera *fake_camera; ArvStreamCallback callback; void *callback_data; gboolean cancel; /* Statistics */ guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; guint64 n_transferred_bytes; guint64 n_ignored_bytes; } ArvFakeStreamThreadData; typedef struct { GThread *thread; ArvFakeStreamThreadData *thread_data; } ArvFakeStreamPrivate; struct _ArvFakeStream { ArvStream stream; }; struct _ArvFakeStreamClass { ArvStreamClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvFakeStream, arv_fake_stream, ARV_TYPE_STREAM, G_ADD_PRIVATE (ArvFakeStream)) /* Acquisition thread */ static void * arv_fake_stream_thread (void *data) { ArvFakeStreamThreadData *thread_data = data; ArvBuffer *buffer; arv_debug_stream_thread ("[FakeStream::thread] Start"); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_INIT, NULL); while (!g_atomic_int_get (&thread_data->cancel)) { arv_fake_camera_wait_for_next_frame (thread_data->fake_camera); buffer = arv_stream_pop_input_buffer (thread_data->stream); if (buffer != NULL) { buffer->priv->received_size = 0; if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_START_BUFFER, NULL); arv_fake_camera_fill_buffer (thread_data->fake_camera, buffer, NULL); thread_data->n_transferred_bytes += buffer->priv->allocated_size; if (buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS) thread_data->n_completed_buffers++; else thread_data->n_failures++; arv_stream_push_output_buffer (thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); } else thread_data->n_underruns++; } if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_EXIT, NULL); arv_debug_stream_thread ("[FakeStream::thread] Stop"); return NULL; } /* ArvFakeStream implemenation */ static void arv_fake_stream_start_thread (ArvStream *stream) { ArvFakeStream *fake_stream = ARV_FAKE_STREAM (stream); ArvFakeStreamPrivate *priv = arv_fake_stream_get_instance_private (fake_stream); ArvFakeStreamThreadData *thread_data; g_return_if_fail (priv->thread == NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; thread_data->cancel = FALSE; priv->thread = g_thread_new ("arv_fake_stream", arv_fake_stream_thread, priv->thread_data); } static void arv_fake_stream_stop_thread (ArvStream *stream) { ArvFakeStream *fake_stream = ARV_FAKE_STREAM (stream); ArvFakeStreamPrivate *priv = arv_fake_stream_get_instance_private (fake_stream); ArvFakeStreamThreadData *thread_data; g_return_if_fail (priv->thread != NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; g_atomic_int_set (&thread_data->cancel, TRUE); g_thread_join (priv->thread); priv->thread = NULL; } /** * arv_fake_stream_new: (skip) * @camera: a #ArvFakeDevice * @callback: (scope call): image processing callback * @callback_data: (closure): user data for @callback * @error: a #GError placeholder, %NULL to ignore * * Return Value: (transfer full): a new #ArvStream. */ ArvStream * arv_fake_stream_new (ArvFakeDevice *device, ArvStreamCallback callback, void *callback_data, GDestroyNotify destroy, GError **error) { return g_initable_new (ARV_TYPE_FAKE_STREAM, NULL, error, "device", device, "callback", callback, "callback-data", callback_data, "destroy-notify", destroy, NULL); } static void arv_fake_stream_constructed (GObject *object) { ArvStream *stream = ARV_STREAM (object); ArvFakeStream *fake_stream = ARV_FAKE_STREAM (object); ArvFakeStreamPrivate *priv = arv_fake_stream_get_instance_private (fake_stream); ArvFakeStreamThreadData *thread_data; ArvFakeDevice *fake_device = NULL; thread_data = g_new0 (ArvFakeStreamThreadData, 1); thread_data->stream = stream; g_object_get (object, "device", &fake_device, "callback", &thread_data->callback, "callback-data", &thread_data->callback_data, NULL); thread_data->fake_camera = arv_fake_device_get_fake_camera (fake_device); thread_data->cancel = FALSE; arv_stream_declare_info (ARV_STREAM (fake_stream), "n_completed_buffers", G_TYPE_UINT64, &thread_data->n_completed_buffers); arv_stream_declare_info (ARV_STREAM (fake_stream), "n_failures", G_TYPE_UINT64, &thread_data->n_failures); arv_stream_declare_info (ARV_STREAM (fake_stream), "n_underruns", G_TYPE_UINT64, &thread_data->n_underruns); arv_stream_declare_info (ARV_STREAM (fake_stream), "n_transferred_bytes", G_TYPE_UINT64, &thread_data->n_transferred_bytes); arv_stream_declare_info (ARV_STREAM (fake_stream), "n_ignored_bytes", G_TYPE_UINT64, &thread_data->n_ignored_bytes); priv->thread_data = thread_data; arv_fake_stream_start_thread (ARV_STREAM (fake_stream)); G_OBJECT_CLASS (arv_fake_stream_parent_class)->constructed (object); g_clear_object (&fake_device); } /* ArvStream implementation */ static void arv_fake_stream_init (ArvFakeStream *fake_stream) { } static void arv_fake_stream_finalize (GObject *object) { ArvFakeStream *fake_stream = ARV_FAKE_STREAM (object); ArvFakeStreamPrivate *priv = arv_fake_stream_get_instance_private (fake_stream); arv_fake_stream_stop_thread (ARV_STREAM (fake_stream)); if (priv->thread_data != NULL) { g_clear_pointer (&priv->thread_data, g_free); } G_OBJECT_CLASS (arv_fake_stream_parent_class)->finalize (object); } static void arv_fake_stream_class_init (ArvFakeStreamClass *fake_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (fake_stream_class); ArvStreamClass *stream_class = ARV_STREAM_CLASS (fake_stream_class); object_class->constructed = arv_fake_stream_constructed; object_class->finalize = arv_fake_stream_finalize; stream_class->start_thread = arv_fake_stream_start_thread; stream_class->stop_thread = arv_fake_stream_stop_thread; } aravis-0.8.34/src/arvfakestream.h000066400000000000000000000024231475431451200166750ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_STREAM_H #define ARV_FAKE_STREAM_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_FAKE_STREAM (arv_fake_stream_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvFakeStream, arv_fake_stream, ARV, FAKE_STREAM, ArvStream) G_END_DECLS #endif aravis-0.8.34/src/arvfakestreamprivate.h000066400000000000000000000024371475431451200202750ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FAKE_STREAM_PRIVATE_H #define ARV_FAKE_STREAM_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ArvStream * arv_fake_stream_new (ArvFakeDevice *device, ArvStreamCallback callback, void *callback_data, GDestroyNotify destroy, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvfeatures.h.in000066400000000000000000000032231475431451200167750ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_FEATURES_H #define ARV_FEATURES_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif /** * ARAVIS_HAS_USB * * ARAVIS_HAS_USB is defined as 1 if aravis is compiled with USB support, 0 if not. * * Since: 0.6.0 */ #define ARAVIS_HAS_USB @ARAVIS_HAS_USB@ /** * ARAVIS_HAS_PACKET_SOCKET * * ARAVIS_HAS_PACKET_SOCKET is defined as 1 if aravis is compiled with packet socket support, 0 if not. * * Since: 0.6.0 */ #define ARAVIS_HAS_PACKET_SOCKET @ARAVIS_HAS_PACKET_SOCKET@ /** * ARAVIS_HAS_FAST_HEARTBEAT * * ARAVIS_HAS_FAST_HEARTBEAT is defined as 1 if aravis is compiled with fast hearbeat option, 0 if not. * * Since: 0.8.0 */ #define ARAVIS_HAS_FAST_HEARTBEAT @ARAVIS_HAS_FAST_HEARTBEAT@ #endif aravis-0.8.34/src/arvgc.c000066400000000000000000000373531475431451200151510ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION:arvgc * @short_description: Genicam root document class * * #ArvGc implements the root document for the storage of the Genicam feature * nodes. It builds the node tree by parsing an xml file in the Genicam * standard format. See http://www.genicam.org. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { GHashTable *nodes; ArvDevice *device; ArvBuffer *buffer; ArvRegisterCachePolicy cache_policy; ArvRangeCheckPolicy range_check_policy; ArvAccessCheckPolicy access_check_policy; unsigned n_register_cache_errors; } ArvGcPrivate; struct _ArvGc { ArvDomDocument base; ArvGcPrivate *priv; }; struct _ArvGcClass { ArvDomDocumentClass parent_class; }; GQuark arv_gc_error_quark (void) { return g_quark_from_static_string ("arv-gc-error-quark"); } /* ArvDomNode implementation */ static gboolean arv_gc_can_append_child (ArvDomNode *self, ArvDomNode *child) { return ARV_IS_GC_NODE (child); } /* ArvDomDocument implementation */ static ArvDomElement * arv_gc_create_element (ArvDomDocument *document, const char *tag_name) { ArvGcNode *node = NULL; if (strcmp (tag_name, "Category") == 0) node = arv_gc_category_new (); else if (strcmp (tag_name, "Command") == 0) node = arv_gc_command_new (); else if (strcmp (tag_name, "Converter") == 0) node = arv_gc_converter_node_new (); else if (strcmp (tag_name, "IntConverter") == 0) node = arv_gc_int_converter_node_new (); else if (strcmp (tag_name, "Register") == 0) node = arv_gc_register_node_new (); else if (strcmp (tag_name, "IntReg") == 0) node = arv_gc_int_reg_node_new (); else if (strcmp (tag_name, "MaskedIntReg") == 0) node = arv_gc_masked_int_reg_node_new (); else if (strcmp (tag_name, "FloatReg") == 0) node = arv_gc_float_reg_node_new (); else if (strcmp (tag_name, "String") == 0) node = arv_gc_string_node_new (); else if (strcmp (tag_name, "StringReg") == 0) node = arv_gc_string_reg_node_new (); else if (strcmp (tag_name, "StructReg") == 0) node = arv_gc_struct_reg_node_new (); else if (strcmp (tag_name, "StructEntry") == 0) node = arv_gc_struct_entry_node_new (); else if (strcmp (tag_name, "Integer") == 0) node = arv_gc_integer_node_new (); else if (strcmp (tag_name, "Float") == 0) node = arv_gc_float_node_new (); else if (strcmp (tag_name, "Boolean") == 0) node = arv_gc_boolean_new (); else if (strcmp (tag_name, "Enumeration") == 0) node = arv_gc_enumeration_new (); else if (strcmp (tag_name, "EnumEntry") == 0) node = arv_gc_enum_entry_new (); else if (strcmp (tag_name, "SwissKnife") == 0) node = arv_gc_swiss_knife_node_new (); else if (strcmp (tag_name, "IntSwissKnife") == 0) node = arv_gc_int_swiss_knife_node_new (); else if (strcmp (tag_name, "Port") == 0) node = arv_gc_port_new (); else if (strcmp (tag_name, "pIndex") == 0) node = arv_gc_index_node_new (); else if (strcmp (tag_name, "RegisterDescription") == 0) node = arv_gc_register_description_node_new (); else if (strcmp (tag_name, "pFeature") == 0) node = arv_gc_property_node_new_p_feature (); else if (strcmp (tag_name, "Value") == 0) node = arv_gc_property_node_new_value (); else if (strcmp (tag_name, "pValue") == 0) node = arv_gc_property_node_new_p_value (); else if (strcmp (tag_name, "Address") == 0) node = arv_gc_property_node_new_address (); else if (strcmp (tag_name, "pAddress") == 0) node = arv_gc_property_node_new_p_address (); else if (strcmp (tag_name, "Description") == 0) node = arv_gc_property_node_new_description (); else if (strcmp (tag_name, "Visibility") == 0) node = arv_gc_property_node_new_visibility (); else if (strcmp (tag_name, "ToolTip") == 0) node = arv_gc_property_node_new_tooltip (); else if (strcmp (tag_name, "DisplayName") == 0) node = arv_gc_property_node_new_display_name (); else if (strcmp (tag_name, "Min") == 0) node = arv_gc_property_node_new_minimum (); else if (strcmp (tag_name, "pMin") == 0) node = arv_gc_property_node_new_p_minimum (); else if (strcmp (tag_name, "Max") == 0) node = arv_gc_property_node_new_maximum (); else if (strcmp (tag_name, "pMax") == 0) node = arv_gc_property_node_new_p_maximum (); else if (strcmp (tag_name, "Inc") == 0) node = arv_gc_property_node_new_increment (); else if (strcmp (tag_name, "pInc") == 0) node = arv_gc_property_node_new_p_increment (); else if (strcmp (tag_name, "IsLinear") == 0) node = arv_gc_property_node_new_is_linear (); else if (strcmp (tag_name, "Slope") == 0) node = arv_gc_property_node_new_slope (); else if (strcmp (tag_name, "Unit") == 0) node = arv_gc_property_node_new_unit (); else if (strcmp (tag_name, "Representation") == 0) node = arv_gc_property_node_new_representation (); else if (strcmp (tag_name, "DisplayNotation") == 0) node = arv_gc_property_node_new_display_notation (); else if (strcmp (tag_name, "DisplayPrecision") == 0) node = arv_gc_property_node_new_display_precision (); else if (strcmp (tag_name, "OnValue") == 0) node = arv_gc_property_node_new_on_value (); else if (strcmp (tag_name, "OffValue") == 0) node = arv_gc_property_node_new_off_value (); else if (strcmp (tag_name, "pIsImplemented") == 0) node = arv_gc_property_node_new_p_is_implemented (); else if (strcmp (tag_name, "pIsAvailable") == 0) node = arv_gc_property_node_new_p_is_available (); else if (strcmp (tag_name, "pIsLocked") == 0) node = arv_gc_property_node_new_p_is_locked (); else if (strcmp (tag_name, "pSelected") == 0) node = arv_gc_property_node_new_p_selected (); else if (strcmp (tag_name, "Length") == 0) node = arv_gc_property_node_new_length (); else if (strcmp (tag_name, "pLength") == 0) node = arv_gc_property_node_new_p_length (); else if (strcmp (tag_name, "pPort") == 0) node = arv_gc_property_node_new_p_port (); else if (strcmp (tag_name, "pVariable") == 0) node = arv_gc_property_node_new_p_variable (); else if (strcmp (tag_name, "ValueIndexed") == 0) node = arv_gc_value_indexed_node_new (); else if (strcmp (tag_name, "pValueIndexed") == 0) node = arv_gc_p_value_indexed_node_new (); else if (strcmp (tag_name, "ValueDefault") == 0) node = arv_gc_property_node_new_value_default (); else if (strcmp (tag_name, "pValueDefault") == 0) node = arv_gc_property_node_new_p_value_default (); else if (strcmp (tag_name, "Formula") == 0) node = arv_gc_property_node_new_formula (); else if (strcmp (tag_name, "FormulaTo") == 0) node = arv_gc_property_node_new_formula_to (); else if (strcmp (tag_name, "FormulaFrom") == 0) node = arv_gc_property_node_new_formula_from (); else if (strcmp (tag_name, "Expression") == 0) node = arv_gc_property_node_new_expression (); else if (strcmp (tag_name, "Constant") == 0) node = arv_gc_property_node_new_constant (); else if (strcmp (tag_name, "AccessMode") == 0) node = arv_gc_property_node_new_access_mode (); else if (strcmp (tag_name, "ImposedAccessMode") == 0) node = arv_gc_property_node_new_imposed_access_mode (); else if (strcmp (tag_name, "Cachable") == 0) node = arv_gc_property_node_new_cachable (); else if (strcmp (tag_name, "PollingTime") == 0) node = arv_gc_property_node_new_polling_time (); else if (strcmp (tag_name, "Endianess") == 0) node = arv_gc_property_node_new_endianness (); else if (strcmp (tag_name, "Sign") == 0) node = arv_gc_property_node_new_sign (); else if (strcmp (tag_name, "LSB") == 0) node = arv_gc_property_node_new_lsb (); else if (strcmp (tag_name, "MSB") == 0) node = arv_gc_property_node_new_msb (); else if (strcmp (tag_name, "Bit") == 0) node = arv_gc_property_node_new_bit (); else if (strcmp (tag_name, "pInvalidator") == 0) node = arv_gc_invalidator_node_new (); else if (strcmp (tag_name, "Streamable") == 0) node = arv_gc_property_node_new_streamable (); else if (strcmp (tag_name, "IsDeprecated") == 0) node = arv_gc_property_node_new_is_deprecated (); else if (strcmp (tag_name, "pAlias") == 0) node = arv_gc_property_node_new_p_alias (); else if (strcmp (tag_name, "pCastAlias") == 0) node = arv_gc_property_node_new_p_cast_alias (); else if (strcmp (tag_name, "CommandValue") == 0) node = arv_gc_property_node_new_command_value (); else if (strcmp (tag_name, "pCommandValue") == 0) node = arv_gc_property_node_new_p_command_value (); else if (strcmp (tag_name, "ChunkID") == 0) node = arv_gc_property_node_new_chunk_id (); else if (strcmp (tag_name, "EventID") == 0) node = arv_gc_property_node_new_event_id (); else if (strcmp (tag_name, "Group") == 0) node = arv_gc_group_node_new (); else if (strcmp (tag_name, "Extension") == 0) node = NULL; else arv_info_dom ("[Genicam::create_element] Unknown tag (%s)", tag_name); return ARV_DOM_ELEMENT (node); } /* ArvGc implementation */ /** * arv_gc_get_node: * @genicam: a #ArvGc object * @name: node name * * Retrieves a genicam node by name. * * Return value: (transfer none): a #ArvGcNode, null if not found. */ ArvGcNode * arv_gc_get_node (ArvGc *genicam, const char *name) { g_return_val_if_fail (ARV_IS_GC (genicam), NULL); g_return_val_if_fail (name != NULL, NULL); return g_hash_table_lookup (genicam->priv->nodes, name); } /** * arv_gc_get_device: * @genicam: a #ArvGc object * * Retrieves the device handled by this genicam interface. The device is used for register access. * * Return value: (transfer none): a #ArvDevice. */ ArvDevice * arv_gc_get_device (ArvGc *genicam) { g_return_val_if_fail (ARV_IS_GC (genicam), NULL); return genicam->priv->device; } void arv_gc_register_feature_node (ArvGc *genicam, ArvGcFeatureNode *node) { const char *name; g_return_if_fail (ARV_IS_GC (genicam)); g_return_if_fail (ARV_IS_GC_FEATURE_NODE (node)); name = arv_gc_feature_node_get_name (node); if (name == NULL) return; g_object_ref (node); g_hash_table_remove (genicam->priv->nodes, (char *) name); g_hash_table_insert (genicam->priv->nodes, (char *) name, node); arv_debug_genicam ("[Gc::register_feature_node] Register node '%s' [%s]", name, arv_dom_node_get_node_name (ARV_DOM_NODE (node))); } void arv_gc_set_default_node_data (ArvGc *genicam, const char *node_name, ...) { va_list args; char *node_data; g_return_if_fail (ARV_IS_GC (genicam)); g_return_if_fail (node_name != NULL); if (arv_gc_get_node (genicam, node_name) != NULL) return; arv_info_genicam ("[Gc::set_default_node_data] Add '%s'", node_name); va_start (args, node_name); do { node_data = va_arg (args, char *); if (node_data != NULL) arv_dom_document_append_from_memory (ARV_DOM_DOCUMENT (genicam), NULL, node_data, -1, NULL); } while (node_data != NULL); va_end (args); } void arv_gc_set_register_cache_policy (ArvGc *genicam, ArvRegisterCachePolicy policy) { g_return_if_fail (ARV_IS_GC (genicam)); genicam->priv->cache_policy = policy; } ArvRegisterCachePolicy arv_gc_get_register_cache_policy (ArvGc *genicam) { g_return_val_if_fail (ARV_IS_GC (genicam), ARV_REGISTER_CACHE_POLICY_DISABLE); return genicam->priv->cache_policy; } void arv_gc_set_range_check_policy (ArvGc *genicam, ArvRangeCheckPolicy policy) { g_return_if_fail (ARV_IS_GC (genicam)); genicam->priv->range_check_policy = policy; } ArvRangeCheckPolicy arv_gc_get_range_check_policy (ArvGc *genicam) { g_return_val_if_fail (ARV_IS_GC (genicam), ARV_RANGE_CHECK_POLICY_DISABLE); return genicam->priv->range_check_policy; } void arv_gc_set_access_check_policy (ArvGc *genicam, ArvAccessCheckPolicy policy) { g_return_if_fail (ARV_IS_GC (genicam)); genicam->priv->access_check_policy = policy; } ArvAccessCheckPolicy arv_gc_get_access_check_policy (ArvGc *genicam) { g_return_val_if_fail (ARV_IS_GC (genicam), ARV_ACCESS_CHECK_POLICY_DISABLE); return genicam->priv->access_check_policy; } static void _weak_notify_cb (gpointer data, GObject *object) { ArvGc *genicam = data; genicam->priv->buffer = NULL; } void arv_gc_set_buffer (ArvGc *genicam, ArvBuffer *buffer) { g_return_if_fail (ARV_IS_GC (genicam)); g_return_if_fail (ARV_IS_BUFFER (buffer)); if (genicam->priv->buffer != NULL) g_object_weak_unref (G_OBJECT (genicam->priv->buffer), _weak_notify_cb, genicam); g_object_weak_ref (G_OBJECT (buffer), _weak_notify_cb, genicam); genicam->priv->buffer = buffer; } /** * arv_gc_get_buffer: * @genicam: a #ArvGc object * * Retrieves the binded buffer. * * Return value: (transfer none): a #ArvBuffer. */ ArvBuffer * arv_gc_get_buffer (ArvGc *genicam) { g_return_val_if_fail (ARV_IS_GC (genicam), NULL); return genicam->priv->buffer; } guint64 arv_gc_register_cache_error_add (ArvGc *genicam, guint64 n_errors) { g_return_val_if_fail (ARV_IS_GC (genicam), 0); genicam->priv->n_register_cache_errors += n_errors; return genicam->priv->n_register_cache_errors; } ArvGc * arv_gc_new (ArvDevice *device, const void *xml, size_t size) { ArvDomDocument *document; ArvGc *genicam; document = arv_dom_document_new_from_memory (xml, size, NULL); if (!ARV_IS_GC (document)) { if (document != NULL) g_object_unref (document); return NULL; } genicam = ARV_GC (document); genicam->priv->device = device; return genicam; } G_DEFINE_TYPE_WITH_CODE (ArvGc, arv_gc, ARV_TYPE_DOM_DOCUMENT, G_ADD_PRIVATE (ArvGc)) static void arv_gc_init (ArvGc *genicam) { genicam->priv = arv_gc_get_instance_private (genicam); genicam->priv->nodes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); genicam->priv->cache_policy = ARV_REGISTER_CACHE_POLICY_DISABLE; } static void arv_gc_finalize (GObject *object) { ArvGc *genicam = ARV_GC (object); if (genicam->priv->buffer != NULL) g_object_weak_unref (G_OBJECT (genicam->priv->buffer), _weak_notify_cb, genicam); g_hash_table_unref (genicam->priv->nodes); G_OBJECT_CLASS (arv_gc_parent_class)->finalize (object); } static void arv_gc_class_init (ArvGcClass *node_class) { GObjectClass *object_class = G_OBJECT_CLASS (node_class); ArvDomNodeClass *d_node_class = ARV_DOM_NODE_CLASS (node_class); ArvDomDocumentClass *d_document_class = ARV_DOM_DOCUMENT_CLASS (node_class); object_class->finalize = arv_gc_finalize; d_node_class->can_append_child = arv_gc_can_append_child; d_document_class->create_element = arv_gc_create_element; } aravis-0.8.34/src/arvgc.h000066400000000000000000000112141475431451200151420ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_H #define ARV_GC_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_GC_ERROR arv_gc_error_quark() ARV_API GQuark arv_gc_error_quark (void); typedef enum { ARV_GC_ERROR_PROPERTY_NOT_DEFINED, ARV_GC_ERROR_PVALUE_NOT_DEFINED, ARV_GC_ERROR_INVALID_PVALUE, ARV_GC_ERROR_EMPTY_ENUMERATION, ARV_GC_ERROR_OUT_OF_RANGE, ARV_GC_ERROR_NO_DEVICE_SET, ARV_GC_ERROR_NO_EVENT_IMPLEMENTATION, ARV_GC_ERROR_NODE_NOT_FOUND, ARV_GC_ERROR_ENUM_ENTRY_NOT_FOUND, ARV_GC_ERROR_INVALID_LENGTH, ARV_GC_ERROR_READ_ONLY, ARV_GC_ERROR_SET_FROM_STRING_UNDEFINED, ARV_GC_ERROR_GET_AS_STRING_UNDEFINED, ARV_GC_ERROR_INVALID_BIT_RANGE, ARV_GC_ERROR_INVALID_SYNTAX } ArvGcError; /** * ArvRegisterCachePolicy: * @ARV_REGISTER_CACHE_POLICY_DISABLE: disable register caching * @ARV_REGISTER_CACHE_POLICY_ENABLE: enable register caching * @ARV_REGISTER_CACHE_POLICY_DEBUG: enable register caching, but read the acual register value for comparison * @ARV_REGISTER_CACHE_POLICY_DEFAULT: default cache policy * * Since: 0.8.0 */ typedef enum { ARV_REGISTER_CACHE_POLICY_DISABLE, ARV_REGISTER_CACHE_POLICY_ENABLE, ARV_REGISTER_CACHE_POLICY_DEBUG, ARV_REGISTER_CACHE_POLICY_DEFAULT = ARV_REGISTER_CACHE_POLICY_DISABLE } ArvRegisterCachePolicy; /** * ArvRangeCheckPolicy: * @ARV_RANGE_CHECK_POLICY_DISABLE: never check if float or integer node value is in min/max range * @ARV_RANGE_CHECK_POLICY_ENABLE: always check if if float or integer node is in min/max range * @ARV_RANGE_CHECK_POLICY_DEBUG: check the value, but only display an error message if the value is not allowed (Since 0.8.8) * @ARV_RANGE_CHECK_POLICY_DEFAULT: default range check policy * * Since: 0.8.6 */ typedef enum { ARV_RANGE_CHECK_POLICY_DISABLE, ARV_RANGE_CHECK_POLICY_ENABLE, ARV_RANGE_CHECK_POLICY_DEBUG, ARV_RANGE_CHECK_POLICY_DEFAULT = ARV_RANGE_CHECK_POLICY_DISABLE } ArvRangeCheckPolicy; /** * ArvAccessCheckPolicy: * @ARV_ACCESS_CHECK_POLICY_DISABLE: never check the register access mode * @ARV_ACCESS_CHECK_POLICY_ENABLE: always check the register access mode * @ARV_ACCESSE_CHECK_POLICY_DEFAULT: default access check policy * * Since: 0.8.6 */ typedef enum { ARV_ACCESS_CHECK_POLICY_DISABLE, ARV_ACCESS_CHECK_POLICY_ENABLE, ARV_ACCESS_CHECK_POLICY_DEFAULT = ARV_ACCESS_CHECK_POLICY_DISABLE } ArvAccessCheckPolicy; #define ARV_TYPE_GC (arv_gc_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGc, arv_gc, ARV, GC, ArvDomDocument) ARV_API ArvGc * arv_gc_new (ArvDevice *device, const void *xml, size_t size); ARV_API void arv_gc_register_feature_node (ArvGc *genicam, ArvGcFeatureNode *node); ARV_API void arv_gc_set_register_cache_policy (ArvGc *genicam, ArvRegisterCachePolicy policy); ARV_API ArvRegisterCachePolicy arv_gc_get_register_cache_policy (ArvGc *genicam); ARV_API void arv_gc_set_range_check_policy (ArvGc *genicam, ArvRangeCheckPolicy policy); ARV_API ArvRangeCheckPolicy arv_gc_get_range_check_policy (ArvGc *genicam); ARV_API void arv_gc_set_access_check_policy (ArvGc *genicam, ArvAccessCheckPolicy policy); ARV_API ArvAccessCheckPolicy arv_gc_get_access_check_policy (ArvGc *genicam); ARV_API void arv_gc_set_default_node_data (ArvGc *genicam, const char *node_name, ...) G_GNUC_NULL_TERMINATED; ARV_API ArvGcNode * arv_gc_get_node (ArvGc *genicam, const char *name); ARV_API ArvDevice * arv_gc_get_device (ArvGc *genicam); ARV_API void arv_gc_set_buffer (ArvGc *genicam, ArvBuffer *buffer); ARV_API ArvBuffer * arv_gc_get_buffer (ArvGc *genicam); G_END_DECLS #endif aravis-0.8.34/src/arvgcboolean.c000066400000000000000000000173101475431451200165000ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcboolean * @short_description: Class for Boolean nodes */ #include #include #include #include #include #include static GObjectClass *parent_class = NULL; struct _ArvGcBoolean { ArvGcFeatureNode node; ArvGcPropertyNode *value; ArvGcPropertyNode *on_value; ArvGcPropertyNode *off_value; }; struct _ArvGcBooleanClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcBoolean, arv_gc_boolean, ARV_TYPE_GC_FEATURE_NODE); /* ArvDomNode implementation */ static const char * arv_gc_boolean_get_node_name (ArvDomNode *node) { return "Boolean"; } static void arv_gc_boolean_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcBoolean *node = ARV_GC_BOOLEAN (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ON_VALUE: node->on_value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_OFF_VALUE: node->off_value = property_node; break; default: ARV_DOM_NODE_CLASS (parent_class)->post_new_child (self, child); break; } } } static void arv_gc_boolean_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcBoolean implementation */ static gint64 arv_gc_boolean_get_on_value (ArvGcBoolean *gc_boolean, GError **error) { gint64 on_value; GError *local_error = NULL; if (gc_boolean->on_value == NULL) return 1; on_value = arv_gc_property_node_get_int64 (gc_boolean->on_value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); return 1; } return on_value; } static gint64 arv_gc_boolean_get_off_value (ArvGcBoolean *gc_boolean, GError **error) { gint64 off_value; GError *local_error = NULL; if (gc_boolean->off_value == NULL) return 0; off_value = arv_gc_property_node_get_int64 (gc_boolean->off_value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); return 0; } return off_value; } /** * arv_gc_boolean_get_value: * @gc_boolean: a #ArvGcBoolean * @error: a #GError placeholder, %NULL to ignore * * Returns: the feature value. * * Since: 0.8.0 */ gboolean arv_gc_boolean_get_value (ArvGcBoolean *gc_boolean, GError **error) { gboolean value; gint64 on_value; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_BOOLEAN (gc_boolean), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (gc_boolean->value == NULL) return FALSE; if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (gc_boolean), error)) return FALSE; value = arv_gc_property_node_get_int64 (gc_boolean->value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); return FALSE; } on_value = arv_gc_boolean_get_on_value (gc_boolean, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); return FALSE; } return value == on_value; } /** * arv_gc_boolean_get_value_gi: (rename-to arv_gc_boolean_get_value) * @gc_boolean: a #ArvGcBoolean * @value: (out): feature value * @error: a #GError placeholder, %NULL to ignore * * Get the feature value. * * Since: 0.8.0 */ void arv_gc_boolean_get_value_gi (ArvGcBoolean *gc_boolean, gboolean *value, GError **error) { GError *local_error = NULL; g_return_if_fail (value != NULL); *value = arv_gc_boolean_get_value (gc_boolean, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); } void arv_gc_boolean_set_value (ArvGcBoolean *gc_boolean, gboolean v_boolean, GError **error) { gboolean value; GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_BOOLEAN (gc_boolean)); g_return_if_fail (error == NULL || *error == NULL); if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (gc_boolean), error)) return; if (v_boolean) value = arv_gc_boolean_get_on_value (gc_boolean, &local_error); else value = arv_gc_boolean_get_off_value (gc_boolean, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); return; } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_boolean)); arv_gc_property_node_set_int64 (gc_boolean->value, value, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_boolean))); } static ArvGcFeatureNode * arv_gc_boolean_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcBoolean *gc_boolean = ARV_GC_BOOLEAN (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_boolean->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_boolean->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } ArvGcNode * arv_gc_boolean_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_BOOLEAN, NULL); return node; } static void arv_gc_boolean_init (ArvGcBoolean *gc_boolean) { } static void arv_gc_boolean_finalize (GObject *object) { parent_class->finalize (object); } static void arv_gc_boolean_class_init (ArvGcBooleanClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); parent_class = g_type_class_peek_parent (this_class); object_class->finalize = arv_gc_boolean_finalize; dom_node_class->get_node_name = arv_gc_boolean_get_node_name; dom_node_class->post_new_child = arv_gc_boolean_post_new_child; dom_node_class->pre_remove_child = arv_gc_boolean_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_boolean_get_linked_feature; gc_feature_node_class->default_access_mode = ARV_GC_ACCESS_MODE_RW; } aravis-0.8.34/src/arvgcboolean.h000066400000000000000000000032451475431451200165070ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_BOOLEAN_H #define ARV_GC_BOOLEAN_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_BOOLEAN (arv_gc_boolean_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcBoolean, arv_gc_boolean, ARV, GC_BOOLEAN, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_boolean_new (void); ARV_API gboolean arv_gc_boolean_get_value (ArvGcBoolean *gc_boolean, GError **error); ARV_API void arv_gc_boolean_get_value_gi (ArvGcBoolean *gc_boolean, gboolean *value, GError **error); ARV_API void arv_gc_boolean_set_value (ArvGcBoolean *gc_boolean, gboolean v_boolean, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgccategory.c000066400000000000000000000074231475431451200167020ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgccategory * @short_description: Class for Category nodes */ #include #include #include #include struct _ArvGcCategory { ArvGcFeatureNode base; GSList *features; }; struct _ArvGcCategoryClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcCategory, arv_gc_category, ARV_TYPE_GC_FEATURE_NODE) /* ArvDomNode implementation */ static const char * arv_gc_category_get_node_name (ArvDomNode *node) { return "Category"; } static gboolean arv_gc_category_can_append_child (ArvDomNode *parent, ArvDomNode *child) { return ARV_IS_GC_PROPERTY_NODE (child); } /* ArvGcCategory implementation */ static void _free_features (ArvGcCategory *category) { GSList *iter; for (iter = category->features; iter != NULL; iter = iter->next) g_free (iter->data); g_slist_free (category->features); category->features = NULL; } /** * arv_gc_category_get_features: * @category: a #ArvGcCategory * * Get a list of strings with the name of the features listed in the given category node. * * Returns: (element-type utf8) (transfer none): a list of strings. */ const GSList * arv_gc_category_get_features (ArvGcCategory *category) { ArvDomNode *iter; ArvGcNode *node; g_return_val_if_fail (ARV_IS_GC_CATEGORY (category), NULL); _free_features (category); for (iter = arv_dom_node_get_first_child (ARV_DOM_NODE (category)); iter != NULL; iter = arv_dom_node_get_next_sibling (iter)) { if (arv_gc_property_node_get_node_type (ARV_GC_PROPERTY_NODE (iter)) == ARV_GC_PROPERTY_NODE_TYPE_P_FEATURE) { node = arv_gc_property_node_get_linked_node (ARV_GC_PROPERTY_NODE (iter)); if (ARV_IS_GC_FEATURE_NODE (node)) { char *name; name = g_strdup (arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (node))); category->features = g_slist_append (category->features, name); } } } return category->features; } ArvGcNode * arv_gc_category_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_CATEGORY, NULL); return node; } static void arv_gc_category_init (ArvGcCategory *gc_category) { gc_category->features = NULL; } static void arv_gc_category_finalize (GObject *object) { ArvGcCategory *category = ARV_GC_CATEGORY (object); _free_features (category); G_OBJECT_CLASS (arv_gc_category_parent_class)->finalize (object); } static void arv_gc_category_class_init (ArvGcCategoryClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); object_class->finalize = arv_gc_category_finalize; dom_node_class->get_node_name = arv_gc_category_get_node_name; dom_node_class->can_append_child = arv_gc_category_can_append_child; } aravis-0.8.34/src/arvgccategory.h000066400000000000000000000026721475431451200167100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_CATEGORY_H #define ARV_GC_CATEGORY_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_CATEGORY (arv_gc_category_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcCategory, arv_gc_category, ARV, GC_CATEGORY, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_category_new (void); ARV_API const GSList * arv_gc_category_get_features (ArvGcCategory *category); G_END_DECLS #endif aravis-0.8.34/src/arvgccommand.c000066400000000000000000000120001475431451200164660ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgccommand * @short_description: Class for Command nodes */ #include #include #include #include #include #include #include #include #include struct _ArvGcCommand { ArvGcFeatureNode node; ArvGcPropertyNode *command_value; ArvGcPropertyNode *value; }; struct _ArvGcCommandClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcCommand, arv_gc_command, ARV_TYPE_GC_FEATURE_NODE) /* ArvGcFeatureNode implementation */ static const char * arv_gc_command_get_node_name (ArvDomNode *node) { return "Command"; } static void arv_gc_command_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcCommand *node = ARV_GC_COMMAND (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_COMMAND_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_COMMAND_VALUE: node->command_value = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_command_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_command_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcFeatureNode implementation */ /* ArvGcCommand implementation */ void arv_gc_command_execute (ArvGcCommand *gc_command, GError **error) { ArvGc *genicam; GError *local_error = NULL; gint64 command_value; g_return_if_fail (ARV_IS_GC_COMMAND (gc_command)); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (gc_command)); g_return_if_fail (ARV_IS_GC (genicam)); if (gc_command->value == NULL) return; if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (gc_command), error)) return; command_value = arv_gc_property_node_get_int64 (gc_command->command_value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_command))); return; } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_command)); arv_gc_property_node_set_int64 (gc_command->value, command_value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_command))); return; } arv_debug_genicam ("[GcCommand::execute] %s (0x%" G_GINT64_MODIFIER "x)", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_command)), command_value); } static ArvGcFeatureNode * arv_gc_command_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcCommand *gc_command = ARV_GC_COMMAND (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_command->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_command->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } ArvGcNode * arv_gc_command_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_COMMAND, NULL); return node; } static void arv_gc_command_init (ArvGcCommand *gc_command) { } static void arv_gc_command_finalize (GObject *object) { G_OBJECT_CLASS (arv_gc_command_parent_class)->finalize (object); } static void arv_gc_command_class_init (ArvGcCommandClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_command_finalize; dom_node_class->get_node_name = arv_gc_command_get_node_name; dom_node_class->post_new_child = arv_gc_command_post_new_child; dom_node_class->pre_remove_child = arv_gc_command_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_command_get_linked_feature; } aravis-0.8.34/src/arvgccommand.h000066400000000000000000000027221475431451200165050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_COMMAND_H #define ARV_GC_COMMAND_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_COMMAND (arv_gc_command_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcCommand, arv_gc_command, ARV, GC_COMMAND, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_command_new (void); ARV_API void arv_gc_command_execute (ArvGcCommand *gc_command, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcconverter.c000066400000000000000000000477121475431451200171010ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcconverter * @short_description: Class for Converter and IntConverter nodes */ #include #include #include #include #include #include #include #include #include typedef struct { GSList *variables; /* ArvGcVariableNode list */ GSList *constants; /* ArvGcVariableNode list */ GSList *expressions; /* ArvGcVariableNode list */ ArvGcPropertyNode *value; ArvGcPropertyNode *formula_to_node; ArvGcPropertyNode *formula_from_node; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; ArvGcPropertyNode *display_notation; ArvGcPropertyNode *display_precision; ArvGcPropertyNode *is_linear; ArvGcPropertyNode *slope; ArvEvaluator *formula_to; ArvEvaluator *formula_from; } ArvGcConverterPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvGcConverter, arv_gc_converter, ARV_TYPE_GC_FEATURE_NODE, G_ADD_PRIVATE (ArvGcConverter)) /* ArvDomNode implementation */ static void arv_gc_converter_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (ARV_GC_CONVERTER (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_P_VARIABLE: priv->variables = g_slist_prepend (priv->variables, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: priv->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA_TO: priv->formula_to_node = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA_FROM: priv->formula_from_node = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_EXPRESSION: priv->expressions = g_slist_prepend (priv->expressions, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_CONSTANT: priv->constants = g_slist_prepend (priv->constants, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: priv->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: priv->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION: priv->display_notation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION: priv->display_precision = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_IS_LINEAR: priv->is_linear = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_SLOPE: priv->slope = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_converter_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_converter_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcConverter implementation */ static void arv_gc_converter_init (ArvGcConverter *self) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (ARV_GC_CONVERTER (self)); priv->formula_to = arv_evaluator_new (NULL); priv->formula_from = arv_evaluator_new (NULL); priv->value = NULL; } static ArvGcFeatureNode * arv_gc_converter_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (ARV_GC_CONVERTER (gc_feature_node)); ArvGcNode *pvalue_node = NULL; if (priv->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (priv->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } static void arv_gc_converter_finalize (GObject *object) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (ARV_GC_CONVERTER (object)); g_slist_free (priv->variables); g_slist_free (priv->expressions); g_slist_free (priv->constants); g_object_unref (priv->formula_to); g_object_unref (priv->formula_from); G_OBJECT_CLASS (arv_gc_converter_parent_class)->finalize (object); } static void arv_gc_converter_class_init (ArvGcConverterClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_converter_finalize; dom_node_class->post_new_child = arv_gc_converter_post_new_child; dom_node_class->pre_remove_child = arv_gc_converter_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_converter_get_linked_feature; } /* ArvGcInteger interface implementation */ ArvGcIsLinear arv_gc_converter_get_is_linear (ArvGcConverter *gc_converter, GError **error) { GError *local_error = NULL; ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); const char *string; g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), ARV_GC_IS_LINEAR_NO); if (priv->is_linear == NULL) return ARV_GC_IS_LINEAR_NO; string = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (priv->is_linear), &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); if (g_strcmp0 ("Yes", string) == 0) return ARV_GC_IS_LINEAR_YES; return ARV_GC_IS_LINEAR_NO; } static gboolean arv_gc_converter_update_from_variables (ArvGcConverter *gc_converter, ArvGcConverterNodeType node_type, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); ArvGcNode *node = NULL; GError *local_error = NULL; GSList *iter; const char *expression; if (priv->formula_from_node != NULL) expression = arv_gc_property_node_get_string (priv->formula_from_node, &local_error); else expression = ""; if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } arv_evaluator_set_expression (priv->formula_from, expression); for (iter = priv->expressions; iter != NULL; iter = iter->next) { const char *expression; const char *name; expression = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_sub_expression (priv->formula_from, name, expression); } for (iter = priv->constants; iter != NULL; iter = iter->next) { const char *constant; const char *name; constant = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_constant (priv->formula_from, name, constant); } for (iter = priv->variables; iter != NULL; iter = iter->next) { ArvGcPropertyNode *variable_node = iter->data; node = arv_gc_property_node_get_linked_node (ARV_GC_PROPERTY_NODE (variable_node)); if (ARV_IS_GC_INTEGER (node)) { gint64 value; value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } arv_evaluator_set_int64_variable (priv->formula_from, arv_gc_property_node_get_name (variable_node), value); } else if (ARV_IS_GC_FLOAT (node)) { double value; value = arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } arv_evaluator_set_double_variable (priv->formula_from, arv_gc_property_node_get_name (variable_node), value); } } if (priv->value != NULL) { node = arv_gc_property_node_get_linked_node (priv->value); if (ARV_IS_GC_INTEGER (node)) { gint64 value; switch (node_type) { case ARV_GC_CONVERTER_NODE_TYPE_MIN: value = arv_gc_integer_get_min (ARV_GC_INTEGER (node), &local_error); /* Default minimum, don't convert it */ if (value == G_MININT64) return FALSE; break; case ARV_GC_CONVERTER_NODE_TYPE_MAX: value = arv_gc_integer_get_max (ARV_GC_INTEGER (node), &local_error); /* Default maximum, don't convert it */ if (value == G_MAXINT64) return FALSE; break; case ARV_GC_CONVERTER_NODE_TYPE_INC: value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node), &local_error); break; default: value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); break; } if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } arv_evaluator_set_int64_variable (priv->formula_from, "TO", value); } else if (ARV_IS_GC_FLOAT (node)) { double value; switch (node_type) { case ARV_GC_CONVERTER_NODE_TYPE_MIN: value = arv_gc_float_get_min (ARV_GC_FLOAT (node), &local_error); /* Default minimum, don't convert it */ if (value == -G_MAXDOUBLE) return FALSE; break; case ARV_GC_CONVERTER_NODE_TYPE_MAX: value = arv_gc_float_get_max (ARV_GC_FLOAT (node), &local_error); /* Default maximum, don't convert it */ if (value == G_MAXDOUBLE) return FALSE; break; case ARV_GC_CONVERTER_NODE_TYPE_INC: value = arv_gc_float_get_inc (ARV_GC_FLOAT (node), &local_error); /* Default increment, don't convert it */ if (value == G_MINDOUBLE) return FALSE; break; default: value = arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error); break; } if (local_error != NULL) { g_propagate_error (error, local_error); return FALSE; } arv_evaluator_set_double_variable (priv->formula_from, "TO", value); } else { arv_warning_genicam ("[GcConverter::set_value] Invalid pValue node '%s'", arv_gc_property_node_get_string (priv->value, NULL)); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_PVALUE, "Invalid pValue node '%s'", arv_gc_property_node_get_string (priv->value, NULL)); return FALSE; } } else { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PVALUE_NOT_DEFINED, "pValue node not defined"); return FALSE; } return TRUE; } double arv_gc_converter_convert_to_double (ArvGcConverter *gc_converter, ArvGcConverterNodeType node_type, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); GError *local_error = NULL; double value; g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), 0.0); if (!arv_gc_converter_update_from_variables (gc_converter, node_type, &local_error)) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); switch (node_type) { case ARV_GC_CONVERTER_NODE_TYPE_MIN: return -G_MAXDOUBLE; case ARV_GC_CONVERTER_NODE_TYPE_MAX: return G_MAXDOUBLE; default: return 0.0; } } value = arv_evaluator_evaluate_as_double (priv->formula_from, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); return value; } gint64 arv_gc_converter_convert_to_int64 (ArvGcConverter *gc_converter, ArvGcConverterNodeType node_type, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); GError *local_error = NULL; gint64 value; g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), 0); if (!arv_gc_converter_update_from_variables (gc_converter, node_type, &local_error)) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); switch (node_type) { case ARV_GC_CONVERTER_NODE_TYPE_MIN: return G_MININT64; case ARV_GC_CONVERTER_NODE_TYPE_MAX: return G_MAXINT64; default: return 0; } } value = arv_evaluator_evaluate_as_double (priv->formula_from, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); return value; } static void arv_gc_converter_update_to_variables (ArvGcConverter *gc_converter, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); ArvGcNode *node; GError *local_error = NULL; GSList *iter; const char *expression; if (priv->formula_to_node != NULL) expression = arv_gc_property_node_get_string (priv->formula_to_node, &local_error); else expression = ""; if (local_error != NULL) { g_propagate_error (error, local_error); return; } arv_evaluator_set_expression (priv->formula_to, expression); for (iter = priv->expressions; iter != NULL; iter = iter->next) { const char *expression; const char *name; expression = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_sub_expression (priv->formula_to, name, expression); } for (iter = priv->constants; iter != NULL; iter = iter->next) { const char *constant; const char *name; constant = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_constant (priv->formula_to, name, constant); } for (iter = priv->variables; iter != NULL; iter = iter->next) { ArvGcPropertyNode *variable_node = iter->data; node = arv_gc_property_node_get_linked_node (ARV_GC_PROPERTY_NODE (variable_node)); if (ARV_IS_GC_INTEGER (node)) { gint64 value; value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } arv_evaluator_set_int64_variable (priv->formula_to, arv_gc_property_node_get_name (variable_node), value); } else if (ARV_IS_GC_FLOAT (node)) { double value; value = arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } arv_evaluator_set_double_variable (priv->formula_to, arv_gc_property_node_get_name (variable_node), value); } } if (priv->value != NULL) { node = arv_gc_property_node_get_linked_node (priv->value); if (ARV_IS_GC_INTEGER (node)) { arv_gc_integer_set_value (ARV_GC_INTEGER (node), arv_evaluator_evaluate_as_double (priv->formula_to, NULL), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } } else if (ARV_IS_GC_FLOAT (node)) { arv_gc_float_set_value (ARV_GC_FLOAT (node), arv_evaluator_evaluate_as_double (priv->formula_to, NULL), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } } else arv_warning_genicam ("[GcConverter::set_value] Invalid pValue node '%s'", arv_gc_property_node_get_string (priv->value, NULL)); } } void arv_gc_converter_convert_from_double (ArvGcConverter *gc_converter, double value, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_CONVERTER (gc_converter)); arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_converter)); arv_evaluator_set_double_variable (priv->formula_to, "FROM", value); arv_gc_converter_update_to_variables (gc_converter, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); } void arv_gc_converter_convert_from_int64 (ArvGcConverter *gc_converter, gint64 value, GError **error) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_CONVERTER (gc_converter)); arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_converter)); arv_evaluator_set_int64_variable (priv->formula_to, "FROM", value); arv_gc_converter_update_to_variables (gc_converter, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_converter))); } ArvGcRepresentation arv_gc_converter_get_representation (ArvGcConverter *gc_converter) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), ARV_GC_REPRESENTATION_UNDEFINED); if (priv->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (ARV_GC_PROPERTY_NODE (priv->representation), ARV_GC_REPRESENTATION_UNDEFINED); } const char * arv_gc_converter_get_unit (ArvGcConverter *gc_converter) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), NULL); if (priv->unit == NULL) return NULL; return arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (priv->unit), NULL); } ArvGcDisplayNotation arv_gc_converter_get_display_notation (ArvGcConverter *gc_converter) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), ARV_GC_DISPLAY_NOTATION_DEFAULT); if (priv->display_notation == NULL) return ARV_GC_DISPLAY_NOTATION_DEFAULT; return arv_gc_property_node_get_display_notation (ARV_GC_PROPERTY_NODE (priv->display_notation), ARV_GC_DISPLAY_NOTATION_DEFAULT); } gint64 arv_gc_converter_get_display_precision (ArvGcConverter *gc_converter) { ArvGcConverterPrivate *priv = arv_gc_converter_get_instance_private (gc_converter); g_return_val_if_fail (ARV_IS_GC_CONVERTER (gc_converter), ARV_GC_DISPLAY_PRECISION_DEFAULT); if (priv->display_precision == NULL) return ARV_GC_DISPLAY_PRECISION_DEFAULT; return arv_gc_property_node_get_display_precision (ARV_GC_PROPERTY_NODE (priv->display_precision), ARV_GC_DISPLAY_PRECISION_DEFAULT); } aravis-0.8.34/src/arvgcconverter.h000066400000000000000000000026221475431451200170750ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_CONVERTER_H #define ARV_GC_CONVERTER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_CONVERTER (arv_gc_converter_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcConverter, arv_gc_converter, ARV, GC_CONVERTER, ArvGcFeatureNode) struct _ArvGcConverterClass { ArvGcFeatureNodeClass parent_class; }; G_END_DECLS #endif aravis-0.8.34/src/arvgcconverternode.c000066400000000000000000000125331475431451200177400ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcconverternode * @short_description: Converter node class */ #include #include #include #include #include struct _ArvGcConverterNode { ArvGcConverter converter; }; struct _ArvGcConverterNodeClass { ArvGcConverterClass parent_class; }; static const char * arv_gc_converter_node_get_node_name (ArvDomNode *node) { return "Converter"; } ArvGcNode * arv_gc_converter_node_new (void) { return g_object_new (ARV_TYPE_GC_CONVERTER_NODE, NULL); } static void arv_gc_converter_node_init (ArvGcConverterNode *gc_converter_node) { } static void arv_gc_converter_node_class_init (ArvGcConverterNodeClass *this_class) { ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); dom_node_class->get_node_name = arv_gc_converter_node_get_node_name; } static double arv_gc_converter_get_float_value (ArvGcFloat *gc_float, GError **error) { return arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_VALUE, error); } static double arv_gc_converter_get_float_min (ArvGcFloat *gc_float, GError **error) { GError *local_error = NULL; double a, b; /* TODO: we should use the Slope node here, instead of using MIN (min, max) */ a = arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_MIN, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return -G_MAXDOUBLE; } b = arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_MAX, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return -G_MAXDOUBLE; } return MIN (a, b); } static double arv_gc_converter_get_float_max (ArvGcFloat *gc_float, GError **error) { GError *local_error = NULL; double a, b; /* TODO: we should use the Slope node here, instead of using MAX (min, max) */ a = arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_MIN, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXDOUBLE; } b = arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_MAX, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXDOUBLE; } return MAX (a, b); } static double _get_inc (ArvGcFloat *gc_float, GError **error) { GError *local_error = NULL; ArvGcIsLinear is_linear; is_linear = arv_gc_converter_get_is_linear (ARV_GC_CONVERTER (gc_float), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MINDOUBLE; } if (is_linear == ARV_GC_IS_LINEAR_NO) return G_MINDOUBLE; return arv_gc_converter_convert_to_double (ARV_GC_CONVERTER (gc_float), ARV_GC_CONVERTER_NODE_TYPE_INC, &local_error); } static ArvGcRepresentation arv_gc_converter_get_float_representation (ArvGcFloat *gc_float) { return arv_gc_converter_get_representation (ARV_GC_CONVERTER (gc_float)); } static const char * arv_gc_converter_get_float_unit (ArvGcFloat *gc_float) { return arv_gc_converter_get_unit (ARV_GC_CONVERTER (gc_float)); } static ArvGcDisplayNotation arv_gc_converter_get_float_display_notation (ArvGcFloat *gc_float) { return arv_gc_converter_get_display_notation (ARV_GC_CONVERTER (gc_float)); } static gint64 arv_gc_converter_get_float_display_precision (ArvGcFloat *gc_float) { return arv_gc_converter_get_display_precision (ARV_GC_CONVERTER (gc_float)); } static void arv_gc_converter_set_float_value (ArvGcFloat *gc_float, double value, GError **error) { arv_gc_converter_convert_from_double (ARV_GC_CONVERTER (gc_float), value, error); } static void arv_gc_converter_node_float_interface_init (ArvGcFloatInterface *interface) { interface->get_value = arv_gc_converter_get_float_value; interface->get_min = arv_gc_converter_get_float_min; interface->get_max = arv_gc_converter_get_float_max; interface->get_inc = _get_inc; interface->set_value = arv_gc_converter_set_float_value; interface->get_representation = arv_gc_converter_get_float_representation; interface->get_unit = arv_gc_converter_get_float_unit; interface->get_display_notation = arv_gc_converter_get_float_display_notation; interface->get_display_precision = arv_gc_converter_get_float_display_precision; } G_DEFINE_TYPE_WITH_CODE (ArvGcConverterNode, arv_gc_converter_node, ARV_TYPE_GC_CONVERTER, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_FLOAT, arv_gc_converter_node_float_interface_init)) aravis-0.8.34/src/arvgcconverternode.h000066400000000000000000000025751475431451200177520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_CONVERTER_NODE_H #define ARV_GC_CONVERTER_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_CONVERTER_NODE (arv_gc_converter_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcConverterNode, arv_gc_converter_node, ARV, GC_CONVERTER_NODE, ArvGcConverter) ARV_API ArvGcNode * arv_gc_converter_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcconverterprivate.h000066400000000000000000000041341475431451200204700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_CONVERTER_PRIVATE_H #define ARV_GC_CONVERTER_PRIVATE_H #include #include G_BEGIN_DECLS typedef enum { ARV_GC_CONVERTER_NODE_TYPE_VALUE, ARV_GC_CONVERTER_NODE_TYPE_MIN, ARV_GC_CONVERTER_NODE_TYPE_MAX, ARV_GC_CONVERTER_NODE_TYPE_INC } ArvGcConverterNodeType; ArvGcRepresentation arv_gc_converter_get_representation (ArvGcConverter *gc_converter); ArvGcDisplayNotation arv_gc_converter_get_display_notation (ArvGcConverter *gc_converter); gint64 arv_gc_converter_get_display_precision (ArvGcConverter *gc_converter); const char * arv_gc_converter_get_unit (ArvGcConverter *gc_converter); ArvGcIsLinear arv_gc_converter_get_is_linear (ArvGcConverter *gc_converter, GError **error); gint64 arv_gc_converter_convert_to_int64 (ArvGcConverter *gc_converter, ArvGcConverterNodeType node_type, GError **error); double arv_gc_converter_convert_to_double (ArvGcConverter *gc_converter, ArvGcConverterNodeType node_type, GError **error); void arv_gc_converter_convert_from_int64 (ArvGcConverter *gc_converter, gint64 value, GError **error); void arv_gc_converter_convert_from_double (ArvGcConverter *gc_converter, double value, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcdefaultsprivate.h000066400000000000000000000050061475431451200202670ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_DEFAULTS_PRIVATE_H #define ARV_GC_DEFAULTS_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /* Node default values */ #define ARV_GC_OFF_VALUE_DEFAULT 0 #define ARV_GC_ON_VALUE_DEFAULT 1 /* TODO #define ARV_GC_STREAMABLE_DEFAULT ARV_GC_STREAMABLE_NO */ #define ARV_GC_DISPLAY_NOTATION_DEFAULT ARV_GC_DISPLAY_NOTATION_AUTOMATIC #define ARV_GC_DISPLAY_PRECISION_DEFAULT 6 #define ARV_GC_IS_LINEAR_DEFAULT ARV_GC_IS_LINEAR_NO #define ARV_GC_REPRESENTATION_DEFAULT ARV_GC_REPRESENTATION_PURE_NUMBER /* TODO #define ARV_GC_SLOPE_DEFAULT ARV_GC_SLOPE_AUTOMATIC */ #define ARV_GC_UNIT_DEFAULT "" /* TODO #define ARV_GC_IS_SELF_CLEARING_DEFAULT ARV_GC_IS_SELF_CLEARING_NO */ #define ARV_GC_CACHABLE_DEFAULT ARV_GC_CACHABLE_WRITE_AROUND #define ARV_GC_SIGN_DEFAULT ARV_GC_SIGNEDNESS_UNSIGNED /* TODO #define ARV_GC_IS_DEPRECATED_DEFAULT ARV_GC_IS_DEPRECATED_NO */ #define ARV_GC_IMPOSED_ACCESS_MODE_DEFAULT ARV_GC_ACCESS_MODE_RW #define ARV_GC_VISIBILITY_DEFAULT ARV_GC_VISIBILITY_BEGINNER /* TODO #define ARV_GC_CACHE_CHUNK_DATA ARV_GC_CACHE_CHUNK_DATA_NO */ /* TODO #define ARV_GC_SWAP_ENDIANNESS_DEFAULT ARV_GC_SWAP_ENDIANNESS_NO */ #define ARV_GC_FLOAT_MIN_DEFAULT -G_MAXDOUBLE #define ARV_GC_FLOAT_MAX_DEFAULT G_MAXDOUBLE #define ARV_GC_INTEGER_INC_DEFAULT 1 #define ARV_GC_INTEGER_MIN_DEFAULT G_MININT64 #define ARV_GC_INTEGER_MAX_DEFAULT G_MAXINT64 /* Node attribute defaults */ #define ARV_GC_NAME_SPACE_DEFAULT ARV_GC_NAME_SPACE_CUSTOM #define ARV_GC_MERGE_PRIORITY_DEFAULT 0 G_END_DECLS #endif aravis-0.8.34/src/arvgcenumentry.c000066400000000000000000000067461475431451200171220ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcenumentry * @short_description: Class for EnumEntry nodes */ #include #include #include struct _ArvGcEnumEntry { ArvGcFeatureNode base; ArvGcPropertyNode *value; }; struct _ArvGcEnumEntryClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcEnumEntry, arv_gc_enum_entry, ARV_TYPE_GC_FEATURE_NODE) /* ArvDomNode implementation */ static const char * arv_gc_enum_entry_get_node_name (ArvDomNode *node) { return "EnumEntry"; } static void arv_gc_enum_entry_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcEnumEntry *node = ARV_GC_ENUM_ENTRY (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_enum_entry_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_integer_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcFeatureNode implementation */ /* ArvGcEnumEntry implementation */ gint64 arv_gc_enum_entry_get_value (ArvGcEnumEntry *entry, GError **error) { gint64 value; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_ENUM_ENTRY (entry), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); if (entry->value == NULL) return 0; value = arv_gc_property_node_get_int64 (entry->value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (entry))); return 0; } return value; } ArvGcNode * arv_gc_enum_entry_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_ENUM_ENTRY, NULL); return node; } static void arv_gc_enum_entry_init (ArvGcEnumEntry *gc_enum_entry) { gc_enum_entry->value = 0; } static void arv_gc_enum_entry_finalize (GObject *object) { G_OBJECT_CLASS (arv_gc_enum_entry_parent_class)->finalize (object); } static void arv_gc_enum_entry_class_init (ArvGcEnumEntryClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); object_class->finalize = arv_gc_enum_entry_finalize; dom_node_class->get_node_name = arv_gc_enum_entry_get_node_name; dom_node_class->post_new_child = arv_gc_enum_entry_post_new_child; dom_node_class->pre_remove_child = arv_gc_integer_node_pre_remove_child; } aravis-0.8.34/src/arvgcenumentry.h000066400000000000000000000027551475431451200171230ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_ENUM_ENTRY_H #define ARV_GC_ENUM_ENTRY_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_ENUM_ENTRY (arv_gc_enum_entry_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcEnumEntry, arv_gc_enum_entry, ARV, GC_ENUM_ENTRY, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_enum_entry_new (void); ARV_API gint64 arv_gc_enum_entry_get_value (ArvGcEnumEntry *entry, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcenumeration.c000066400000000000000000000516411475431451200174140ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcenumeration * @short_description: Class for Enumeration nodes */ #include #include #include #include #include #include #include #include #include #include struct _ArvGcEnumeration { ArvGcFeatureNode base; ArvGcPropertyNode *value; GSList *entries; GSList *selecteds; /* #ArvGcPropertyNode */ GSList *selected_features; /* #ArvGcFeatureNode */ }; struct _ArvGcEnumerationClass { ArvGcFeatureNodeClass parent_class; }; static void arv_gc_enumeration_integer_interface_init (ArvGcIntegerInterface *interface); static void arv_gc_enumeration_string_interface_init (ArvGcStringInterface *interface); static void arv_gc_enumeration_selector_interface_init (ArvGcSelectorInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcEnumeration, arv_gc_enumeration, ARV_TYPE_GC_FEATURE_NODE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_enumeration_integer_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_STRING, arv_gc_enumeration_string_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_SELECTOR, arv_gc_enumeration_selector_interface_init)) /* ArvGcDomNode implementation */ static const char * arv_gc_enumeration_get_node_name (ArvDomNode *node) { return "Enumeration"; } static gboolean arv_gc_enumeration_can_append_child (ArvDomNode *node, ArvDomNode *child) { return (ARV_IS_GC_ENUM_ENTRY (child) || ARV_IS_GC_PROPERTY_NODE (child)); } static void arv_gc_enumeration_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcEnumeration *node = ARV_GC_ENUMERATION (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: node->selecteds = g_slist_prepend (node->selecteds, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_enumeration_parent_class)->post_new_child (self, child); break; } } else if (ARV_IS_GC_ENUM_ENTRY (child)) node->entries = g_slist_prepend (node->entries, child); } static void arv_gc_enumeration_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcEnumeration implementation */ /** * arv_gc_enumeration_dup_available_int_values: * @enumeration: a #ArvGcEnumeration * @n_values: (out): the number of values * @error: (out): the error that occured, or NULL * * Return value: (transfer full) (array length=n_values): a newly allocated array of 64 bit integers, to be freed after * use using g_free(). * * Since: 0.8.0 */ gint64 * arv_gc_enumeration_dup_available_int_values (ArvGcEnumeration *enumeration, guint *n_values, GError **error) { gint64 *values; const GSList *entries, *iter; GSList *available_entries = NULL; unsigned int i; GError *local_error = NULL; g_return_val_if_fail (n_values != NULL, NULL); *n_values = 0; g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); entries = arv_gc_enumeration_get_entries (enumeration); *n_values = 0; for (iter = entries; iter != NULL; iter = iter->next) { gboolean is_available; is_available = arv_gc_feature_node_is_available (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); *n_values = 0; g_slist_free (available_entries); return NULL; } if (is_available) { gboolean is_implemented; is_implemented = arv_gc_feature_node_is_implemented (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); *n_values = 0; g_slist_free (available_entries); return NULL; } if (is_implemented) { (*n_values)++; available_entries = g_slist_prepend (available_entries, iter->data); } } } if (*n_values == 0) { g_slist_free (available_entries); return NULL; } values = g_new (gint64, *n_values); for (iter = available_entries, i = 0; iter != NULL; iter = iter->next) { values[i] = arv_gc_enum_entry_get_value (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); *n_values = 0; g_slist_free (available_entries); g_free (values); return NULL; } i++; } g_slist_free (available_entries); return values; } static const char ** _dup_available_string_values (ArvGcEnumeration *enumeration, gboolean display_name ,guint *n_values, GError **error) { const char ** strings; const GSList *entries, *iter; GSList *available_entries = NULL; unsigned int i; GError *local_error = NULL; g_return_val_if_fail (n_values != NULL, NULL); *n_values = 0; g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); entries = arv_gc_enumeration_get_entries (enumeration); *n_values = 0; for (iter = entries; iter != NULL; iter = iter->next) { gboolean is_available; is_available = arv_gc_feature_node_is_available (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); *n_values = 0; g_slist_free (available_entries); return NULL; } if (is_available) { gboolean is_implemented; is_implemented = arv_gc_feature_node_is_implemented (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); *n_values = 0; g_slist_free (available_entries); return NULL; } if (is_implemented) { (*n_values)++; available_entries = g_slist_prepend (available_entries, iter->data); } } } if (*n_values == 0) { g_slist_free (available_entries); return NULL; } strings = g_new (const char*, *n_values); for (iter = available_entries, i = 0; iter != NULL; iter = iter->next, i++) { const char *string = NULL; if (display_name) string = arv_gc_feature_node_get_display_name (iter->data); if (string == NULL) string = arv_gc_feature_node_get_name (iter->data); strings[i] = string; } g_slist_free (available_entries); return strings; } /** * arv_gc_enumeration_dup_available_string_values: * @enumeration: an #ArvGcEnumeration * @n_values: (out): placeholder for the number of values * @error: placeholder for error, may be NULL * * Create an array of all available values of @enumeration, as strings. * * Returns: (array length=n_values) (transfer container): an newly created array of const strings, which must freed after use using g_free, * %NULL on error. * * Since: 0.8.0 */ const char ** arv_gc_enumeration_dup_available_string_values (ArvGcEnumeration *enumeration, guint *n_values, GError **error) { return _dup_available_string_values (enumeration, FALSE, n_values, error); } /** * arv_gc_enumeration_dup_available_display_names: * @enumeration: an #ArvGcEnumeration * @n_values: (out): placeholder for the number of values * @error: placeholder for error, may be NULL * * Create an array of display names of all available entries. * * Returns: (array length=n_values) (transfer container): an newly created array of const strings, which must freed after use using g_free, * %NULL on error. * * Since: 0.8.0 */ const char ** arv_gc_enumeration_dup_available_display_names (ArvGcEnumeration *enumeration, guint *n_values, GError **error) { return _dup_available_string_values (enumeration, TRUE, n_values, error); } static gint64 _get_int_value (ArvGcEnumeration *enumeration, GError **error) { GError *local_error = NULL; gint64 value; g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); if (enumeration->value == NULL) return 0; value = arv_gc_property_node_get_int64 (enumeration->value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return 0; } return value; } gint64 arv_gc_enumeration_get_int_value (ArvGcEnumeration *enumeration, GError **error) { if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (enumeration), error)) return 0; return _get_int_value (enumeration, error); } static gboolean _set_int_value (ArvGcEnumeration *enumeration, gint64 value, GError **error) { g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (enumeration->value) { GError *local_error = NULL; { gint64 *available_values; unsigned n_values; unsigned i; gboolean found = FALSE; available_values = arv_gc_enumeration_dup_available_int_values (enumeration, &n_values, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } if (available_values == NULL) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_EMPTY_ENUMERATION, "[%s] No available entry found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } for (i = 0; i < n_values; i++) if (available_values[i] == value) found = TRUE; g_free (available_values); if (!found) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_OUT_OF_RANGE, "[%s] Value %" G_GINT64_FORMAT " not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration)), value); return FALSE; } } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (enumeration)); arv_gc_property_node_set_int64 (enumeration->value, value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } return TRUE; } g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } gboolean arv_gc_enumeration_set_int_value (ArvGcEnumeration *enumeration, gint64 value, GError **error) { if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (enumeration), error)) return FALSE; return _set_int_value (enumeration, value, error); } static const char * _get_string_value (ArvGcEnumeration *enumeration, GError **error) { const GSList *iter; GError *local_error = NULL; gint64 value; g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); value = _get_int_value (enumeration, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return NULL; } for (iter = enumeration->entries; iter != NULL; iter = iter->next) { gint64 enum_value; enum_value = arv_gc_enum_entry_get_value (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return NULL; } if (enum_value == value) { const char *string; string = arv_gc_feature_node_get_name (iter->data); arv_debug_genicam ("[GcEnumeration::get_string_value] value = %" G_GINT64_FORMAT " - string = %s", value, string); return string; } } arv_warning_genicam ("[GcEnumeration::get_string_value] value = %" G_GINT64_FORMAT " not found for node %s", value, arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return NULL; } const char * arv_gc_enumeration_get_string_value (ArvGcEnumeration *enumeration, GError **error) { if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (enumeration), error)) return NULL; return _get_string_value (enumeration, error); } static gboolean _set_string_value (ArvGcEnumeration *enumeration, const char *value, GError **error) { const GSList *iter; g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); for (iter = enumeration->entries; iter != NULL; iter = iter->next) if (g_strcmp0 (arv_gc_feature_node_get_name (iter->data), value) == 0) { GError *local_error = NULL; gint64 enum_value; enum_value = arv_gc_enum_entry_get_value (iter->data, &local_error); arv_debug_genicam ("[GcEnumeration::set_string_value] value = %" G_GINT64_FORMAT " - string = %s", enum_value, value); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } _set_int_value (enumeration, enum_value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration))); return FALSE; } return TRUE; } arv_warning_genicam ("[GcEnumeration::set_string_value] entry %s not found", value); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_ENUM_ENTRY_NOT_FOUND, "[%s] '%s' not an entry", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (enumeration)), value); return FALSE; } gboolean arv_gc_enumeration_set_string_value (ArvGcEnumeration *enumeration, const char *value, GError **error) { if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (enumeration), error)) return FALSE; return _set_string_value (enumeration, value, error); } /** * arv_gc_enumeration_get_entries: * @enumeration: a #ArvGcEnumeration * * Returns: (element-type ArvGcFeatureNode) (transfer none): the list of enumeration entry nodes. */ const GSList * arv_gc_enumeration_get_entries (ArvGcEnumeration *enumeration) { g_return_val_if_fail (ARV_IS_GC_ENUMERATION (enumeration), NULL); return enumeration->entries; } static ArvGcFeatureNode * arv_gc_enumeration_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_enumeration->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_enumeration->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } ArvGcNode * arv_gc_enumeration_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_ENUMERATION, NULL); return node; } static void arv_gc_enumeration_init (ArvGcEnumeration *gc_enumeration) { } static void arv_gc_enumeration_finalize (GObject *object) { ArvGcEnumeration *enumeration = ARV_GC_ENUMERATION (object); g_clear_pointer (&enumeration->entries, g_slist_free); g_clear_pointer (&enumeration->selecteds, g_slist_free); g_clear_pointer (&enumeration->selected_features, g_slist_free); G_OBJECT_CLASS (arv_gc_enumeration_parent_class)->finalize (object); } static void arv_gc_enumeration_class_init (ArvGcEnumerationClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_enumeration_finalize; dom_node_class->get_node_name = arv_gc_enumeration_get_node_name; dom_node_class->can_append_child = arv_gc_enumeration_can_append_child; dom_node_class->post_new_child = arv_gc_enumeration_post_new_child; dom_node_class->pre_remove_child = arv_gc_enumeration_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_enumeration_get_linked_feature; gc_feature_node_class->default_access_mode = ARV_GC_ACCESS_MODE_RW; } /* ArvGcInteger interface implementation */ static gint64 arv_gc_enumeration_get_integer_value (ArvGcInteger *gc_integer, GError **error) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_integer); return _get_int_value (gc_enumeration, error); } static void arv_gc_enumeration_set_integer_value (ArvGcInteger *gc_integer, gint64 value, GError **error) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_integer); _set_int_value (gc_enumeration, value, error); } static void arv_gc_enumeration_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_enumeration_get_integer_value; interface->set_value = arv_gc_enumeration_set_integer_value; } static const char * arv_gc_enumeration_get_str_value (ArvGcString *gc_string, GError **error) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_string); return _get_string_value (gc_enumeration, error); } static void arv_gc_enumeration_set_str_value (ArvGcString *gc_string, const char *value, GError **error) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_string); _set_string_value (gc_enumeration, value, error); } static gint64 arv_gc_enumeration_get_max_string_length (ArvGcString *gc_string, GError **error) { ArvGcEnumeration *gc_enumeration = ARV_GC_ENUMERATION (gc_string); const GSList *entries, *iter; gint64 length, max_length = 0; entries = arv_gc_enumeration_get_entries (gc_enumeration); for (iter = entries; iter != NULL; iter = iter->next) { const char *name; name = arv_gc_feature_node_get_name (iter->data); length = name != NULL ? strlen (name) : 0; if (length > max_length) max_length = length; } return max_length; } static void arv_gc_enumeration_string_interface_init (ArvGcStringInterface *interface) { interface->get_value = arv_gc_enumeration_get_str_value; interface->set_value = arv_gc_enumeration_set_str_value; interface->get_max_length = arv_gc_enumeration_get_max_string_length; } static const GSList * arv_gc_enumeration_get_selected_features (ArvGcSelector *selector) { ArvGcEnumeration *enumeration = ARV_GC_ENUMERATION (selector); GSList *iter; g_clear_pointer (&enumeration->selected_features, g_slist_free); for (iter = enumeration->selecteds; iter != NULL; iter = iter->next) { ArvGcFeatureNode *feature_node = ARV_GC_FEATURE_NODE (arv_gc_property_node_get_linked_node (iter->data)); if (ARV_IS_GC_FEATURE_NODE (feature_node)) enumeration->selected_features = g_slist_prepend (enumeration->selected_features, feature_node); } return enumeration->selected_features; } static void arv_gc_enumeration_selector_interface_init (ArvGcSelectorInterface *interface) { interface->get_selected_features = arv_gc_enumeration_get_selected_features; } aravis-0.8.34/src/arvgcenumeration.h000066400000000000000000000045011475431451200174120ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_ENUMERATION_H #define ARV_GC_ENUMERATION_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_ENUMERATION (arv_gc_enumeration_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcEnumeration, arv_gc_enumeration, ARV, GC_ENUMERATION, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_enumeration_new (void); ARV_API const GSList * arv_gc_enumeration_get_entries (ArvGcEnumeration *enumeration); ARV_API const char * arv_gc_enumeration_get_string_value (ArvGcEnumeration *enumeration, GError **error); ARV_API gboolean arv_gc_enumeration_set_string_value (ArvGcEnumeration *enumeration, const char *value, GError **error); ARV_API gint64 arv_gc_enumeration_get_int_value (ArvGcEnumeration *enumeration, GError **error); ARV_API gboolean arv_gc_enumeration_set_int_value (ArvGcEnumeration *enumeration, gint64 value, GError **error); ARV_API gint64 * arv_gc_enumeration_dup_available_int_values (ArvGcEnumeration *enumeration, guint *n_values, GError **error); ARV_API const char ** arv_gc_enumeration_dup_available_string_values (ArvGcEnumeration *enumeration, guint *n_values, GError **error); ARV_API const char ** arv_gc_enumeration_dup_available_display_names (ArvGcEnumeration *enumeration, guint *n_values, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcenums.c000066400000000000000000000030501475431451200162040ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include static unsigned int _from_string (const char *string, const char **strings, unsigned int n_strings) { unsigned int i; if (string == NULL) return 0; for (i = 0; i < n_strings; i++) if (g_strcmp0 (string, strings[i]) == 0) return i; return 0; } static const char *arv_gc_access_mode_strings[] = { "RO", "WO", "RW" }; const char * arv_gc_access_mode_to_string (ArvGcAccessMode value) { return arv_gc_access_mode_strings[CLAMP (value, 0, ARV_GC_ACCESS_MODE_RW)]; } ArvGcAccessMode arv_gc_access_mode_from_string (const char *string) { return _from_string (string, arv_gc_access_mode_strings, G_N_ELEMENTS (arv_gc_access_mode_strings)); } aravis-0.8.34/src/arvgcenums.h000066400000000000000000000147721475431451200162260ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_ENUMS_H #define ARV_GC_ENUMS_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * ArvGcNameSpace: * @ARV_GC_NAME_SPACE_UNDEFINED: undefined name space * @ARV_GC_NAME_SPACE_STANDARD: Genicam standardized name space * @ARV_GC_NAME_SPACE_CUSTOM: non-standardized name space * * Specifies feature node or register name space type. Standard name space features are listed in * Genicam materials. Any other vendor-specific features should use custom name space type. */ typedef enum { ARV_GC_NAME_SPACE_UNDEFINED = -1, ARV_GC_NAME_SPACE_STANDARD, ARV_GC_NAME_SPACE_CUSTOM } ArvGcNameSpace; /** * ArvGcAccessMode: * @ARV_GC_ACCESS_MODE_UNDEFINED: undefined access mode * @ARV_GC_ACCESS_MODE_RO: read-only access * @ARV_GC_ACCESS_MODE_WO: write-only access * @ARV_GC_ACCESS_MODE_RW: read and write access * * Specifies access mode for feature nodes and registers. */ typedef enum { ARV_GC_ACCESS_MODE_UNDEFINED = -1, ARV_GC_ACCESS_MODE_RO, ARV_GC_ACCESS_MODE_WO, ARV_GC_ACCESS_MODE_RW } ArvGcAccessMode; ARV_API const char * arv_gc_access_mode_to_string (ArvGcAccessMode value); ARV_API ArvGcAccessMode arv_gc_access_mode_from_string (const char *string); /** * ArvGcCachable: * @ARV_GC_CACHABLE_UNDEFINED: undefined cache mode * @ARV_GC_CACHABLE_NO_CACHE: no value caching * @ARV_GC_CACHABLE_WRITE_THROUGH: write-through cache mode * @ARV_GC_CACHABLE_WRITE_AROUND: write-around cache mode * * Specifies caching mode for register values. */ typedef enum { ARV_GC_CACHABLE_UNDEFINED = -1, ARV_GC_CACHABLE_NO_CACHE, ARV_GC_CACHABLE_WRITE_THROUGH, ARV_GC_CACHABLE_WRITE_AROUND } ArvGcCachable; /** * ArvGcSignedness: * @ARV_GC_SIGNEDNESS_UNDEFINED: undefined sign * @ARV_GC_SIGNEDNESS_SIGNED: signed integer * @ARV_GC_SIGNEDNESS_UNSIGNED: unsigned integer * * Specifies signedness of integer registers. Per standard Genicam internally uses signed 64-bit * signed integers for representing all integer registers. Therefore unsigned 64-bit integers are * not available. */ typedef enum { ARV_GC_SIGNEDNESS_UNDEFINED = -1, ARV_GC_SIGNEDNESS_SIGNED, ARV_GC_SIGNEDNESS_UNSIGNED } ArvGcSignedness; /** * ArvGcIsLinear: * @ARV_GC_IS_LINEAR_UNDEFINED: undefined relationship between variables * @ARV_GC_IS_LINEAR_NO: non-linear relationship between variables * @ARV_GC_IS_LINEAR_YES: linear relationship between variables * * Describes relationship between TO and FROM variables in Converter feature nodes. */ typedef enum { ARV_GC_IS_LINEAR_UNDEFINED = -1, ARV_GC_IS_LINEAR_NO, ARV_GC_IS_LINEAR_YES } ArvGcIsLinear; /** * ArvGcVisibility: * @ARV_GC_VISIBILITY_UNDEFINED: undefined feature visibility level * @ARV_GC_VISIBILITY_INVISIBLE: feature should be not be visible in user interface * @ARV_GC_VISIBILITY_GURU: very advanced feature to be shown to very experienced users * @ARV_GC_VISIBILITY_EXPERT: advanced feature to be shown to expert users * @ARV_GC_VISIBILITY_BEGINNER: basic feature to be shown to all users * * Specifies feature node recommended visibility in user interfaces. */ typedef enum { ARV_GC_VISIBILITY_UNDEFINED = -1, ARV_GC_VISIBILITY_INVISIBLE, ARV_GC_VISIBILITY_GURU, ARV_GC_VISIBILITY_EXPERT, ARV_GC_VISIBILITY_BEGINNER } ArvGcVisibility; /** * ArvGcRepresentation: * @ARV_GC_REPRESENTATION_UNDEFINED: undefined representation * @ARV_GC_REPRESENTATION_LINEAR: number presented on linear scale (e.g. on a linear slider) * @ARV_GC_REPRESENTATION_LOGARITHMIC: number presented on logarithmic scale (e.g. on a logarithmic slider) * @ARV_GC_REPRESENTATION_BOOLEAN: binary choice (e.g. a checkbox) * @ARV_GC_REPRESENTATION_PURE_NUMBER: number presented in an editable field (e.g. a spinbox) * @ARV_GC_REPRESENTATION_HEX_NUMBER: number presented in hexadecimal format * @ARV_GC_REPRESENTATION_IPV4_ADDRESS: IPv4 address * @ARV_GC_REPRESENTATION_MAC_ADDRESS: MAC address * * Number representation formats. * * Since: 0.8.0 */ typedef enum { ARV_GC_REPRESENTATION_UNDEFINED = -1, ARV_GC_REPRESENTATION_LINEAR, ARV_GC_REPRESENTATION_LOGARITHMIC, ARV_GC_REPRESENTATION_BOOLEAN, ARV_GC_REPRESENTATION_PURE_NUMBER, ARV_GC_REPRESENTATION_HEX_NUMBER, ARV_GC_REPRESENTATION_IPV4_ADDRESS, ARV_GC_REPRESENTATION_MAC_ADDRESS } ArvGcRepresentation; /** * ArvGcDisplayNotation: * @ARV_GC_DISPLAY_NOTATION_UNDEFINED: undefined number notation * @ARV_GC_DISPLAY_NOTATION_AUTOMATIC: automatically detect whether to use fixed or scientific number notation * @ARV_GC_DISPLAY_NOTATION_FIXED: used fixed (i.e. decimal) notation for displaying numbers * @ARV_GC_DISPLAY_NOTATION_SCIENTIFIC: use scientific notation for displaying numbers * * Number display notations for showing numbers in user interfaces. * * Since: 0.8.0 */ typedef enum { ARV_GC_DISPLAY_NOTATION_UNDEFINED = -1, ARV_GC_DISPLAY_NOTATION_AUTOMATIC, ARV_GC_DISPLAY_NOTATION_FIXED, ARV_GC_DISPLAY_NOTATION_SCIENTIFIC } ArvGcDisplayNotation; /** * ArvGcStreamable: * @ARV_GC_STREAMABLE_UNDEFINED: undefined streamable * @ARV_GC_STREAMABLE_NO: the feature can't be used for camera state persistence * @ARV_GC_STREAMABLE_YES: the feature can be used for camera state persistence * * Denotes that the corresponding feature is prepared to be stored to and loaded from a file via the node tree. * The idea is to persist the state of a camera by storing the features marked as Streamable and restore the state by * writing those features back to the node tree. * * Since: 0.8.8 */ typedef enum { ARV_GC_STREAMABLE_UNDEFINED = -1, ARV_GC_STREAMABLE_NO, ARV_GC_STREAMABLE_YES } ArvGcStreamable; G_END_DECLS #endif aravis-0.8.34/src/arvgcfeaturenode.c000066400000000000000000000505261475431451200173700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcfeaturenode * @short_description: Base class for Genicam feature nodes * * #ArvGcFeatureNode provides a base class for the implementation of the different * types of Genicam feature node (Group, Integer, Float, Enumeration...). */ #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { char *name; ArvGcNameSpace name_space; char *comment; ArvGcPropertyNode *tooltip; ArvGcPropertyNode *description; ArvGcPropertyNode *visibility; ArvGcPropertyNode *display_name; ArvGcPropertyNode *is_implemented; ArvGcPropertyNode *is_available; ArvGcPropertyNode *is_locked; ArvGcPropertyNode *imposed_access_mode; ArvGcPropertyNode *streamable; ArvGcPropertyNode *is_deprecated; ArvGcPropertyNode *alias; ArvGcPropertyNode *cast_alias; guint64 change_count; char *string_buffer; } ArvGcFeatureNodePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvGcFeatureNode, arv_gc_feature_node, ARV_TYPE_GC_NODE, G_ADD_PRIVATE (ArvGcFeatureNode)) /* ArvDomNode implementation */ static gboolean arv_gc_feature_node_can_append_child (ArvDomNode *self, ArvDomNode *child) { return ARV_IS_GC_NODE (child); } static void arv_gc_feature_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (ARV_GC_FEATURE_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_TOOLTIP: priv->tooltip = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DESCRIPTION: priv->description = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY: priv->visibility = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NAME: priv->display_name = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_AVAILABLE: priv->is_available = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_IMPLEMENTED: priv->is_implemented = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_LOCKED: priv->is_locked = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE: priv->imposed_access_mode = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_STREAMABLE: priv->streamable = property_node; /* TODO */ break; case ARV_GC_PROPERTY_NODE_TYPE_IS_DEPRECATED: priv->is_deprecated = property_node; /* TODO */ break; case ARV_GC_PROPERTY_NODE_TYPE_P_ALIAS: priv->alias = property_node; /* TODO */ break; case ARV_GC_PROPERTY_NODE_TYPE_P_CAST_ALIAS: priv->alias = property_node; /* TODO */ break; default: break; } } } static void arv_gc_feature_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (ARV_GC_FEATURE_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_TOOLTIP: priv->tooltip = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_DESCRIPTION: priv->description = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY: priv->visibility = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NAME: priv->description = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_AVAILABLE: priv->is_available = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_IMPLEMENTED: priv->is_implemented = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_LOCKED: priv->is_locked = NULL; break; case ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE: priv->imposed_access_mode = NULL; break; default: break; } } } /* ArvDomNode implementation */ static void arv_gc_feature_node_set_attribute (ArvDomElement *self, const char *name, const char *value) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (ARV_GC_FEATURE_NODE (self)); if (strcmp (name, "Name") == 0) { ArvGc *genicam; g_free (priv->name); priv->name = g_strdup (value); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (self)); /* Kludge around ugly Genicam specification (Really, pre-parsing for EnumEntry Name substitution ?) */ if (strcmp (arv_dom_node_get_node_name (ARV_DOM_NODE (self)), "EnumEntry") != 0) arv_gc_register_feature_node (genicam, ARV_GC_FEATURE_NODE (self)); } else if (strcmp (name, "NameSpace") == 0) { if (g_strcmp0 (value, "Standard") == 0) priv->name_space = ARV_GC_NAME_SPACE_STANDARD; else priv->name_space = ARV_GC_NAME_SPACE_CUSTOM; } else if (strcmp (name, "Comment") == 0) { g_free (priv->comment); priv->comment = g_strdup (value); } else arv_info_dom ("[GcFeature::set_attribute] Unknown attribute '%s'", name); } static const char * arv_gc_feature_node_get_attribute (ArvDomElement *self, const char *name) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (ARV_GC_FEATURE_NODE (self)); if (strcmp (name, "Name") == 0) return priv->name; else if (strcmp (name, "NameSpace") == 0) { switch (priv->name_space) { case ARV_GC_NAME_SPACE_STANDARD: return "Standard"; default: return "Custom"; } } else if (strcmp (name, "Comment") == 0) { return priv->comment; } arv_info_dom ("[GcFeature::set_attribute] Unknown attribute '%s'", name); return NULL; } /* ArvGcFeatureNode implementation */ static ArvGcFeatureNode * arv_gc_feature_node_get_linked_feature (ArvGcFeatureNode *node) { g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), NULL); return ARV_GC_FEATURE_NODE_GET_CLASS (node)->get_linked_feature (node); } static ArvGcAccessMode arv_gc_feature_node_get_access_mode (ArvGcFeatureNode *node) { g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), ARV_GC_ACCESS_MODE_UNDEFINED); return ARV_GC_FEATURE_NODE_GET_CLASS (node)->get_access_mode (node); } const char * arv_gc_feature_node_get_name (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), NULL); return priv->name; } /** * arv_gc_feature_node_get_name_space: * @gc_feature_node: a #ArvGcFeatureNode * * Get feature name space. * * Returns: Name space value as #ArvGcNameSpace. * * Since: 0.8.0 */ ArvGcNameSpace arv_gc_feature_node_get_name_space (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), ARV_GC_NAME_SPACE_CUSTOM); return priv->name_space; } const char * arv_gc_feature_node_get_tooltip (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), NULL); if (priv->tooltip == NULL) return NULL; return arv_gc_property_node_get_string (priv->tooltip, NULL); } const char * arv_gc_feature_node_get_description (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), NULL); if (priv->description == NULL) return NULL; return arv_gc_property_node_get_string (priv->description, NULL); } ArvGcVisibility arv_gc_feature_node_get_visibility (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), ARV_GC_VISIBILITY_UNDEFINED); return arv_gc_property_node_get_visibility (priv->visibility, ARV_GC_VISIBILITY_BEGINNER); } const char * arv_gc_feature_node_get_display_name (ArvGcFeatureNode *node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (node), NULL); if (priv->display_name == NULL) return NULL; return arv_gc_property_node_get_string (priv->display_name, NULL); } gboolean arv_gc_feature_node_is_implemented (ArvGcFeatureNode *gc_feature_node, GError **error) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (gc_feature_node); gboolean value; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (priv->is_implemented == NULL) return TRUE; value = arv_gc_property_node_get_int64 (priv->is_implemented, &local_error) != 0; if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (gc_feature_node)); return FALSE; } return value; } gboolean arv_gc_feature_node_is_available (ArvGcFeatureNode *gc_feature_node, GError **error) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (gc_feature_node); gboolean value; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (priv->is_available == NULL) return TRUE; value = arv_gc_property_node_get_int64 (priv->is_available, &local_error) != 0; if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (gc_feature_node)); return FALSE; } return value; } gboolean arv_gc_feature_node_is_locked (ArvGcFeatureNode *gc_feature_node, GError **error) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (gc_feature_node); gboolean locked; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), FALSE); if (priv->is_locked == NULL) return FALSE; locked = arv_gc_property_node_get_int64 (priv->is_locked, &local_error) != 0; if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (gc_feature_node)); return FALSE; } return locked; } /** * arv_gc_feature_node_get_imposed_access_mode: * @gc_feature_node: a #ArvGcFeatureNode * * Gets feature node imposed access mode property. * * Note that this function will not give the actual access mode. Please use * #arv_gc_feature_node_get_actual_access_mode to get an access mode combined from imposed access * mode and underlying register access mode properties. * * Returns: Access mode as #ArvGcAccessMode * * Since: 0.8.0 */ ArvGcAccessMode arv_gc_feature_node_get_imposed_access_mode (ArvGcFeatureNode *gc_feature_node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (gc_feature_node); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), ARV_GC_ACCESS_MODE_UNDEFINED); if (priv->imposed_access_mode == NULL) return ARV_GC_ACCESS_MODE_RW; return arv_gc_property_node_get_access_mode (priv->imposed_access_mode, ARV_GC_ACCESS_MODE_RW); } /** * arv_gc_feature_node_get_actual_access_mode: * @gc_feature_node: a #ArvGcFeatureNode * * Gets feature node allowed access mode. This is a combination of Genicam ImposedAccessMode and * AccessMode properties of underlying features and registers. * * Returns: Access mode as #ArvGcAccessMode * * Since: 0.8.0 */ ArvGcAccessMode arv_gc_feature_node_get_actual_access_mode (ArvGcFeatureNode *gc_feature_node) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (gc_feature_node); ArvGcAccessMode access_mode = ARV_GC_ACCESS_MODE_RO; ArvGcAccessMode imposed_access_mode = ARV_GC_ACCESS_MODE_RW; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), ARV_GC_ACCESS_MODE_UNDEFINED); if (ARV_IS_GC_PROPERTY_NODE (priv->imposed_access_mode)) imposed_access_mode = arv_gc_property_node_get_access_mode (priv->imposed_access_mode, imposed_access_mode); access_mode = arv_gc_feature_node_get_access_mode (gc_feature_node); if (access_mode == ARV_GC_ACCESS_MODE_RO && imposed_access_mode == ARV_GC_ACCESS_MODE_RW) { imposed_access_mode = ARV_GC_ACCESS_MODE_RO; } else if (access_mode == ARV_GC_ACCESS_MODE_WO && imposed_access_mode == ARV_GC_ACCESS_MODE_RW) { imposed_access_mode = ARV_GC_ACCESS_MODE_WO; } return imposed_access_mode; } /** * arv_gc_feature_node_set_value_from_string: * @gc_feature_node: a #ArvGcFeatureNode * @string: new node value, as string * @error: return location for a GError, or NULL * * Set the node value using a string representation of the value. May not be applicable to every node type, but safe. */ void arv_gc_feature_node_set_value_from_string (ArvGcFeatureNode *self, const char *string, GError **error) { GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_FEATURE_NODE (self)); g_return_if_fail (string != NULL); if (ARV_IS_GC_ENUMERATION (self)) { arv_gc_enumeration_set_string_value (ARV_GC_ENUMERATION (self), string, &local_error); } else if (ARV_IS_GC_INTEGER (self)) { gint64 value; char *end = NULL; value = g_ascii_strtoll (string, &end, 0); if (end == NULL || end[0] != '\0') { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_SYNTAX, "Invalid string for an integer feature (%s)", string); return; } arv_gc_integer_set_value (ARV_GC_INTEGER (self), value, &local_error); } else if (ARV_IS_GC_FLOAT (self)) { double value; char *end = NULL; value = g_ascii_strtod (string, &end); if (end == NULL || end[0] != '\0') { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_SYNTAX, "Invalid string for a float feature (%s)", string); return; } arv_gc_float_set_value (ARV_GC_FLOAT (self), value, &local_error); } else if (ARV_IS_GC_STRING (self)) { arv_gc_string_set_value (ARV_GC_STRING (self), string, &local_error); } else if (ARV_IS_GC_BOOLEAN (self)) { gboolean value; if (g_ascii_strcasecmp (string, "false") == 0 || g_ascii_strcasecmp (string, "0") == 0) value = FALSE; else if (g_ascii_strcasecmp (string, "true") == 0 || g_ascii_strcasecmp (string, "1") == 0) value = TRUE; else { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_SYNTAX, "Invalid string for a boolean feature (%s)", string); return; } arv_gc_boolean_set_value (ARV_GC_BOOLEAN (self), value, &local_error); } else { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_SET_FROM_STRING_UNDEFINED, "Don't know how to set value from string"); } if (local_error != NULL) g_propagate_error (error, local_error); } /** * arv_gc_feature_node_get_value_as_string: * @gc_feature_node: a #ArvGcFeatureNode * @error: return location for a GError, or NULL * * Retrieve the node value a string. * * Please note the string content is still owned by the @node object, which means the returned pointer may not be still valid after a new call to this function. * * Returns: (transfer none): a string representation of the node value, %NULL if not applicable. */ const char * arv_gc_feature_node_get_value_as_string (ArvGcFeatureNode *self, GError **error) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (self); GError *local_error = NULL; const char *value = NULL; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (self), NULL); if (ARV_IS_GC_ENUMERATION (self)) { value = arv_gc_enumeration_get_string_value (ARV_GC_ENUMERATION (self), &local_error); } else if (ARV_IS_GC_INTEGER (self)) { g_free (priv->string_buffer); priv->string_buffer = g_strdup_printf ("%" G_GINT64_FORMAT, arv_gc_integer_get_value (ARV_GC_INTEGER (self), &local_error)); value = priv->string_buffer; } else if (ARV_IS_GC_FLOAT (self)) { g_free (priv->string_buffer); priv->string_buffer = g_strdup_printf ("%g", arv_gc_float_get_value (ARV_GC_FLOAT (self), &local_error)); value = priv->string_buffer; } else if (ARV_IS_GC_STRING (self)) { value = arv_gc_string_get_value (ARV_GC_STRING (self), &local_error); } else if (ARV_IS_GC_BOOLEAN (self)) { value = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (self), &local_error) ? "true" : "false"; } else { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_SET_FROM_STRING_UNDEFINED, "Don't know how to set value from string"); } if (local_error != NULL) g_propagate_error (error, local_error); return value; } void arv_gc_feature_node_increment_change_count (ArvGcFeatureNode *self) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (self); g_return_if_fail (ARV_IS_GC_FEATURE_NODE (self)); priv->change_count++; } guint64 arv_gc_feature_node_get_change_count (ArvGcFeatureNode *self) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (self); g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (self), 0); return priv->change_count; } static void arv_gc_feature_node_init (ArvGcFeatureNode *self) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (self); priv->change_count = 0; } static void arv_gc_feature_node_finalize (GObject *object) { ArvGcFeatureNodePrivate *priv = arv_gc_feature_node_get_instance_private (ARV_GC_FEATURE_NODE(object)); g_clear_pointer (&priv->name, g_free); g_clear_pointer (&priv->comment, g_free); g_clear_pointer (&priv->string_buffer, g_free); G_OBJECT_CLASS (arv_gc_feature_node_parent_class)->finalize (object); } static ArvGcFeatureNode * _get_linked_feature (ArvGcFeatureNode *gc_feature_node) { return NULL; } static ArvGcAccessMode _get_access_mode (ArvGcFeatureNode *gc_feature_node) { ArvGcFeatureNode *pointed_node = arv_gc_feature_node_get_linked_feature (gc_feature_node); if (pointed_node) return arv_gc_feature_node_get_access_mode (pointed_node); return ARV_GC_FEATURE_NODE_GET_CLASS (gc_feature_node)->default_access_mode; } static void arv_gc_feature_node_class_init (ArvGcFeatureNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_feature_node_finalize; dom_node_class->can_append_child = arv_gc_feature_node_can_append_child; dom_node_class->post_new_child = arv_gc_feature_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_feature_node_pre_remove_child; dom_element_class->set_attribute = arv_gc_feature_node_set_attribute; dom_element_class->get_attribute = arv_gc_feature_node_get_attribute; this_class->get_linked_feature = _get_linked_feature; this_class->get_access_mode = _get_access_mode; this_class->default_access_mode = ARV_GC_ACCESS_MODE_RO; } aravis-0.8.34/src/arvgcfeaturenode.h000066400000000000000000000057001475431451200173670ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_FEATURE_NODE_H #define ARV_GC_FEATURE_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_FEATURE_NODE (arv_gc_feature_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcFeatureNode, arv_gc_feature_node, ARV, GC_FEATURE_NODE, ArvGcNode) struct _ArvGcFeatureNodeClass { ArvGcNodeClass parent_class; ArvGcFeatureNode * (*get_linked_feature) (ArvGcFeatureNode *gc_feature_node); ArvGcAccessMode (*get_access_mode) (ArvGcFeatureNode *gc_feature_node); ArvGcAccessMode default_access_mode; }; ARV_API const char * arv_gc_feature_node_get_name (ArvGcFeatureNode *gc_feature_node); ARV_API ArvGcNameSpace arv_gc_feature_node_get_name_space (ArvGcFeatureNode *gc_feature_node); ARV_API const char * arv_gc_feature_node_get_tooltip (ArvGcFeatureNode *gc_feature_node); ARV_API const char * arv_gc_feature_node_get_description (ArvGcFeatureNode *gc_feature_node); ARV_API const char * arv_gc_feature_node_get_display_name (ArvGcFeatureNode *gc_feature_node); ARV_API ArvGcVisibility arv_gc_feature_node_get_visibility (ArvGcFeatureNode *gc_feature_node); ARV_API gboolean arv_gc_feature_node_is_available (ArvGcFeatureNode *gc_feature_node, GError **error); ARV_API gboolean arv_gc_feature_node_is_implemented (ArvGcFeatureNode *gc_feature_node, GError **error); ARV_API gboolean arv_gc_feature_node_is_locked (ArvGcFeatureNode *gc_feature_node, GError **error); ARV_API ArvGcAccessMode arv_gc_feature_node_get_imposed_access_mode (ArvGcFeatureNode *gc_feature_node); ARV_API ArvGcAccessMode arv_gc_feature_node_get_actual_access_mode (ArvGcFeatureNode *gc_feature_node); ARV_API void arv_gc_feature_node_set_value_from_string (ArvGcFeatureNode *gc_feature_node, const char *string, GError **error); ARV_API const char * arv_gc_feature_node_get_value_as_string (ArvGcFeatureNode *gc_feature_node, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcfeaturenodeprivate.h000066400000000000000000000055751475431451200207740ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_FEATURE_NODE_PRIVATE_H #define ARV_GC_FEATURE_NODE_PRIVATE_H #include #include #include G_BEGIN_DECLS void arv_gc_feature_node_increment_change_count (ArvGcFeatureNode *gc_feature_node); guint64 arv_gc_feature_node_get_change_count (ArvGcFeatureNode *gc_feature_node); static inline gboolean arv_gc_feature_node_check_write_access (ArvGcFeatureNode *gc_feature_node, GError **error) { ArvGc *genicam; ArvAccessCheckPolicy policy; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), FALSE); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (gc_feature_node)); g_return_val_if_fail (ARV_IS_GC (genicam), FALSE); policy = arv_gc_get_access_check_policy (genicam); if (policy != ARV_ACCESS_CHECK_POLICY_ENABLE) return TRUE; if (arv_gc_feature_node_get_actual_access_mode (gc_feature_node) != ARV_GC_ACCESS_MODE_RO) return TRUE; g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_READ_ONLY, "[%s] Write error on read only feature", arv_gc_feature_node_get_name (gc_feature_node)); return FALSE; } static inline gboolean arv_gc_feature_node_check_read_access (ArvGcFeatureNode *gc_feature_node, GError **error) { ArvGc *genicam; ArvAccessCheckPolicy policy; g_return_val_if_fail (ARV_IS_GC_FEATURE_NODE (gc_feature_node), FALSE); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (gc_feature_node)); g_return_val_if_fail (ARV_IS_GC (genicam), FALSE); policy = arv_gc_get_access_check_policy (genicam); if (policy != ARV_ACCESS_CHECK_POLICY_ENABLE) return TRUE; if (arv_gc_feature_node_get_actual_access_mode (gc_feature_node) != ARV_GC_ACCESS_MODE_WO) return TRUE; g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_READ_ONLY, "[%s] Read error on write only feature", arv_gc_feature_node_get_name (gc_feature_node)); return FALSE; } G_END_DECLS #endif aravis-0.8.34/src/arvgcfloat.c000066400000000000000000000216571475431451200161770ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcfloat * @short_description: Float interface */ #include #include #include #include #include #include #include static void arv_gc_float_default_init (ArvGcFloatInterface *gc_float_iface) { } G_DEFINE_INTERFACE (ArvGcFloat, arv_gc_float, G_TYPE_OBJECT) double arv_gc_float_get_value (ArvGcFloat *gc_float, GError **error) { g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), 0.0); g_return_val_if_fail (error == NULL || *error == NULL, 0.0); if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (gc_float), error)) return 0.0; return ARV_GC_FLOAT_GET_IFACE (gc_float)->get_value (gc_float, error); } void arv_gc_float_set_value (ArvGcFloat *gc_float, double value, GError **error) { ArvGc *genicam; ArvRangeCheckPolicy policy; GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_FLOAT (gc_float)); g_return_if_fail (error == NULL || *error == NULL); if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (gc_float), error)) return; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (gc_float)); g_return_if_fail (ARV_IS_GC (genicam)); policy = arv_gc_get_range_check_policy (genicam); if (policy != ARV_RANGE_CHECK_POLICY_DISABLE) { ArvGcFloatInterface *iface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (iface->get_min != NULL) { double min = iface->get_min (gc_float, &local_error); if (local_error == NULL && value < min) { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_OUT_OF_RANGE, "[%s] Value '%g' lower than allowed minimum '%g'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float)), value, min); } } if (local_error == NULL && iface->get_max != NULL) { double max = iface->get_max (gc_float, &local_error); if (local_error == NULL && value > max) { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_OUT_OF_RANGE, "[%s] Value '%g' greater than allowed maximum '%g'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float)), value, max); } } if (local_error != NULL) { if (policy == ARV_RANGE_CHECK_POLICY_DEBUG) { arv_warning_policies ("Range check (%s) ignored", local_error->message); } else if (policy == ARV_RANGE_CHECK_POLICY_ENABLE) { g_propagate_error (error, local_error); return; } g_clear_error (&local_error); } } ARV_GC_FLOAT_GET_IFACE (gc_float)->set_value (gc_float, value, error); } /** * arv_gc_float_get_min: * @gc_float: a #ArvGcFloat * @error: a #GError placeholder, NULL to ignore * * Returns: The feature Min property, -#G_MAXDOUBLE if not defined or on error */ double arv_gc_float_get_min (ArvGcFloat *gc_float, GError **error) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), 0.0); g_return_val_if_fail (error == NULL || *error == NULL, 0.0); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_min != NULL) return float_interface->get_min (gc_float, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return -G_MAXDOUBLE; } /** * arv_gc_float_get_max: * @gc_float: a #ArvGcFloat * @error: a #GError placeholder, NULL to ignore * * Returns: The feature Max property, #G_MAXDOUBLE if not defined or on error */ double arv_gc_float_get_max (ArvGcFloat *gc_float, GError **error) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), 0.0); g_return_val_if_fail (error == NULL || *error == NULL, 0.0); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_max != NULL) return float_interface->get_max (gc_float, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return G_MAXDOUBLE; } /** * arv_gc_float_get_inc: * @gc_float: a #ArvGcFloat * @error: a #GError placeholder, NULL to ignore * * Returns: The feature Inc property, #G_MINDOUBLE if not defined or on error */ double arv_gc_float_get_inc (ArvGcFloat *gc_float, GError **error) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), G_MINDOUBLE); g_return_val_if_fail (error == NULL || *error == NULL, G_MINDOUBLE); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_inc != NULL) return float_interface->get_inc (gc_float, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return G_MINDOUBLE; } /** * arv_gc_float_get_representation: * @gc_float: a #ArvGcFloat * * Get number representation format. * * Returns: Number representation format as #ArvGcRepresentation. */ ArvGcRepresentation arv_gc_float_get_representation (ArvGcFloat *gc_float) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), ARV_GC_REPRESENTATION_UNDEFINED); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_representation != NULL) return float_interface->get_representation (gc_float); return ARV_GC_REPRESENTATION_UNDEFINED; } const char * arv_gc_float_get_unit (ArvGcFloat *gc_float) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), NULL); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_unit != NULL) return float_interface->get_unit (gc_float); return NULL; } /** * arv_gc_float_get_display_notation: * @gc_float: a #ArvGcFloat * * Get number display notation. * * Returns: Number display notation as #ArvGcDisplayNotation. * * Since: 0.8.0 */ ArvGcDisplayNotation arv_gc_float_get_display_notation (ArvGcFloat *gc_float) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), ARV_GC_DISPLAY_NOTATION_DEFAULT); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_display_notation != NULL) return float_interface->get_display_notation (gc_float); return ARV_GC_DISPLAY_NOTATION_DEFAULT; } /** * arv_gc_float_get_display_precision: * @gc_float: a #ArvGcFloat * * Gets number of digits to show in user interface. This number should always be positive and represent * total number of digits on left and right side of decimal. * * Returns: Number of digits to show. * * Since: 0.8.0 */ gint64 arv_gc_float_get_display_precision (ArvGcFloat *gc_float) { ArvGcFloatInterface *float_interface; g_return_val_if_fail (ARV_IS_GC_FLOAT (gc_float), ARV_GC_DISPLAY_PRECISION_DEFAULT); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->get_display_precision != NULL) return float_interface->get_display_precision (gc_float); return ARV_GC_DISPLAY_PRECISION_DEFAULT; } void arv_gc_float_impose_min (ArvGcFloat *gc_float, double minimum, GError **error) { ArvGcFloatInterface *float_interface; g_return_if_fail (ARV_IS_GC_FLOAT (gc_float)); g_return_if_fail (error == NULL || *error == NULL); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->impose_min != NULL) float_interface->impose_min (gc_float, minimum, error); else g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); } void arv_gc_float_impose_max (ArvGcFloat *gc_float, double maximum, GError **error) { ArvGcFloatInterface *float_interface; g_return_if_fail (ARV_IS_GC_FLOAT (gc_float)); g_return_if_fail (error == NULL || *error == NULL); float_interface = ARV_GC_FLOAT_GET_IFACE (gc_float); if (float_interface->impose_max != NULL) float_interface->impose_max (gc_float, maximum, error); else g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); } aravis-0.8.34/src/arvgcfloat.h000066400000000000000000000057521475431451200162020ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_FLOAT_H #define ARV_GC_FLOAT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_FLOAT (arv_gc_float_get_type ()) ARV_API G_DECLARE_INTERFACE (ArvGcFloat, arv_gc_float, ARV, GC_FLOAT, GObject) struct _ArvGcFloatInterface { GTypeInterface parent; double (*get_value) (ArvGcFloat *gc_float, GError **error); void (*set_value) (ArvGcFloat *gc_float, double value, GError **error); double (*get_min) (ArvGcFloat *gc_float, GError **error); double (*get_max) (ArvGcFloat *gc_float, GError **error); double (*get_inc) (ArvGcFloat *gc_float, GError **error); ArvGcRepresentation (*get_representation) (ArvGcFloat *gc_float); ArvGcDisplayNotation (*get_display_notation) (ArvGcFloat *gc_float); gint64 (*get_display_precision)(ArvGcFloat *gc_float); const char * (*get_unit) (ArvGcFloat *gc_float); void (*impose_min) (ArvGcFloat *gc_float, double minimum, GError **error); void (*impose_max) (ArvGcFloat *gc_float, double maximum, GError **error); }; ARV_API double arv_gc_float_get_value (ArvGcFloat *gc_float, GError **error); ARV_API void arv_gc_float_set_value (ArvGcFloat *gc_float, double value, GError **error); ARV_API double arv_gc_float_get_min (ArvGcFloat *gc_float, GError **error); ARV_API double arv_gc_float_get_max (ArvGcFloat *gc_float, GError **error); ARV_API double arv_gc_float_get_inc (ArvGcFloat *gc_float, GError **error); ARV_API ArvGcRepresentation arv_gc_float_get_representation (ArvGcFloat *gc_float); ARV_API const char * arv_gc_float_get_unit (ArvGcFloat *gc_float); ARV_API ArvGcDisplayNotation arv_gc_float_get_display_notation (ArvGcFloat *gc_float); ARV_API gint64 arv_gc_float_get_display_precision (ArvGcFloat *gc_float); ARV_API void arv_gc_float_impose_min (ArvGcFloat *gc_float, double minimum, GError **error); ARV_API void arv_gc_float_impose_max (ArvGcFloat *gc_float, double maximum, GError **error); /* FIXME has_inc is missing */ G_END_DECLS #endif aravis-0.8.34/src/arvgcfloatnode.c000066400000000000000000000377531475431451200170510ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcfloatnode * @short_description: Class for Float nodes */ #include #include #include #include #include #include #include #include #include #include struct _ArvGcFloatNode { ArvGcFeatureNode node; ArvGcPropertyNode *value; ArvGcPropertyNode *minimum; ArvGcPropertyNode *maximum; ArvGcPropertyNode *increment; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; ArvGcPropertyNode *display_notation; ArvGcPropertyNode *display_precision; ArvGcPropertyNode *index; GSList *value_indexed_nodes; ArvGcPropertyNode *value_default; }; struct _ArvGcFloatNodeClass { ArvGcFeatureNodeClass parent_class; }; static void arv_gc_float_node_float_interface_init (ArvGcFloatInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcFloatNode, arv_gc_float_node, ARV_TYPE_GC_FEATURE_NODE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_FLOAT, arv_gc_float_node_float_interface_init)) /* ArvDomNode implementation */ static const char * arv_gc_float_node_get_node_name (ArvDomNode *node) { return "Float"; } static void arv_gc_float_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcFloatNode *node = ARV_GC_FLOAT_NODE (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MINIMUM: case ARV_GC_PROPERTY_NODE_TYPE_P_MINIMUM: node->minimum = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MAXIMUM: case ARV_GC_PROPERTY_NODE_TYPE_P_MAXIMUM: node->maximum = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_INCREMENT: case ARV_GC_PROPERTY_NODE_TYPE_P_INCREMENT: node->increment = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: node->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: node->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION: node->display_notation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION: node->display_precision = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_INDEX: node->index = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_VALUE_INDEXED: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_INDEXED: node->value_indexed_nodes = g_slist_prepend (node->value_indexed_nodes, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_VALUE_DEFAULT: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_DEFAULT: node->value_default = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_float_node_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_float_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcFeatureNode implementation */ static ArvGcPropertyNode * _get_value_node (ArvGcFloatNode *gc_float_node, GError **error) { GError *local_error = NULL; if (gc_float_node->value != NULL) return gc_float_node->value; if (gc_float_node->index != NULL) { gint64 index; GSList *iter; index = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (gc_float_node->index), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return NULL; } for (iter = gc_float_node->value_indexed_nodes; iter != NULL; iter = iter->next) if (arv_gc_value_indexed_node_get_index (iter->data) == index) return iter->data; if (gc_float_node->value_default != NULL) return gc_float_node->value_default; } return NULL; } /* ArvGcFloatNode implementation */ ArvGcNode * arv_gc_float_node_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_FLOAT_NODE, NULL); return node; } static ArvGcFeatureNode * arv_gc_float_node_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_float_node->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_float_node->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } static void arv_gc_float_node_init (ArvGcFloatNode *gc_float_node) { } static void arv_gc_float_node_finalize (GObject *object) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (object); G_OBJECT_CLASS (arv_gc_float_node_parent_class)->finalize (object); g_slist_free (gc_float_node->value_indexed_nodes); } static void arv_gc_float_node_class_init (ArvGcFloatNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_float_node_finalize; dom_node_class->get_node_name = arv_gc_float_node_get_node_name; dom_node_class->post_new_child = arv_gc_float_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_float_node_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_float_node_get_linked_feature; gc_feature_node_class->default_access_mode = ARV_GC_ACCESS_MODE_RW; } /* ArvGcFloat interface implementation */ static double arv_gc_float_node_get_float_value (ArvGcFloat *gc_float, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); ArvGcPropertyNode *value_node; GError *local_error = NULL; double value; value_node = _get_value_node (gc_float_node, &local_error); if (value_node == NULL) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return 0.0; } value = arv_gc_property_node_get_double (ARV_GC_PROPERTY_NODE (value_node), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return 0.0; } return value; } static void arv_gc_float_node_set_float_value (ArvGcFloat *gc_float, double value, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); ArvGcPropertyNode *value_node; GError *local_error = NULL; value_node = _get_value_node (gc_float_node, error); if (value_node == NULL) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return; } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_float)); arv_gc_property_node_set_double (ARV_GC_PROPERTY_NODE (value_node), value, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); } static double arv_gc_float_node_get_min (ArvGcFloat *gc_float, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); GError *local_error = NULL; double value; if (gc_float_node->minimum == NULL) { ArvGcPropertyNode *value_node; double value = -G_MAXDOUBLE; value_node = _get_value_node (gc_float_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_min (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_min (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return value; } value = arv_gc_property_node_get_double (ARV_GC_PROPERTY_NODE (gc_float_node->minimum), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return -G_MAXDOUBLE; } return value; } static double arv_gc_float_node_get_max (ArvGcFloat *gc_float, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); GError *local_error = NULL; double value; if (gc_float_node->maximum == NULL) { ArvGcPropertyNode *value_node; double value = G_MAXDOUBLE; value_node = _get_value_node (gc_float_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_max (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_max (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return value; } value = arv_gc_property_node_get_double (ARV_GC_PROPERTY_NODE (gc_float_node->maximum), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return G_MAXDOUBLE; } return value; } static double arv_gc_float_node_get_inc (ArvGcFloat *gc_float, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); GError *local_error = NULL; double value; if (gc_float_node->increment == NULL) { ArvGcPropertyNode *value_node; double value = G_MINDOUBLE; value_node = _get_value_node (gc_float_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_inc (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_inc (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return value; } value = arv_gc_property_node_get_double (ARV_GC_PROPERTY_NODE (gc_float_node->increment), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); return G_MINDOUBLE; } return value; } static ArvGcRepresentation arv_gc_float_node_get_representation (ArvGcFloat *gc_float) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); if (gc_float_node->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (ARV_GC_PROPERTY_NODE (gc_float_node->representation), ARV_GC_REPRESENTATION_UNDEFINED); } static const char * arv_gc_float_node_get_unit (ArvGcFloat *gc_float) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); const char *string; if (gc_float_node->unit == NULL) return NULL; string = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (gc_float_node->unit), NULL); return string; } static ArvGcDisplayNotation arv_gc_float_node_get_display_notation (ArvGcFloat *gc_float) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); if (gc_float_node->display_notation == NULL) return ARV_GC_DISPLAY_NOTATION_DEFAULT; return arv_gc_property_node_get_display_notation (ARV_GC_PROPERTY_NODE (gc_float_node->display_notation), ARV_GC_DISPLAY_NOTATION_DEFAULT); } static gint64 arv_gc_float_node_get_display_precision (ArvGcFloat *gc_float) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); if (gc_float_node->display_precision == NULL) return ARV_GC_DISPLAY_PRECISION_DEFAULT; return arv_gc_property_node_get_display_precision (ARV_GC_PROPERTY_NODE (gc_float_node->display_precision), ARV_GC_DISPLAY_PRECISION_DEFAULT); } static void arv_gc_float_node_impose_min (ArvGcFloat *gc_float, double minimum, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); GError *local_error = NULL; if (gc_float_node->minimum == NULL) return; arv_gc_property_node_set_double (ARV_GC_PROPERTY_NODE (gc_float_node->minimum), minimum, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); } static void arv_gc_float_node_impose_max (ArvGcFloat *gc_float, double maximum, GError **error) { ArvGcFloatNode *gc_float_node = ARV_GC_FLOAT_NODE (gc_float); GError *local_error = NULL; if (gc_float_node->maximum == NULL) return; arv_gc_property_node_set_double (ARV_GC_PROPERTY_NODE (gc_float_node->maximum), maximum, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_float))); } static void arv_gc_float_node_float_interface_init (ArvGcFloatInterface *interface) { interface->get_value = arv_gc_float_node_get_float_value; interface->set_value = arv_gc_float_node_set_float_value; interface->get_min = arv_gc_float_node_get_min; interface->get_max = arv_gc_float_node_get_max; interface->get_inc = arv_gc_float_node_get_inc; interface->get_representation = arv_gc_float_node_get_representation; interface->get_unit = arv_gc_float_node_get_unit; interface->get_display_notation = arv_gc_float_node_get_display_notation; interface->get_display_precision = arv_gc_float_node_get_display_precision; interface->impose_min = arv_gc_float_node_impose_min; interface->impose_max = arv_gc_float_node_impose_max; } aravis-0.8.34/src/arvgcfloatnode.h000066400000000000000000000026301475431451200170400ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_FLOAT_NODE_H #define ARV_GC_FLOAT_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_FLOAT_NODE (arv_gc_float_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcFloatNode, arv_gc_float_node, ARV, GC_FLOAT_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_float_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcfloatregnode.c000066400000000000000000000236571475431451200175450ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include typedef struct { ArvGcPropertyNode *endianness; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; ArvGcPropertyNode *display_notation; ArvGcPropertyNode *display_precision; GSList *selecteds; } ArvGcFloatRegNodePrivate; static void arv_gc_float_reg_node_init (ArvGcFloatRegNode *self); static void arv_gc_float_reg_node_float_interface_init (ArvGcFloatInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcFloatRegNode, arv_gc_float_reg_node, ARV_TYPE_GC_REGISTER_NODE, G_ADD_PRIVATE (ArvGcFloatRegNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_FLOAT, arv_gc_float_reg_node_float_interface_init)) static const char * arv_gc_float_reg_node_get_node_name (ArvDomNode *node) { return "FloatReg"; } static void arv_gc_float_reg_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS: priv->endianness = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: priv->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: priv->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION: priv->display_notation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION: priv->display_precision = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: priv->selecteds = g_slist_prepend (priv->selecteds, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_float_reg_node_parent_class)->post_new_child (self, child); break; } } else { ARV_DOM_NODE_CLASS (arv_gc_float_reg_node_parent_class)->post_new_child (self, child); } } static gdouble arv_gc_float_reg_node_get_float_value (ArvGcFloat *self, GError **error) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); GError *local_error = NULL; guint endianness; gint64 length; double v_double = 0.0; endianness = arv_gc_property_node_get_endianness (priv->endianness, G_LITTLE_ENDIAN); length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error == NULL) { char *buffer; buffer = g_malloc (length); arv_gc_register_get (ARV_GC_REGISTER (self), buffer, length, &local_error); if (local_error == NULL) { if (length == 4) { float v_float = 0.0; arv_copy_memory_with_endianness (&v_float, sizeof (v_float), G_BYTE_ORDER, buffer, length, endianness); v_double = v_float; } else if (length == 8) { arv_copy_memory_with_endianness (&v_double, sizeof (v_double), G_BYTE_ORDER, buffer, length, endianness); } else { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "Invalid register length for FloatReg node"); } } g_free (buffer); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return v_double; } static void arv_gc_float_reg_node_set_float_value (ArvGcFloat *self, gdouble value, GError **error) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); GError *local_error = NULL; guint endianness; gint64 length; endianness = arv_gc_property_node_get_endianness (priv->endianness, G_LITTLE_ENDIAN); length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error == NULL) { char *buffer; buffer = g_malloc (length); if (local_error == NULL) { if (length == 4) { float v_float = value; arv_copy_memory_with_endianness (buffer, length, endianness, &v_float, sizeof (v_float), G_BYTE_ORDER); } else if (length == 8) { arv_copy_memory_with_endianness (buffer, length, endianness, &value, sizeof (value), G_BYTE_ORDER); } else { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "Invalid register length for FloatReg node"); } } if (local_error == NULL) arv_gc_register_set (ARV_GC_REGISTER (self), buffer, length, &local_error); g_free (buffer); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); } static double arv_gc_float_reg_node_get_min (ArvGcFloat *self, GError **error) { GError *local_error = NULL; gint64 length; length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return -G_MAXDOUBLE; } if (length == 4) return -G_MAXFLOAT; else if (length == 8) return -G_MAXDOUBLE; g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "[%s] Invalid register length for FloatReg node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return -G_MAXDOUBLE; } static double arv_gc_float_reg_node_get_max (ArvGcFloat *self, GError **error) { GError *local_error = NULL; gint64 length; length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXDOUBLE; } if (length == 4) return G_MAXFLOAT; else if (length == 8) return G_MAXDOUBLE; g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "[%s] Invalid register length for FloatReg node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return G_MAXDOUBLE; } static ArvGcRepresentation arv_gc_float_reg_get_representation (ArvGcFloat *self) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); if (priv->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (priv->representation, ARV_GC_REPRESENTATION_UNDEFINED); } static const char * arv_gc_float_reg_node_get_unit (ArvGcFloat *self) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); if (priv->unit == NULL) return NULL; return arv_gc_property_node_get_string (priv->unit, NULL); } static ArvGcDisplayNotation arv_gc_float_reg_node_get_display_notation (ArvGcFloat *self) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); if (priv->display_notation == NULL) return ARV_GC_DISPLAY_NOTATION_DEFAULT; return arv_gc_property_node_get_display_notation (ARV_GC_PROPERTY_NODE (priv->display_notation), ARV_GC_DISPLAY_NOTATION_DEFAULT); } static gint64 arv_gc_float_reg_node_get_display_precision (ArvGcFloat *self) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); if (priv->display_precision == NULL) return ARV_GC_DISPLAY_PRECISION_DEFAULT; return arv_gc_property_node_get_display_precision (ARV_GC_PROPERTY_NODE (priv->display_precision), ARV_GC_DISPLAY_PRECISION_DEFAULT); } /** * arv_gc_float_reg_node_new: * * Returns: a new FloatReg node * * Since:0.8.0 */ ArvGcNode * arv_gc_float_reg_node_new (void) { return g_object_new (ARV_TYPE_GC_FLOAT_REG_NODE, NULL); } static void arv_gc_float_reg_node_float_interface_init (ArvGcFloatInterface *interface) { interface->get_value = arv_gc_float_reg_node_get_float_value; interface->set_value = arv_gc_float_reg_node_set_float_value; interface->get_min = arv_gc_float_reg_node_get_min; interface->get_max = arv_gc_float_reg_node_get_max; interface->get_representation = arv_gc_float_reg_get_representation; interface->get_unit = arv_gc_float_reg_node_get_unit; interface->get_display_notation = arv_gc_float_reg_node_get_display_notation; interface->get_display_precision = arv_gc_float_reg_node_get_display_precision; } static void arv_gc_float_reg_node_init (ArvGcFloatRegNode *self) { } static void arv_gc_float_reg_node_finalize (GObject *self) { ArvGcFloatRegNodePrivate *priv = arv_gc_float_reg_node_get_instance_private (ARV_GC_FLOAT_REG_NODE (self)); g_clear_pointer (&priv->selecteds, g_slist_free); G_OBJECT_CLASS (arv_gc_float_reg_node_parent_class)->finalize (self); } static void arv_gc_float_reg_node_class_init (ArvGcFloatRegNodeClass *this_class) { ArvGcRegisterNodeClass *gc_register_node_class = ARV_GC_REGISTER_NODE_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_float_reg_node_finalize; dom_node_class->get_node_name = arv_gc_float_reg_node_get_node_name; dom_node_class->post_new_child = arv_gc_float_reg_node_post_new_child; gc_register_node_class->default_cachable = ARV_GC_CACHABLE_WRITE_AROUND; } aravis-0.8.34/src/arvgcfloatregnode.h000066400000000000000000000027231475431451200175410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_FLOAT_REG_NODE_H #define ARV_GC_FLOAT_REG_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_FLOAT_REG_NODE (arv_gc_float_reg_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcFloatRegNode, arv_gc_float_reg_node, ARV, GC_FLOAT_REG_NODE, ArvGcRegisterNode) struct _ArvGcFloatRegNodeClass { ArvGcRegisterNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_float_reg_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcgroupnode.c000066400000000000000000000055401475431451200170650ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcgroupnode * @short_description: Class for Group nodes */ #include #include #include struct _ArvGcGroupNode { ArvGcFeatureNode base; char *comment; }; struct _ArvGcGroupNodeClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcGroupNode, arv_gc_group_node, ARV_TYPE_GC_FEATURE_NODE) /* ArvDomNode implementation */ static const char * arv_gc_group_node_get_node_name (ArvDomNode *node) { return "Group"; } /* ArvGcFeatureNode implementation */ static void arv_gc_group_node_set_attribute (ArvDomElement *self, const char* name, const char *value) { ArvGcGroupNode *node = ARV_GC_GROUP_NODE (self); if (strcmp (name, "Comment") == 0) { g_free (node->comment); node->comment = g_strdup (value); } } static const char * arv_gc_group_node_get_attribute (ArvDomElement *self, const char *name) { ArvGcGroupNode *node = ARV_GC_GROUP_NODE (self); if (strcmp (name, "ModelName") == 0) return node->comment; return NULL; } /* ArvGcGroupNode implementation */ ArvGcNode * arv_gc_group_node_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_GROUP_NODE, NULL); return node; } static void arv_gc_group_node_init (ArvGcGroupNode *gc_group_node) { } static void arv_gc_group_node_finalize (GObject *object) { ArvGcGroupNode *group_node = ARV_GC_GROUP_NODE (object); g_free (group_node->comment); G_OBJECT_CLASS (arv_gc_group_node_parent_class)->finalize (object); } static void arv_gc_group_node_class_init (ArvGcGroupNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_group_node_finalize; dom_node_class->get_node_name = arv_gc_group_node_get_node_name; dom_element_class->set_attribute = arv_gc_group_node_set_attribute; dom_element_class->get_attribute = arv_gc_group_node_get_attribute; } aravis-0.8.34/src/arvgcgroupnode.h000066400000000000000000000025711475431451200170730ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_GROUP_NODE_H #define ARV_GC_GROUP_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_GROUP_NODE (arv_gc_group_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcGroupNode, arv_gc_group_node, ARV, GC_GROUP_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_group_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcindexnode.c000066400000000000000000000105271475431451200170410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcindexnode * @short_description: Class for Index nodes */ #include #include #include #include #include #include #include struct _ArvGcIndexNode { ArvGcPropertyNode base; char *offset; gboolean is_p_offset; }; struct _ArvGcIndexNodeClass { ArvGcPropertyNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcIndexNode, arv_gc_index_node, ARV_TYPE_GC_PROPERTY_NODE) /* ArvDomNode implementation */ static const char * arv_gc_index_node_get_node_name (ArvDomNode *node) { return "pIndex"; } static gboolean arv_gc_index_node_can_append_child (ArvDomNode *parent, ArvDomNode *child) { return ARV_IS_DOM_TEXT (child); } /* ArvDomElement implementation */ static void arv_gc_index_node_set_attribute (ArvDomElement *self, const char *name, const char *value) { ArvGcIndexNode *index_node = ARV_GC_INDEX_NODE (self); if (strcmp (name, "Offset") == 0) { g_free (index_node->offset); index_node->offset = g_strdup (value); index_node->is_p_offset = FALSE; } else if (strcmp (name, "pOffset") == 0) { g_free (index_node->offset); index_node->offset = g_strdup (value); index_node->is_p_offset = TRUE; } } static const char * arv_gc_index_node_get_attribute (ArvDomElement *self, const char *name) { g_assert_not_reached (); return NULL; } /* ArvGcIndexNode implementation */ gint64 arv_gc_index_node_get_index (ArvGcIndexNode *index_node, gint64 default_offset, GError **error) { gint64 offset; gint64 node_value; GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_INDEX_NODE (index_node), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); if (index_node->offset == NULL) offset = default_offset; else { if (index_node->is_p_offset) { ArvGcNode *node; ArvGc *genicam; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (index_node)); node = arv_gc_get_node (genicam, index_node->offset); offset = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } } else offset = g_ascii_strtoll (index_node->offset, NULL, 0); } node_value = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (index_node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } return offset * node_value; } ArvGcNode * arv_gc_index_node_new (void) { return g_object_new (ARV_TYPE_GC_INDEX_NODE, "node-type", ARV_GC_PROPERTY_NODE_TYPE_P_INDEX, NULL); } static void arv_gc_index_node_init (ArvGcIndexNode *index_node) { index_node->offset = NULL; index_node->is_p_offset = FALSE; } static void arv_gc_index_node_finalize (GObject *object) { ArvGcIndexNode *index_node = ARV_GC_INDEX_NODE (object); g_free (index_node->offset); G_OBJECT_CLASS (arv_gc_index_node_parent_class)->finalize (object); } static void arv_gc_index_node_class_init (ArvGcIndexNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_index_node_finalize; dom_node_class->get_node_name = arv_gc_index_node_get_node_name; dom_node_class->can_append_child = arv_gc_index_node_can_append_child; dom_element_class->set_attribute = arv_gc_index_node_set_attribute; dom_element_class->get_attribute = arv_gc_index_node_get_attribute; } aravis-0.8.34/src/arvgcindexnode.h000066400000000000000000000027551475431451200170520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INDEX_NODE_H #define ARV_GC_INDEX_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INDEX_NODE (arv_gc_index_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcIndexNode, arv_gc_index_node, ARV, GC_INDEX_NODE, ArvGcPropertyNode) ARV_API ArvGcNode * arv_gc_index_node_new (void); ARV_API gint64 arv_gc_index_node_get_index (ArvGcIndexNode *index_node, gint64 default_offset, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcintconverternode.c000066400000000000000000000112101475431451200204420ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcintconverternode * @short_description: IntConverter node class */ #include #include #include #include #include struct _ArvGcIntConverterNode { ArvGcConverter converter; }; struct _ArvGcIntConverterNodeClass { ArvGcConverterClass parent_class; }; static const char * arv_gc_int_converter_node_get_node_name (ArvDomNode *node) { return "IntConverter"; } ArvGcNode * arv_gc_int_converter_node_new (void) { return g_object_new (ARV_TYPE_GC_INT_CONVERTER_NODE, NULL); } static void arv_gc_int_converter_node_init (ArvGcIntConverterNode *node) { } static void arv_gc_int_converter_node_class_init (ArvGcIntConverterNodeClass *this_class) { ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); dom_node_class->get_node_name = arv_gc_int_converter_node_get_node_name; } static gint64 arv_gc_converter_get_integer_value (ArvGcInteger *gc_integer, GError **error) { return arv_gc_converter_convert_to_int64 (ARV_GC_CONVERTER (gc_integer), ARV_GC_CONVERTER_NODE_TYPE_VALUE, error); } static gint64 arv_gc_converter_get_integer_min (ArvGcInteger *gc_integer, GError **error) { GError *local_error = NULL; gint64 a, b; /* TODO: we should use the Slope node here, instead of using MIN (min, max) */ a = arv_gc_converter_convert_to_int64 (ARV_GC_CONVERTER (gc_integer), ARV_GC_CONVERTER_NODE_TYPE_MIN, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MININT64; } b = arv_gc_converter_convert_to_int64 (ARV_GC_CONVERTER (gc_integer), ARV_GC_CONVERTER_NODE_TYPE_MAX, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MININT64; } return MIN (a, b); } static gint64 arv_gc_converter_get_integer_max (ArvGcInteger *gc_integer, GError **error) { GError *local_error = NULL; gint64 a, b; /* TODO: we should use the Slope node here, instead of using MAX (min, max) */ a = arv_gc_converter_convert_to_int64 (ARV_GC_CONVERTER (gc_integer), ARV_GC_CONVERTER_NODE_TYPE_MIN, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXINT64; } b = arv_gc_converter_convert_to_int64 (ARV_GC_CONVERTER (gc_integer), ARV_GC_CONVERTER_NODE_TYPE_MAX, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXINT64; } return MAX (a, b); } static gint64 _get_inc (ArvGcInteger *self, GError **error) { /* Genicam 2.1.1 p. 41 says: The increment of an IntConvertor is always 1. */ return 1; } static void arv_gc_converter_set_integer_value (ArvGcInteger *gc_integer, gint64 value, GError **error) { arv_gc_converter_convert_from_int64 (ARV_GC_CONVERTER (gc_integer), value, error); } static ArvGcRepresentation arv_gc_converter_get_integer_representation (ArvGcInteger *gc_integer) { return arv_gc_converter_get_representation (ARV_GC_CONVERTER (gc_integer)); } static const char * arv_gc_converter_get_integer_unit (ArvGcInteger *gc_integer) { return arv_gc_converter_get_unit (ARV_GC_CONVERTER (gc_integer)); } static void arv_gc_int_converter_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_converter_get_integer_value; interface->get_min = arv_gc_converter_get_integer_min; interface->get_max = arv_gc_converter_get_integer_max; interface->get_inc = _get_inc; interface->set_value = arv_gc_converter_set_integer_value; interface->get_representation = arv_gc_converter_get_integer_representation; interface->get_unit = arv_gc_converter_get_integer_unit; } G_DEFINE_TYPE_WITH_CODE (ArvGcIntConverterNode, arv_gc_int_converter_node, ARV_TYPE_GC_CONVERTER, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_int_converter_node_integer_interface_init)) aravis-0.8.34/src/arvgcintconverternode.h000066400000000000000000000026361475431451200204630ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INT_CONVERTER_NODE_H #define ARV_GC_INT_CONVERTER_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INT_CONVERTER_NODE (arv_gc_int_converter_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcIntConverterNode, arv_gc_int_converter_node, ARV, GC_INT_CONVERTER_NODE, ArvGcConverter) ARV_API ArvGcNode * arv_gc_int_converter_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcinteger.c000066400000000000000000000164551475431451200165270ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcinteger * @short_description: Integer interface */ #include #include #include #include #include static void arv_gc_integer_default_init (ArvGcIntegerInterface *gc_integer_iface) { } G_DEFINE_INTERFACE (ArvGcInteger, arv_gc_integer, G_TYPE_OBJECT) gint64 arv_gc_integer_get_value (ArvGcInteger *gc_integer, GError **error) { g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (gc_integer), error)) return 0; return ARV_GC_INTEGER_GET_IFACE (gc_integer)->get_value (gc_integer, error); } void arv_gc_integer_set_value (ArvGcInteger *gc_integer, gint64 value, GError **error) { ArvGc *genicam; ArvRangeCheckPolicy policy; GError *local_error = NULL; g_return_if_fail (ARV_IS_GC_INTEGER (gc_integer)); g_return_if_fail (error == NULL || *error == NULL); if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (gc_integer), error)) return; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (gc_integer)); g_return_if_fail (ARV_IS_GC (genicam)); policy = arv_gc_get_range_check_policy (genicam); if (policy != ARV_RANGE_CHECK_POLICY_DISABLE) { ArvGcIntegerInterface *iface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (iface->get_min != NULL) { gint64 min = iface->get_min (gc_integer, &local_error); if (local_error == NULL && value < min) { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_OUT_OF_RANGE, "[%s] Value '%" G_GINT64_FORMAT "' " "lower than allowed minimum '%" G_GINT64_FORMAT "'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer)), value, min); } } if (local_error == NULL && iface->get_max != NULL) { gint64 max = iface->get_max (gc_integer, &local_error); if (local_error == NULL && value > max) { g_set_error (&local_error, ARV_GC_ERROR, ARV_GC_ERROR_OUT_OF_RANGE, "[%s] Value '%" G_GINT64_FORMAT "' " "greater than allowed maximum '%" G_GINT64_FORMAT "'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer)), value, max); } } if (local_error != NULL) { if (policy == ARV_RANGE_CHECK_POLICY_DEBUG) { arv_warning_policies ("Range check (%s) ignored", local_error->message); } else if (policy == ARV_RANGE_CHECK_POLICY_ENABLE) { g_propagate_error (error, local_error); return; } g_clear_error (&local_error); } } ARV_GC_INTEGER_GET_IFACE (gc_integer)->set_value (gc_integer, value, error); } gint64 arv_gc_integer_get_min (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerInterface *integer_interface; g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->get_min != NULL) return integer_interface->get_min (gc_integer, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer))); return G_MININT64; } gint64 arv_gc_integer_get_max (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerInterface *integer_interface; g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->get_max != NULL) return integer_interface->get_max (gc_integer, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer))); return G_MAXINT64; } gint64 arv_gc_integer_get_inc (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerInterface *integer_interface; g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->get_inc != NULL) return integer_interface->get_inc (gc_integer, error); g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer))); return 1; } /** * arv_gc_integer_get_representation: * @gc_integer: a #ArvGcInteger * * Get number representation format. * * Returns: Number representation format as #ArvGcRepresentation. */ ArvGcRepresentation arv_gc_integer_get_representation (ArvGcInteger *gc_integer) { ArvGcIntegerInterface *integer_interface; g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), 0); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->get_representation != NULL) return integer_interface->get_representation (gc_integer); return ARV_GC_REPRESENTATION_UNDEFINED; } const char * arv_gc_integer_get_unit (ArvGcInteger *gc_integer) { ArvGcIntegerInterface *integer_interface; g_return_val_if_fail (ARV_IS_GC_INTEGER (gc_integer), NULL); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->get_unit != NULL) return integer_interface->get_unit (gc_integer); return NULL; } void arv_gc_integer_impose_min (ArvGcInteger *gc_integer, gint64 minimum, GError **error) { ArvGcIntegerInterface *integer_interface; g_return_if_fail (ARV_IS_GC_INTEGER (gc_integer)); g_return_if_fail (error == NULL || *error == NULL); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->impose_min != NULL) integer_interface->impose_min (gc_integer, minimum, error); else g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer))); } void arv_gc_integer_impose_max (ArvGcInteger *gc_integer, gint64 maximum, GError **error) { ArvGcIntegerInterface *integer_interface; g_return_if_fail (ARV_IS_GC_INTEGER (gc_integer)); g_return_if_fail (error == NULL || *error == NULL); integer_interface = ARV_GC_INTEGER_GET_IFACE (gc_integer); if (integer_interface->impose_max != NULL) integer_interface->impose_max (gc_integer, maximum, error); else g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_PROPERTY_NOT_DEFINED, "[%s] node not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer))); } aravis-0.8.34/src/arvgcinteger.h000066400000000000000000000054141475431451200165250ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INTEGER_H #define ARV_GC_INTEGER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INTEGER (arv_gc_integer_get_type ()) ARV_API G_DECLARE_INTERFACE (ArvGcInteger, arv_gc_integer, ARV, GC_INTEGER, GObject) struct _ArvGcIntegerInterface { GTypeInterface parent; gint64 (*get_value) (ArvGcInteger *gc_integer, GError **error); void (*set_value) (ArvGcInteger *gc_integer, gint64 value, GError **error); gint64 (*get_min) (ArvGcInteger *gc_integer, GError **error); gint64 (*get_max) (ArvGcInteger *gc_integer, GError **error); gint64 (*get_inc) (ArvGcInteger *gc_integer, GError **error); ArvGcRepresentation (*get_representation) (ArvGcInteger *gc_integer); const char * (*get_unit) (ArvGcInteger *gc_integer); void (*impose_min) (ArvGcInteger *gc_integer, gint64 minimum, GError **error); void (*impose_max) (ArvGcInteger *gc_integer, gint64 maximum, GError **error); }; ARV_API gint64 arv_gc_integer_get_value (ArvGcInteger *gc_integer, GError **error); ARV_API void arv_gc_integer_set_value (ArvGcInteger *gc_integer, gint64 value, GError **error); ARV_API gint64 arv_gc_integer_get_min (ArvGcInteger *gc_integer, GError **error); ARV_API gint64 arv_gc_integer_get_max (ArvGcInteger *gc_integer, GError **error); ARV_API gint64 arv_gc_integer_get_inc (ArvGcInteger *gc_integer, GError **error); ARV_API ArvGcRepresentation arv_gc_integer_get_representation (ArvGcInteger *gc_integer); ARV_API const char * arv_gc_integer_get_unit (ArvGcInteger *gc_integer); ARV_API void arv_gc_integer_impose_min (ArvGcInteger *gc_integer, gint64 minimum, GError **error); ARV_API void arv_gc_integer_impose_max (ArvGcInteger *gc_integer, gint64 maximum, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcintegernode.c000066400000000000000000000376251475431451200173770ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcintegernode * @short_description: Class for Integer nodes */ #include #include #include #include #include #include #include #include #include #include struct _ArvGcIntegerNode { ArvGcFeatureNode node; ArvGcPropertyNode *value; ArvGcPropertyNode *minimum; ArvGcPropertyNode *maximum; ArvGcPropertyNode *increment; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; ArvGcPropertyNode *index; GSList *value_indexed_nodes; ArvGcPropertyNode *value_default; GSList *selecteds; /* #ArvGcPropertyNode */ GSList *selected_features; /* #ArvGcFeatureNode */ }; struct _ArvGcIntegerNodeClass { ArvGcFeatureNodeClass parent_class; }; static void arv_gc_integer_node_integer_interface_init (ArvGcIntegerInterface *interface); static void arv_gc_integer_node_selector_interface_init (ArvGcSelectorInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcIntegerNode, arv_gc_integer_node, ARV_TYPE_GC_FEATURE_NODE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_integer_node_integer_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_SELECTOR, arv_gc_integer_node_selector_interface_init)) /* ArvDomNode implementation */ static const char * arv_gc_integer_node_get_node_name (ArvDomNode *node) { return "Integer"; } static void arv_gc_integer_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcIntegerNode *node = ARV_GC_INTEGER_NODE (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MINIMUM: case ARV_GC_PROPERTY_NODE_TYPE_P_MINIMUM: node->minimum = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MAXIMUM: case ARV_GC_PROPERTY_NODE_TYPE_P_MAXIMUM: node->maximum = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_INCREMENT: case ARV_GC_PROPERTY_NODE_TYPE_P_INCREMENT: node->increment = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: node->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: node->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_INDEX: node->index = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_VALUE_INDEXED: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_INDEXED: node->value_indexed_nodes = g_slist_prepend (node->value_indexed_nodes, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_VALUE_DEFAULT: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_DEFAULT: node->value_default = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: node->selecteds = g_slist_prepend (node->selecteds, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_integer_node_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_integer_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcFeatureNode implementation */ static ArvGcPropertyNode * _get_value_node (ArvGcIntegerNode *gc_integer_node, GError **error) { GError *local_error = NULL; if (gc_integer_node->value != NULL) return gc_integer_node->value; if (gc_integer_node->index != NULL) { gint64 index; GSList *iter; index = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->index), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return NULL; } for (iter = gc_integer_node->value_indexed_nodes; iter != NULL; iter = iter->next) { if (arv_gc_value_indexed_node_get_index (iter->data) == index) return iter->data; } if (gc_integer_node->value_default != NULL) return gc_integer_node->value_default; } return NULL; } /* ArvGcIntegerNode implementation */ ArvGcNode * arv_gc_integer_node_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_INTEGER_NODE, NULL); return node; } static ArvGcFeatureNode * arv_gc_integer_node_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_integer_node->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_integer_node->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } static void arv_gc_integer_node_init (ArvGcIntegerNode *gc_integer_node) { } static void arv_gc_integer_node_finalize (GObject *object) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (object); G_OBJECT_CLASS (arv_gc_integer_node_parent_class)->finalize (object); g_clear_pointer (&gc_integer_node->value_indexed_nodes, g_slist_free); g_clear_pointer (&gc_integer_node->selecteds, g_slist_free); g_clear_pointer (&gc_integer_node->selected_features, g_slist_free); } static void arv_gc_integer_node_class_init (ArvGcIntegerNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_integer_node_finalize; dom_node_class->get_node_name = arv_gc_integer_node_get_node_name; dom_node_class->post_new_child = arv_gc_integer_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_integer_node_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_integer_node_get_linked_feature; gc_feature_node_class->default_access_mode = ARV_GC_ACCESS_MODE_RW; } /* ArvGcInteger interface implementation */ static gint64 arv_gc_integer_node_get_integer_value (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); ArvGcPropertyNode *value_node; GError *local_error = NULL; gint64 value; value_node = _get_value_node (gc_integer_node, error); if (value_node == NULL) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return 0; } value = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (value_node), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return 0; } return value; } static void arv_gc_integer_node_set_integer_value (ArvGcInteger *gc_integer, gint64 value, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); ArvGcPropertyNode *value_node; GError *local_error = NULL; value_node = _get_value_node (gc_integer_node, error); if (value_node == NULL) { if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return; } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_integer_node)); arv_gc_property_node_set_int64 (ARV_GC_PROPERTY_NODE (value_node), value, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); } static gint64 arv_gc_integer_node_get_min (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); GError *local_error = NULL; gint64 value; if (gc_integer_node->minimum == NULL) { ArvGcPropertyNode *value_node; gint64 value = G_MININT64; value_node = _get_value_node (gc_integer_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_min (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_min (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return value; } value = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->minimum), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return G_MININT64; } return value; } static gint64 arv_gc_integer_node_get_max (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); GError *local_error = NULL; gint64 value; if (gc_integer_node->maximum == NULL) { ArvGcPropertyNode *value_node; gint64 value = G_MAXINT64; value_node = _get_value_node (gc_integer_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_max (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_max (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return value; } value = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->maximum), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return G_MAXINT64; } return value; } static gint64 arv_gc_integer_node_get_inc (ArvGcInteger *gc_integer, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); GError *local_error = NULL; gint64 value; if (gc_integer_node->increment == NULL) { ArvGcPropertyNode *value_node; gint64 value = 1; value_node = _get_value_node (gc_integer_node, &local_error); if (local_error == NULL && ARV_IS_GC_PROPERTY_NODE (value_node)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (value_node); if (ARV_IS_GC_INTEGER (linked_node)) value = arv_gc_integer_get_inc (ARV_GC_INTEGER (linked_node), &local_error); else if (ARV_IS_GC_FLOAT (linked_node)) value = arv_gc_float_get_inc (ARV_GC_FLOAT (linked_node), &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return value; } value = arv_gc_property_node_get_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->increment), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); return 1; } return value; } static ArvGcRepresentation arv_gc_integer_node_get_representation (ArvGcInteger *gc_integer) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); if (gc_integer_node->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (ARV_GC_PROPERTY_NODE (gc_integer_node->representation), ARV_GC_REPRESENTATION_UNDEFINED); } static const char * arv_gc_integer_node_get_unit (ArvGcInteger *gc_integer) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); if (gc_integer_node->unit == NULL) return NULL; return arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (gc_integer_node->unit), NULL); } static void arv_gc_integer_node_impose_min (ArvGcInteger *gc_integer, gint64 minimum, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); GError *local_error = NULL; if (gc_integer_node->minimum == NULL) return; arv_gc_property_node_set_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->minimum), minimum, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); } static void arv_gc_integer_node_impose_max (ArvGcInteger *gc_integer, gint64 maximum, GError **error) { ArvGcIntegerNode *gc_integer_node = ARV_GC_INTEGER_NODE (gc_integer); GError *local_error = NULL; if (gc_integer_node->maximum == NULL) return; arv_gc_property_node_set_int64 (ARV_GC_PROPERTY_NODE (gc_integer_node->maximum), maximum, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_integer_node))); } static void arv_gc_integer_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_integer_node_get_integer_value; interface->set_value = arv_gc_integer_node_set_integer_value; interface->get_min = arv_gc_integer_node_get_min; interface->get_max = arv_gc_integer_node_get_max; interface->get_inc = arv_gc_integer_node_get_inc; interface->get_representation = arv_gc_integer_node_get_representation; interface->get_unit = arv_gc_integer_node_get_unit; interface->impose_min = arv_gc_integer_node_impose_min; interface->impose_max = arv_gc_integer_node_impose_max; } static const GSList * arv_gc_integer_node_get_selected_features (ArvGcSelector *selector) { ArvGcIntegerNode *integer = ARV_GC_INTEGER_NODE (selector); GSList *iter; g_clear_pointer (&integer->selected_features, g_slist_free); for (iter = integer->selecteds; iter != NULL; iter = iter->next) { ArvGcFeatureNode *feature_node = ARV_GC_FEATURE_NODE (arv_gc_property_node_get_linked_node (iter->data)); if (ARV_IS_GC_FEATURE_NODE (feature_node)) integer->selected_features = g_slist_prepend (integer->selected_features, feature_node); } return integer->selected_features; } static void arv_gc_integer_node_selector_interface_init (ArvGcSelectorInterface *interface) { interface->get_selected_features = arv_gc_integer_node_get_selected_features; } aravis-0.8.34/src/arvgcintegernode.h000066400000000000000000000026511475431451200173730ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INTEGER_NODE_H #define ARV_GC_INTEGER_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INTEGER_NODE (arv_gc_integer_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcIntegerNode, arv_gc_integer_node, ARV, GC_INTEGER_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_integer_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcintregnode.c000066400000000000000000000216621475431451200172240ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include typedef struct { ArvGcPropertyNode *sign; ArvGcPropertyNode *endianness; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; GSList *selecteds; GSList *selected_features; } ArvGcIntRegNodePrivate; static void arv_gc_int_reg_node_init (ArvGcIntRegNode *self); static void arv_gc_int_reg_node_integer_interface_init (ArvGcIntegerInterface *interface); static void arv_gc_int_reg_node_selector_interface_init (ArvGcSelectorInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcIntRegNode, arv_gc_int_reg_node, ARV_TYPE_GC_REGISTER_NODE, G_ADD_PRIVATE (ArvGcIntRegNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_int_reg_node_integer_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_SELECTOR, arv_gc_int_reg_node_selector_interface_init)) static const char * arv_gc_int_reg_node_get_node_name (ArvDomNode *node) { return "IntReg"; } static void arv_gc_int_reg_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_SIGN: priv->sign = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS: priv->endianness = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: priv->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: priv->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: priv->selecteds = g_slist_prepend (priv->selecteds, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_int_reg_node_parent_class)->post_new_child (self, child); break; } } else { ARV_DOM_NODE_CLASS (arv_gc_int_reg_node_parent_class)->post_new_child (self, child); } } static gint64 arv_gc_int_reg_node_get_integer_value (ArvGcInteger *self, GError **error) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); return arv_gc_register_node_get_masked_integer_value (ARV_GC_REGISTER_NODE (self), 0, 31, arv_gc_property_node_get_sign (priv->sign, ARV_GC_SIGNEDNESS_UNSIGNED), arv_gc_property_node_get_endianness (priv->endianness, G_LITTLE_ENDIAN), ARV_GC_CACHABLE_UNDEFINED, FALSE, error); } static void arv_gc_int_reg_node_set_integer_value (ArvGcInteger *self, gint64 value, GError **error) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); arv_gc_register_node_set_masked_integer_value (ARV_GC_REGISTER_NODE (self), 0, 31, arv_gc_property_node_get_sign (priv->sign, ARV_GC_SIGNEDNESS_UNSIGNED), arv_gc_property_node_get_endianness (priv->endianness, G_LITTLE_ENDIAN), ARV_GC_CACHABLE_UNDEFINED, FALSE, value, error); } static gint64 arv_gc_int_reg_node_get_min (ArvGcInteger *self, GError **error) { #if 0 return G_MININT64; #else ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); GError *local_error = NULL; gint64 length; ArvGcSignedness signedness; gint64 minimum; signedness = arv_gc_property_node_get_sign (priv->sign, ARV_GC_SIGNEDNESS_UNSIGNED); length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return signedness == ARV_GC_SIGNEDNESS_UNSIGNED ? 0 : G_MININT64; } if (length > 8) minimum = signedness == ARV_GC_SIGNEDNESS_UNSIGNED ? 0 : G_MININT64; else minimum = signedness == ARV_GC_SIGNEDNESS_UNSIGNED ? 0 :- ((gint64) 1 << (8 * length - 1)); /* printf ("minimum = %" G_GINT64_MODIFIER "x %" G_GINT64_FORMAT "\n", minimum, length);*/ return minimum; #endif } static gint64 arv_gc_int_reg_node_get_max (ArvGcInteger *self, GError **error) { #if 0 return G_MAXINT64; #else ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); GError *local_error = NULL; gint64 length; ArvGcSignedness signedness; gint64 maximum; signedness = arv_gc_property_node_get_sign (priv->sign, ARV_GC_SIGNEDNESS_UNSIGNED); length = arv_gc_register_get_length (ARV_GC_REGISTER (self), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return G_MAXINT64; } if (length >= 8) maximum = G_MAXINT64; else maximum = signedness == ARV_GC_SIGNEDNESS_UNSIGNED ? ((gint64) 1 << (8 * length)) - 1 : ((gint64) 1 << (8 * length - 1)) - 1; /* printf ("maximum = %" G_GINT64_MODIFIER "x %" G_GINT64_FORMAT "\n", maximum, length);*/ return maximum; #endif } static gint64 arv_gc_int_reg_node_get_inc (ArvGcInteger *self, GError **error) { return 1; } static ArvGcRepresentation arv_gc_int_reg_node_get_representation (ArvGcInteger *self) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); if (priv->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (priv->representation, ARV_GC_REPRESENTATION_UNDEFINED); } static const char * arv_gc_int_reg_node_get_unit (ArvGcInteger *self) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); if (priv->unit == NULL) return NULL; return arv_gc_property_node_get_string (priv->unit, NULL); } /** * arv_gc_int_reg_node_new: * * Returns: a new IntReg node * * Since:0.8.0 */ ArvGcNode * arv_gc_int_reg_node_new (void) { return g_object_new (ARV_TYPE_GC_INT_REG_NODE, NULL); } static void arv_gc_int_reg_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_int_reg_node_get_integer_value; interface->set_value = arv_gc_int_reg_node_set_integer_value; interface->get_min = arv_gc_int_reg_node_get_min; interface->get_max = arv_gc_int_reg_node_get_max; interface->get_inc = arv_gc_int_reg_node_get_inc; interface->get_representation = arv_gc_int_reg_node_get_representation; interface->get_unit = arv_gc_int_reg_node_get_unit; } static const GSList * arv_gc_int_reg_node_get_selected_features (ArvGcSelector *self) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); GSList *iter; g_clear_pointer (&priv->selected_features, g_slist_free); for (iter = priv->selecteds; iter != NULL; iter = iter->next) { ArvGcFeatureNode *feature_node = ARV_GC_FEATURE_NODE (arv_gc_property_node_get_linked_node (iter->data)); if (ARV_IS_GC_FEATURE_NODE (feature_node)) priv->selected_features = g_slist_prepend (priv->selected_features, feature_node); } return priv->selected_features; } static void arv_gc_int_reg_node_selector_interface_init (ArvGcSelectorInterface *interface) { interface->get_selected_features = arv_gc_int_reg_node_get_selected_features; } static void arv_gc_int_reg_node_init (ArvGcIntRegNode *self) { } static void arv_gc_int_reg_node_finalize (GObject *self) { ArvGcIntRegNodePrivate *priv = arv_gc_int_reg_node_get_instance_private (ARV_GC_INT_REG_NODE (self)); g_clear_pointer (&priv->selecteds, g_slist_free); g_slist_free (priv->selecteds); g_slist_free (priv->selected_features); G_OBJECT_CLASS (arv_gc_int_reg_node_parent_class)->finalize (self); } static void arv_gc_int_reg_node_class_init (ArvGcIntRegNodeClass *this_class) { ArvGcRegisterNodeClass *gc_register_node_class = ARV_GC_REGISTER_NODE_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_int_reg_node_finalize; dom_node_class->get_node_name = arv_gc_int_reg_node_get_node_name; dom_node_class->post_new_child = arv_gc_int_reg_node_post_new_child; gc_register_node_class->default_cachable = ARV_GC_CACHABLE_WRITE_THROUGH; } aravis-0.8.34/src/arvgcintregnode.h000066400000000000000000000027021475431451200172230ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INT_REG_NODE_H #define ARV_GC_INT_REG_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INT_REG_NODE (arv_gc_int_reg_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcIntRegNode, arv_gc_int_reg_node, ARV, GC_INT_REG_NODE, ArvGcRegisterNode) struct _ArvGcIntRegNodeClass { ArvGcRegisterNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_int_reg_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcintswissknifenode.c000066400000000000000000000065751475431451200206420ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include static void arv_gc_int_swiss_knife_node_init (ArvGcIntSwissKnifeNode *self); static void arv_gc_int_swiss_knife_node_integer_interface_init (ArvGcIntegerInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcIntSwissKnifeNode, arv_gc_int_swiss_knife_node, ARV_TYPE_GC_SWISS_KNIFE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_int_swiss_knife_node_integer_interface_init)) static const char * arv_gc_int_swiss_knife_node_get_node_name (ArvDomNode *node) { return "IntSwissKnife"; } static gint64 arv_gc_int_swiss_knife_node_get_integer_value (ArvGcInteger *self, GError **error) { return arv_gc_swiss_knife_get_integer_value (ARV_GC_SWISS_KNIFE (self), error); } static void arv_gc_int_swiss_knife_node_set_integer_value (ArvGcInteger *self, gint64 value, GError **error) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_READ_ONLY, "[%s] Read only IntSwissKnife", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); } static ArvGcRepresentation arv_gc_swiss_knife_node_get_integer_representation (ArvGcInteger *self) { return arv_gc_swiss_knife_get_representation (ARV_GC_SWISS_KNIFE (self)); } static const char * arv_gc_swiss_knife_node_get_integer_unit (ArvGcInteger *self) { return arv_gc_swiss_knife_get_unit (ARV_GC_SWISS_KNIFE (self)); } ArvGcNode * arv_gc_int_swiss_knife_node_new (void) { return g_object_new (ARV_TYPE_GC_INT_SWISS_KNIFE_NODE, NULL); } static void arv_gc_int_swiss_knife_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_int_swiss_knife_node_get_integer_value; interface->set_value = arv_gc_int_swiss_knife_node_set_integer_value; interface->get_representation = arv_gc_swiss_knife_node_get_integer_representation; interface->get_unit = arv_gc_swiss_knife_node_get_integer_unit; } static void arv_gc_int_swiss_knife_node_init (ArvGcIntSwissKnifeNode *self) { } static void arv_gc_int_swiss_knife_node_finalize (GObject *self) { G_OBJECT_CLASS (arv_gc_int_swiss_knife_node_parent_class)->finalize (self); } static void arv_gc_int_swiss_knife_node_class_init (ArvGcIntSwissKnifeNodeClass *this_class) { ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_int_swiss_knife_node_finalize; dom_node_class->get_node_name = arv_gc_int_swiss_knife_node_get_node_name; } aravis-0.8.34/src/arvgcintswissknifenode.h000066400000000000000000000030011475431451200206240ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INT_SWISS_KNIFE_NODE_H #define ARV_GC_INT_SWISS_KNIFE_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INT_SWISS_KNIFE_NODE (arv_gc_int_swiss_knife_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcIntSwissKnifeNode, arv_gc_int_swiss_knife_node, ARV, GC_INT_SWISS_KNIFE_NODE, ArvGcSwissKnife) struct _ArvGcIntSwissKnifeNodeClass { ArvGcSwissKnifeClass parent_class; }; ARV_API ArvGcNode * arv_gc_int_swiss_knife_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcinvalidatornode.c000066400000000000000000000047571475431451200202560ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcinvalidatornode * @short_description: Class for Invalidator nodes */ #include #include #include #include #include #include #include struct _ArvGcInvalidatorNode { ArvGcPropertyNode base; guint64 change_index; }; struct _ArvGcInvalidatorNodeClass { ArvGcPropertyNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcInvalidatorNode, arv_gc_invalidator_node, ARV_TYPE_GC_PROPERTY_NODE) /* ArvDomNode implementation */ static const char * arv_gc_invalidator_node_get_node_name (ArvDomNode *node) { return "pInvalidator"; } /* ArvGcInvalidatorNode implementation */ gboolean arv_gc_invalidator_has_changed (ArvGcInvalidatorNode *self) { ArvGcNode *node; guint64 change_count; g_return_val_if_fail (ARV_IS_GC_INVALIDATOR_NODE (self), FALSE); node = arv_gc_property_node_get_linked_node (ARV_GC_PROPERTY_NODE (self)); change_count = arv_gc_feature_node_get_change_count (ARV_GC_FEATURE_NODE (node)); if (change_count != self->change_index) { self->change_index = change_count; return TRUE; } return FALSE; } ArvGcNode * arv_gc_invalidator_node_new (void) { return g_object_new (ARV_TYPE_GC_INVALIDATOR_NODE, "node-type", ARV_GC_PROPERTY_NODE_TYPE_P_INVALIDATOR, NULL); } static void arv_gc_invalidator_node_init (ArvGcInvalidatorNode *invalidator_node) { } static void arv_gc_invalidator_node_class_init (ArvGcInvalidatorNodeClass *this_class) { ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); dom_node_class->get_node_name = arv_gc_invalidator_node_get_node_name; } aravis-0.8.34/src/arvgcinvalidatornode.h000066400000000000000000000027731475431451200202570ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_INVALIDATOR_NODE_H #define ARV_GC_INVALIDATOR_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_INVALIDATOR_NODE (arv_gc_invalidator_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcInvalidatorNode, arv_gc_invalidator_node, ARV, GC_INVALIDATOR_NODE, ArvGcPropertyNode) ARV_API ArvGcNode * arv_gc_invalidator_node_new (void); ARV_API gboolean arv_gc_invalidator_has_changed (ArvGcInvalidatorNode *self); G_END_DECLS #endif aravis-0.8.34/src/arvgcmaskedintregnode.c000066400000000000000000000245501475431451200204100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #define ARV_GC_MASKED_INT_REG_NODE_DEFAULT_SIGNEDNESS ARV_GC_SIGNEDNESS_UNSIGNED #define ARV_GC_MASKED_INT_REG_NODE_DEFAULT_ENDIANNESS G_LITTLE_ENDIAN typedef struct { ArvGcPropertyNode *lsb; ArvGcPropertyNode *msb; ArvGcPropertyNode *sign; ArvGcPropertyNode *endianness; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; GSList *selecteds; GSList *selected_features; } ArvGcMaskedIntRegNodePrivate; static void arv_gc_masked_int_reg_node_init (ArvGcMaskedIntRegNode *self); static void arv_gc_masked_int_reg_node_integer_interface_init (ArvGcIntegerInterface *interface); static void arv_gc_masked_int_reg_node_selector_interface_init (ArvGcSelectorInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcMaskedIntRegNode, arv_gc_masked_int_reg_node, ARV_TYPE_GC_REGISTER_NODE, G_ADD_PRIVATE (ArvGcMaskedIntRegNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_masked_int_reg_node_integer_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_SELECTOR, arv_gc_masked_int_reg_node_selector_interface_init)) static const char * arv_gc_masked_int_reg_node_get_node_name (ArvDomNode *node) { return "MaskedIntReg"; } static void arv_gc_masked_int_reg_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_LSB: priv->lsb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MSB: priv->msb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_BIT: priv->msb = property_node; priv->lsb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_SIGN: priv->sign = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS: priv->endianness = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: priv->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: priv->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: priv->selecteds = g_slist_prepend (priv->selecteds, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_masked_int_reg_node_parent_class)->post_new_child (self, child); break; } } else { ARV_DOM_NODE_CLASS (arv_gc_masked_int_reg_node_parent_class)->post_new_child (self, child); } } static gint64 arv_gc_masked_int_reg_node_get_integer_value (ArvGcInteger *self, GError **error) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); return arv_gc_register_node_get_masked_integer_value (ARV_GC_REGISTER_NODE (self), arv_gc_property_node_get_lsb (priv->lsb, 0), arv_gc_property_node_get_msb (priv->msb, 31), arv_gc_property_node_get_sign (priv->sign, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_SIGNEDNESS), arv_gc_property_node_get_endianness (priv->endianness, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_ENDIANNESS), ARV_GC_CACHABLE_UNDEFINED, TRUE, error); } static void arv_gc_masked_int_reg_node_set_integer_value (ArvGcInteger *self, gint64 value, GError **error) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); arv_gc_register_node_set_masked_integer_value (ARV_GC_REGISTER_NODE (self), arv_gc_property_node_get_lsb (priv->lsb, 0), arv_gc_property_node_get_msb (priv->msb, 31), arv_gc_property_node_get_sign (priv->sign, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_SIGNEDNESS), arv_gc_property_node_get_endianness (priv->endianness, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_ENDIANNESS), ARV_GC_CACHABLE_UNDEFINED, TRUE, value, error); } static gint64 arv_gc_masked_int_reg_node_get_min (ArvGcInteger *self, GError **error) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); gint64 lsb, msb; ArvGcSignedness signedness; guint endianness; lsb = arv_gc_property_node_get_lsb (priv->lsb, 0); msb = arv_gc_property_node_get_msb (priv->msb, 31); signedness = arv_gc_property_node_get_sign (priv->sign, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_SIGNEDNESS); endianness =arv_gc_property_node_get_endianness (priv->endianness, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_ENDIANNESS); if ((endianness == G_BIG_ENDIAN && (msb > lsb)) || (endianness != G_BIG_ENDIAN && (lsb > msb))) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_BIT_RANGE, "[%s] Invalid bit range", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return G_MININT64; } if (signedness == ARV_GC_SIGNEDNESS_SIGNED) { return endianness == G_BIG_ENDIAN ? -((1 << (lsb - msb))) : -((1 << (msb - lsb))); } else { return 0; } } static gint64 arv_gc_masked_int_reg_node_get_max (ArvGcInteger *self, GError **error) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); gint64 lsb, msb; ArvGcSignedness signedness; guint endianness; signedness = arv_gc_property_node_get_sign (priv->sign, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_SIGNEDNESS); endianness = arv_gc_property_node_get_endianness (priv->endianness, ARV_GC_MASKED_INT_REG_NODE_DEFAULT_ENDIANNESS); lsb = arv_gc_property_node_get_lsb (priv->lsb, endianness == G_BIG_ENDIAN ? 31 : 0); msb = arv_gc_property_node_get_msb (priv->msb, endianness == G_BIG_ENDIAN ? 0 : 31); if ((endianness == G_BIG_ENDIAN && (msb > lsb)) || (endianness != G_BIG_ENDIAN && (lsb > msb))) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_BIT_RANGE, "[%s] Invalid bit range", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return G_MAXINT64; } if (signedness == ARV_GC_SIGNEDNESS_SIGNED) { return endianness == G_BIG_ENDIAN ? (((gint64) 1 << (lsb - msb)) - 1) : (((gint64) 1 << (msb - lsb)) - 1); } else { return endianness == G_BIG_ENDIAN ? ((gint64) 1 << (lsb - msb + 1)) - 1 : ((gint64) 1 << (msb - lsb + 1)) - 1; } } static ArvGcRepresentation arv_gc_masked_int_reg_node_get_representation (ArvGcInteger *self) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); if (priv->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (priv->representation, ARV_GC_REPRESENTATION_UNDEFINED); } static const char * arv_gc_masked_int_reg_node_get_unit (ArvGcInteger *self) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); if (priv->unit == NULL) return NULL; return arv_gc_property_node_get_string (priv->unit, NULL); } /** * arv_gc_masked_int_reg_node_new: * * Returns: a new MaskedIntReg node * * Since:0.8.0 */ ArvGcNode * arv_gc_masked_int_reg_node_new (void) { return g_object_new (ARV_TYPE_GC_MASKED_INT_REG_NODE, NULL); } static void arv_gc_masked_int_reg_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_masked_int_reg_node_get_integer_value; interface->set_value = arv_gc_masked_int_reg_node_set_integer_value; interface->get_min = arv_gc_masked_int_reg_node_get_min; interface->get_max = arv_gc_masked_int_reg_node_get_max; interface->get_representation = arv_gc_masked_int_reg_node_get_representation; interface->get_unit = arv_gc_masked_int_reg_node_get_unit; } static const GSList * arv_gc_masked_int_reg_node_get_selected_features (ArvGcSelector *self) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); GSList *iter; g_clear_pointer (&priv->selected_features, g_slist_free); for (iter = priv->selecteds; iter != NULL; iter = iter->next) { ArvGcFeatureNode *feature_node = ARV_GC_FEATURE_NODE (arv_gc_property_node_get_linked_node (iter->data)); if (ARV_IS_GC_FEATURE_NODE (feature_node)) priv->selected_features = g_slist_prepend (priv->selected_features, feature_node); } return priv->selected_features; } static void arv_gc_masked_int_reg_node_selector_interface_init (ArvGcSelectorInterface *interface) { interface->get_selected_features = arv_gc_masked_int_reg_node_get_selected_features; } static void arv_gc_masked_int_reg_node_init (ArvGcMaskedIntRegNode *self) { } static void arv_gc_masked_int_reg_node_finalize (GObject *self) { ArvGcMaskedIntRegNodePrivate *priv = arv_gc_masked_int_reg_node_get_instance_private (ARV_GC_MASKED_INT_REG_NODE (self)); g_clear_pointer (&priv->selecteds, g_slist_free); g_slist_free (priv->selecteds); g_slist_free (priv->selected_features); G_OBJECT_CLASS (arv_gc_masked_int_reg_node_parent_class)->finalize (self); } static void arv_gc_masked_int_reg_node_class_init (ArvGcMaskedIntRegNodeClass *this_class) { ArvGcRegisterNodeClass *gc_register_node_class = ARV_GC_REGISTER_NODE_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_masked_int_reg_node_finalize; dom_node_class->get_node_name = arv_gc_masked_int_reg_node_get_node_name; dom_node_class->post_new_child = arv_gc_masked_int_reg_node_post_new_child; gc_register_node_class->default_cachable = ARV_GC_CACHABLE_WRITE_THROUGH; } aravis-0.8.34/src/arvgcmaskedintregnode.h000066400000000000000000000027761475431451200204230ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_MASKED_INT_REG_NODE_H #define ARV_GC_MASKED_INT_REG_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_MASKED_INT_REG_NODE (arv_gc_masked_int_reg_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcMaskedIntRegNode, arv_gc_masked_int_reg_node, ARV, GC_MASKED_INT_REG_NODE, ArvGcRegisterNode) struct _ArvGcMaskedIntRegNodeClass { ArvGcRegisterNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_masked_int_reg_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcnode.c000066400000000000000000000040461475431451200160100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcnode * @short_description: Base class for all Genicam nodes * * #ArvGcNode provides a base class for the implementation of the different * types of Genicam nodes. */ #include #include #include #include #include static GObjectClass *parent_class = NULL; /* ArvDomNode implementation */ /* ArvDomElement implementation */ /* ArvGcNode implementation */ /** * arv_gc_node_get_genicam: * @gc_node: a #ArvGcNode * * Retrieves the parent genicam document of @gc_node. * * Return value: (transfer none): the parent #ArvGc */ ArvGc * arv_gc_node_get_genicam (ArvGcNode *gc_node) { return ARV_GC (arv_dom_node_get_owner_document (ARV_DOM_NODE (gc_node))); } static void arv_gc_node_init (ArvGcNode *gc_node) { } static void arv_gc_node_finalize (GObject *object) { parent_class->finalize (object); } static void arv_gc_node_class_init (ArvGcNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); parent_class = g_type_class_peek_parent (this_class); object_class->finalize = arv_gc_node_finalize; } G_DEFINE_ABSTRACT_TYPE (ArvGcNode, arv_gc_node, ARV_TYPE_DOM_ELEMENT) aravis-0.8.34/src/arvgcnode.h000066400000000000000000000026331475431451200160150ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_NODE_H #define ARV_GC_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_NODE (arv_gc_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcNode, arv_gc_node, ARV, GC_NODE, ArvDomElement) struct _ArvGcNodeClass { ArvDomElementClass parent_class; }; ARV_API ArvGc * arv_gc_node_get_genicam (ArvGcNode *gc_node); G_END_DECLS #endif aravis-0.8.34/src/arvgcport.c000066400000000000000000000274561475431451200160610ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcport * @short_description: Class for Port nodes */ #include #include #include #include #include #include #include #include #include #include typedef struct { const char *vendor_selection; const char *model_selection; } ArvGvLegacyInfos; /* * Some GigEVision devices incorrectly report a Genicam schema version greater or equal to 1.1, while implementing the * legacy behavior for register access. This list allows to force the use of the legacy endianness mechanism. Vendor and * model listed below are those found in the Genicam XML data, which can be obtained using the `genicam` command of * `arv-tool`. They are stored in the ModelName and VendorName attributes of the RegisterDescription element. * * The documentation about the legacy endianness mechanism is in the 3.1 appendix ('Endianess of GigE Vision Cameras') * of the GenICam Standard. * * There is a chance this part of Aravis is due to a misunderstanding of how a GigEVision device is supposed to behave * (Remember we can not use the GigEVision specification documentation). But until now, there was no evidence in the * issue reports it is the case. If you think this should be implemented differently, don't hesitate to explain your * thoughts on Aravis issue report system, or even better, to open a pull request. The related Aravis issues are * available here: * * https://github.com/AravisProject/aravis/labels/5.%20Genicam%201.0%20legacy%20mode */ static ArvGvLegacyInfos arv_gc_port_legacy_infos[] = { { .vendor_selection = "Imperx", .model_selection = "IpxGEVCamera"}, { .vendor_selection = "KowaOptronics", .model_selection = "SC130ET3"}, { .vendor_selection = "NIT", .model_selection = "Tachyon16k"}, { .vendor_selection = "NIT", .model_selection = "NSC1601GIGE"}, { .vendor_selection = "PleoraTechnologiesInc", .model_selection = "iPORTCLGigE"}, { .vendor_selection = "PleoraTechnologiesInc", .model_selection = "NTxGigE"}, { .vendor_selection = "TeledyneDALSA", .model_selection = "ICE"}, { .vendor_selection = "Sony", .model_selection = "XCG_CGSeries"}, { .vendor_selection = "Sony", .model_selection = "XCG_CPSeries"}, { .vendor_selection = "EVK", .model_selection = "HELIOS"}, { .vendor_selection = "AT_Automation_Technology_GmbH", .model_selection = "C6_X_GigE"}, }; typedef struct { ArvGcPropertyNode *chunk_id; ArvGcPropertyNode *event_id; gboolean has_done_legacy_check; gboolean has_legacy_infos; } ArvGcPortPrivate; struct _ArvGcPort { ArvGcFeatureNode node; ArvGcPortPrivate *priv; }; struct _ArvGcPortClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvGcPort, arv_gc_port, ARV_TYPE_GC_FEATURE_NODE, G_ADD_PRIVATE(ArvGcPort)) /* ArvDomNode implementation */ static const char * _get_node_name (ArvDomNode *node) { return "Port"; } static void _post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcPort *node = ARV_GC_PORT (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_CHUNK_ID: node->priv->chunk_id = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_EVENT_ID: node->priv->event_id = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_port_parent_class)->post_new_child (self, child); break; } } else ARV_DOM_NODE_CLASS (arv_gc_port_parent_class)->post_new_child (self, child); } static void _pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcPort implementation */ static gboolean _use_legacy_endianness_mechanism (ArvGcPort *port, guint64 length) { if (!port->priv->has_done_legacy_check) { ArvDomDocument *document; ArvGcRegisterDescriptionNode *register_description; const char *vendor_name; const char *model_name; int i; document = arv_dom_node_get_owner_document (ARV_DOM_NODE (port)); register_description = ARV_GC_REGISTER_DESCRIPTION_NODE (arv_dom_document_get_document_element (document)); vendor_name = arv_gc_register_description_node_get_vendor_name(register_description); model_name = arv_gc_register_description_node_get_model_name(register_description); if (arv_gc_register_description_node_compare_schema_version (register_description, 1, 1, 0) < 0) { port->priv->has_legacy_infos = TRUE; } else { for (i = 0; i < G_N_ELEMENTS (arv_gc_port_legacy_infos); i++) { if (g_pattern_match_simple(arv_gc_port_legacy_infos[i].vendor_selection, vendor_name) == TRUE && g_pattern_match_simple(arv_gc_port_legacy_infos[i].model_selection, model_name) == TRUE) { port->priv->has_legacy_infos = TRUE; break; } } } port->priv->has_done_legacy_check = TRUE; } return length == 4 && port->priv->has_legacy_infos; } void arv_gc_port_read (ArvGcPort *port, void *buffer, guint64 address, guint64 length, GError **error) { ArvGc *genicam; g_return_if_fail (ARV_IS_GC_PORT (port)); g_return_if_fail (buffer != NULL); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (port)); if (port->priv->chunk_id != NULL) { ArvBuffer *chunk_data_buffer; chunk_data_buffer = arv_gc_get_buffer (genicam); if (!ARV_IS_BUFFER (chunk_data_buffer)) { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_BUFFER_NOT_FOUND, "[%s] Buffer not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } else { char *chunk_data; size_t chunk_data_size; guint chunk_id; chunk_id = g_ascii_strtoll (arv_gc_property_node_get_string (port->priv->chunk_id, NULL), NULL, 16); chunk_data = (char *) arv_buffer_get_chunk_data (chunk_data_buffer, chunk_id, &chunk_data_size); if (chunk_data != NULL) { memcpy (buffer, chunk_data + address, MIN (chunk_data_size - address, length)); } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_CHUNK_NOT_FOUND, "[%s] Chunk 0x%08x not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port)), chunk_id); } } } else if (port->priv->event_id != NULL) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NO_EVENT_IMPLEMENTATION, "[%s] Events not implemented", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } else { ArvDevice *device; device = arv_gc_get_device (genicam); if (ARV_IS_DEVICE (device)) { /* For schema < 1.1.0 and length == 4, register read must be used instead of memory read. * Only applies to GigE Vision devices. See Appendix 3 of Genicam 2.0 specification. */ if (ARV_IS_GV_DEVICE (device) && _use_legacy_endianness_mechanism (port, length)) { guint32 value = 0; arv_device_read_register (device, address, &value, error); /* For schema < 1.1.0, all registers are big endian. */ *((guint32 *) buffer) = GUINT32_TO_BE (value); } else arv_device_read_memory (device, address, length, buffer, error); } else { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NO_DEVICE_SET, "[%s] No device set", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } } } void arv_gc_port_write (ArvGcPort *port, void *buffer, guint64 address, guint64 length, GError **error) { ArvGc *genicam; ArvDevice *device; g_return_if_fail (ARV_IS_GC_PORT (port)); g_return_if_fail (buffer != NULL); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (port)); arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (port)); if (port->priv->chunk_id != NULL) { ArvBuffer *chunk_data_buffer; chunk_data_buffer = arv_gc_get_buffer (genicam); if (!ARV_IS_BUFFER (chunk_data_buffer)) { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_BUFFER_NOT_FOUND, "[%s] Buffer not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } else { char *chunk_data; size_t chunk_data_size; guint chunk_id; chunk_id = g_ascii_strtoll (arv_gc_property_node_get_string (port->priv->chunk_id, NULL), NULL, 16); chunk_data = (char *) arv_buffer_get_chunk_data (chunk_data_buffer, chunk_id, &chunk_data_size); if (chunk_data != NULL) { memcpy (chunk_data + address, buffer, MIN (chunk_data_size - address, length)); } else { g_set_error (error, ARV_CHUNK_PARSER_ERROR, ARV_CHUNK_PARSER_ERROR_CHUNK_NOT_FOUND, "[%s] Chunk 0x%08x not found", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port)), chunk_id); } } } else if (port->priv->event_id != NULL) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NO_EVENT_IMPLEMENTATION, "[%s] Events not implemented", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } else { device = arv_gc_get_device (genicam); if (ARV_IS_DEVICE (device)) { /* For schema < 1.1.0 and length == 4, register write must be used instead of memory write. * Only applies to GigE Vision devices. See Appendix 3 of Genicam 2.0 specification. */ if (ARV_IS_GV_DEVICE (device) && _use_legacy_endianness_mechanism (port, length)) { guint32 value; /* For schema < 1.1.0, all registers are big endian. */ value = *((guint32 *) buffer); value = GUINT32_FROM_BE (value); arv_device_write_register (device, address, value, error); } else arv_device_write_memory (device, address, length, buffer, error); } else { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NO_DEVICE_SET, "[%s] No device set", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (port))); } } } ArvGcNode * arv_gc_port_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_PORT, NULL); return node; } static void arv_gc_port_init (ArvGcPort *gc_port) { gc_port->priv = arv_gc_port_get_instance_private (gc_port); gc_port->priv->chunk_id = 0; gc_port->priv->has_done_legacy_check = FALSE; gc_port->priv->has_legacy_infos = FALSE; } static void _finalize (GObject *object) { G_OBJECT_CLASS (arv_gc_port_parent_class)->finalize (object); } static void arv_gc_port_class_init (ArvGcPortClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); object_class->finalize = _finalize; dom_node_class->get_node_name = _get_node_name; dom_node_class->post_new_child = _post_new_child; dom_node_class->pre_remove_child = _pre_remove_child; } aravis-0.8.34/src/arvgcport.h000066400000000000000000000030401475431451200160450ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_PORT_H #define ARV_GC_PORT_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_PORT (arv_gc_port_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcPort, arv_gc_port, ARV, GC_PORT, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_port_new (void); ARV_API void arv_gc_port_read (ArvGcPort *port, void *buffer, guint64 address, guint64 length, GError **error); ARV_API void arv_gc_port_write (ArvGcPort *port, void *buffer, guint64 address, guint64 length, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcprivate.h000066400000000000000000000020551475431451200165400ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_PRIVATE_H #define ARV_GC_PRIVATE_H #include ARV_API guint64 arv_gc_register_cache_error_add (ArvGc *genicam, guint64 n_errors); #endif aravis-0.8.34/src/arvgcpropertynode.c000066400000000000000000000763311475431451200176230ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcpropertynode * @short_description: Base class for Genicam property nodes * * #ArvGcPropertyNode provides a base class for the implementation of the different * types of Genicam property nodes (Value, pValue, Endianness...). */ #include #include #include #include #include #include #include #include #include #include #include #include enum { ARV_GC_PROPERTY_NODE_PROPERTY_0, ARV_GC_PROPERTY_NODE_PROPERTY_TYPE } ArvGcPropertyNodeProperties; typedef struct { ArvGcNode base; ArvGcPropertyNodeType type; char *name; gboolean value_data_up_to_date; char *value_data; } ArvGcPropertyNodePrivate; G_DEFINE_TYPE_WITH_CODE (ArvGcPropertyNode, arv_gc_property_node, ARV_TYPE_GC_NODE, G_ADD_PRIVATE (ArvGcPropertyNode)) /* ArvDomNode implementation */ static const char * arv_gc_property_node_get_node_name (ArvDomNode *node) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (node)); switch (priv->type) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: return "Value"; case ARV_GC_PROPERTY_NODE_TYPE_ADDRESS: return "Address"; case ARV_GC_PROPERTY_NODE_TYPE_DESCRIPTION: return "Description"; case ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY: return "Visibility"; case ARV_GC_PROPERTY_NODE_TYPE_TOOLTIP: return "ToolTip"; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NAME: return "DisplayName"; case ARV_GC_PROPERTY_NODE_TYPE_MINIMUM: return "Min"; case ARV_GC_PROPERTY_NODE_TYPE_MAXIMUM: return "Max"; case ARV_GC_PROPERTY_NODE_TYPE_SLOPE: return "Slope"; case ARV_GC_PROPERTY_NODE_TYPE_INCREMENT: return "Inc"; case ARV_GC_PROPERTY_NODE_TYPE_IS_LINEAR: return "IsLinear"; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: return "Representation"; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: return "Unit"; case ARV_GC_PROPERTY_NODE_TYPE_ON_VALUE: return "OnValue"; case ARV_GC_PROPERTY_NODE_TYPE_OFF_VALUE: return "OffValue"; case ARV_GC_PROPERTY_NODE_TYPE_LENGTH: return "Length"; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA: return "Formula"; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA_TO: return "FormulaTo"; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA_FROM: return "FormulaFrom"; case ARV_GC_PROPERTY_NODE_TYPE_EXPRESSION: return "Expression"; case ARV_GC_PROPERTY_NODE_TYPE_CONSTANT: return "Constant"; case ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE: return "AccessMode"; case ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE: return "ImposedAccessMode"; case ARV_GC_PROPERTY_NODE_TYPE_CACHABLE: return "Cachable"; case ARV_GC_PROPERTY_NODE_TYPE_POLLING_TIME: return "PollingTime"; case ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS: return "Endianess"; case ARV_GC_PROPERTY_NODE_TYPE_SIGN: return "Sign"; case ARV_GC_PROPERTY_NODE_TYPE_LSB: return "LSB"; case ARV_GC_PROPERTY_NODE_TYPE_MSB: return "MSB"; case ARV_GC_PROPERTY_NODE_TYPE_BIT: return "Bit"; case ARV_GC_PROPERTY_NODE_TYPE_COMMAND_VALUE: return "CommandValue"; case ARV_GC_PROPERTY_NODE_TYPE_CHUNK_ID: return "ChunkID"; case ARV_GC_PROPERTY_NODE_TYPE_EVENT_ID: return "EventID"; case ARV_GC_PROPERTY_NODE_TYPE_VALUE_DEFAULT: return "ValueDefault"; case ARV_GC_PROPERTY_NODE_TYPE_STREAMABLE: return "Streamable"; case ARV_GC_PROPERTY_NODE_TYPE_P_FEATURE: return "pFeature"; case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: return "pValue"; case ARV_GC_PROPERTY_NODE_TYPE_P_ADDRESS: return "pAddress"; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_IMPLEMENTED: return "pIsImplemented"; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_AVAILABLE: return "pIsAvailable"; case ARV_GC_PROPERTY_NODE_TYPE_P_IS_LOCKED: return "pIsLocked"; case ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED: return "pSelected"; case ARV_GC_PROPERTY_NODE_TYPE_P_MINIMUM: return "pMin"; case ARV_GC_PROPERTY_NODE_TYPE_P_MAXIMUM: return "pMax"; case ARV_GC_PROPERTY_NODE_TYPE_P_INCREMENT: return "pInc"; case ARV_GC_PROPERTY_NODE_TYPE_P_LENGTH: return "pLength"; case ARV_GC_PROPERTY_NODE_TYPE_P_PORT: return "pPort"; case ARV_GC_PROPERTY_NODE_TYPE_P_VARIABLE: return "pVariable"; case ARV_GC_PROPERTY_NODE_TYPE_P_COMMAND_VALUE: return "pCommandValue"; case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_DEFAULT: return "pValueDefault"; default: return "Unknown"; } } /* ArvDomElement implementation */ static gboolean _can_append_child (ArvDomNode *self, ArvDomNode *child) { return ARV_IS_DOM_TEXT (child); } static void _post_new_child (ArvDomNode *parent, ArvDomNode *child) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (parent)); priv->value_data_up_to_date = FALSE; } static void _pre_remove_child (ArvDomNode *parent, ArvDomNode *child) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (parent)); priv->value_data_up_to_date = FALSE; } /* ArvDomElement implementation */ static void arv_gc_property_node_set_attribute (ArvDomElement *self, const char *name, const char *value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (self)); if (strcmp (name, "Name") == 0) { g_free (priv->name); priv->name = g_strdup (value); } else arv_info_dom ("[GcPropertyNode::set_attribute] Unknown attribute '%s'", name); } static const char * arv_gc_property_node_get_attribute (ArvDomElement *self, const char *name) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (self)); if (strcmp (name, "Name") == 0) return priv->name; arv_info_dom ("[GcPropertyNode::set_attribute] Unknown attribute '%s'", name); return NULL; } /* ArvGcPropertyNode implementation */ static const char * _get_value_data (ArvGcPropertyNode *property_node) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (property_node); ArvDomNode *dom_node = ARV_DOM_NODE (property_node); if (!priv->value_data_up_to_date) { ArvDomNode *iter; GString *string = g_string_new (NULL); for (iter = arv_dom_node_get_first_child (dom_node); iter != NULL; iter = arv_dom_node_get_next_sibling (iter)) g_string_append (string, arv_dom_character_data_get_data (ARV_DOM_CHARACTER_DATA (iter))); g_free (priv->value_data); priv->value_data = arv_g_string_free_and_steal(string); priv->value_data_up_to_date = TRUE; } return priv->value_data; } static void _set_value_data (ArvGcPropertyNode *property_node, const char *data) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (property_node); ArvDomNode *dom_node = ARV_DOM_NODE (property_node); if (arv_dom_node_get_first_child (dom_node) != NULL) { ArvDomNode *iter; arv_dom_character_data_set_data (ARV_DOM_CHARACTER_DATA (arv_dom_node_get_first_child (dom_node)), data); for (iter = arv_dom_node_get_next_sibling (arv_dom_node_get_first_child (dom_node)); iter != NULL; iter = arv_dom_node_get_next_sibling (iter)) arv_dom_character_data_set_data (ARV_DOM_CHARACTER_DATA (iter), ""); } g_free (priv->value_data); priv->value_data = g_strdup (data); priv->value_data_up_to_date = TRUE; } static ArvDomNode * _get_pvalue_node (ArvGcPropertyNode *property_node) { ArvDomNode *pvalue_node; const char *node_name; ArvGc *genicam; if (arv_gc_property_node_get_node_type (property_node) < ARV_GC_PROPERTY_NODE_TYPE_P_UNKNONW) return NULL; node_name = _get_value_data (property_node); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (property_node)); pvalue_node = ARV_DOM_NODE (arv_gc_get_node (genicam, node_name)); return pvalue_node; } /** * arv_gc_property_node_get_name: * @node: a #ArvGcPropertyNode * * Returns: node Name property value. * * Since: 0.6.0 */ const char * arv_gc_property_node_get_name (ArvGcPropertyNode *node) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), NULL); return priv->name; } const char * arv_gc_property_node_get_string (ArvGcPropertyNode *node, GError **error) { ArvDomNode *pvalue_node; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) return _get_value_data (node); if (ARV_IS_GC_STRING (pvalue_node)) { GError *local_error = NULL; const char *value; value = arv_gc_string_get_value (ARV_GC_STRING (pvalue_node), &local_error); if (local_error != NULL) g_propagate_error (error, local_error); return value; } arv_warning_genicam ("[GcPropertyNode::get_string] Invalid node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); return NULL; } void arv_gc_property_node_set_string (ArvGcPropertyNode *node, const char *string, GError **error) { ArvDomNode *pvalue_node; g_return_if_fail (ARV_IS_GC_PROPERTY_NODE (node)); g_return_if_fail (error == NULL || *error == NULL); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) { _set_value_data (node, string); return; } if (ARV_IS_GC_STRING (pvalue_node)) { GError *local_error = NULL; arv_gc_string_set_value (ARV_GC_STRING (pvalue_node), string, &local_error); if (local_error != NULL) g_propagate_error (error, local_error); return; } arv_warning_genicam ("[GcPropertyNode::set_string] Invalid linked node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); } gint64 arv_gc_property_node_get_int64 (ArvGcPropertyNode *node, GError **error) { ArvDomNode *pvalue_node; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) return g_ascii_strtoll (_get_value_data (node), NULL, 0); if (ARV_IS_GC_INTEGER (pvalue_node)) { return arv_gc_integer_get_value (ARV_GC_INTEGER (pvalue_node), error); } else if (ARV_IS_GC_FLOAT (pvalue_node)) { return (gint64) arv_gc_float_get_value (ARV_GC_FLOAT (pvalue_node), error); } else if (ARV_IS_GC_BOOLEAN (pvalue_node)) { return arv_gc_boolean_get_value (ARV_GC_BOOLEAN (pvalue_node), error) ? 1 : 0; } arv_warning_genicam ("[GcPropertyNode::get_int64] Invalid node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); return 0; } void arv_gc_property_node_set_int64 (ArvGcPropertyNode *node, gint64 v_int64, GError **error) { ArvDomNode *pvalue_node; g_return_if_fail (ARV_IS_GC_PROPERTY_NODE (node)); g_return_if_fail (error == NULL || *error == NULL); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) { char *buffer; buffer = g_strdup_printf ("%" G_GINT64_FORMAT, v_int64); _set_value_data (node, buffer); g_free (buffer); return ; } if (ARV_IS_GC_INTEGER (pvalue_node)) { arv_gc_integer_set_value (ARV_GC_INTEGER (pvalue_node), v_int64, error); return; } else if (ARV_IS_GC_FLOAT (pvalue_node)) { arv_gc_float_set_value (ARV_GC_FLOAT (pvalue_node), (double) v_int64, error); return; } arv_warning_genicam ("[GcPropertyNode::set_int64] Invalid linked node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); } double arv_gc_property_node_get_double (ArvGcPropertyNode *node, GError **error) { ArvDomNode *pvalue_node; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), 0.0); g_return_val_if_fail (error == NULL || *error == NULL, 0.0); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) return g_ascii_strtod (_get_value_data (node), NULL); if (ARV_IS_GC_FLOAT (pvalue_node)) { return arv_gc_float_get_value (ARV_GC_FLOAT (pvalue_node), error); } else if (ARV_IS_GC_INTEGER (pvalue_node)) { return arv_gc_integer_get_value (ARV_GC_INTEGER (pvalue_node), error); } arv_warning_genicam ("[GcPropertyNode::get_double] Invalid node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); return 0.0; } void arv_gc_property_node_set_double (ArvGcPropertyNode *node, double v_double, GError **error) { ArvDomNode *pvalue_node; g_return_if_fail (ARV_IS_GC_PROPERTY_NODE (node)); g_return_if_fail (error == NULL || *error == NULL); pvalue_node = _get_pvalue_node (node); if (pvalue_node == NULL) { char buffer[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr (buffer, G_ASCII_DTOSTR_BUF_SIZE, v_double); _set_value_data (node, buffer); return ; } if (ARV_IS_GC_FLOAT (pvalue_node)) { arv_gc_float_set_value (ARV_GC_FLOAT (pvalue_node), v_double, error); return; } else if (ARV_IS_GC_INTEGER (pvalue_node)) { arv_gc_integer_set_value (ARV_GC_INTEGER (pvalue_node), v_double, error); return; } arv_warning_genicam ("[GcPropertyNode::set_double] Invalid linked node '%s'", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (pvalue_node))); } ArvGcPropertyNodeType arv_gc_property_node_get_node_type (ArvGcPropertyNode *node) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (node); g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), ARV_GC_PROPERTY_NODE_TYPE_UNKNOWN); return priv->type; } /** * arv_gc_property_node_get_linked_node: * @node: a #ArvGcPropertyNode * * Returns: (transfer none): the #ArvGcNode which @node points to, %NULL if the property is not a pointer. */ ArvGcNode * arv_gc_property_node_get_linked_node (ArvGcPropertyNode *node) { ArvGc *genicam; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (node), NULL); if (arv_gc_property_node_get_node_type (node) <= ARV_GC_PROPERTY_NODE_TYPE_P_UNKNONW) return NULL; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (node)); return arv_gc_get_node (genicam, _get_value_data (node)); } static ArvGcNode * arv_gc_property_node_new (ArvGcPropertyNodeType type) { return g_object_new (ARV_TYPE_GC_PROPERTY_NODE, "node-type", type, NULL); } ArvGcNode * arv_gc_property_node_new_p_feature (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_FEATURE); } ArvGcNode * arv_gc_property_node_new_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_VALUE); } ArvGcNode * arv_gc_property_node_new_p_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_VALUE); } ArvGcNode * arv_gc_property_node_new_address (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_ADDRESS); } ArvGcNode * arv_gc_property_node_new_p_address (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_ADDRESS); } ArvGcNode * arv_gc_property_node_new_description (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_DESCRIPTION); } ArvGcNode * arv_gc_property_node_new_visibility (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY); } ArvGcVisibility arv_gc_property_node_get_visibility (ArvGcPropertyNode *self, ArvGcVisibility default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY, ARV_GC_VISIBILITY_UNDEFINED); value = _get_value_data (self); if (g_strcmp0 (value, "Invisible") == 0) return ARV_GC_VISIBILITY_INVISIBLE; else if (g_strcmp0 (value, "Guru") == 0) return ARV_GC_VISIBILITY_GURU; else if (g_strcmp0 (value, "Expert") == 0) return ARV_GC_VISIBILITY_EXPERT; else if (g_strcmp0 (value, "Beginner") == 0) return ARV_GC_VISIBILITY_BEGINNER; return ARV_GC_VISIBILITY_UNDEFINED; } ArvGcNode * arv_gc_property_node_new_tooltip (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_TOOLTIP); } ArvGcNode * arv_gc_property_node_new_display_name (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NAME); } ArvGcNode * arv_gc_property_node_new_minimum (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_MINIMUM); } ArvGcNode * arv_gc_property_node_new_p_minimum (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_MINIMUM); } ArvGcNode * arv_gc_property_node_new_maximum (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_MAXIMUM); } ArvGcNode * arv_gc_property_node_new_p_maximum (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_MAXIMUM); } ArvGcNode * arv_gc_property_node_new_slope (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_SLOPE); } ArvGcNode * arv_gc_property_node_new_increment (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_INCREMENT); } ArvGcNode * arv_gc_property_node_new_p_increment (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_INCREMENT); } ArvGcNode * arv_gc_property_node_new_is_linear (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_IS_LINEAR); } ArvGcNode * arv_gc_property_node_new_representation (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION); } ArvGcRepresentation arv_gc_property_node_get_representation (ArvGcPropertyNode *self, ArvGcRepresentation default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION, default_value); value = _get_value_data (self); if (g_strcmp0 (value, "Linear") == 0) return ARV_GC_REPRESENTATION_LINEAR; else if (g_strcmp0 (value, "Logarithmic") == 0) return ARV_GC_REPRESENTATION_LOGARITHMIC; else if (g_strcmp0 (value, "Boolean") == 0) return ARV_GC_REPRESENTATION_BOOLEAN; else if (g_strcmp0 (value, "PureNumber") == 0) return ARV_GC_REPRESENTATION_PURE_NUMBER; else if (g_strcmp0 (value, "HexNumber") == 0) return ARV_GC_REPRESENTATION_HEX_NUMBER; else if (g_strcmp0 (value, "IPV4Address") == 0) return ARV_GC_REPRESENTATION_IPV4_ADDRESS; else if (g_strcmp0 (value, "MACAddress") == 0) return ARV_GC_REPRESENTATION_MAC_ADDRESS; return default_value; } ArvGcNode * arv_gc_property_node_new_display_notation (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION); } ArvGcDisplayNotation arv_gc_property_node_get_display_notation (ArvGcPropertyNode *self, ArvGcDisplayNotation default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION, default_value); value = _get_value_data (self); if (g_strcmp0 (value, "Automatic") == 0) return ARV_GC_DISPLAY_NOTATION_AUTOMATIC; else if (g_strcmp0 (value, "Fixed") == 0) return ARV_GC_DISPLAY_NOTATION_FIXED; else if (g_strcmp0 (value, "Scientific") == 0) return ARV_GC_DISPLAY_NOTATION_SCIENTIFIC; return default_value; } ArvGcNode * arv_gc_property_node_new_display_precision (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION); } gint64 arv_gc_property_node_get_display_precision (ArvGcPropertyNode *self, gint64 default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION, default_value); return g_ascii_strtoll (_get_value_data (self), NULL, 0); } ArvGcNode * arv_gc_property_node_new_unit (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_UNIT); } ArvGcNode * arv_gc_property_node_new_on_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_ON_VALUE); } ArvGcNode * arv_gc_property_node_new_off_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_OFF_VALUE); } ArvGcNode * arv_gc_property_node_new_p_is_implemented (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_IS_IMPLEMENTED); } ArvGcNode * arv_gc_property_node_new_p_is_available (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_IS_AVAILABLE); } ArvGcNode * arv_gc_property_node_new_p_is_locked (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_IS_LOCKED); } ArvGcNode * arv_gc_property_node_new_p_selected (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED); } ArvGcNode * arv_gc_property_node_new_length (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_LENGTH); } ArvGcNode * arv_gc_property_node_new_p_length (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_LENGTH); } ArvGcNode * arv_gc_property_node_new_p_port (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_PORT); } ArvGcNode * arv_gc_property_node_new_p_variable (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_VARIABLE); } ArvGcNode * arv_gc_property_node_new_formula (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_FORMULA); } ArvGcNode * arv_gc_property_node_new_formula_to (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_FORMULA_TO); } ArvGcNode * arv_gc_property_node_new_formula_from (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_FORMULA_FROM); } ArvGcNode * arv_gc_property_node_new_expression (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_EXPRESSION); } ArvGcNode * arv_gc_property_node_new_constant (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_CONSTANT); } ArvGcNode * arv_gc_property_node_new_access_mode (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE); } ArvGcNode * arv_gc_property_node_new_imposed_access_mode (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE); } ArvGcAccessMode arv_gc_property_node_get_access_mode (ArvGcPropertyNode *self, ArvGcAccessMode default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE || priv->type == ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE, default_value); value = _get_value_data (self); if (g_strcmp0 (value, "RO") == 0) return ARV_GC_ACCESS_MODE_RO; else if (g_strcmp0 (value, "WO") == 0) return ARV_GC_ACCESS_MODE_WO; else if (g_strcmp0 (value, "RW") == 0) return ARV_GC_ACCESS_MODE_RW; return default_value; } ArvGcNode * arv_gc_property_node_new_cachable (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_CACHABLE); } ArvGcCachable arv_gc_property_node_get_cachable (ArvGcPropertyNode *self, ArvGcCachable default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_CACHABLE, default_value); value = _get_value_data (self); if (g_strcmp0 (value, "WriteAround") == 0) return ARV_GC_CACHABLE_WRITE_AROUND; else if (g_strcmp0 (value, "WriteThrough") == 0) return ARV_GC_CACHABLE_WRITE_THROUGH; return ARV_GC_CACHABLE_NO_CACHE; } ArvGcNode * arv_gc_property_node_new_polling_time (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_POLLING_TIME); } ArvGcNode * arv_gc_property_node_new_endianness (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS); } guint arv_gc_property_node_get_endianness (ArvGcPropertyNode *self, guint default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS, default_value); if (g_strcmp0 (_get_value_data (self), "BigEndian") == 0) return G_BIG_ENDIAN; return G_LITTLE_ENDIAN; } ArvGcNode * arv_gc_property_node_new_sign (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_SIGN); } ArvGcSignedness arv_gc_property_node_get_sign (ArvGcPropertyNode *self, ArvGcSignedness default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_SIGN, default_value); if (g_strcmp0 (_get_value_data (self), "Unsigned") == 0) return ARV_GC_SIGNEDNESS_UNSIGNED; return ARV_GC_SIGNEDNESS_SIGNED; } ArvGcNode * arv_gc_property_node_new_lsb (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_LSB); } guint arv_gc_property_node_get_lsb (ArvGcPropertyNode *self, guint default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_LSB || priv->type == ARV_GC_PROPERTY_NODE_TYPE_BIT, default_value); return g_ascii_strtoll (_get_value_data (self), NULL, 10); } ArvGcNode * arv_gc_property_node_new_msb (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_MSB); } guint arv_gc_property_node_get_msb (ArvGcPropertyNode *self, guint default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_MSB || priv->type == ARV_GC_PROPERTY_NODE_TYPE_BIT, default_value); return g_ascii_strtoll (_get_value_data (self), NULL, 10); } ArvGcNode * arv_gc_property_node_new_bit (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_BIT); } ArvGcNode * arv_gc_property_node_new_command_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_COMMAND_VALUE); } ArvGcNode * arv_gc_property_node_new_p_command_value (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_COMMAND_VALUE); } ArvGcNode * arv_gc_property_node_new_chunk_id (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_CHUNK_ID); } ArvGcNode * arv_gc_property_node_new_event_id (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_EVENT_ID); } ArvGcNode * arv_gc_property_node_new_value_default (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_VALUE_DEFAULT); } ArvGcNode * arv_gc_property_node_new_p_value_default (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_DEFAULT); } ArvGcStreamable arv_gc_property_node_get_streamable (ArvGcPropertyNode *self, ArvGcStreamable default_value) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); const char *value; if (self == NULL) return default_value; g_return_val_if_fail (ARV_IS_GC_PROPERTY_NODE (self), default_value); g_return_val_if_fail (priv->type == ARV_GC_PROPERTY_NODE_TYPE_STREAMABLE, default_value); value = _get_value_data (self); if (g_strcmp0 (value, "Yes") == 0) return ARV_GC_STREAMABLE_YES; else if (g_strcmp0 (value, "No") == 0) return ARV_GC_STREAMABLE_NO; return ARV_GC_STREAMABLE_NO; } ArvGcNode * arv_gc_property_node_new_streamable (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_STREAMABLE); } ArvGcNode * arv_gc_property_node_new_is_deprecated (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_IS_DEPRECATED); } ArvGcNode * arv_gc_property_node_new_p_alias (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_ALIAS); } ArvGcNode * arv_gc_property_node_new_p_cast_alias (void) { return arv_gc_property_node_new (ARV_GC_PROPERTY_NODE_TYPE_P_CAST_ALIAS); } static void _set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (object)); switch (prop_id) { case ARV_GC_PROPERTY_NODE_PROPERTY_TYPE: priv->type = g_value_get_enum (value); break; default: g_assert_not_reached (); } } static void _get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (object)); switch (prop_id) { case ARV_GC_PROPERTY_NODE_PROPERTY_TYPE: g_value_set_enum (value, priv->type); break; default: g_assert_not_reached (); } } static void arv_gc_property_node_init (ArvGcPropertyNode *self) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (self); priv->type = ARV_GC_PROPERTY_NODE_TYPE_UNKNOWN; priv->value_data = NULL; priv->value_data_up_to_date = FALSE; } static void arv_gc_property_node_finalize (GObject *object) { ArvGcPropertyNodePrivate *priv = arv_gc_property_node_get_instance_private (ARV_GC_PROPERTY_NODE (object)); G_OBJECT_CLASS (arv_gc_property_node_parent_class)->finalize (object); g_free (priv->value_data); g_free (priv->name); } static void arv_gc_property_node_class_init (ArvGcPropertyNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_property_node_finalize; object_class->set_property = _set_property; object_class->get_property = _get_property; dom_node_class->get_node_name = arv_gc_property_node_get_node_name; dom_node_class->can_append_child = _can_append_child; dom_node_class->post_new_child = _post_new_child; dom_node_class->pre_remove_child = _pre_remove_child; dom_element_class->set_attribute = arv_gc_property_node_set_attribute; dom_element_class->get_attribute = arv_gc_property_node_get_attribute; g_object_class_install_property ( object_class, ARV_GC_PROPERTY_NODE_PROPERTY_TYPE, g_param_spec_enum ("node-type", "Node type", "Actual node type", ARV_TYPE_GC_PROPERTY_NODE_TYPE, ARV_GC_PROPERTY_NODE_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY) ); } aravis-0.8.34/src/arvgcpropertynode.h000066400000000000000000000226361475431451200176270ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_PROPERTY_NODE_H #define ARV_GC_PROPERTY_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS typedef enum { ARV_GC_PROPERTY_NODE_TYPE_UNKNOWN = 0, ARV_GC_PROPERTY_NODE_TYPE_VALUE, ARV_GC_PROPERTY_NODE_TYPE_ADDRESS, ARV_GC_PROPERTY_NODE_TYPE_DESCRIPTION, ARV_GC_PROPERTY_NODE_TYPE_VISIBILITY, ARV_GC_PROPERTY_NODE_TYPE_TOOLTIP, ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NAME, ARV_GC_PROPERTY_NODE_TYPE_MINIMUM, ARV_GC_PROPERTY_NODE_TYPE_MAXIMUM, ARV_GC_PROPERTY_NODE_TYPE_SLOPE, ARV_GC_PROPERTY_NODE_TYPE_INCREMENT, ARV_GC_PROPERTY_NODE_TYPE_IS_LINEAR, ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION, ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION, ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION, ARV_GC_PROPERTY_NODE_TYPE_UNIT, ARV_GC_PROPERTY_NODE_TYPE_ON_VALUE, ARV_GC_PROPERTY_NODE_TYPE_OFF_VALUE, ARV_GC_PROPERTY_NODE_TYPE_LENGTH, ARV_GC_PROPERTY_NODE_TYPE_FORMULA, ARV_GC_PROPERTY_NODE_TYPE_FORMULA_TO, ARV_GC_PROPERTY_NODE_TYPE_FORMULA_FROM, ARV_GC_PROPERTY_NODE_TYPE_EXPRESSION, ARV_GC_PROPERTY_NODE_TYPE_CONSTANT, ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE, ARV_GC_PROPERTY_NODE_TYPE_IMPOSED_ACCESS_MODE, ARV_GC_PROPERTY_NODE_TYPE_CACHABLE, ARV_GC_PROPERTY_NODE_TYPE_POLLING_TIME, ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS, ARV_GC_PROPERTY_NODE_TYPE_SIGN, ARV_GC_PROPERTY_NODE_TYPE_LSB, ARV_GC_PROPERTY_NODE_TYPE_MSB, ARV_GC_PROPERTY_NODE_TYPE_BIT, ARV_GC_PROPERTY_NODE_TYPE_COMMAND_VALUE, ARV_GC_PROPERTY_NODE_TYPE_CHUNK_ID, ARV_GC_PROPERTY_NODE_TYPE_EVENT_ID, ARV_GC_PROPERTY_NODE_TYPE_VALUE_INDEXED, ARV_GC_PROPERTY_NODE_TYPE_VALUE_DEFAULT, ARV_GC_PROPERTY_NODE_TYPE_STREAMABLE, ARV_GC_PROPERTY_NODE_TYPE_IS_DEPRECATED, ARV_GC_PROPERTY_NODE_TYPE_P_UNKNONW = 1000, ARV_GC_PROPERTY_NODE_TYPE_P_FEATURE, ARV_GC_PROPERTY_NODE_TYPE_P_VALUE, ARV_GC_PROPERTY_NODE_TYPE_P_ADDRESS, ARV_GC_PROPERTY_NODE_TYPE_P_IS_IMPLEMENTED, ARV_GC_PROPERTY_NODE_TYPE_P_IS_LOCKED, ARV_GC_PROPERTY_NODE_TYPE_P_IS_AVAILABLE, ARV_GC_PROPERTY_NODE_TYPE_P_SELECTED, ARV_GC_PROPERTY_NODE_TYPE_P_MINIMUM, ARV_GC_PROPERTY_NODE_TYPE_P_MAXIMUM, ARV_GC_PROPERTY_NODE_TYPE_P_INCREMENT, ARV_GC_PROPERTY_NODE_TYPE_P_INDEX, ARV_GC_PROPERTY_NODE_TYPE_P_LENGTH, ARV_GC_PROPERTY_NODE_TYPE_P_PORT, ARV_GC_PROPERTY_NODE_TYPE_P_VARIABLE, ARV_GC_PROPERTY_NODE_TYPE_P_INVALIDATOR, ARV_GC_PROPERTY_NODE_TYPE_P_COMMAND_VALUE, ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_INDEXED, ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_DEFAULT, ARV_GC_PROPERTY_NODE_TYPE_P_ALIAS, ARV_GC_PROPERTY_NODE_TYPE_P_CAST_ALIAS } ArvGcPropertyNodeType; #define ARV_TYPE_GC_PROPERTY_NODE (arv_gc_property_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcPropertyNode, arv_gc_property_node, ARV, GC_PROPERTY_NODE, ArvGcNode) struct _ArvGcPropertyNodeClass { ArvGcNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_property_node_new_p_feature (void); ARV_API ArvGcNode * arv_gc_property_node_new_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_address (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_address (void); ARV_API ArvGcNode * arv_gc_property_node_new_description (void); ARV_API ArvGcNode * arv_gc_property_node_new_visibility (void); ARV_API ArvGcNode * arv_gc_property_node_new_tooltip (void); ARV_API ArvGcNode * arv_gc_property_node_new_display_name (void); ARV_API ArvGcNode * arv_gc_property_node_new_minimum (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_minimum (void); ARV_API ArvGcNode * arv_gc_property_node_new_maximum (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_maximum (void); ARV_API ArvGcNode * arv_gc_property_node_new_slope (void); ARV_API ArvGcNode * arv_gc_property_node_new_increment (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_increment (void); ARV_API ArvGcNode * arv_gc_property_node_new_is_linear (void); ARV_API ArvGcNode * arv_gc_property_node_new_representation (void); ARV_API ArvGcNode * arv_gc_property_node_new_display_notation (void); ARV_API ArvGcNode * arv_gc_property_node_new_display_precision (void); ARV_API ArvGcNode * arv_gc_property_node_new_unit (void); ARV_API ArvGcNode * arv_gc_property_node_new_on_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_off_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_is_implemented (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_is_available (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_is_locked (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_selected (void); ARV_API ArvGcNode * arv_gc_property_node_new_length (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_length (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_port (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_variable (void); ARV_API ArvGcNode * arv_gc_property_node_new_formula (void); ARV_API ArvGcNode * arv_gc_property_node_new_formula_to (void); ARV_API ArvGcNode * arv_gc_property_node_new_formula_from (void); ARV_API ArvGcNode * arv_gc_property_node_new_expression (void); ARV_API ArvGcNode * arv_gc_property_node_new_constant (void); ARV_API ArvGcNode * arv_gc_property_node_new_access_mode (void); ARV_API ArvGcNode * arv_gc_property_node_new_imposed_access_mode (void); ARV_API ArvGcNode * arv_gc_property_node_new_cachable (void); ARV_API ArvGcNode * arv_gc_property_node_new_polling_time (void); ARV_API ArvGcNode * arv_gc_property_node_new_endianness (void); ARV_API ArvGcNode * arv_gc_property_node_new_sign (void); ARV_API ArvGcNode * arv_gc_property_node_new_lsb (void); ARV_API ArvGcNode * arv_gc_property_node_new_msb (void); ARV_API ArvGcNode * arv_gc_property_node_new_bit (void); ARV_API ArvGcNode * arv_gc_property_node_new_command_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_command_value (void); ARV_API ArvGcNode * arv_gc_property_node_new_chunk_id (void); ARV_API ArvGcNode * arv_gc_property_node_new_event_id (void); ARV_API ArvGcNode * arv_gc_property_node_new_value_default (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_value_default (void); ARV_API ArvGcNode * arv_gc_property_node_new_streamable (void); ARV_API ArvGcNode * arv_gc_property_node_new_is_deprecated (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_alias (void); ARV_API ArvGcNode * arv_gc_property_node_new_p_cast_alias (void); ARV_API const char * arv_gc_property_node_get_name (ArvGcPropertyNode *node); ARV_API const char * arv_gc_property_node_get_string (ArvGcPropertyNode *node, GError **error); ARV_API void arv_gc_property_node_set_string (ArvGcPropertyNode *node, const char *string, GError **error); ARV_API gint64 arv_gc_property_node_get_int64 (ArvGcPropertyNode *node, GError **error); ARV_API void arv_gc_property_node_set_int64 (ArvGcPropertyNode *node, gint64 v_int64, GError **error); ARV_API double arv_gc_property_node_get_double (ArvGcPropertyNode *node, GError **error); ARV_API void arv_gc_property_node_set_double (ArvGcPropertyNode *node, double v_double, GError **error); ARV_API guint arv_gc_property_node_get_endianness (ArvGcPropertyNode *self, guint default_value); ARV_API ArvGcSignedness arv_gc_property_node_get_sign (ArvGcPropertyNode *self, ArvGcSignedness default_value); ARV_API guint arv_gc_property_node_get_lsb (ArvGcPropertyNode *self, guint default_value); ARV_API guint arv_gc_property_node_get_msb (ArvGcPropertyNode *self, guint default_value); ARV_API ArvGcCachable arv_gc_property_node_get_cachable (ArvGcPropertyNode *self, ArvGcCachable default_value); ARV_API ArvGcAccessMode arv_gc_property_node_get_access_mode (ArvGcPropertyNode *self, ArvGcAccessMode default_value); ARV_API ArvGcVisibility arv_gc_property_node_get_visibility (ArvGcPropertyNode *self, ArvGcVisibility default_value); ARV_API ArvGcRepresentation arv_gc_property_node_get_representation (ArvGcPropertyNode *self, ArvGcRepresentation default_value); ARV_API ArvGcDisplayNotation arv_gc_property_node_get_display_notation (ArvGcPropertyNode *self, ArvGcDisplayNotation default_value); ARV_API gint64 arv_gc_property_node_get_display_precision (ArvGcPropertyNode *self, gint64 default_value); ARV_API ArvGcStreamable arv_gc_property_node_get_streamable (ArvGcPropertyNode *self, ArvGcStreamable default_value); ARV_API ArvGcNode * arv_gc_property_node_get_linked_node (ArvGcPropertyNode *node); ARV_API ArvGcPropertyNodeType arv_gc_property_node_get_node_type (ArvGcPropertyNode *node); G_END_DECLS #endif aravis-0.8.34/src/arvgcregister.c000066400000000000000000000071521475431451200167100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcregister * @short_description: Register interface */ #include #include static void arv_gc_register_default_init (ArvGcRegisterInterface *gc_register_iface) { } G_DEFINE_INTERFACE (ArvGcRegister, arv_gc_register, G_TYPE_OBJECT) void arv_gc_register_get (ArvGcRegister *gc_register, void *buffer, guint64 length, GError **error) { g_return_if_fail (ARV_IS_GC_REGISTER (gc_register)); g_return_if_fail (buffer != NULL); g_return_if_fail (length > 0); g_return_if_fail (error == NULL || *error == NULL); ARV_GC_REGISTER_GET_IFACE (gc_register)->get (gc_register, buffer, length, error); } void arv_gc_register_set (ArvGcRegister *gc_register, const void *buffer, guint64 length, GError **error) { g_return_if_fail (ARV_IS_GC_REGISTER (gc_register)); g_return_if_fail (buffer != NULL); g_return_if_fail (length > 0); g_return_if_fail (error == NULL || *error == NULL); ARV_GC_REGISTER_GET_IFACE (gc_register)->set (gc_register, buffer, length, error); } guint64 arv_gc_register_get_address (ArvGcRegister *gc_register, GError **error) { g_return_val_if_fail (ARV_IS_GC_REGISTER (gc_register), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); return ARV_GC_REGISTER_GET_IFACE (gc_register)->get_address (gc_register, error); } guint64 arv_gc_register_get_length (ArvGcRegister *gc_register, GError **error) { g_return_val_if_fail (ARV_IS_GC_REGISTER (gc_register), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); return ARV_GC_REGISTER_GET_IFACE (gc_register)->get_length (gc_register, error); } /** * arv_gc_register_dup: * @gc_register: a #ArvGcRegister * @length: (out) (allow-none): register length * @error: a #GError placeholder * * Returns: the register feature content, must be freed using [func@GLib.free]. * * Since: 0.8.31 */ void * arv_gc_register_dup (ArvGcRegister *gc_register, guint64 *length, GError **error) { GError *local_error = NULL; void *buffer = NULL; guint64 register_length = 0; if (length != NULL) *length = 0; g_return_val_if_fail (ARV_IS_GC_REGISTER(gc_register), NULL); register_length = arv_gc_register_get_length(gc_register, &local_error); if (register_length < 65536 && local_error == NULL) { buffer = g_malloc (register_length); if (buffer != NULL) arv_gc_register_get (gc_register, buffer, register_length, &local_error); } if (local_error != NULL) { g_clear_pointer (&buffer, g_free); register_length = 0; g_propagate_error (error, local_error); } if (length != NULL) *length = register_length; return buffer; } aravis-0.8.34/src/arvgcregister.h000066400000000000000000000046031475431451200167130ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_REGISTER_H #define ARV_GC_REGISTER_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_REGISTER (arv_gc_register_get_type ()) ARV_API G_DECLARE_INTERFACE (ArvGcRegister, arv_gc_register, ARV, GC_REGISTER, GObject) struct _ArvGcRegisterInterface { GTypeInterface parent; void (*get) (ArvGcRegister *gc_register, void *buffer, guint64 length, GError **error); void (*set) (ArvGcRegister *gc_register, const void *buffer, guint64 length, GError **error); guint64 (*get_address) (ArvGcRegister *gc_register, GError **error); guint64 (*get_length) (ArvGcRegister *gc_register, GError **error); }; ARV_API void arv_gc_register_get (ArvGcRegister *gc_register, void *buffer, guint64 length, GError **error); ARV_API void arv_gc_register_set (ArvGcRegister *gc_register, const void *buffer, guint64 length, GError **error); ARV_API guint64 arv_gc_register_get_address (ArvGcRegister *gc_register, GError **error); ARV_API guint64 arv_gc_register_get_length (ArvGcRegister *gc_register, GError **error); ARV_API void * arv_gc_register_dup (ArvGcRegister *gc_register, guint64 *length, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcregisterdescriptionnode.c000066400000000000000000000250011475431451200220130ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcregisterdescriptionnode * @short_description: Class for RegisterDescription nodes */ #include #include struct _ArvGcRegisterDescriptionNode { ArvGcFeatureNode node; char *model_name; char *vendor_name; guint major_version; guint minor_version; guint subminor_version; guint schema_major_version; guint schema_minor_version; guint schema_subminor_version; char *product_guid; char *version_guid; char *standard_namespace; char *tooltip; }; struct _ArvGcRegisterDescriptionNodeClass { ArvGcFeatureNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcRegisterDescriptionNode, arv_gc_register_description_node, ARV_TYPE_GC_FEATURE_NODE) /* ArvDomNode implementation */ static const char * arv_gc_register_description_node_get_node_name (ArvDomNode *node) { return "RegisterDescription"; } static void arv_gc_register_description_node_set_attribute (ArvDomElement *self, const char* name, const char *value) { ArvGcRegisterDescriptionNode *node = ARV_GC_REGISTER_DESCRIPTION_NODE (self); if (strcmp (name, "ModelName") == 0) { g_free (node->model_name); node->model_name = g_strdup (value); } else if (strcmp (name, "VendorName") == 0) { g_free (node->vendor_name); node->vendor_name = g_strdup (value); } else if (strcmp (name, "SchemaMajorVersion") == 0) { node->schema_major_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "SchemaMinorVersion") == 0) { node->schema_minor_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "SchemaSubMinorVersion") == 0) { node->schema_subminor_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "MajorVersion") == 0) { node->major_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "MinorVersion") == 0) { node->minor_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "SubMinorVersion") == 0) { node->subminor_version = g_ascii_strtoll (value, NULL, 0); } else if (strcmp (name, "ProductGuid") == 0) { g_free (node->product_guid); node->product_guid = g_strdup (value); } else if (strcmp (name, "VersionGuid") == 0) { g_free (node->version_guid); node->version_guid = g_strdup (value); } else if (strcmp (name, "StandardNameSpace") == 0) { g_free (node->standard_namespace); node->standard_namespace = g_strdup (value); } else if (strcmp (name, "ToolTip") == 0) { g_free (node->tooltip); node->tooltip = g_strdup (value); } else if (strcmp (name, "xmlns:xsi") != 0 && strcmp (name, "xmlns") != 0 && strcmp (name, "xsi:schemaLocation") != 0) { ARV_DOM_ELEMENT_CLASS (arv_gc_register_description_node_parent_class)->set_attribute (self, name, value); } } static const char * arv_gc_register_description_node_get_attribute (ArvDomElement *self, const char *name) { ArvGcRegisterDescriptionNode *node = ARV_GC_REGISTER_DESCRIPTION_NODE (self); if (strcmp (name, "ModelName") == 0) return node->model_name; else if (strcmp (name, "VendorName") == 0) return node->vendor_name; else return ARV_DOM_ELEMENT_CLASS (arv_gc_register_description_node_parent_class)->get_attribute (self, name); } /* ArvGcRegisterDescriptionNode implementation */ /** * arv_gc_register_description_node_compare_schema_version: * @node: a #ArvGcRegisterDescriptionNode * @major: major version number * @minor: minor version number * @subminor: sub minor version number * * Compare the Genicam document version to the given version. * * Returns: -1 if document version is lower than the given version, 0 if equal and 1 if greater. * * Since: 0.6.0 */ int arv_gc_register_description_node_compare_schema_version (ArvGcRegisterDescriptionNode *node, guint major, guint minor, guint subminor) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), FALSE); if (node->schema_major_version < major) return -1; if (node->schema_major_version > major) return 1; if (node->schema_minor_version < minor) return -1; if (node->schema_minor_version > minor) return 1; if (node->schema_subminor_version < subminor) return -1; if (node->schema_subminor_version > subminor) return 1; return 0; } /** * arv_gc_register_description_node_check_schema_version: * @node: a #ArvGcRegisterDescriptionNode * @required_major: required major version number * @required_minor: required minor version number * @required_subminor: required sub minor version number * * Checks if the Genicam document version is higher or equal to the given version. * * Returns: True if document version is higher or equal than the given version. * * Since: 0.6.0 */ gboolean arv_gc_register_description_node_check_schema_version (ArvGcRegisterDescriptionNode *node, guint required_major, guint required_minor, guint required_subminor) { return arv_gc_register_description_node_compare_schema_version (node, required_major, required_minor, required_subminor) >= 0; } /** * arv_gc_register_description_node_get_model_name: * @node: a #ArvGcRegisterDescriptionNode * * Gets camera model name for given Genicam document. * * Returns: Model name string. * * Since: 0.8.0 */ char * arv_gc_register_description_node_get_model_name (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), NULL); return node->model_name; } /** * arv_gc_register_description_node_get_vendor_name: * @node: a #ArvGcRegisterDescriptionNode * * Gets camera vendor name for given Genicam document. * * Returns: Vendor name string. * * Since: 0.8.0 */ char * arv_gc_register_description_node_get_vendor_name (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), NULL); return node->vendor_name; } /** * arv_gc_register_description_node_get_major_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document major version. * * Returns: Major version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_major_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->major_version; } /** * arv_gc_register_description_node_get_minor_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document minor version. * * Returns: Minor version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_minor_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->minor_version; } /** * arv_gc_register_description_node_get_subminor_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document sub minor version. * * Returns: Sub minor version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_subminor_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->subminor_version; } /** * arv_gc_register_description_node_get_schema_major_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document schema major version. * * Returns: Schema major version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_schema_major_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->schema_major_version; } /** * arv_gc_register_description_node_get_schema_minor_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document schema minor version. * * Returns: Schema minor version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_schema_minor_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->schema_minor_version; } /** * arv_gc_register_description_node_get_schema_subminor_version: * @node: a #ArvGcRegisterDescriptionNode * * Gets Genicam document schema sub minor version. * * Returns: Schema sub minor version. * * Since: 0.8.0 */ guint arv_gc_register_description_node_get_schema_subminor_version (ArvGcRegisterDescriptionNode* node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_DESCRIPTION_NODE (node), 0); return node->schema_subminor_version; } ArvGcNode * arv_gc_register_description_node_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_REGISTER_DESCRIPTION_NODE, NULL); return node; } static void arv_gc_register_description_node_init (ArvGcRegisterDescriptionNode *gc_register_description_node) { gc_register_description_node->schema_major_version = 0; gc_register_description_node->schema_minor_version = 0; gc_register_description_node->schema_subminor_version = 0; } static void arv_gc_register_description_node_finalize (GObject *object) { ArvGcRegisterDescriptionNode *node = ARV_GC_REGISTER_DESCRIPTION_NODE (object); g_free (node->model_name); g_free (node->vendor_name); g_free (node->product_guid); g_free (node->version_guid); g_free (node->standard_namespace); g_free (node->tooltip); G_OBJECT_CLASS (arv_gc_register_description_node_parent_class)->finalize (object); } static void arv_gc_register_description_node_class_init (ArvGcRegisterDescriptionNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_register_description_node_finalize; dom_node_class->get_node_name = arv_gc_register_description_node_get_node_name; dom_element_class->set_attribute = arv_gc_register_description_node_set_attribute; dom_element_class->get_attribute = arv_gc_register_description_node_get_attribute; } aravis-0.8.34/src/arvgcregisterdescriptionnode.h000066400000000000000000000053411475431451200220250ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_REGISTER_DESCRIPTION_NODE_H #define ARV_GC_REGISTER_DESCRIPTION_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_REGISTER_DESCRIPTION_NODE (arv_gc_register_description_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcRegisterDescriptionNode, arv_gc_register_description_node, ARV, GC_REGISTER_DESCRIPTION_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_register_description_node_new (void); ARV_API int arv_gc_register_description_node_compare_schema_version (ArvGcRegisterDescriptionNode *node, guint major, guint minor, guint subminor); ARV_API gboolean arv_gc_register_description_node_check_schema_version (ArvGcRegisterDescriptionNode *node, guint required_major, guint required_minor, guint required_subminor); ARV_API char * arv_gc_register_description_node_get_model_name (ArvGcRegisterDescriptionNode* node); ARV_API char * arv_gc_register_description_node_get_vendor_name (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_major_version (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_minor_version (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_subminor_version (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_schema_major_version (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_schema_minor_version (ArvGcRegisterDescriptionNode* node); ARV_API guint arv_gc_register_description_node_get_schema_subminor_version (ArvGcRegisterDescriptionNode* node); G_END_DECLS #endif aravis-0.8.34/src/arvgcregisternode.c000066400000000000000000000572521475431451200175640ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcregisternode * @short_description: Class for Register nodes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { gint64 address; gint64 length; } ArvGcCacheKey; static gboolean arv_gc_cache_key_equal (gconstpointer v1, gconstpointer v2) { return ((((ArvGcCacheKey *) v1)->address == ((ArvGcCacheKey *) v2)->address) && (((ArvGcCacheKey *) v1)->length == ((ArvGcCacheKey *) v2)->length)); } static guint arv_gc_cache_key_hash (gconstpointer v) { return g_int64_hash (&((ArvGcCacheKey *) v)->address); } static ArvGcCacheKey * arv_gc_cache_key_new (gint64 address, gint64 length) { ArvGcCacheKey *key = g_new (ArvGcCacheKey, 1); key->address = address; key->length = length; return key; } typedef struct { GSList *addresses; GSList *swiss_knives; GSList *indexes; ArvGcPropertyNode *length; ArvGcPropertyNode *port; ArvGcPropertyNode *cachable; ArvGcPropertyNode *polling_time; ArvGcPropertyNode *access_mode; ArvGcPropertyNode *endianness; GSList *invalidators; /* #ArvGcPropertyNode */ gboolean cached; GHashTable *caches; guint n_cache_hits; guint n_cache_misses; guint n_cache_errors; char v_string[G_ASCII_DTOSTR_BUF_SIZE]; } ArvGcRegisterNodePrivate; static void arv_gc_register_node_register_interface_init (ArvGcRegisterInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcRegisterNode, arv_gc_register_node, ARV_TYPE_GC_FEATURE_NODE, G_ADD_PRIVATE (ArvGcRegisterNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_REGISTER, arv_gc_register_node_register_interface_init)) /* ArvDomNode implementation */ static const char * arv_gc_register_node_get_node_name (ArvDomNode *node) { return "Register"; } static void arv_gc_register_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_ADDRESS: case ARV_GC_PROPERTY_NODE_TYPE_P_ADDRESS: priv->addresses = g_slist_prepend (priv->addresses, child); break; case ARV_GC_PROPERTY_NODE_TYPE_P_INDEX: priv->indexes = g_slist_prepend (priv->indexes, child); break; case ARV_GC_PROPERTY_NODE_TYPE_LENGTH: case ARV_GC_PROPERTY_NODE_TYPE_P_LENGTH: priv->length = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_PORT: priv->port = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_CACHABLE: priv->cachable = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_POLLING_TIME: /* TODO */ priv->polling_time = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ENDIANNESS: priv->endianness = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE: priv->access_mode = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_P_INVALIDATOR: priv->invalidators = g_slist_prepend (priv->invalidators, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_register_node_parent_class)->post_new_child (self, child); break; } } else if (ARV_IS_GC_SWISS_KNIFE (child)) priv->swiss_knives = g_slist_prepend (priv->swiss_knives, child); else ARV_DOM_NODE_CLASS (arv_gc_register_node_parent_class)->post_new_child (self, child); } static void arv_gc_register_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcRegisterNode implementation */ static gint64 _get_length (ArvGcRegisterNode *self, GError **error) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); GError *local_error = NULL; gint64 value; if (priv->length == NULL) return 4; value = arv_gc_property_node_get_int64 (priv->length, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return value; } static guint64 _get_address (ArvGcRegisterNode *self, GError **error) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); ArvGc *genicam; GError *local_error = NULL; GSList *iter; guint64 value = 0; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (self)); g_return_val_if_fail (ARV_IS_GC (genicam), 0); for (iter = priv->addresses; iter != NULL; iter = iter->next) { value += arv_gc_property_node_get_int64 (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0; } } for (iter = priv->swiss_knives; iter != NULL; iter = iter->next) { value += arv_gc_integer_get_value (iter->data, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0; } } if (priv->indexes != NULL) { gint64 length; length = _get_length (self, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0; } for (iter = priv->indexes; iter != NULL; iter = iter->next) { value += arv_gc_index_node_get_index (ARV_GC_INDEX_NODE (iter->data), length, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0; } } } return value; } static ArvGcCachable _get_cachable (ArvGcRegisterNode *self) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); return arv_gc_property_node_get_cachable (priv->cachable, ARV_GC_REGISTER_NODE_GET_CLASS (self)->default_cachable); } static guint _get_endianness (ArvGcRegisterNode *self) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); return arv_gc_property_node_get_endianness (priv->endianness, G_LITTLE_ENDIAN); } static gboolean _get_cached (ArvGcRegisterNode *self, ArvRegisterCachePolicy *cache_policy) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); ArvGc *genicam; GSList *iter; gboolean cached = priv->cached; *cache_policy = ARV_REGISTER_CACHE_POLICY_DISABLE; genicam = arv_gc_node_get_genicam (ARV_GC_NODE (self)); g_return_val_if_fail (ARV_IS_GC (genicam), FALSE); *cache_policy = arv_gc_get_register_cache_policy (genicam); if (*cache_policy == ARV_REGISTER_CACHE_POLICY_DISABLE) return FALSE; for (iter = priv->invalidators; iter != NULL; iter = iter->next) { if (arv_gc_invalidator_has_changed (iter->data)) cached = FALSE; } if (cached) priv->n_cache_hits++; else priv->n_cache_misses++; return cached; } static void * _get_cache (ArvGcRegisterNode *self, gint64 *address, gint64 *length, GError **error) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); GError *local_error = NULL; ArvGcCacheKey key; void *cache; key.address = _get_address (self, &local_error); if (local_error == NULL) key.length = _get_length (self, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return NULL; } cache = g_hash_table_lookup (priv->caches, &key); if (cache == NULL) { cache = g_malloc0 (key.length); g_hash_table_replace (priv->caches, arv_gc_cache_key_new (key.address, key.length), cache); } if (address != NULL) *address = key.address; if (length != NULL) *length = key.length; return cache; } static ArvGcAccessMode arv_gc_register_node_get_access_mode (ArvGcFeatureNode *gc_feature_node) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (gc_feature_node)); if (priv->access_mode == NULL) return ARV_GC_ACCESS_MODE_RO; return arv_gc_property_node_get_access_mode (priv->access_mode, ARV_GC_ACCESS_MODE_RO); } static void _read_from_port (ArvGcRegisterNode *self, gint64 address, gint64 length, void *buffer, ArvGcCachable cachable, GError **error) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); GError *local_error = NULL; ArvGcNode *port; ArvRegisterCachePolicy cache_policy; void *cache = NULL; gboolean cached; cached = _get_cached (self, &cache_policy); port = arv_gc_property_node_get_linked_node (priv->port); if (!ARV_IS_GC_PORT (port)) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NODE_NOT_FOUND, "[%s] Port not found for node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); priv->cached = FALSE; return; } if (cached && cache_policy == ARV_REGISTER_CACHE_POLICY_DEBUG) { cache = g_malloc (length); memcpy (cache, buffer, length); } if (!cached || cache_policy == ARV_REGISTER_CACHE_POLICY_DEBUG) arv_gc_port_read (ARV_GC_PORT (port), buffer, address, length, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); priv->cached = FALSE; g_free (cache); return; } if (cached && cache_policy == ARV_REGISTER_CACHE_POLICY_DEBUG) { if (memcmp (cache, buffer, length) != 0) { arv_warning_policies ("Current and cached value mismatch for '%s'\n", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); priv->n_cache_errors++; arv_gc_register_cache_error_add (arv_gc_node_get_genicam (ARV_GC_NODE (self)), 1); } g_free (cache); } if (cachable != ARV_GC_CACHABLE_NO_CACHE) priv->cached = TRUE; else priv->cached = FALSE; } static void _write_to_port (ArvGcRegisterNode *self, gint64 address, gint64 length, void *buffer, ArvGcCachable cachable, GError **error) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); GError *local_error = NULL; ArvGcNode *port; port = arv_gc_property_node_get_linked_node (priv->port); if (!ARV_IS_GC_PORT (port)) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_NODE_NOT_FOUND, "[%s] Port not found for node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); priv->cached = FALSE; return; } arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (self)); arv_gc_port_write (ARV_GC_PORT (port), buffer, address, length, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); priv->cached = FALSE; return; } if (cachable == ARV_GC_CACHABLE_WRITE_THROUGH) priv->cached = TRUE; else priv->cached = FALSE; } ArvGcNode * arv_gc_register_node_new (void) { return g_object_new (ARV_TYPE_GC_REGISTER_NODE, NULL); } static void arv_gc_register_node_init (ArvGcRegisterNode *self) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); priv->cached = FALSE; priv->caches = g_hash_table_new_full (arv_gc_cache_key_hash, arv_gc_cache_key_equal, g_free, g_free); priv->n_cache_hits = 0; priv->n_cache_misses = 0; priv->n_cache_errors = 0; } static void arv_gc_register_node_finalize (GObject *self) { ArvGcRegisterNodePrivate *priv = arv_gc_register_node_get_instance_private (ARV_GC_REGISTER_NODE (self)); ArvGc *genicam; g_slist_free (priv->addresses); g_slist_free (priv->swiss_knives); g_slist_free (priv->indexes); g_slist_free (priv->invalidators); g_clear_pointer (&priv->caches, g_hash_table_unref); genicam = arv_gc_node_get_genicam (ARV_GC_NODE (self)); if (ARV_IS_GC (genicam)) { ArvRegisterCachePolicy cache_policy; cache_policy = arv_gc_get_register_cache_policy (genicam); if (priv->n_cache_hits > 0 || priv->n_cache_misses > 0) { const char *name = arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self)); if (name == NULL) name = arv_dom_node_get_node_name (ARV_DOM_NODE (self)); if (cache_policy == ARV_REGISTER_CACHE_POLICY_DEBUG && priv->n_cache_errors) arv_warning_policies ("%15s: cache hit(s) = %3u / %-3u [%d error(s)]", name, priv->n_cache_hits, priv->n_cache_hits + priv->n_cache_misses, priv->n_cache_errors); else arv_debug_policies ("%-15s: cache hit(s) = %3u / %-3u", name, priv->n_cache_hits, priv->n_cache_hits + priv->n_cache_misses); } } G_OBJECT_CLASS (arv_gc_register_node_parent_class)->finalize (self); } static void arv_gc_register_node_class_init (ArvGcRegisterNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_register_node_finalize; dom_node_class->get_node_name = arv_gc_register_node_get_node_name; dom_node_class->post_new_child = arv_gc_register_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_register_node_pre_remove_child; gc_feature_node_class->get_access_mode = arv_gc_register_node_get_access_mode; this_class->default_cachable = ARV_GC_CACHABLE_WRITE_THROUGH; } /* ArvGcRegister interface implementation */ static void arv_gc_register_node_get (ArvGcRegister *gc_register, void *buffer, guint64 length, GError **error) { ArvGcRegisterNode *gc_register_node = ARV_GC_REGISTER_NODE (gc_register); GError *local_error = NULL; void *cache; gint64 address; gint64 cache_length; cache = _get_cache (gc_register_node, &address, &cache_length, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (length < cache_length) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "[%s] Register get failed due to data not fitting into buffer", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_register))); return; } _read_from_port (gc_register_node, address, cache_length, cache, _get_cachable (gc_register_node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (length > cache_length) { memcpy (buffer, cache, cache_length); memset (((char *) buffer) + cache_length, 0, length - cache_length); } else memcpy (buffer, cache, length); arv_debug_genicam ("[GcRegisterNode::get] 0x%" G_GINT64_MODIFIER "x,%" G_GUINT64_FORMAT, address, length); } static void arv_gc_register_node_set (ArvGcRegister *gc_register, const void *buffer, guint64 length, GError **error) { ArvGcRegisterNode *gc_register_node = ARV_GC_REGISTER_NODE (gc_register); GError *local_error = NULL; void *cache; gint64 address; gint64 cache_length; cache = _get_cache (gc_register_node, &address, &cache_length, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (cache_length < length) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "[%s] Register set failed due to data not fitting into register", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_register))); return; } if (cache_length > length) { memcpy (cache, buffer, length); memset (((char *) cache) + length, 0, cache_length - length); } else memcpy (cache, buffer, cache_length); _write_to_port (gc_register_node, address, cache_length, cache, _get_cachable (gc_register_node), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } arv_debug_genicam ("[GcRegisterNode::set] 0x%" G_GINT64_MODIFIER "x,%" G_GUINT64_FORMAT, address, length); } static guint64 arv_gc_register_node_get_address (ArvGcRegister *gc_register, GError **error) { ArvGcRegisterNode *gc_register_node = ARV_GC_REGISTER_NODE (gc_register); return _get_address (gc_register_node, error); } static guint64 arv_gc_register_node_get_length (ArvGcRegister *gc_register, GError **error) { ArvGcRegisterNode *gc_register_node = ARV_GC_REGISTER_NODE (gc_register); return _get_length (gc_register_node, error); } static void arv_gc_register_node_register_interface_init (ArvGcRegisterInterface *interface) { interface->get = arv_gc_register_node_get; interface->set = arv_gc_register_node_set; interface->get_address = arv_gc_register_node_get_address; interface->get_length = arv_gc_register_node_get_length; } /* ArvGcInteger interface implementation */ static gint64 _get_integer_value (ArvGcRegisterNode *gc_register_node, guint register_lsb, guint register_msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, GError **error) { GError *local_error = NULL; gint64 value; guint lsb; guint msb; void *cache; gint64 address; gint64 length; cache = _get_cache (gc_register_node, &address, &length, &local_error); if (local_error == NULL) _read_from_port (gc_register_node, address, length, cache, cachable, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } arv_copy_memory_with_endianness (&value, sizeof (value), G_BYTE_ORDER, cache, length, endianness); if (is_masked) { guint64 mask; if (endianness == G_LITTLE_ENDIAN) { msb = register_msb; lsb = register_lsb; } else { lsb = 8 * length - register_lsb - 1; msb = 8 * length - register_msb - 1; } arv_debug_genicam ("[GcRegisterNode::_get_integer_value] reglsb = %d, regmsb, %d, lsb = %d, msb = %d", register_lsb, register_msb, lsb, msb); arv_debug_genicam ("[GcRegisterNode::_get_integer_value] value = 0x%08" G_GINT64_MODIFIER "x", value); if (msb - lsb < 63) mask = ((((guint64) 1) << (msb - lsb + 1)) - 1) << lsb; else mask = G_MAXUINT64; value = (value & mask) >> lsb; if (msb-lsb < 63 && (value & (((guint64) 1) << (msb - lsb))) != 0 && signedness == ARV_GC_SIGNEDNESS_SIGNED) { value |= G_MAXUINT64 ^ (mask >> lsb); } arv_debug_genicam ("[GcRegisterNode::_get_integer_value] mask = 0x%08" G_GINT64_MODIFIER "x", mask); } else { if (length < 8 && ((value & (((guint64) 1) << (length * 8 - 1))) != 0) && signedness == ARV_GC_SIGNEDNESS_SIGNED) value |= G_MAXUINT64 ^ ((((guint64) 1) << (length * 8)) - 1); } arv_debug_genicam ("[GcRegisterNode::_get_integer_value] address = 0x%" G_GINT64_MODIFIER "x, value = 0x%" G_GINT64_MODIFIER "x", _get_address (gc_register_node, NULL), value); return value; } gint64 arv_gc_register_node_get_masked_integer_value (ArvGcRegisterNode *self, guint lsb, guint msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, GError **error) { g_return_val_if_fail (ARV_IS_GC_REGISTER_NODE (self), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); if (cachable == ARV_GC_CACHABLE_UNDEFINED) cachable = _get_cachable (self); if (endianness == 0) endianness = _get_endianness (self); return _get_integer_value (self, lsb, msb, signedness, endianness, cachable, is_masked, error); } static void _set_integer_value (ArvGcRegisterNode *gc_register_node, guint register_lsb, guint register_msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, gint64 value, GError **error) { GError *local_error = NULL; guint lsb; guint msb; void *cache; gint64 address; gint64 length; cache = _get_cache (gc_register_node, &address, &length, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (is_masked) { gint64 current_value; guint64 mask; if (ARV_GC_ACCESS_MODE_WO != arv_gc_register_node_get_access_mode(ARV_GC_FEATURE_NODE(gc_register_node))) { _read_from_port (gc_register_node, address, length, cache, cachable, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } } arv_copy_memory_with_endianness (¤t_value, sizeof (current_value), G_BYTE_ORDER, cache, length, endianness); if (endianness == G_LITTLE_ENDIAN) { msb = register_msb; lsb = register_lsb; } else { lsb = 8 * length - register_lsb - 1; msb = 8 * length - register_msb - 1; } arv_debug_genicam ("[GcRegisterNode::_set_integer_value] reglsb = %d, regmsb, %d, lsb = %d, msb = %d", register_lsb, register_msb, lsb, msb); arv_debug_genicam ("[GcRegisterNode::_set_integer_value] value = 0x%08" G_GINT64_MODIFIER "x", value); if (msb - lsb < 63) mask = ((((guint64) 1) << (msb - lsb + 1)) - 1) << lsb; else mask = G_MAXUINT64; value = ((value << lsb) & mask) | (current_value & ~mask); arv_debug_genicam ("[GcRegisterNode::_set_integer_value] mask = 0x%08" G_GINT64_MODIFIER "x", mask); } arv_debug_genicam ("[GcRegisterNode::_set_integer_value] address = 0x%" G_GINT64_MODIFIER "x, value = 0x%" G_GINT64_MODIFIER "x", _get_address (gc_register_node, NULL), value); arv_copy_memory_with_endianness (cache, length, endianness, &value, sizeof (value), G_BYTE_ORDER); _write_to_port (gc_register_node, address, length, cache, cachable, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } } void arv_gc_register_node_set_masked_integer_value (ArvGcRegisterNode *self, guint lsb, guint msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, gint64 value, GError **error) { g_return_if_fail (ARV_IS_GC_REGISTER_NODE (self)); g_return_if_fail (error == NULL || *error == NULL); if (cachable == ARV_GC_CACHABLE_UNDEFINED) cachable = _get_cachable (self); if (endianness == 0) endianness = _get_endianness (self); _set_integer_value (self, lsb, msb, signedness, endianness, cachable, is_masked, value, error); } guint arv_gc_register_node_get_endianness (ArvGcRegisterNode *register_node) { g_return_val_if_fail (ARV_IS_GC_REGISTER_NODE (register_node), G_BIG_ENDIAN); return _get_endianness (register_node); } aravis-0.8.34/src/arvgcregisternode.h000066400000000000000000000027651475431451200175700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_REGISTER_NODE_H #define ARV_GC_REGISTER_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_REGISTER_NODE (arv_gc_register_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcRegisterNode, arv_gc_register_node, ARV, GC_REGISTER_NODE, ArvGcFeatureNode) struct _ArvGcRegisterNodeClass { ArvGcFeatureNodeClass parent_class; ArvGcCachable default_cachable; }; ARV_API ArvGcNode * arv_gc_register_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcregisternodeprivate.h000066400000000000000000000031341475431451200211520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_REGISTER_NODE_PRIVATE_H #define ARV_GC_REGISTER_NODE_PRIVATE_H #include gint64 arv_gc_register_node_get_masked_integer_value (ArvGcRegisterNode *gc_register_node, guint lsb, guint msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, GError **error); void arv_gc_register_node_set_masked_integer_value (ArvGcRegisterNode *gc_register_node, guint lsb, guint msb, ArvGcSignedness signedness, guint endianness, ArvGcCachable cachable, gboolean is_masked, gint64 value, GError **error); guint arv_gc_register_node_get_endianness (ArvGcRegisterNode *register_node); #endif aravis-0.8.34/src/arvgcselector.c000066400000000000000000000041711475431451200167020ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcselector * @short_description: Selector interface */ #include #include #include static void arv_gc_selector_default_init (ArvGcSelectorInterface *gc_selector_iface) { } G_DEFINE_INTERFACE (ArvGcSelector, arv_gc_selector, G_TYPE_OBJECT) /** * arv_gc_selector_is_selector: * @gc_selector: a #ArvGcSelector * * Returns: %TRUE if this node is a selector, i.e. it has pSelected childs. * * Since: 0.8.0 */ gboolean arv_gc_selector_is_selector (ArvGcSelector *gc_selector) { g_return_val_if_fail (ARV_IS_GC_SELECTOR (gc_selector), FALSE); return arv_gc_selector_get_selected_features (gc_selector) != NULL; } /** * arv_gc_selector_get_selected_features: * @gc_selector: a #ArvGcSelector * * Returns: (element-type ArvGcFeatureNode) (transfer none): a list of selected #ArvGcFeatureNode * * Since: 0.8.0 */ const GSList * arv_gc_selector_get_selected_features (ArvGcSelector *gc_selector) { ArvGcSelectorInterface *selector_interface; g_return_val_if_fail (ARV_IS_GC_SELECTOR (gc_selector), 0); selector_interface = ARV_GC_SELECTOR_GET_IFACE (gc_selector); if (selector_interface->get_selected_features != NULL) return selector_interface->get_selected_features (gc_selector); return NULL; } aravis-0.8.34/src/arvgcselector.h000066400000000000000000000030621475431451200167050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_SELECTOR_H #define ARV_GC_SELECTOR_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_SELECTOR (arv_gc_selector_get_type ()) ARV_API G_DECLARE_INTERFACE (ArvGcSelector, arv_gc_selector, ARV, GC_SELECTOR, GObject) struct _ArvGcSelectorInterface { GTypeInterface parent; const GSList * (*get_selected_features) (ArvGcSelector *gc_selector); }; ARV_API gboolean arv_gc_selector_is_selector (ArvGcSelector *gc_selector); ARV_API const GSList * arv_gc_selector_get_selected_features (ArvGcSelector *gc_selector); G_END_DECLS #endif aravis-0.8.34/src/arvgcstring.c000066400000000000000000000062561475431451200163760ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcstring * @short_description: String interface */ #include #include #include static void arv_gc_string_default_init (ArvGcStringInterface *gc_string_iface) { } G_DEFINE_INTERFACE (ArvGcString, arv_gc_string, G_TYPE_OBJECT) /** * arv_gc_string_get_value: * @gc_string: an object implementing #ArvGcString * @error: a #GError placeholder, or %NULL to ignore * * Please note the string content is still owned by the @gc_string object, which means the returned pointer may not be still valid after a new call to this function. * * Returns: the string value. */ const char * arv_gc_string_get_value (ArvGcString *gc_string, GError **error) { g_return_val_if_fail (ARV_IS_GC_STRING (gc_string), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (!arv_gc_feature_node_check_read_access (ARV_GC_FEATURE_NODE (gc_string), error)) return NULL; return ARV_GC_STRING_GET_IFACE (gc_string)->get_value (gc_string, error); } /** * arv_gc_string_set_value: * @gc_string: an object implementing #ArvGcString * @value: new string value * @error: a #GError placeholder, or %NULL to ignore * * Set @value as the new @gc_string value. */ void arv_gc_string_set_value (ArvGcString *gc_string, const char *value, GError **error) { g_return_if_fail (ARV_IS_GC_STRING (gc_string)); g_return_if_fail (error == NULL || *error == NULL); if (!arv_gc_feature_node_check_write_access (ARV_GC_FEATURE_NODE (gc_string), error)) return; ARV_GC_STRING_GET_IFACE (gc_string)->set_value (gc_string, value, error); } /** * arv_gc_string_get_max_length: * @gc_string: an object implementing #ArvGcString * @error: a #GError placeholder, or %NULL to ignore * * Returns: the maximum length @gc_string can store, excluding the NULL terminal character. */ gint64 arv_gc_string_get_max_length (ArvGcString *gc_string, GError **error) { ArvGcStringInterface *string_interface; g_return_val_if_fail (ARV_IS_GC_STRING (gc_string), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); string_interface = ARV_GC_STRING_GET_IFACE (gc_string); if (string_interface->get_max_length != NULL) return string_interface->get_max_length (gc_string, error); else return 0; } aravis-0.8.34/src/arvgcstring.h000066400000000000000000000034601475431451200163750ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_STRING_H #define ARV_GC_STRING_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_STRING (arv_gc_string_get_type ()) ARV_API G_DECLARE_INTERFACE (ArvGcString, arv_gc_string, ARV, GC_STRING, GObject) struct _ArvGcStringInterface { GTypeInterface parent; const char * (*get_value) (ArvGcString *gc_string, GError **error); void (*set_value) (ArvGcString *gc_string, const char *value, GError **error); gint64 (*get_max_length) (ArvGcString *gc_string, GError **error); }; ARV_API const char * arv_gc_string_get_value (ArvGcString *gc_string, GError **error); ARV_API void arv_gc_string_set_value (ArvGcString *gc_string, const char *value, GError **error); ARV_API gint64 arv_gc_string_get_max_length (ArvGcString *gc_string, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvgcstringnode.c000066400000000000000000000135341475431451200172410ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcstringnode * @short_description: Class for String nodes */ #include #include #include #include #include #include #include #include #include #include struct _ArvGcStringNode { ArvGcFeatureNode node; ArvGcPropertyNode *value; }; struct _ArvGcStringNodeClass { ArvGcFeatureNodeClass parent_class; }; static void arv_gc_string_node_string_interface_init (ArvGcStringInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcStringNode, arv_gc_string_node, ARV_TYPE_GC_FEATURE_NODE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_STRING, arv_gc_string_node_string_interface_init)) /* ArvDomNode implementation */ static const char * arv_gc_string_node_get_node_name (ArvDomNode *node) { return "String"; } static void arv_gc_string_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcStringNode *node = ARV_GC_STRING_NODE (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE: case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE: node->value = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_string_node_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_string_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcStringNode implementation */ ArvGcNode * arv_gc_string_node_new (void) { ArvGcNode *node; node = g_object_new (ARV_TYPE_GC_STRING_NODE, NULL); return node; } static ArvGcFeatureNode * arv_gc_string_node_get_linked_feature (ArvGcFeatureNode *gc_feature_node) { ArvGcStringNode *gc_string_node = ARV_GC_STRING_NODE (gc_feature_node); ArvGcNode *pvalue_node = NULL; if (gc_string_node->value == NULL) return NULL; pvalue_node = arv_gc_property_node_get_linked_node (gc_string_node->value); if (ARV_IS_GC_FEATURE_NODE (pvalue_node)) return ARV_GC_FEATURE_NODE (pvalue_node); return NULL; } static void arv_gc_string_node_init (ArvGcStringNode *gc_string_node) { } static void arv_gc_string_node_finalize (GObject *object) { G_OBJECT_CLASS (arv_gc_string_node_parent_class)->finalize (object); } static void arv_gc_string_node_class_init (ArvGcStringNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_string_node_finalize; dom_node_class->get_node_name = arv_gc_string_node_get_node_name; dom_node_class->post_new_child = arv_gc_string_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_string_node_pre_remove_child; gc_feature_node_class->get_linked_feature = arv_gc_string_node_get_linked_feature; gc_feature_node_class->default_access_mode = ARV_GC_ACCESS_MODE_RW; } /* ArvGcString interface implementation */ static const char * arv_gc_string_node_get_string_value (ArvGcString *gc_string, GError **error) { ArvGcStringNode *gc_string_node = ARV_GC_STRING_NODE (gc_string); GError *local_error = NULL; const char *value = NULL; if (ARV_IS_GC_PROPERTY_NODE (gc_string_node->value)) { value = arv_gc_property_node_get_string (gc_string_node->value, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_string))); return NULL; } } return value; } static void arv_gc_string_node_set_string_value (ArvGcString *gc_string, const char *value, GError **error) { ArvGcStringNode *gc_string_node = ARV_GC_STRING_NODE (gc_string); GError *local_error = NULL; if (ARV_IS_GC_PROPERTY_NODE (gc_string_node->value)) { arv_gc_property_node_set_string (gc_string_node->value, value, &local_error); if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (gc_string))); } } static gint64 arv_gc_string_node_get_max_length (ArvGcString *gc_string, GError **error) { ArvGcStringNode *gc_string_node = ARV_GC_STRING_NODE (gc_string); if (ARV_IS_GC_PROPERTY_NODE (gc_string_node->value)) { ArvGcNode *linked_node = arv_gc_property_node_get_linked_node (gc_string_node->value); if (ARV_IS_GC_STRING (linked_node)) return arv_gc_string_get_max_length (ARV_GC_STRING (linked_node), error); } return G_MAXINT64; } static void arv_gc_string_node_string_interface_init (ArvGcStringInterface *interface) { interface->get_value = arv_gc_string_node_get_string_value; interface->set_value = arv_gc_string_node_set_string_value; interface->get_max_length = arv_gc_string_node_get_max_length; } aravis-0.8.34/src/arvgcstringnode.h000066400000000000000000000026411475431451200172430ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_STRING_NODE_H #define ARV_GC_STRING_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_STRING_NODE (arv_gc_string_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcStringNode, arv_gc_string_node, ARV, GC_STRING_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_string_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcstringregnode.c000066400000000000000000000115031475431451200177310ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include typedef struct { char *string; } ArvGcStringRegNodePrivate; static void arv_gc_string_reg_node_init (ArvGcStringRegNode *self); static void arv_gc_string_reg_node_string_interface_init (ArvGcStringInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcStringRegNode, arv_gc_string_reg_node, ARV_TYPE_GC_REGISTER_NODE, G_ADD_PRIVATE (ArvGcStringRegNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_STRING, arv_gc_string_reg_node_string_interface_init)) static const char * arv_gc_string_reg_node_get_node_name (ArvDomNode *node) { return "StringReg"; } static const char * arv_gc_string_reg_node_get_string_value (ArvGcString *self, GError **error) { ArvGcStringRegNodePrivate *priv = arv_gc_string_reg_node_get_instance_private (ARV_GC_STRING_REG_NODE (self)); GError *local_error = NULL; gint64 length; length = arv_gc_string_get_max_length (self, &local_error); if (local_error == NULL) { priv->string = g_realloc (priv->string, length + 1); if (priv->string != NULL) { arv_gc_register_get (ARV_GC_REGISTER (self), priv->string, length, &local_error); if (local_error == NULL) priv->string[length] = '\0'; } } else { g_clear_pointer (&priv->string, g_free); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return priv->string; } static void arv_gc_string_reg_node_set_string_value (ArvGcString *self, const char *value, GError **error) { GError *local_error = NULL; gint64 str_length; gint64 max_length; if (value == NULL) return; str_length = strlen(value); max_length = arv_gc_string_get_max_length (self, &local_error); if (local_error == NULL) { if (max_length < str_length) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_LENGTH, "[%s] string '%s' too long (max: %" G_GINT64_FORMAT ")", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self)), value, max_length); return; } if (max_length > str_length) str_length++; arv_gc_register_set (ARV_GC_REGISTER (self), value, str_length, &local_error); } if (local_error != NULL) g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); } static gint64 arv_gc_string_reg_node_get_max_string_length (ArvGcString *self, GError **error) { return arv_gc_register_get_length (ARV_GC_REGISTER (self), error); } /** * arv_gc_string_reg_node_new: * * Returns: a new StringReg node * * Since:0.8.0 */ ArvGcNode * arv_gc_string_reg_node_new (void) { return g_object_new (ARV_TYPE_GC_STRING_REG_NODE, NULL); } static void arv_gc_string_reg_node_string_interface_init (ArvGcStringInterface *interface) { interface->get_value = arv_gc_string_reg_node_get_string_value; interface->set_value = arv_gc_string_reg_node_set_string_value; interface->get_max_length = arv_gc_string_reg_node_get_max_string_length; } static void arv_gc_string_reg_node_init (ArvGcStringRegNode *self) { } static void arv_gc_string_reg_node_finalize (GObject *self) { ArvGcStringRegNodePrivate *priv = arv_gc_string_reg_node_get_instance_private (ARV_GC_STRING_REG_NODE (self)); g_clear_pointer (&priv->string, g_free); G_OBJECT_CLASS (arv_gc_string_reg_node_parent_class)->finalize (self); } static void arv_gc_string_reg_node_class_init (ArvGcStringRegNodeClass *this_class) { ArvGcRegisterNodeClass *gc_register_node_class = ARV_GC_REGISTER_NODE_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_string_reg_node_finalize; dom_node_class->get_node_name = arv_gc_string_reg_node_get_node_name; gc_register_node_class->default_cachable = ARV_GC_CACHABLE_WRITE_THROUGH; } aravis-0.8.34/src/arvgcstringregnode.h000066400000000000000000000027341475431451200177440ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_STRING_REG_NODE_H #define ARV_GC_STRING_REG_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_STRING_REG_NODE (arv_gc_string_reg_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcStringRegNode, arv_gc_string_reg_node, ARV, GC_STRING_REG_NODE, ArvGcRegisterNode) struct _ArvGcStringRegNodeClass { ArvGcRegisterNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_string_reg_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcstructentrynode.c000066400000000000000000000304141475431451200203350ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcstructentrynode * @short_description: Class for StructEntry nodes */ #include #include #include #include #include #include #include #include #include #include #include #include struct _ArvGcStructEntryNode { ArvGcFeatureNode node; ArvGcPropertyNode *sign; ArvGcPropertyNode *representation; ArvGcPropertyNode *lsb; ArvGcPropertyNode *msb; ArvGcPropertyNode *access_mode; ArvGcPropertyNode *cachable; char v_string[G_ASCII_DTOSTR_BUF_SIZE]; }; struct _ArvGcStructEntryNodeClass { ArvGcFeatureNodeClass parent_class; }; static void arv_gc_struct_entry_node_register_interface_init (ArvGcRegisterInterface *interface); static void arv_gc_struct_entry_node_integer_interface_init (ArvGcIntegerInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcStructEntryNode, arv_gc_struct_entry_node, ARV_TYPE_GC_FEATURE_NODE, G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_REGISTER, arv_gc_struct_entry_node_register_interface_init) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_INTEGER, arv_gc_struct_entry_node_integer_interface_init)) /* ArvDomNode implementation */ static const char * arv_gc_struct_entry_node_get_node_name (ArvDomNode *node) { return "StructEntry"; } static void arv_gc_struct_entry_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcStructEntryNode *node = ARV_GC_STRUCT_ENTRY_NODE (self); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_SIGN: node->sign = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: node->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_LSB: node->lsb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_MSB: node->msb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_BIT: node->msb = property_node; node->lsb = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_ACCESS_MODE: node->access_mode = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_CACHABLE: node->cachable = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_struct_entry_node_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_struct_entry_node_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcStructEntryNode implementation */ /** * arv_gc_struct_entry_node_new: * * Returns: (transfer full): a newly created #ArvGcStructEntryNode. */ ArvGcNode * arv_gc_struct_entry_node_new (void) { ArvGcStructEntryNode *gc_struct_entry_node; gc_struct_entry_node = g_object_new (ARV_TYPE_GC_STRUCT_ENTRY_NODE, NULL); return ARV_GC_NODE (gc_struct_entry_node); } static void arv_gc_struct_entry_node_init (ArvGcStructEntryNode *gc_struct_entry_node) { } static ArvGcAccessMode arv_gc_struct_entry_node_get_access_mode (ArvGcFeatureNode *gc_feature_node) { ArvGcStructEntryNode *self = ARV_GC_STRUCT_ENTRY_NODE(gc_feature_node); ArvDomNode *struct_register; if (ARV_IS_GC_PROPERTY_NODE(self->access_mode)) return arv_gc_property_node_get_access_mode (self->access_mode, ARV_GC_ACCESS_MODE_RO); struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_feature_node)); if (ARV_IS_GC_REGISTER_NODE (struct_register)) return arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (struct_register)); return ARV_GC_ACCESS_MODE_RO; } static void arv_gc_struct_entry_node_finalize (GObject *object) { G_OBJECT_CLASS (arv_gc_struct_entry_node_parent_class)->finalize (object); } static void arv_gc_struct_entry_node_class_init (ArvGcStructEntryNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_struct_entry_node_finalize; dom_node_class->get_node_name = arv_gc_struct_entry_node_get_node_name; dom_node_class->post_new_child = arv_gc_struct_entry_node_post_new_child; dom_node_class->pre_remove_child = arv_gc_struct_entry_node_pre_remove_child; gc_feature_node_class->get_access_mode = arv_gc_struct_entry_node_get_access_mode; } /* ArvGcRegister interface implementation */ static void arv_gc_struct_entry_node_get (ArvGcRegister *gc_register, void *buffer, guint64 length, GError **error) { ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_register)); g_return_if_fail (ARV_IS_GC_REGISTER (struct_register)); arv_gc_register_get (ARV_GC_REGISTER (struct_register), buffer, length, error); } static void arv_gc_struct_entry_node_set (ArvGcRegister *gc_register, const void *buffer, guint64 length, GError **error) { ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_register)); g_return_if_fail (ARV_IS_GC_REGISTER (struct_register)); arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_register)); arv_gc_register_set (ARV_GC_REGISTER (struct_register), buffer, length, error); } static guint64 arv_gc_struct_entry_node_get_address (ArvGcRegister *gc_register, GError **error) { ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_register)); GError *local_error = NULL; gint64 address; g_return_val_if_fail (ARV_IS_GC_REGISTER (struct_register), 0); address = arv_gc_register_get_address (ARV_GC_REGISTER (struct_register), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } return address; } static guint64 arv_gc_struct_entry_node_get_length (ArvGcRegister *gc_register, GError **error) { ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_register)); GError *local_error = NULL; gint64 length; g_return_val_if_fail (ARV_IS_GC_REGISTER (struct_register), 0); length = arv_gc_register_get_length (ARV_GC_REGISTER (struct_register), &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } return length; } static void arv_gc_struct_entry_node_register_interface_init (ArvGcRegisterInterface *interface) { interface->get = arv_gc_struct_entry_node_get; interface->set = arv_gc_struct_entry_node_set; interface->get_address = arv_gc_struct_entry_node_get_address; interface->get_length = arv_gc_struct_entry_node_get_length; } /* ArvGcInteger interface implementation */ static gint64 arv_gc_struct_entry_node_get_integer_value (ArvGcInteger *gc_integer, GError **error) { ArvGcStructEntryNode *struct_entry = ARV_GC_STRUCT_ENTRY_NODE (gc_integer); ArvDomNode *struct_register; struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_integer)); if (!ARV_IS_GC_REGISTER_NODE (struct_register)) return 0; return arv_gc_register_node_get_masked_integer_value (ARV_GC_REGISTER_NODE (struct_register), arv_gc_property_node_get_lsb (struct_entry->lsb, 0), arv_gc_property_node_get_msb (struct_entry->msb, 31), arv_gc_property_node_get_sign (struct_entry->sign, ARV_GC_SIGNEDNESS_UNSIGNED), 0, arv_gc_property_node_get_cachable (struct_entry->cachable, ARV_GC_CACHABLE_WRITE_AROUND), TRUE, error); } static void arv_gc_struct_entry_node_set_integer_value (ArvGcInteger *gc_integer, gint64 value, GError **error) { ArvGcStructEntryNode *struct_entry = ARV_GC_STRUCT_ENTRY_NODE (gc_integer); ArvDomNode *struct_register; struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (gc_integer)); if (!ARV_IS_GC_REGISTER_NODE (struct_register)) return; arv_gc_feature_node_increment_change_count (ARV_GC_FEATURE_NODE (gc_integer)); arv_gc_register_node_set_masked_integer_value (ARV_GC_REGISTER_NODE (struct_register), arv_gc_property_node_get_lsb (struct_entry->lsb, 0), arv_gc_property_node_get_msb (struct_entry->msb, 31), arv_gc_property_node_get_sign (struct_entry->sign, ARV_GC_SIGNEDNESS_UNSIGNED), 0, arv_gc_property_node_get_cachable (struct_entry->cachable, ARV_GC_CACHABLE_WRITE_AROUND), TRUE, value, error); } static gint64 arv_gc_struct_entry_node_get_min (ArvGcInteger *self, GError **error) { ArvGcStructEntryNode *struct_entry = ARV_GC_STRUCT_ENTRY_NODE (self); ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (self)); gint64 lsb, msb, min; ArvGcSignedness signedness; guint endianness; signedness = arv_gc_property_node_get_sign (struct_entry->sign, ARV_GC_SIGNEDNESS_UNSIGNED); endianness = arv_gc_register_node_get_endianness (ARV_GC_REGISTER_NODE (struct_register)); lsb = arv_gc_property_node_get_lsb (struct_entry->lsb, endianness == G_BIG_ENDIAN ? 31 : 0); msb = arv_gc_property_node_get_msb (struct_entry->msb, endianness == G_BIG_ENDIAN ? 0 : 31); if ((endianness == G_BIG_ENDIAN && (msb > lsb)) || (endianness != G_BIG_ENDIAN && (lsb > msb))) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_BIT_RANGE, "[%s] Invalid bit range for node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return G_MAXINT64; } if (signedness == ARV_GC_SIGNEDNESS_SIGNED) { min = endianness == G_BIG_ENDIAN ? -(((gint64) 1 << (lsb - msb))) : -(((gint64) 1 << (msb - lsb))); } else { min = 0; } return min; } static gint64 arv_gc_struct_entry_node_get_max (ArvGcInteger *self, GError **error) { ArvGcStructEntryNode *struct_entry = ARV_GC_STRUCT_ENTRY_NODE (self); ArvDomNode *struct_register = arv_dom_node_get_parent_node (ARV_DOM_NODE (self)); gint64 lsb, msb, max; ArvGcSignedness signedness; guint endianness; signedness = arv_gc_property_node_get_sign (struct_entry->sign, ARV_GC_SIGNEDNESS_UNSIGNED); endianness = arv_gc_register_node_get_endianness (ARV_GC_REGISTER_NODE (struct_register)); lsb = arv_gc_property_node_get_lsb (struct_entry->lsb, endianness == G_BIG_ENDIAN ? 31 : 0); msb = arv_gc_property_node_get_msb (struct_entry->msb, endianness == G_BIG_ENDIAN ? 0 : 31); if ((endianness == G_BIG_ENDIAN && (msb > lsb)) || (endianness != G_BIG_ENDIAN && (lsb > msb))) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_INVALID_BIT_RANGE, "[%s] Invalid bit range for node", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return G_MAXINT64; } if (signedness == ARV_GC_SIGNEDNESS_SIGNED) { max = endianness == G_BIG_ENDIAN ? (((gint64) 1 << (lsb - msb)) - 1) : (((gint64) 1 << (msb - lsb)) - 1); } else { max = endianness == G_BIG_ENDIAN ? ((gint64) 1 << (lsb - msb + 1)) - 1 : ((gint64) 1 << (msb - lsb + 1)) - 1; } return max; } static ArvGcRepresentation arv_gc_struct_entry_node_get_representation (ArvGcInteger *self) { ArvGcStructEntryNode *struct_entry = ARV_GC_STRUCT_ENTRY_NODE (self); if (struct_entry->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (struct_entry->representation, ARV_GC_REPRESENTATION_UNDEFINED); } static void arv_gc_struct_entry_node_integer_interface_init (ArvGcIntegerInterface *interface) { interface->get_value = arv_gc_struct_entry_node_get_integer_value; interface->set_value = arv_gc_struct_entry_node_set_integer_value; interface->get_min = arv_gc_struct_entry_node_get_min; interface->get_max = arv_gc_struct_entry_node_get_max; interface->get_representation = arv_gc_struct_entry_node_get_representation; } aravis-0.8.34/src/arvgcstructentrynode.h000066400000000000000000000027171475431451200203470ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_STRUCT_ENTRY_NODE_H #define ARV_GC_STRUCT_ENTRY_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_STRUCT_ENTRY_NODE (arv_gc_struct_entry_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcStructEntryNode, arv_gc_struct_entry_node, ARV, GC_STRUCT_ENTRY_NODE, ArvGcFeatureNode) ARV_API ArvGcNode * arv_gc_struct_entry_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcstructregnode.c000066400000000000000000000041101475431451200177430ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include static void arv_gc_struct_reg_node_init (ArvGcStructRegNode *self); G_DEFINE_TYPE (ArvGcStructRegNode, arv_gc_struct_reg_node, ARV_TYPE_GC_REGISTER_NODE) static const char * arv_gc_struct_reg_node_get_node_name (ArvDomNode *node) { return "StructReg"; } /** * arv_gc_struct_reg_node_new: * * Returns: (transfer full): a new StructReg #ArvGcNode * * Since:0.8.0 */ ArvGcNode * arv_gc_struct_reg_node_new (void) { return g_object_new (ARV_TYPE_GC_STRUCT_REG_NODE, NULL); } static void arv_gc_struct_reg_node_init (ArvGcStructRegNode *self) { } static void arv_gc_struct_reg_node_finalize (GObject *self) { G_OBJECT_CLASS (arv_gc_struct_reg_node_parent_class)->finalize (self); } static void arv_gc_struct_reg_node_class_init (ArvGcStructRegNodeClass *this_class) { ArvGcRegisterNodeClass *gc_register_node_class = ARV_GC_REGISTER_NODE_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_struct_reg_node_finalize; dom_node_class->get_node_name = arv_gc_struct_reg_node_get_node_name; gc_register_node_class->default_cachable = ARV_GC_CACHABLE_NO_CACHE; } aravis-0.8.34/src/arvgcstructregnode.h000066400000000000000000000027341475431451200177620ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_STRUCT_REG_NODE_H #define ARV_GC_STRUCT_REG_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_STRUCT_REG_NODE (arv_gc_struct_reg_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcStructRegNode, arv_gc_struct_reg_node, ARV, GC_STRUCT_REG_NODE, ArvGcRegisterNode) struct _ArvGcStructRegNodeClass { ArvGcRegisterNodeClass parent_class; }; ARV_API ArvGcNode * arv_gc_struct_reg_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcswissknife.c000066400000000000000000000222651475431451200172530ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcswissknife * @short_description: Class for SwissKnife and IntSwissKnife nodes */ #include #include #include #include #include #include #include #include typedef struct { GType value_type; GSList *variables; /* ArvGcVariableNode list */ GSList *constants; /* ArvGcVariableNode list */ GSList *expressions; /* ArvGcVariableNode list */ ArvGcPropertyNode *formula_node; ArvGcPropertyNode *unit; ArvGcPropertyNode *representation; ArvEvaluator *formula; } ArvGcSwissKnifePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvGcSwissKnife, arv_gc_swiss_knife, ARV_TYPE_GC_FEATURE_NODE, G_ADD_PRIVATE (ArvGcSwissKnife)) /* ArvDomNode implementation */ static void arv_gc_swiss_knife_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (ARV_GC_SWISS_KNIFE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_P_VARIABLE: priv->variables = g_slist_prepend (priv->variables, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_FORMULA: priv->formula_node = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_UNIT: priv->unit = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_REPRESENTATION: priv->representation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_EXPRESSION: priv->expressions = g_slist_prepend (priv->expressions, property_node); break; case ARV_GC_PROPERTY_NODE_TYPE_CONSTANT: priv->constants = g_slist_prepend (priv->constants, property_node); break; default: ARV_DOM_NODE_CLASS (arv_gc_swiss_knife_parent_class)->post_new_child (self, child); break; } } } static void arv_gc_swiss_knife_pre_remove_child (ArvDomNode *self, ArvDomNode *child) { g_assert_not_reached (); } /* ArvGcFeatureNode implementation */ static void arv_gc_swiss_knife_init (ArvGcSwissKnife *self) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); priv->formula = arv_evaluator_new (NULL); } static ArvGcAccessMode arv_gc_swiss_knife_get_access_mode (ArvGcFeatureNode *gc_feature_node) { return ARV_GC_ACCESS_MODE_RO; } static void arv_gc_swiss_knife_finalize (GObject *object) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (ARV_GC_SWISS_KNIFE (object)); g_slist_free (priv->variables); g_slist_free (priv->expressions); g_slist_free (priv->constants); g_clear_object (&priv->formula); G_OBJECT_CLASS (arv_gc_swiss_knife_parent_class)->finalize (object); } static void arv_gc_swiss_knife_class_init (ArvGcSwissKnifeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvGcFeatureNodeClass *gc_feature_node_class = ARV_GC_FEATURE_NODE_CLASS (this_class); object_class->finalize = arv_gc_swiss_knife_finalize; dom_node_class->post_new_child = arv_gc_swiss_knife_post_new_child; dom_node_class->pre_remove_child = arv_gc_swiss_knife_pre_remove_child; gc_feature_node_class->get_access_mode = arv_gc_swiss_knife_get_access_mode; } /* ArvGcInteger interface implementation */ static void _update_variables (ArvGcSwissKnife *self, GError **error) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); ArvGcNode *node; GError *local_error = NULL; GSList *iter; const char *expression; if (priv->formula_node != NULL) expression = arv_gc_property_node_get_string (priv->formula_node, &local_error); else expression = ""; if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return; } arv_evaluator_set_expression (priv->formula, expression); for (iter = priv->expressions; iter != NULL; iter = iter->next) { const char *expression; const char *name; expression = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_sub_expression (priv->formula, name, expression); } for (iter = priv->constants; iter != NULL; iter = iter->next) { const char *constant; const char *name; constant = arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (iter->data), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return; } name = arv_gc_property_node_get_name (iter->data); arv_evaluator_set_constant (priv->formula, name, constant); } for (iter = priv->variables; iter != NULL; iter = iter->next) { ArvGcPropertyNode *variable_node = iter->data; node = arv_gc_property_node_get_linked_node (ARV_GC_PROPERTY_NODE (variable_node)); if (ARV_IS_GC_INTEGER (node)) { gint64 value; value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return; } arv_evaluator_set_int64_variable (priv->formula, arv_gc_property_node_get_name (variable_node), value); } else if (ARV_IS_GC_FLOAT (node)) { double value; value = arv_gc_float_get_value (ARV_GC_FLOAT (node), &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return; } arv_evaluator_set_double_variable (priv->formula, arv_gc_property_node_get_name (variable_node), value); } } } gint64 arv_gc_swiss_knife_get_integer_value (ArvGcSwissKnife *self, GError **error) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_SWISS_KNIFE (self), 0); _update_variables (self, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0; } return arv_evaluator_evaluate_as_int64 (priv->formula, NULL); } double arv_gc_swiss_knife_get_float_value (ArvGcSwissKnife *self, GError **error) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GC_SWISS_KNIFE (self), 0.0); _update_variables (self, &local_error); if (local_error != NULL) { g_propagate_prefixed_error (error, local_error, "[%s] ", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); return 0.0; } return arv_evaluator_evaluate_as_double (priv->formula, NULL); } ArvGcRepresentation arv_gc_swiss_knife_get_representation (ArvGcSwissKnife *self) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); g_return_val_if_fail (ARV_IS_GC_SWISS_KNIFE (self), ARV_GC_REPRESENTATION_UNDEFINED); if (priv->representation == NULL) return ARV_GC_REPRESENTATION_UNDEFINED; return arv_gc_property_node_get_representation (ARV_GC_PROPERTY_NODE (priv->representation), ARV_GC_REPRESENTATION_UNDEFINED); } const char * arv_gc_swiss_knife_get_unit (ArvGcSwissKnife *self) { ArvGcSwissKnifePrivate *priv = arv_gc_swiss_knife_get_instance_private (self); g_return_val_if_fail (ARV_IS_GC_SWISS_KNIFE (self), NULL); if (priv->unit == NULL) return NULL; return arv_gc_property_node_get_string (ARV_GC_PROPERTY_NODE (priv->unit), NULL); } aravis-0.8.34/src/arvgcswissknife.h000066400000000000000000000026531475431451200172570ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_SWISS_KNIFE_H #define ARV_GC_SWISS_KNIFE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_SWISS_KNIFE (arv_gc_swiss_knife_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcSwissKnife, arv_gc_swiss_knife, ARV, GC_SWISS_KNIFE, ArvGcFeatureNode) struct _ArvGcSwissKnifeClass { ArvGcFeatureNodeClass parent_class; }; G_END_DECLS #endif aravis-0.8.34/src/arvgcswissknifenode.c000066400000000000000000000124621475431451200201170ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include typedef struct { ArvGcPropertyNode *display_notation; ArvGcPropertyNode *display_precision; } ArvGcSwissKnifeNodePrivate; static void arv_gc_swiss_knife_node_init (ArvGcSwissKnifeNode *self); static void arv_gc_swiss_knife_node_float_interface_init (ArvGcFloatInterface *interface); G_DEFINE_TYPE_WITH_CODE (ArvGcSwissKnifeNode, arv_gc_swiss_knife_node, ARV_TYPE_GC_SWISS_KNIFE, G_ADD_PRIVATE (ArvGcSwissKnifeNode) G_IMPLEMENT_INTERFACE (ARV_TYPE_GC_FLOAT, arv_gc_swiss_knife_node_float_interface_init)) static const char * arv_gc_swiss_knife_node_get_node_name (ArvDomNode *node) { return "SwissKnife"; } static void arv_gc_swiss_knife_node_post_new_child (ArvDomNode *self, ArvDomNode *child) { ArvGcSwissKnifeNodePrivate *priv = arv_gc_swiss_knife_node_get_instance_private (ARV_GC_SWISS_KNIFE_NODE (self)); if (ARV_IS_GC_PROPERTY_NODE (child)) { ArvGcPropertyNode *property_node = ARV_GC_PROPERTY_NODE (child); switch (arv_gc_property_node_get_node_type (property_node)) { case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_NOTATION: priv->display_notation = property_node; break; case ARV_GC_PROPERTY_NODE_TYPE_DISPLAY_PRECISION: priv->display_precision = property_node; break; default: ARV_DOM_NODE_CLASS (arv_gc_swiss_knife_node_parent_class)->post_new_child (self, child); break; } } else { ARV_DOM_NODE_CLASS (arv_gc_swiss_knife_node_parent_class)->post_new_child (self, child); } } static gdouble arv_gc_swiss_knife_node_get_float_value (ArvGcFloat *self, GError **error) { return arv_gc_swiss_knife_get_float_value (ARV_GC_SWISS_KNIFE (self), error); } static void arv_gc_swiss_knife_node_set_float_value (ArvGcFloat *self, gdouble value, GError **error) { g_set_error (error, ARV_GC_ERROR, ARV_GC_ERROR_READ_ONLY, "[%s] SwissKnife is read only", arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (self))); } static ArvGcRepresentation arv_gc_swiss_knife_node_get_float_representation (ArvGcFloat *self) { return arv_gc_swiss_knife_get_representation (ARV_GC_SWISS_KNIFE (self)); } static const char * arv_gc_swiss_knife_node_get_float_unit (ArvGcFloat *self) { return arv_gc_swiss_knife_get_unit (ARV_GC_SWISS_KNIFE (self)); } static ArvGcDisplayNotation arv_gc_swiss_knife_node_get_display_notation (ArvGcFloat *self) { ArvGcSwissKnifeNodePrivate *priv = arv_gc_swiss_knife_node_get_instance_private (ARV_GC_SWISS_KNIFE_NODE (self)); if (priv->display_notation == NULL) return ARV_GC_DISPLAY_NOTATION_DEFAULT; return arv_gc_property_node_get_display_notation (ARV_GC_PROPERTY_NODE (priv->display_notation), ARV_GC_DISPLAY_NOTATION_DEFAULT); } static gint64 arv_gc_swiss_knife_node_get_display_precision (ArvGcFloat *self) { ArvGcSwissKnifeNodePrivate *priv = arv_gc_swiss_knife_node_get_instance_private (ARV_GC_SWISS_KNIFE_NODE (self)); if (priv->display_precision == NULL) return ARV_GC_DISPLAY_PRECISION_DEFAULT; return arv_gc_property_node_get_display_precision (ARV_GC_PROPERTY_NODE (priv->display_precision), ARV_GC_DISPLAY_PRECISION_DEFAULT); } ArvGcNode * arv_gc_swiss_knife_node_new (void) { return g_object_new (ARV_TYPE_GC_SWISS_KNIFE_NODE, NULL); } static void arv_gc_swiss_knife_node_float_interface_init (ArvGcFloatInterface *interface) { interface->get_value = arv_gc_swiss_knife_node_get_float_value; interface->set_value = arv_gc_swiss_knife_node_set_float_value; interface->get_representation = arv_gc_swiss_knife_node_get_float_representation; interface->get_unit = arv_gc_swiss_knife_node_get_float_unit; interface->get_display_notation = arv_gc_swiss_knife_node_get_display_notation; interface->get_display_precision = arv_gc_swiss_knife_node_get_display_precision; } static void arv_gc_swiss_knife_node_init (ArvGcSwissKnifeNode *self) { } static void arv_gc_swiss_knife_node_finalize (GObject *self) { G_OBJECT_CLASS (arv_gc_swiss_knife_node_parent_class)->finalize (self); } static void arv_gc_swiss_knife_node_class_init (ArvGcSwissKnifeNodeClass *this_class) { ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->finalize = arv_gc_swiss_knife_node_finalize; dom_node_class->get_node_name = arv_gc_swiss_knife_node_get_node_name; dom_node_class->post_new_child = arv_gc_swiss_knife_node_post_new_child; } aravis-0.8.34/src/arvgcswissknifenode.h000066400000000000000000000027371475431451200201300ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_SWISS_KNIFE_NODE_H #define ARV_GC_SWISS_KNIFE_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_SWISS_KNIFE_NODE (arv_gc_swiss_knife_node_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvGcSwissKnifeNode, arv_gc_swiss_knife_node, ARV, GC_SWISS_KNIFE_NODE, ArvGcSwissKnife) struct _ArvGcSwissKnifeNodeClass { ArvGcSwissKnifeClass parent_class; }; ARV_API ArvGcNode * arv_gc_swiss_knife_node_new (void); G_END_DECLS #endif aravis-0.8.34/src/arvgcswissknifeprivate.h000066400000000000000000000024541475431451200206510ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_SWISS_KNIFE_PRIVATE_H #define ARV_GC_SWISS_KNIFE_PRIVATE_H #include ArvGcRepresentation arv_gc_swiss_knife_get_representation (ArvGcSwissKnife *self); const char * arv_gc_swiss_knife_get_unit (ArvGcSwissKnife *self); gint64 arv_gc_swiss_knife_get_integer_value (ArvGcSwissKnife *self, GError **error); double arv_gc_swiss_knife_get_float_value (ArvGcSwissKnife *self, GError **error); #endif aravis-0.8.34/src/arvgcvalueindexednode.c000066400000000000000000000101741475431451200204050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgcvalueindexednode * @short_description: Class for Index nodes */ #include #include #include #include #include #include #include struct _ArvGcValueIndexedNode { ArvGcPropertyNode base; char *index; }; struct _ArvGcValueIndexedNodeClass { ArvGcPropertyNodeClass parent_class; }; G_DEFINE_TYPE (ArvGcValueIndexedNode, arv_gc_value_indexed_node, ARV_TYPE_GC_PROPERTY_NODE) /* ArvDomNode implementation */ static const char * arv_gc_value_indexed_node_get_node_name (ArvDomNode *node) { switch (arv_gc_property_node_get_node_type (ARV_GC_PROPERTY_NODE (node))) { case ARV_GC_PROPERTY_NODE_TYPE_VALUE_INDEXED: return "ValueIndexed"; case ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_INDEXED: return "pValueIndexed"; default: g_assert_not_reached (); } } static gboolean arv_gc_value_indexed_node_can_append_child (ArvDomNode *parent, ArvDomNode *child) { return ARV_IS_DOM_TEXT (child); } /* ArvDomElement implementation */ static void arv_gc_value_indexed_node_set_attribute (ArvDomElement *self, const char *name, const char *value) { ArvGcValueIndexedNode *value_indexed_node = ARV_GC_VALUE_INDEXED_NODE (self); if (strcmp (name, "Index") == 0) { g_free (value_indexed_node->index); value_indexed_node->index = g_strdup (value); } } static const char * arv_gc_value_indexed_node_get_attribute (ArvDomElement *self, const char *name) { g_assert_not_reached (); return NULL; } /* ArvGcValueIndexedNode implementation */ gint64 arv_gc_value_indexed_node_get_index (ArvGcValueIndexedNode *value_indexed_node) { gint64 index; g_return_val_if_fail (ARV_IS_GC_VALUE_INDEXED_NODE (value_indexed_node), 0); if (value_indexed_node->index == NULL) index = 0; else index = g_ascii_strtoll (value_indexed_node->index, NULL, 0); return index; } ArvGcNode * arv_gc_value_indexed_node_new (void) { return g_object_new (ARV_TYPE_GC_VALUE_INDEXED_NODE, "node-type", ARV_GC_PROPERTY_NODE_TYPE_VALUE_INDEXED, NULL); } ArvGcNode * arv_gc_p_value_indexed_node_new (void) { return g_object_new (ARV_TYPE_GC_VALUE_INDEXED_NODE, "node-type", ARV_GC_PROPERTY_NODE_TYPE_P_VALUE_INDEXED, NULL); } static void arv_gc_value_indexed_node_init (ArvGcValueIndexedNode *value_indexed_node) { value_indexed_node->index = NULL; } static void arv_gc_value_indexed_node_finalize (GObject *object) { ArvGcValueIndexedNode *value_indexed_node = ARV_GC_VALUE_INDEXED_NODE (object); g_clear_pointer (&value_indexed_node->index, g_free); G_OBJECT_CLASS (arv_gc_value_indexed_node_parent_class)->finalize (object); } static void arv_gc_value_indexed_node_class_init (ArvGcValueIndexedNodeClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); ArvDomNodeClass *dom_node_class = ARV_DOM_NODE_CLASS (this_class); ArvDomElementClass *dom_element_class = ARV_DOM_ELEMENT_CLASS (this_class); object_class->finalize = arv_gc_value_indexed_node_finalize; dom_node_class->get_node_name = arv_gc_value_indexed_node_get_node_name; dom_node_class->can_append_child = arv_gc_value_indexed_node_can_append_child; dom_element_class->set_attribute = arv_gc_value_indexed_node_set_attribute; dom_element_class->get_attribute = arv_gc_value_indexed_node_get_attribute; } aravis-0.8.34/src/arvgcvalueindexednode.h000066400000000000000000000031131475431451200204050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GC_VALUE_INDEXED_NODE_H #define ARV_GC_VALUE_INDEXED_NODE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_GC_VALUE_INDEXED_NODE (arv_gc_value_indexed_node_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGcValueIndexedNode, arv_gc_value_indexed_node, ARV, GC_VALUE_INDEXED_NODE, ArvGcPropertyNode) ARV_API ArvGcNode * arv_gc_value_indexed_node_new (void); ARV_API ArvGcNode * arv_gc_p_value_indexed_node_new (void); ARV_API gint64 arv_gc_value_indexed_node_get_index (ArvGcValueIndexedNode *value_indexed_node); G_END_DECLS #endif aravis-0.8.34/src/arvgvcp.c000066400000000000000000000467151475431451200155210ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /* * SECTION: arvgvcp * @short_description: GigEVision control packet handling */ #include #include #include #include #include #include #include #include void arv_gvcp_packet_free (ArvGvcpPacket *packet) { g_free (packet); } /** * arv_gvcp_packet_new_read_memory_cmd: (skip) * @address: read address * @size: read size, in bytes * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a memory read command. */ ArvGvcpPacket * arv_gvcp_packet_new_read_memory_cmd (guint32 address, guint32 size, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); guint32 n_size; g_return_val_if_fail (packet_size != NULL, NULL); n_size = g_htonl (((size + sizeof (guint32) - 1) / sizeof (guint32)) * sizeof (guint32)); *packet_size = sizeof (ArvGvcpHeader) + 2 * sizeof (guint32); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED; packet->header.command = g_htons (ARV_GVCP_COMMAND_READ_MEMORY_CMD); packet->header.size = g_htons (2 * sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); memcpy (&packet->data[sizeof(guint32)], &n_size, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_read_memory_ack: (skip) * @address: read address * @size: read size, in bytes * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a memory read acknowledge. */ ArvGvcpPacket * arv_gvcp_packet_new_read_memory_ack (guint32 address, guint32 size, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + sizeof (guint32) + size; packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_ACK; packet->header.packet_flags = 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_READ_MEMORY_ACK); packet->header.size = g_htons (sizeof (guint32) + size); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_write_memory_cmd: (skip) * @address: write address * @size: write size, in bytes * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a memory write command. */ ArvGvcpPacket * arv_gvcp_packet_new_write_memory_cmd (guint32 address, guint32 size, const char *buffer, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); guint32 actual_size; g_return_val_if_fail (packet_size != NULL, NULL); actual_size = ((size + sizeof (guint32) - 1) / sizeof (guint32)) * sizeof (guint32); *packet_size = sizeof (ArvGvcpHeader) + sizeof (guint32) + actual_size; packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED; packet->header.command = g_htons (ARV_GVCP_COMMAND_WRITE_MEMORY_CMD); packet->header.size = g_htons (sizeof (guint32) + actual_size); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); memcpy ((char *) packet + sizeof (ArvGvcpPacket) + sizeof (guint32), buffer, size); return packet; } /** * arv_gvcp_packet_new_write_memory_ack: (skip) * @address: write address * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a memory write acknowledge. */ ArvGvcpPacket * arv_gvcp_packet_new_write_memory_ack (guint32 address, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + sizeof (guint32); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_ACK; packet->header.packet_flags = 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_WRITE_MEMORY_ACK); packet->header.size = g_htons (sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_read_register_cmd: (skip) * @address: write address * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a register read command. */ ArvGvcpPacket * arv_gvcp_packet_new_read_register_cmd (guint32 address, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + sizeof (guint32); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED; packet->header.command = g_htons (ARV_GVCP_COMMAND_READ_REGISTER_CMD); packet->header.size = g_htons (sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_read_register_ack: (skip) * @value: read value * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a register read acknowledge. */ ArvGvcpPacket * arv_gvcp_packet_new_read_register_ack (guint32 value, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_value = g_htonl (value); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = arv_gvcp_packet_get_read_register_ack_size (); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_ACK; packet->header.packet_flags = 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_READ_REGISTER_ACK); packet->header.size = g_htons (sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_value, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_write_register_cmd: (skip) * @address: write address * @value: value to write * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a register write command. */ ArvGvcpPacket * arv_gvcp_packet_new_write_register_cmd (guint32 address, guint32 value, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_address = g_htonl (address); guint32 n_value = g_htonl (value); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + 2 * sizeof (guint32); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED; packet->header.command = g_htons (ARV_GVCP_COMMAND_WRITE_REGISTER_CMD); packet->header.size = g_htons (2 * sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_address, sizeof (guint32)); memcpy (&packet->data[sizeof (guint32)], &n_value, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_write_register_ack: (skip) * @data_index: data index * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a register write acknowledge. */ ArvGvcpPacket * arv_gvcp_packet_new_write_register_ack (guint32 data_index, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 n_data_index = g_htonl (data_index); g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = arv_gvcp_packet_get_write_register_ack_size (); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_ACK; packet->header.packet_flags = 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_WRITE_REGISTER_ACK); packet->header.size = g_htons (sizeof (guint32)); packet->header.id = g_htons (packet_id); memcpy (&packet->data, &n_data_index, sizeof (guint32)); return packet; } /** * arv_gvcp_packet_new_discovery_cmd: (skip) * @size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvGvcpPacket * * Create a gvcp packet for a discovery command. */ ArvGvcpPacket * arv_gvcp_packet_new_discovery_cmd (gboolean allow_broadcat_discovery_ack, size_t *packet_size) { ArvGvcpPacket *packet; g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED | (allow_broadcat_discovery_ack ? ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK : 0); packet->header.command = g_htons (ARV_GVCP_COMMAND_DISCOVERY_CMD); packet->header.size = g_htons (0x0000); packet->header.id = g_htons (0xffff); return packet; } /** * arv_gvcp_packet_new_discovery_ack: (skip) * @id: packet id * @packet_size: (out): packet size, in bytes * * Create a gvcp packet for a discovery acknowledge. * * Return value: (transfer full): a new #ArvGvcpPacket */ ArvGvcpPacket * arv_gvcp_packet_new_discovery_ack (guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + ARV_GVBS_DISCOVERY_DATA_SIZE ; packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_ACK; packet->header.packet_flags = 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_DISCOVERY_ACK); packet->header.size = g_htons (ARV_GVBS_DISCOVERY_DATA_SIZE); packet->header.id = g_htons (packet_id); return packet; } /** * arv_gvcp_packet_new_packet_resend_cmd: (skip) * @frame_id: frame id * @first_block: first missing packet * @last_block: last missing packet * @extended_ids: use extended frame and block ids * @packet_id: packet id * @packet_size: (out): packet size, in bytes * * Create a gvcp packet for a packet resend command. * * Return value: (transfer full): a new #ArvGvcpPacket */ ArvGvcpPacket * arv_gvcp_packet_new_packet_resend_cmd (guint64 frame_id, guint32 first_block, guint32 last_block, gboolean extended_ids, guint16 packet_id, size_t *packet_size) { ArvGvcpPacket *packet; guint32 *data; g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvGvcpHeader) + sizeof (guint32) * (extended_ids ? 5 : 3); packet = g_malloc (*packet_size); packet->header.packet_type = ARV_GVCP_PACKET_TYPE_CMD; packet->header.packet_flags = extended_ids ? ARV_GVCP_CMD_PACKET_FLAGS_EXTENDED_IDS : 0; packet->header.command = g_htons (ARV_GVCP_COMMAND_PACKET_RESEND_CMD); packet->header.size = g_htons ((extended_ids ? 5 : 3) * sizeof (guint32)); packet->header.id = g_htons (packet_id); data = (guint32 *) &packet->data; if (extended_ids) { data[0] = 0; data[1] = g_htonl (first_block); data[2] = g_htonl (last_block); *((guint64 *) &data[3]) = GUINT64_TO_BE (frame_id); } else { data[0] = g_htonl ((guint32) frame_id); /* With regular ids, only the 24 bits are valid */ data[1] = g_htonl (first_block & ARV_GVSP_PACKET_ID_MASK); data[2] = g_htonl (last_block & ARV_GVSP_PACKET_ID_MASK); } return packet; } static const char * arv_enum_to_string (GType type, guint enum_value) { GEnumClass *enum_class; GEnumValue *value; const char *retval = NULL; enum_class = g_type_class_ref (type); value = g_enum_get_value (enum_class, enum_value); if (value) retval = value->value_nick; g_type_class_unref (enum_class); return retval; } /** * arv_gvcp_packet_type_to_string: (skip) * @value: a #ArvGvcpPacketType * * Returns: (transfer none): packet type string. */ const char * arv_gvcp_packet_type_to_string (ArvGvcpPacketType value) { const char *text; text = arv_enum_to_string (ARV_TYPE_GVCP_PACKET_TYPE, value); return text != NULL ? text : "unknown"; } /** * arv_gvcp_packet_flags_to_string_new: (skip) * @command: a #ArvGvcpCommand identifier * @flags: a packet flag value * * Returns: (transfer full): a newly allocated string with the name of all active flags, to be freed after use. */ char * arv_gvcp_packet_flags_to_string_new (ArvGvcpCommand command, guint8 flags) { GString *string = g_string_new (""); unsigned i; for (i = 0; i < 8; i++) { if ((1 << i) & flags) g_string_append_printf (string, "%s%s", string->len > 0 ? " " : "", arv_enum_to_string (ARV_TYPE_GVCP_CMD_PACKET_FLAGS, 1 << i)); } switch (command) { case ARV_GVCP_COMMAND_DISCOVERY_CMD: for (i = 0; i < 8; i++) { if ((1 << i) & flags) g_string_append_printf (string, "%s%s", string->len > 0 ? " " : "", arv_enum_to_string (ARV_TYPE_GVCP_DISCOVERY_PACKET_FLAGS, 1 << i)); } break; case ARV_GVCP_COMMAND_PACKET_RESEND_CMD: for (i = 0; i < 8; i++) { if ((1 << i) & flags) g_string_append_printf (string, "%s%s", string->len > 0 ? " " : "", arv_enum_to_string (ARV_TYPE_GVCP_EVENT_PACKET_FLAGS, 1 << i)); } break; default: break; } if (string->len == 0) g_string_append (string, "none"); return arv_g_string_free_and_steal(string); } /** * arv_gvcp_error_to_string: (skip) * @value: a #ArvGvcpError * * Returns: (transfer none): GVCP error name. */ const char * arv_gvcp_error_to_string (ArvGvcpError value) { const char *text; text = arv_enum_to_string (ARV_TYPE_GVCP_ERROR, value); return text != NULL ? text : "unknown"; } /** * arv_gvcp_command_to_string: (skip) * @value: a #ArvGvcpCommand * * Returns: (transfer none): GVCP command name. */ const char * arv_gvcp_command_to_string (ArvGvcpCommand value) { const char *text; text = arv_enum_to_string (ARV_TYPE_GVCP_COMMAND, value); return text != NULL ? text : "unknown"; } /** * arv_gvcp_packet_to_string: * @packet: a #ArvGvcpPacket * * Converts @packet into a human readable string. * * return value: (transfer full): A newly allocated string. */ char * arv_gvcp_packet_to_string (const ArvGvcpPacket *packet) { GString *string; char *data; int packet_size; guint32 value; g_return_val_if_fail (packet != NULL, NULL); string = g_string_new (""); g_string_append_printf (string, "packet_type = %s\n", arv_gvcp_packet_type_to_string (packet->header.packet_type)); switch (packet->header.packet_type) { case ARV_GVCP_PACKET_TYPE_CMD: { char *flags = arv_gvcp_packet_flags_to_string_new (g_ntohs (packet->header.command), packet->header.packet_flags); g_string_append_printf (string, "packet_flags = %s\n", flags); g_free (flags); } break; case ARV_GVCP_PACKET_TYPE_ERROR: g_string_append_printf (string, "error = %s\n", arv_gvcp_error_to_string (packet->header.packet_flags)); break; default: break; } g_string_append_printf (string, "command = %s\n", arv_gvcp_command_to_string (g_ntohs (packet->header.command))); g_string_append_printf (string, "size = %d\n", g_ntohs (packet->header.size)); g_string_append_printf (string, "id = %d\n", g_ntohs (packet->header.id)); data = (char *) &packet->data; switch (g_ntohs (packet->header.command)) { case ARV_GVCP_COMMAND_DISCOVERY_CMD: break; case ARV_GVCP_COMMAND_DISCOVERY_ACK: g_string_append_printf (string, "manufacturer = %s\n", &data[ARV_GVBS_MANUFACTURER_NAME_OFFSET]); g_string_append_printf (string, "name = %s\n", &data[ARV_GVBS_USER_DEFINED_NAME_OFFSET]); g_string_append_printf (string, "model = %s\n", &data[ARV_GVBS_MODEL_NAME_OFFSET]); g_string_append_printf (string, "address = %d.%d.%d.%d\n", data[ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET] & 0xff, data[ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET + 1] & 0xff, data[ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET + 2] & 0xff, data[ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET + 3] & 0xff); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "address = %10u (0x%08x)\n", value, value); value = g_ntohl (*((guint32 *) &data[4])); g_string_append_printf (string, "value = %10u (0x%08x)\n", value, value); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_ACK: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "data index = %10u (0x%08x)\n", value, value); break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "address = %10u (0x%08x)\n", value, value); break; case ARV_GVCP_COMMAND_READ_REGISTER_ACK: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "value = %10u (0x%08x)\n", value, value); break; case ARV_GVCP_COMMAND_READ_MEMORY_CMD: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "address = %10u (0x%08x)\n", value, value); value = g_ntohl (*((guint32 *) &data[4])); g_string_append_printf (string, "size = %10u (0x%08x)\n", value, value); break; case ARV_GVCP_COMMAND_READ_MEMORY_ACK: value = g_ntohl (*((guint32 *) &data[0])); g_string_append_printf (string, "address = %10u (0x%08x)\n", value, value); break; } packet_size = sizeof (ArvGvcpHeader) + g_ntohs (packet->header.size); arv_g_string_append_hex_dump (string, packet, packet_size); return arv_g_string_free_and_steal(string); } /** * arv_gvcp_packet_debug: * @packet: a #ArvGvcpPacket * @level: debug level * * Dumps the content of @packet if level is lower or equal to the current debug level for the cp debug category. See arv_debug_enable(). */ void arv_gvcp_packet_debug (const ArvGvcpPacket *packet, ArvDebugLevel level) { char *string; if (!arv_debug_check (ARV_DEBUG_CATEGORY_CP, level)) return; string = arv_gvcp_packet_to_string (packet); switch (level) { case ARV_DEBUG_LEVEL_TRACE: arv_trace_cp ("%s", string); break; case ARV_DEBUG_LEVEL_DEBUG: arv_debug_cp ("%s", string); break; case ARV_DEBUG_LEVEL_INFO: arv_info_cp ("%s", string); break; case ARV_DEBUG_LEVEL_WARNING: arv_warning_cp ("%s", string); break; default: break; } g_free (string); } aravis-0.8.34/src/arvgvcpprivate.h000066400000000000000000000436011475431451200171100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GVCP_PRIVATE_H #define ARV_GVCP_PRIVATE_H #include #include G_BEGIN_DECLS /** * ARV_GVCP_PORT: * * Standard device listening port for GVCP packets */ #define ARV_GVCP_PORT 3956 #define ARV_GVBS_VERSION_OFFSET 0x00000000 #define ARV_GVBS_VERSION_MINOR_MASK 0x0000ffff #define ARV_GVBS_VERSION_MINOR_POS 0 #define ARV_GVBS_VERSION_MAJOR_MASK 0xffff0000 #define ARV_GVBS_VERSION_MAJOR_POS 16 #define ARV_GVBS_DEVICE_MODE_OFFSET 0x00000004 #define ARV_GVBS_DEVICE_MODE_BIG_ENDIAN 1 << 31 #define ARV_GVBS_DEVICE_MODE_CHARACTER_SET_MASK 0x0000ffff #define ARV_GVBS_DEVICE_MODE_CHARACTER_SET_POS 0 #define ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET 0x00000008 #define ARV_GVBS_DEVICE_MAC_ADDRESS_LOW_OFFSET 0x0000000c #define ARV_GVBS_SUPPORTED_IP_CONFIGURATION_OFFSET 0x00000010 #define ARV_GVBS_CURRENT_IP_CONFIGURATION_OFFSET 0x00000014 #define ARV_GVBS_IP_CONFIGURATION_PERSISTENT 1 << 0 #define ARV_GVBS_IP_CONFIGURATION_DHCP 1 << 1 #define ARV_GVBS_IP_CONFIGURATION_LLA 1 << 2 #define ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET 0x00000024 #define ARV_GVBS_CURRENT_SUBNET_MASK_OFFSET 0x00000034 #define ARV_GVBS_CURRENT_GATEWAY_OFFSET 0x00000044 #define ARV_GVBS_MANUFACTURER_NAME_OFFSET 0x00000048 #define ARV_GVBS_MANUFACTURER_NAME_SIZE 32 #define ARV_GVBS_MODEL_NAME_OFFSET 0x00000068 #define ARV_GVBS_MODEL_NAME_SIZE 32 #define ARV_GVBS_DEVICE_VERSION_OFFSET 0x00000088 #define ARV_GVBS_DEVICE_VERSION_SIZE 32 #define ARV_GVBS_MANUFACTURER_INFO_OFFSET 0x000000a8 #define ARV_GVBS_MANUFACTURER_INFO_SIZE 48 #define ARV_GVBS_SERIAL_NUMBER_OFFSET 0x000000d8 #define ARV_GVBS_SERIAL_NUMBER_SIZE 16 #define ARV_GVBS_USER_DEFINED_NAME_OFFSET 0x000000e8 #define ARV_GVBS_USER_DEFINED_NAME_SIZE 16 #define ARV_GVBS_DISCOVERY_DATA_SIZE 0xf8 #define ARV_GVBS_XML_URL_0_OFFSET 0x00000200 #define ARV_GVBS_XML_URL_1_OFFSET 0x00000400 #define ARV_GVBS_XML_URL_SIZE 512 #define ARV_GVBS_N_NETWORK_INTERFACES_OFFSET 0x00000600 #define ARV_GVBS_PERSISTENT_IP_ADDRESS_0_OFFSET 0x0000064c #define ARV_GVBS_PERSISTENT_SUBNET_MASK_0_OFFSET 0x0000065c #define ARV_GVBS_PERSISTENT_GATEWAY_0_OFFSET 0x0000066c #define ARV_GVBS_N_MESSAGE_CHANNELS_OFFSET 0x00000900 #define ARV_GVBS_N_STREAM_CHANNELS_OFFSET 0x00000904 #define ARV_GVBS_GVCP_CAPABILITY_OFFSET 0x00000934 #define ARV_GVBS_GVCP_CAPABILITY_CONCATENATION 1 << 0 #define ARV_GVBS_GVCP_CAPABILITY_WRITE_MEMORY 1 << 1 #define ARV_GVBS_GVCP_CAPABILITY_PACKET_RESEND 1 << 2 #define ARV_GVBS_GVCP_CAPABILITY_EVENT 1 << 3 #define ARV_GVBS_GVCP_CAPABILITY_EVENT_DATA 1 << 4 #define ARV_GVBS_GVCP_CAPABILITY_PENDING_ACK 1 << 5 #define ARV_GVBS_GVCP_CAPABILITY_ACTION 1 << 6 #define ARV_GVBS_GVCP_CAPABILITY_PRIMARY_APPLICATION_SWITCHOVER 1 << 21 #define ARV_GVBS_GVCP_CAPABILITY_EXTENDED_STATUS_CODES 1 << 22 #define ARV_GVBS_GVCP_CAPABILITY_DISCOVERY_ACK_DELAY_WRITABLE 1 << 23 #define ARV_GVBS_GVCP_CAPABILITY_DISCOVERY_ACK_DELAY 1 << 24 #define ARV_GVBS_GVCP_CAPABILITY_TEST_DATA 1 << 25 #define ARV_GVBS_GVCP_CAPABILITY_MANIFEST_TABLE 1 << 26 #define ARV_GVBS_GVCP_CAPABILITY_CCP_APPLICATION_SOCKET 1 << 27 #define ARV_GVBS_GVCP_CAPABILITY_LINK_SPEED 1 << 28 #define ARV_GVBS_GVCP_CAPABILITY_HEARTBEAT_DISABLE 1 << 29 #define ARV_GVBS_GVCP_CAPABILITY_SERIAL_NUMBER 1 << 30 #define ARV_GVBS_GVCP_CAPABILITY_NAME_REGISTER 1 << 31 #define ARV_GVBS_HEARTBEAT_TIMEOUT_OFFSET 0x00000938 #define ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_HIGH_OFFSET 0x0000093c #define ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_LOW_OFFSET 0x00000940 #define ARV_GVBS_TIMESTAMP_CONTROL_OFFSET 0x00000944 #define ARV_GVBS_TIMESTAMP_LATCHED_VALUE_HIGH_OFFSET 0x00000948 #define ARV_GVBS_TIMESTAMP_LATCHED_VALUE_LOW_OFFSET 0x0000094c #define ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET 0x00000a00 #define ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_CONTROL 1 << 1 #define ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_EXCLUSIVE 1 << 0 #define ARV_GVBS_STREAM_CHANNEL_0_PORT_OFFSET 0x00000d00 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_OFFSET 0x00000d04 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_MASK 0x0000ffff #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_POS 0 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_BIG_ENDIAN 1 << 29 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_DO_NOT_FRAGMENT 1 << 30 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_SIZE_FIRE_TEST 1 << 31 #define ARV_GVBS_STREAM_CHANNEL_0_PACKET_DELAY_OFFSET 0x00000d08 #define ARV_GVBS_STREAM_CHANNEL_0_IP_ADDRESS_OFFSET 0x00000d18 #define ARV_GVCP_DATA_SIZE_MAX 512 /** * ArvGvcpPacketType: * @ARV_GVCP_PACKET_TYPE_ACK: acknowledge packet * @ARV_GVCP_PACKET_TYPE_CMD: command packet * @ARV_GVCP_PACKET_TYPE_ERROR: error packet * @ARV_GVCP_PACKET_TYPE_UNKNOWN_ERROR: unknown error */ typedef enum { ARV_GVCP_PACKET_TYPE_ACK = 0x00, ARV_GVCP_PACKET_TYPE_CMD = 0x42, ARV_GVCP_PACKET_TYPE_ERROR = 0x80, ARV_GVCP_PACKET_TYPE_UNKNOWN_ERROR = 0x8f } ArvGvcpPacketType; /** * ArvGvcpError: * @ARV_GVCP_ERROR_NONE: none * @ARV_GVCP_ERROR_NOT_IMPLEMENTED: not implemented * @ARV_GVCP_ERROR_INVALID_PARAMETER: invalid parameter * @ARV_GVCP_ERROR_INVALID_ACCESS: inavlid access * @ARV_GVCP_ERROR_WRITE_PROTECT: write protect * @ARV_GVCP_ERROR_BAD_ALIGNMENT: bad alignment * @ARV_GVCP_ERROR_ACCESS_DENIED: access denied * @ARV_GVCP_ERROR_BUSY: busy * @ARV_GVCP_ERROR_LOCAL_PROBLEM: local problem * @ARV_GVCP_ERROR_MESSAGE_MISMATCH: message mismatch * @ARV_GVCP_ERROR_INVALID_PROTOCOL: invalid protocol * @ARV_GVCP_ERROR_NO_MESSAGE: no message * @ARV_GVCP_ERROR_PACKET_UNAVAILABLE: packet unavailable * @ARV_GVCP_ERROR_DATA_OVERRUN: data overrun * @ARV_GVCP_ERROR_INVALID_HEADER: invalid header * @ARV_GVCP_ERROR_WRONG_CONFIG: wrong config * @ARV_GVCP_ERROR_PACKET_NOT_YET_AVAILABLE: packet not yet available * @ARV_GVCP_ERROR_PACKET_AND_PREVIOUS_REMOVED_FROM_MEMORY: packet and previous removed from memmory * @ARV_GVCP_ERROR_PACKET__REMOVED_FROM_MEMORY: packet removed from memory * @ARV_GVCP_ERROR_NO_REFERENCE_TIME: no reference time * @ARV_GVCP_ERROR_PACKET_TEMPORARILY_UNAVAILABLE: packet temporarily unavailable * @ARV_GVCP_ERROR_OVERFLOW: overflow * @ARV_GVCP_ERROR_ACTION_LATE: action late * @ARV_GVCP_ERROR_LEADER_TRAILER_OVERFLOW: leader trailer overflow */ typedef enum { ARV_GVCP_ERROR_NONE = 0x00, ARV_GVCP_ERROR_NOT_IMPLEMENTED = 0x01, ARV_GVCP_ERROR_INVALID_PARAMETER = 0x02, ARV_GVCP_ERROR_INVALID_ACCESS = 0x03, ARV_GVCP_ERROR_WRITE_PROTECT = 0x04, ARV_GVCP_ERROR_BAD_ALIGNMENT = 0x05, ARV_GVCP_ERROR_ACCESS_DENIED = 0x06, ARV_GVCP_ERROR_BUSY = 0x07, ARV_GVCP_ERROR_LOCAL_PROBLEM = 0x08, ARV_GVCP_ERROR_MESSAGE_MISMATCH = 0x09, ARV_GVCP_ERROR_INVALID_PROTOCOL = 0x0a, ARV_GVCP_ERROR_NO_MESSAGE = 0x0b, ARV_GVCP_ERROR_PACKET_UNAVAILABLE = 0x0c, ARV_GVCP_ERROR_DATA_OVERRUN = 0x0d, ARV_GVCP_ERROR_INVALID_HEADER = 0x0e, ARV_GVCP_ERROR_WRONG_CONFIG = 0x0f, ARV_GVCP_ERROR_PACKET_NOT_YET_AVAILABLE = 0x10, ARV_GVCP_ERROR_PACKET_AND_PREVIOUS_REMOVED_FROM_MEMORY = 0x11, ARV_GVCP_ERROR_PACKET_REMOVED_FROM_MEMORY = 0x12, ARV_GVCP_ERROR_NO_REFERENCE_TIME = 0x13, ARV_GVCP_ERROR_PACKET_TEMPORARILY_UNAVAILABLE = 0x14, ARV_GVCP_ERROR_OVERFLOW = 0x15, ARV_GVCP_ERROR_ACTION_LATE = 0x16, ARV_GVCP_ERROR_LEADER_TRAILER_OVERFLOW = 0x17, ARV_GVCP_ERROR_GENERIC = 0xff } ArvGvcpError; /** * ArvGvcpCmdPacketFlags: * @ARV_GVCP_CMD_PACKET_FLAGS_NONE: no flag defined * @ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED: acknowledge required * @ARV_GVCP_CMD_PACKET_FLAGS_EXTENDED_IDS: use extended ids */ typedef enum { ARV_GVCP_CMD_PACKET_FLAGS_NONE = 0x00, ARV_GVCP_CMD_PACKET_FLAGS_ACK_REQUIRED = 0x01, ARV_GVCP_CMD_PACKET_FLAGS_EXTENDED_IDS = 0x10, } ArvGvcpCmdPacketFlags; /** * ArvGvcpEventPacketFlags: * @ARV_GVCP_EVENT_PACKET_FLAGS_NONE: no flag defined * @ARV_GVCP_EVENT_PACKET_FLAGS_64BIT_ID: extended id */ typedef enum { ARV_GVCP_EVENT_PACKET_FLAGS_NONE = 0x00, ARV_GVCP_EVENT_PACKET_FLAGS_64BIT_ID = 0x10, } ArvGvcpEventPacketFlags; /** * ArvGvcpDiscoveryPacketFlags: * @ARV_GVCP_DISCOVERY_PACKET_FLAGS_NONE: no flag defined * @ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK: allow broadcast acknowledge */ typedef enum { ARV_GVCP_DISCOVERY_PACKET_FLAGS_NONE = 0x00, ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK = 0x10, } ArvGvcpDiscoveryPacketFlags; /** * ArvGvcpCommand: * @ARV_GVCP_COMMAND_DISCOVERY_CMD: discovery command * @ARV_GVCP_COMMAND_DISCOVERY_ACK: discovery acknowledge * @ARV_GVCP_COMMAND_BYE_CMD: goodbye command, for connection termination * @ARV_GVCP_COMMAND_BYE_ACK: goodbye acknowledge * @ARV_GVCP_COMMAND_PACKET_RESEND_CMD: packet resend request * @ARV_GVCP_COMMAND_PACKET_RESEND_ACK: packet resend acknowledge (not used ?) * @ARV_GVCP_COMMAND_READ_REGISTER_CMD: read register command * @ARV_GVCP_COMMAND_READ_REGISTER_ACK: read register acknowledge * @ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: write register command * @ARV_GVCP_COMMAND_WRITE_REGISTER_ACK: write register acknowledge * @ARV_GVCP_COMMAND_READ_MEMORY_CMD: read memory command * @ARV_GVCP_COMMAND_READ_MEMORY_ACK: read memory acknowledge * @ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: write memory command * @ARV_GVCP_COMMAND_WRITE_MEMORY_ACK: write memory acknowledge * @ARV_GVCP_COMMAND_PENDING_ACK: pending command acknowledge */ typedef enum { ARV_GVCP_COMMAND_DISCOVERY_CMD = 0x0002, ARV_GVCP_COMMAND_DISCOVERY_ACK = 0x0003, ARV_GVCP_COMMAND_BYE_CMD = 0x0004, ARV_GVCP_COMMAND_BYE_ACK = 0x0005, ARV_GVCP_COMMAND_PACKET_RESEND_CMD = 0x0040, ARV_GVCP_COMMAND_PACKET_RESEND_ACK = 0x0041, ARV_GVCP_COMMAND_READ_REGISTER_CMD = 0x0080, ARV_GVCP_COMMAND_READ_REGISTER_ACK = 0x0081, ARV_GVCP_COMMAND_WRITE_REGISTER_CMD = 0x0082, ARV_GVCP_COMMAND_WRITE_REGISTER_ACK = 0x0083, ARV_GVCP_COMMAND_READ_MEMORY_CMD = 0x0084, ARV_GVCP_COMMAND_READ_MEMORY_ACK = 0x0085, ARV_GVCP_COMMAND_WRITE_MEMORY_CMD = 0x0086, ARV_GVCP_COMMAND_WRITE_MEMORY_ACK = 0x0087, ARV_GVCP_COMMAND_PENDING_ACK = 0x0089 } ArvGvcpCommand; #pragma pack(push,1) /** * ArvGvcpHeader: * @packet_type: a #ArvGvcpPacketType identifier * @packet_flags: set of packet flags * @command: a #ArvGvcpCommand identifier * @size: data size * @id: packet identifier * * GVCP packet header structure. */ typedef struct { guint8 packet_type; guint8 packet_flags; guint16 command; guint16 size; guint16 id; } ArvGvcpHeader; /** * ArvGvcpPacket: * @header: packet header * @data: variable size byte array * * GVCP packet structure. */ typedef struct { ArvGvcpHeader header; unsigned char data[]; } ArvGvcpPacket; #pragma pack(pop) void arv_gvcp_packet_free (ArvGvcpPacket *packet); ArvGvcpPacket * arv_gvcp_packet_new_read_memory_cmd (guint32 address, guint32 size, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_read_memory_ack (guint32 address, guint32 size, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_write_memory_cmd (guint32 address, guint32 size, const char *buffer, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_write_memory_ack (guint32 address, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_read_register_cmd (guint32 address, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_read_register_ack (guint32 value, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_write_register_cmd (guint32 address, guint32 value, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_write_register_ack (guint32 data_index, guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_discovery_cmd (gboolean allow_broadcast_discovery_ack, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_discovery_ack (guint16 packet_id, size_t *packet_size); ArvGvcpPacket * arv_gvcp_packet_new_packet_resend_cmd (guint64 frame_id, guint32 first_block, guint32 last_block, gboolean extended_ids, guint16 packet_id, size_t *packet_size); const char * arv_gvcp_packet_type_to_string (ArvGvcpPacketType value); const char * arv_gvcp_command_to_string (ArvGvcpCommand value); char * arv_gvcp_packet_flags_to_string_new (ArvGvcpCommand command, guint8 flags); const char * arv_gvcp_error_to_string (ArvGvcpError value); char * arv_gvcp_packet_to_string (const ArvGvcpPacket *packet); void arv_gvcp_packet_debug (const ArvGvcpPacket *packet, ArvDebugLevel level); /** * arv_gvcp_packet_get_packet_type: * @packet: a #ArvGvcpPacket * * Return value: The #ArvGvcpPacketType code of @packet. */ static inline ArvGvcpPacketType arv_gvcp_packet_get_packet_type (ArvGvcpPacket *packet) { if (packet == NULL) return ARV_GVCP_PACKET_TYPE_ERROR; return (ArvGvcpPacketType) packet->header.packet_type; } /** * arv_gvcp_packet_get_packet_flags: * @packet: a #ArvGvcpPacket * * Return value: The packet flags. */ static inline guint8 arv_gvcp_packet_get_packet_flags (ArvGvcpPacket *packet) { if (packet == NULL) return 0; return (ArvGvcpPacketType) packet->header.packet_flags; } /** * arv_gvcp_packet_get_command: * @packet: a #ArvGvcpPacket * * Return value: The #ArvGvcpCommand code of @packet. */ static inline ArvGvcpCommand arv_gvcp_packet_get_command (ArvGvcpPacket *packet) { if (packet == NULL) return (ArvGvcpCommand) 0; return (ArvGvcpCommand) g_ntohs (packet->header.command); } static inline void arv_gvcp_packet_set_packet_id (ArvGvcpPacket *packet, guint16 id) { if (packet != NULL) packet->header.id = g_htons (id); } static inline guint16 arv_gvcp_packet_get_packet_id (ArvGvcpPacket *packet) { if (packet == NULL) return 0; return g_ntohs (packet->header.id); } static inline void arv_gvcp_packet_get_read_memory_cmd_infos (const ArvGvcpPacket *packet, guint32 *address, guint32 *size) { if (packet == NULL) { if (address != NULL) *address = 0; if (size != NULL) *size = 0; return; } if (address != NULL) *address = g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))); if (size != NULL) *size = (g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket) + sizeof (guint32))))) & 0xffff; } static inline size_t arv_gvcp_packet_get_read_memory_ack_size (guint32 data_size) { return sizeof (ArvGvcpHeader) + sizeof (guint32) + data_size; } static inline void * arv_gvcp_packet_get_read_memory_ack_data (const ArvGvcpPacket *packet) { return (char *) packet + sizeof (ArvGvcpHeader) + sizeof (guint32); } static inline void arv_gvcp_packet_get_write_memory_cmd_infos (const ArvGvcpPacket *packet, guint32 *address, guint32 *size) { if (packet == NULL) { if (address != NULL) *address = 0; if (size != NULL) *size = 0; return; } if (address != NULL) *address = g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))); if (size != NULL) *size = g_ntohs (packet->header.size) - sizeof (guint32); } static inline void * arv_gvcp_packet_get_write_memory_cmd_data (const ArvGvcpPacket *packet) { return (char *) packet + sizeof (ArvGvcpPacket) + sizeof (guint32); } static inline size_t arv_gvcp_packet_get_write_memory_ack_size (void) { return sizeof (ArvGvcpPacket) + sizeof (guint32); } static inline void arv_gvcp_packet_get_read_register_cmd_infos (const ArvGvcpPacket *packet, guint32 *address) { if (packet == NULL) { if (address != NULL) *address = 0; return; } if (address != NULL) *address = g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))); } static inline guint32 arv_gvcp_packet_get_read_register_ack_value (const ArvGvcpPacket *packet) { if (packet == NULL) return 0; return g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))); } static inline size_t arv_gvcp_packet_get_read_register_ack_size (void) { return sizeof (ArvGvcpHeader) + sizeof (guint32); } static inline void arv_gvcp_packet_get_write_register_cmd_infos (const ArvGvcpPacket *packet, guint32 *address, guint32 *value) { if (packet == NULL) { if (address != NULL) *address = 0; if (value != NULL) *value = 0; return; } if (address != NULL) *address = g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))); if (value != NULL) *value = g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket) + sizeof (guint32)))); } static inline size_t arv_gvcp_packet_get_write_register_ack_size (void) { return sizeof (ArvGvcpHeader) + sizeof (guint32); } static inline guint16 arv_gvcp_next_packet_id (guint16 packet_id) { /* packet_id == 0 is an error value */ if (packet_id == 0xffff) return 1; return packet_id + 1; } static inline size_t arv_gvcp_packet_get_pending_ack_size (void) { return sizeof (ArvGvcpHeader) + sizeof (guint32); } /** * arv_gvcp_packet_get_pending_ack_timeout: * @packet: a #ArvGvcpPacket * * Returns: The pending acknowledge timeout stored in @packet, in ms. * * Since: 0.6.0 */ static inline guint32 arv_gvcp_packet_get_pending_ack_timeout (const ArvGvcpPacket *packet) { return packet != NULL ? g_ntohl (*((guint32 *) ((char *) packet + sizeof (ArvGvcpPacket)))) : 0; } G_END_DECLS #endif aravis-0.8.34/src/arvgvdevice.c000066400000000000000000002574741475431451200163640ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgvdevice * @short_description: GigEVision device */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Shared data (main thread - heartbeat) */ enum { PROP_0, PROP_GV_DEVICE_INTERFACE_ADDRESS, PROP_GV_DEVICE_DEVICE_ADDRESS, PROP_GV_DEVICE_PACKET_SIZE_ADJUSTEMENT }; typedef struct { GMutex mutex; guint16 packet_id; GSocket *socket; GSocketAddress *interface_address; GSocketAddress *device_address; GPollFD poll_in_event; void *buffer; unsigned int gvcp_n_retries; unsigned int gvcp_timeout_ms; gboolean is_controller; } ArvGvDeviceIOData; typedef struct { GInetAddress *interface_address; GInetAddress *device_address; ArvGvDeviceIOData *io_data; void *heartbeat_thread; void *heartbeat_data; ArvGc *genicam; char *genicam_xml; size_t genicam_xml_size; gboolean is_big_endian_device; gboolean is_packet_resend_supported; gboolean is_write_memory_supported; ArvGvStreamOption stream_options; ArvGvPacketSizeAdjustment packet_size_adjustment; gboolean first_stream_created; gboolean init_success; } ArvGvDevicePrivate ; struct _ArvGvDevice { ArvDevice device; }; struct _ArvGvDeviceClass { ArvDeviceClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvGvDevice, arv_gv_device, ARV_TYPE_DEVICE, G_ADD_PRIVATE (ArvGvDevice)) static ArvDeviceError arv_gvcp_error_to_device_error (ArvGvcpError code) { switch (code) { case ARV_GVCP_ERROR_NOT_IMPLEMENTED: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_NOT_IMPLEMENTED; case ARV_GVCP_ERROR_INVALID_PARAMETER: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_PARAMETER; case ARV_GVCP_ERROR_INVALID_ACCESS: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_ADDRESS; case ARV_GVCP_ERROR_WRITE_PROTECT: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_WRITE_PROTECT; case ARV_GVCP_ERROR_BAD_ALIGNMENT: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_BAD_ALIGNMENT; case ARV_GVCP_ERROR_ACCESS_DENIED: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_ACCESS_DENIED; case ARV_GVCP_ERROR_BUSY: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_BUSY; default: break; } return ARV_DEVICE_ERROR_PROTOCOL_ERROR; } static gboolean _send_cmd_and_receive_ack (ArvGvDeviceIOData *io_data, ArvGvcpCommand command, guint64 address, size_t size, void *buffer, GError **error) { ArvGvcpCommand expected_ack_command; ArvGvcpPacket *ack_packet = io_data->buffer; ArvGvcpPacket *packet; const char *operation; size_t packet_size; size_t ack_size; unsigned int n_retries = 0; gboolean success = FALSE; ArvGvcpError command_error = ARV_GVCP_ERROR_NONE; int count; switch (command) { case ARV_GVCP_COMMAND_READ_MEMORY_CMD: operation = "read_memory"; expected_ack_command = ARV_GVCP_COMMAND_READ_MEMORY_ACK; ack_size = arv_gvcp_packet_get_read_memory_ack_size (size); break; case ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: operation = "write_memory"; expected_ack_command = ARV_GVCP_COMMAND_WRITE_MEMORY_ACK; ack_size = arv_gvcp_packet_get_write_memory_ack_size (); break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: operation = "read_register"; expected_ack_command = ARV_GVCP_COMMAND_READ_REGISTER_ACK; ack_size = arv_gvcp_packet_get_read_register_ack_size (); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: operation = "write_register"; expected_ack_command = ARV_GVCP_COMMAND_WRITE_REGISTER_ACK; ack_size = arv_gvcp_packet_get_write_register_ack_size (); break; default: g_assert_not_reached (); } g_return_val_if_fail (ack_size <= ARV_GV_DEVICE_BUFFER_SIZE, FALSE); g_mutex_lock (&io_data->mutex); io_data->packet_id = arv_gvcp_next_packet_id (io_data->packet_id); switch (command) { case ARV_GVCP_COMMAND_READ_MEMORY_CMD: packet = arv_gvcp_packet_new_read_memory_cmd (address, size, io_data->packet_id, &packet_size); break; case ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: packet = arv_gvcp_packet_new_write_memory_cmd (address, size, buffer, io_data->packet_id, &packet_size); break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: packet = arv_gvcp_packet_new_read_register_cmd (address, io_data->packet_id, &packet_size); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: packet = arv_gvcp_packet_new_write_register_cmd (address, *((guint32 *) buffer), io_data->packet_id, &packet_size); break; default: g_assert_not_reached (); } do { GError *local_error = NULL; arv_gvcp_packet_debug (packet, ARV_DEBUG_LEVEL_TRACE); success = g_socket_send_to (io_data->socket, io_data->device_address, (const char *) packet, packet_size, NULL, &local_error) >= 0; if (success) { gint timeout_ms; gint64 timeout_stop_ms; gboolean pending_ack; gboolean expected_answer; timeout_stop_ms = g_get_monotonic_time () / 1000 + io_data->gvcp_timeout_ms; do { pending_ack = FALSE; timeout_ms = timeout_stop_ms - g_get_monotonic_time () / 1000; if (timeout_ms < 0) timeout_ms = 0; success = TRUE; success = success && g_poll (&io_data->poll_in_event, 1, timeout_ms) > 0; if (success) { arv_gpollfd_clear_one (&io_data->poll_in_event, io_data->socket); count = g_socket_receive (io_data->socket, io_data->buffer, ARV_GV_DEVICE_BUFFER_SIZE, NULL, &local_error); } else count = 0; success = success && (count >= sizeof (ArvGvcpHeader)); if (success) { ArvGvcpPacketType packet_type; ArvGvcpCommand ack_command; guint16 packet_id; arv_gvcp_packet_debug (ack_packet, ARV_DEBUG_LEVEL_TRACE); packet_type = arv_gvcp_packet_get_packet_type (ack_packet); ack_command = arv_gvcp_packet_get_command (ack_packet); packet_id = arv_gvcp_packet_get_packet_id (ack_packet); if (ack_command == ARV_GVCP_COMMAND_PENDING_ACK && count >= arv_gvcp_packet_get_pending_ack_size ()) { gint64 pending_ack_timeout_ms = arv_gvcp_packet_get_pending_ack_timeout (ack_packet); pending_ack = TRUE; expected_answer = FALSE; timeout_stop_ms = g_get_monotonic_time () / 1000 + pending_ack_timeout_ms; arv_debug_device ("[GvDevice::%s] Pending ack timeout = %" G_GINT64_FORMAT, operation, pending_ack_timeout_ms); } else if (packet_type == ARV_GVCP_PACKET_TYPE_ERROR || packet_type == ARV_GVCP_PACKET_TYPE_UNKNOWN_ERROR) { expected_answer = ack_command == expected_ack_command && packet_id == io_data->packet_id; if (!expected_answer) { arv_info_device ("[GvDevice::%s] Unexpected answer (0x%02x)", operation, packet_type); } else command_error = arv_gvcp_packet_get_packet_flags (ack_packet); } else { expected_answer = packet_type == ARV_GVCP_PACKET_TYPE_ACK && ack_command == expected_ack_command && packet_id == io_data->packet_id && count >= ack_size; if (!expected_answer) { arv_info_device ("[GvDevice::%s] Unexpected answer (0x%02x)", operation, packet_type); } } } else { expected_answer = FALSE; if (local_error != NULL) arv_warning_device ("[GvDevice::%s] Ack reception error: %s", operation, local_error->message); else arv_warning_device ("[GvDevice::%s] Ack reception timeout", operation); g_clear_error (&local_error); } } while (pending_ack || (!expected_answer && timeout_ms > 0)); success = success && expected_answer; if (success && command_error == ARV_GVCP_ERROR_NONE) { switch (command) { case ARV_GVCP_COMMAND_READ_MEMORY_CMD: memcpy (buffer, arv_gvcp_packet_get_read_memory_ack_data (ack_packet), size); break; case ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: *((gint32 *) buffer) = arv_gvcp_packet_get_read_register_ack_value (ack_packet); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: break; default: g_assert_not_reached (); } } } else { if (local_error != NULL) arv_warning_device ("[GvDevice::%s] Command sending error: %s", operation, local_error->message); g_clear_error (&local_error); } n_retries++; } while (!success && n_retries < io_data->gvcp_n_retries); arv_gvcp_packet_free (packet); g_mutex_unlock (&io_data->mutex); success = success && command_error == ARV_GVCP_ERROR_NONE; if (!success) { switch (command) { case ARV_GVCP_COMMAND_READ_MEMORY_CMD: memset (buffer, 0, size); break; case ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: *((guint32 *) buffer) = 0; break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: break; default: g_assert_not_reached (); } if (command_error != ARV_GVCP_ERROR_NONE) g_set_error (error, ARV_DEVICE_ERROR, arv_gvcp_error_to_device_error (command_error), "GigEVision %s error (%s)", operation, arv_gvcp_error_to_string (command_error)); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TIMEOUT, "GigEVision %s timeout", operation); } return success; } static gboolean _read_memory (ArvGvDeviceIOData *io_data, guint64 address, guint32 size, void *buffer, GError **error) { return _send_cmd_and_receive_ack (io_data, ARV_GVCP_COMMAND_READ_MEMORY_CMD, address, size, buffer, error); } static gboolean _write_memory (ArvGvDeviceIOData *io_data, guint64 address, guint32 size, void *buffer, GError **error) { return _send_cmd_and_receive_ack (io_data, ARV_GVCP_COMMAND_WRITE_MEMORY_CMD, address, size, buffer, error); } static gboolean _read_register (ArvGvDeviceIOData *io_data, guint32 address, guint32 *value_placeholder, GError **error) { return _send_cmd_and_receive_ack (io_data, ARV_GVCP_COMMAND_READ_REGISTER_CMD, address, sizeof (guint32), value_placeholder, error); } static gboolean _write_register (ArvGvDeviceIOData *io_data, guint32 address, guint32 value, GError **error) { return _send_cmd_and_receive_ack (io_data, ARV_GVCP_COMMAND_WRITE_REGISTER_CMD, address, sizeof (guint32), &value, error); } static gboolean arv_gv_device_read_memory (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); int i; gint32 block_size; for (i = 0; i < (size + ARV_GVCP_DATA_SIZE_MAX - 1) / ARV_GVCP_DATA_SIZE_MAX; i++) { block_size = MIN (ARV_GVCP_DATA_SIZE_MAX, size - i * ARV_GVCP_DATA_SIZE_MAX); if (!_read_memory (priv->io_data, address + i * ARV_GVCP_DATA_SIZE_MAX, block_size, ((char *) buffer) + i * ARV_GVCP_DATA_SIZE_MAX, error)) return FALSE; } return TRUE; } static gboolean arv_gv_device_write_memory (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); int i; gint32 block_size; for (i = 0; i < (size + ARV_GVCP_DATA_SIZE_MAX - 1) / ARV_GVCP_DATA_SIZE_MAX; i++) { block_size = MIN (ARV_GVCP_DATA_SIZE_MAX, size - i * ARV_GVCP_DATA_SIZE_MAX); if (!_write_memory (priv->io_data, address + i * ARV_GVCP_DATA_SIZE_MAX, block_size, ((char *) buffer) + i * ARV_GVCP_DATA_SIZE_MAX, error)) return FALSE; } return TRUE; } static gboolean arv_gv_device_read_register (ArvDevice *device, guint64 address, guint32 *value, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); return _read_register (priv->io_data, address, value, error); } static gboolean arv_gv_device_write_register (ArvDevice *device, guint64 address, guint32 value, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); return _write_register (priv->io_data, address, value, error); } /* Heartbeat thread */ typedef struct { ArvGvDevice *gv_device; ArvGvDeviceIOData *io_data; int period_us; GCancellable *cancellable; } ArvGvDeviceHeartbeatData; static void * arv_gv_device_heartbeat_thread (void *data) { ArvGvDeviceHeartbeatData *thread_data = data; ArvGvDeviceIOData *io_data = thread_data->io_data; GPollFD poll_fd; gboolean use_poll; GTimer *timer; guint32 value; timer = g_timer_new (); use_poll = g_cancellable_make_pollfd (thread_data->cancellable, &poll_fd); do { if (use_poll) g_poll (&poll_fd, 1, thread_data->period_us / 1000); else g_usleep (thread_data->period_us); if (io_data->is_controller) { guint counter = 1; /* TODO: Instead of reading the control register, Pylon does write the heartbeat * timeout value, which is interresting, as doing this we could get an error * ack packet which will indicate we lost the control access. */ g_timer_start (timer); while (!_read_register (io_data, ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, &value, NULL) && g_timer_elapsed (timer, NULL) < ARV_GV_DEVICE_HEARTBEAT_RETRY_TIMEOUT_S && !g_cancellable_is_cancelled (thread_data->cancellable)) { g_usleep (ARV_GV_DEVICE_HEARTBEAT_RETRY_DELAY_US); counter++; } if (!g_cancellable_is_cancelled (thread_data->cancellable)) { arv_debug_device ("[GvDevice::Heartbeat] Ack value = %d", value); if (counter > 1) arv_debug_device ("[GvDevice::Heartbeat] Tried %u times", counter); if ((value & (ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_CONTROL | ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_EXCLUSIVE)) == 0) { arv_warning_device ("[GvDevice::Heartbeat] Control access lost"); arv_device_emit_control_lost_signal (ARV_DEVICE (thread_data->gv_device)); io_data->is_controller = FALSE; } } else io_data->is_controller = FALSE; } } while (!g_cancellable_is_cancelled (thread_data->cancellable)); if (use_poll) g_cancellable_release_fd (thread_data->cancellable); g_timer_destroy (timer); return NULL; } /* ArvGvDevice implemenation */ /** * arv_gv_device_take_control: * @gv_device: a #ArvGvDevice * @error: a #GError placeholder, %NULL to ignore * * Returns: whether the control was successfully acquired * * Since: 0.8.3 */ gboolean arv_gv_device_take_control (ArvGvDevice *gv_device, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); gboolean success = TRUE; success = arv_gv_device_write_register (ARV_DEVICE (gv_device), ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_CONTROL, error); if (success) priv->io_data->is_controller = TRUE; else arv_warning_device ("[GvDevice::take_control] Can't get control access"); return success; } /** * arv_gv_device_leave_control: * @gv_device: a #ArvGvDevice * @error: a #GError placeholder, %NULL to ignore * * Returns: whether the control was successfully relinquished * * Since: 0.8.3 */ gboolean arv_gv_device_leave_control (ArvGvDevice *gv_device, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); gboolean success = TRUE; success = arv_gv_device_write_register (ARV_DEVICE (gv_device), ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET, 0, error); if (success) priv->io_data->is_controller = FALSE; else arv_warning_device ("[GvDevice::leave_control] Can't relinquish control access"); return success; } guint64 arv_gv_device_get_timestamp_tick_frequency (ArvGvDevice *gv_device, GError **error) { GError *local_error = NULL; guint32 timestamp_tick_frequency_high; guint32 timestamp_tick_frequency_low; guint64 timestamp_tick_frequency; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), 0); arv_gv_device_read_register (ARV_DEVICE (gv_device), ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_HIGH_OFFSET, ×tamp_tick_frequency_high, &local_error); if (local_error == NULL) arv_gv_device_read_register (ARV_DEVICE (gv_device), ARV_GVBS_TIMESTAMP_TICK_FREQUENCY_LOW_OFFSET, ×tamp_tick_frequency_low, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } timestamp_tick_frequency = ((guint64) timestamp_tick_frequency_high << 32) | timestamp_tick_frequency_low; return timestamp_tick_frequency; } guint arv_gv_device_get_packet_size (ArvGvDevice *gv_device, GError **error) { return arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevSCPSPacketSize", error); } void arv_gv_device_set_packet_size (ArvGvDevice *gv_device, gint packet_size, GError **error) { g_return_if_fail (packet_size > 0); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevSCPSPacketSize", packet_size, error); } static gboolean test_packet_check (ArvDevice *device, GPollFD *poll_fd, GSocket *socket, char *buffer, guint max_size, guint packet_size) { GError *error = NULL; unsigned n_tries = 0; int n_events; size_t read_count; do { arv_device_execute_command (device, "ArvGevSCPSFireTestPacket", &error); if (error != NULL) { arv_warning_device("Test packet check fire failed (%s)", error->message); g_clear_error(&error); } do { n_events = g_poll (poll_fd, 1, 10); if (n_events != 0) { arv_gpollfd_clear_one (poll_fd, socket); read_count = g_socket_receive (socket, buffer, max_size, NULL, NULL); } else read_count = 0; /* Discard late packets, read_count should be equal to packet size minus IP and UDP headers */ } while (n_events != 0 && read_count != (packet_size - ARV_GVSP_PACKET_UDP_OVERHEAD)); n_tries++; } while (n_events == 0 && n_tries < 3); return n_events != 0; } static guint auto_packet_size (ArvGvDevice *gv_device, gboolean exit_early, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); ArvDevice *device = ARV_DEVICE (gv_device); ArvGcNode *node; GSocket *socket; GInetAddress *interface_address; GSocketAddress *interface_socket_address; GInetSocketAddress *local_address; GPollFD poll_fd; const guint8 *address_bytes; guint16 port; gboolean do_not_fragment; guint max_size, min_size; gint64 minimum, maximum, packet_size; guint inc; char *buffer; guint last_size = 0; gboolean success; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), 1500); node = arv_device_get_feature (device, "GevSCPSFireTestPacket"); if (!ARV_IS_GC_COMMAND (node) && !ARV_IS_GC_BOOLEAN (node)) { arv_info_device ("[GvDevice::auto_packet_size] No GevSCPSFireTestPacket feature found"); return arv_device_get_integer_feature_value (device, "ArvGevSCPSPacketSize", error); } packet_size = arv_device_get_integer_feature_value (device, "ArvGevSCPSPacketSize", NULL); /* PacketSize boundaries registers are device specific. Use the standard feature name for finding boundaries. If * this feature is not present in the device Genicam data, it will fallback to the default definition inserted * in arv_gv_device_load_genicam */ arv_device_get_integer_feature_bounds (device, "GevSCPSPacketSize", &minimum, &maximum, NULL); inc = arv_device_get_integer_feature_increment (device, "GevSCPSPacketSize", NULL); if (inc < 1) inc = 1; max_size = MIN (ARV_GVSP_MAXIMUM_PACKET_SIZE, maximum); min_size = MAX (ARV_GVSP_MINIMUM_PACKET_SIZE, minimum); if (max_size < min_size || inc > max_size - min_size) { arv_warning_device ("[GvDevice::auto_packet_size] Invalid ArvGevSCPSPacketSize properties"); return arv_device_get_integer_feature_value (device, "ArvGevSCPSPacketSize", error); } interface_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (priv->io_data->interface_address)); interface_socket_address = g_inet_socket_address_new (interface_address, 0); socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); g_socket_bind (socket, interface_socket_address, FALSE, NULL); local_address = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (socket, NULL)); port = g_inet_socket_address_get_port (local_address); address_bytes = g_inet_address_to_bytes (interface_address); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevSCDA", g_htonl (*((guint32 *) address_bytes)), NULL); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevSCPHostPort", port, NULL); g_clear_object (&local_address); g_clear_object (&interface_socket_address); do_not_fragment = arv_device_get_boolean_feature_value (device, "ArvGevSCPSDoNotFragment", NULL); arv_device_set_boolean_feature_value (device, "ArvGevSCPSDoNotFragment", TRUE, NULL); poll_fd.fd = g_socket_get_fd (socket); poll_fd.events = G_IO_IN; poll_fd.revents = 0; arv_gpollfd_prepare_all (&poll_fd, 1); buffer = g_malloc (max_size); success = test_packet_check (device, &poll_fd, socket, buffer, max_size, packet_size); /* When exit_early is set, the function only checks the current packet size is working. * If not, the full automatic packet size adjustment is run. */ if (success && exit_early) { arv_info_device ("[GvDevice::auto_packet_size] Current packet size check successfull " "(%" G_GINT64_FORMAT " bytes)", packet_size); } else { GError *local_error = NULL; guint current_size = packet_size; do { if (current_size == last_size || min_size + inc > max_size) break; last_size = current_size; arv_device_set_integer_feature_value (device, "ArvGevSCPSPacketSize", current_size, NULL); current_size = arv_device_get_integer_feature_value (device, "ArvGevSCPSPacketSize", &local_error); if (local_error != NULL) break; arv_info_device ("[GvDevice::auto_packet_size] Try packet size = %d (%d - min: %d - max: %d - inc: %d)", current_size, last_size, min_size, max_size, inc); success = test_packet_check (device, &poll_fd, socket, buffer, max_size, current_size); if (success) { packet_size = current_size; if (current_size == max_size) break; min_size = current_size; } else { max_size = current_size; } current_size = min_size + (((max_size - min_size) / 2) / inc) * inc; } while (TRUE); if (local_error == NULL) { arv_device_set_integer_feature_value (device, "ArvGevSCPSPacketSize", packet_size, error); arv_info_device ("[GvDevice::auto_packet_size] Packet size set to %" G_GINT64_FORMAT " bytes", packet_size); } else { g_propagate_error (error, local_error); } } g_clear_pointer (&buffer, g_free); g_clear_object (&socket); arv_gpollfd_finish_all (&poll_fd, 1); arv_device_set_boolean_feature_value (device, "ArvGevSCPSDoNotFragment", do_not_fragment, NULL); return packet_size; } /** * arv_gv_device_auto_packet_size: * @gv_device: a #ArvGvDevice * @error: a #GError placeholder, %NULL to ignore * * Automatically determine the biggest packet size that can be used data streaming, and set ArvGevSCPSPacketSize value * accordingly. This function relies on the GevSCPSFireTestPacket feature. * * Returns: The automatic packet size, in bytes, or the current one if GevSCPSFireTestPacket is not supported. * * Since: 0.6.0 */ guint arv_gv_device_auto_packet_size (ArvGvDevice *gv_device, GError **error) { return auto_packet_size (gv_device, FALSE, error); } /** * arv_gv_device_set_packet_size_adjustment: * @gv_device: a #ArvGvDevice * @adjustment: a #ArvGvPacketSizeAdjustment option * * Sets the option for the packet size adjustment happening at stream object creation. See * arv_gv_device_auto_packet_size() for a description of the packet adjustment feature. The default behaviour is * @ARV_GV_PACKET_SIZE_ADJUSTEMENT_ON_FAILURE_ONCE, which means the packet size is adjusted if the current packet size * check fails, and only the first time arv_device_create_stream() is successfully called during @gv_device instance * life. * * Since: 0.8.3 */ void arv_gv_device_set_packet_size_adjustment (ArvGvDevice *gv_device, ArvGvPacketSizeAdjustment adjustment) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); g_return_if_fail (ARV_IS_GV_DEVICE (gv_device)); priv->packet_size_adjustment = adjustment; } /** * arv_gv_device_get_current_ip: * @gv_device: a #ArvGvDevice * @ip: (out) (optional): a IP address placeholder * @mask: (out) (optional): a netmask placeholder * @gateway: (out) (optional): a gateway IP address placeholder * @error: a #GError placeholder, %NULL to ignore * * Get the current IP address setting of device. * * Returns: %TRUE on success * * Since: 0.8.22 */ gboolean arv_gv_device_get_current_ip (ArvGvDevice *gv_device, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error) { GError *local_error = NULL; guint32 be_ip_int; guint32 be_mask_int; guint32 be_gateway_int; guint32 value; GInetAddress *netmask; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), FALSE); if (ip != NULL) { *ip = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPAddress", &local_error); be_ip_int = g_htonl(value); } if (mask != NULL && local_error == NULL) { *mask = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentSubnetMask", &local_error); be_mask_int = g_htonl(value); } if (gateway != NULL && local_error == NULL) { *gateway = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentDefaultGateway", &local_error); be_gateway_int = g_htonl(value); } if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } if (ip != NULL) *ip = g_inet_address_new_from_bytes ((guint8 *) &be_ip_int, G_SOCKET_FAMILY_IPV4); if (mask != NULL) { netmask = g_inet_address_new_from_bytes ((guint8 *) &be_mask_int, G_SOCKET_FAMILY_IPV4); *mask = g_inet_address_mask_new (netmask, 32, NULL); g_object_unref (netmask); } if (gateway != NULL) *gateway = g_inet_address_new_from_bytes ((guint8 *) &be_gateway_int, G_SOCKET_FAMILY_IPV4); return TRUE; } /** * arv_gv_device_get_persistent_ip: * @gv_device: a #ArvGvDevice * @ip: (out): a IP address placeholder * @mask: (out) (optional): a netmask placeholder * @gateway: (out) (optional): a gateway IP address placeholder * @error: a #GError placeholder * * Get the persistent IP address setting of device. * * Returns: %TRUE on success * * Since: 0.8.22 */ gboolean arv_gv_device_get_persistent_ip (ArvGvDevice *gv_device, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error) { GError *local_error = NULL; guint32 be_ip_int; guint32 be_mask_int; guint32 be_gateway_int; guint32 value; GInetAddress *netmask; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), FALSE); if (ip != NULL) { *ip = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentIPAddress", &local_error); be_ip_int = g_htonl(value); } if (mask != NULL && local_error == NULL) { *mask = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentSubnetMask", &local_error); be_mask_int = g_htonl(value); } if (gateway != NULL && local_error == NULL) { *gateway = NULL; value = arv_device_get_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentDefaultGateway", &local_error); be_gateway_int = g_htonl(value); } if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } if (ip != NULL) *ip = g_inet_address_new_from_bytes ((guint8 *) &be_ip_int, G_SOCKET_FAMILY_IPV4); if (mask != NULL) { netmask = g_inet_address_new_from_bytes ((guint8 *) &be_mask_int, G_SOCKET_FAMILY_IPV4); *mask = g_inet_address_mask_new (netmask, 32, NULL); g_object_unref (netmask); } if (gateway != NULL) *gateway = g_inet_address_new_from_bytes ((guint8 *) &be_gateway_int, G_SOCKET_FAMILY_IPV4); return TRUE; } /** * arv_gv_device_set_persistent_ip: * @gv_device: a #ArvGvDevice * @ip: (nullable): IPv4 address * @mask: (nullable): Netmask * @gateway: (nullable): Gateway IPv4 address * @error: a #GError placeholder * * Sets the persistent IP address to device. * Also disable DHCP then enable persistent IP mode. * * Returns: %TRUE on success * * Since: 0.8.22 */ gboolean arv_gv_device_set_persistent_ip (ArvGvDevice *gv_device, GInetAddress *ip, GInetAddressMask *mask, GInetAddress *gateway, GError **error) { g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), FALSE); if (G_IS_INET_ADDRESS (ip)) { GError *local_error = NULL; const guint8 *ip_bytes; guint32 be_value; guint32 ip_int; /* GigEVision specification does not support IPv6 */ if (g_inet_address_get_family (ip) != G_SOCKET_FAMILY_IPV4) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "IP address is not IPv4 address"); return FALSE; } ip_bytes = g_inet_address_to_bytes (ip); be_value = ((guint32)ip_bytes[3] << 24) | (ip_bytes[2] << 16) | (ip_bytes[1] << 8) | ip_bytes[0]; ip_int = GUINT32_FROM_BE(be_value); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentIPAddress", ip_int, &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } } if (G_IS_INET_ADDRESS_MASK (mask)) { GError *local_error = NULL; const guint8 *mask_bytes; guint32 be_value; guint mask_length; guint32 mask_int; /* GigEVision specification does not support IPv6 */ if (g_inet_address_mask_get_family (mask) != G_SOCKET_FAMILY_IPV4) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Netmask is not IPv4 address"); return FALSE; } mask_length = g_inet_address_mask_get_length (mask); mask_bytes = g_inet_address_to_bytes (g_inet_address_mask_get_address (mask)); if (mask_length == 32) { /* Bitmask format (255.255.255.0) */ be_value = ((guint32)mask_bytes[3] << 24) | (mask_bytes[2] << 16) | (mask_bytes[1] << 8) | mask_bytes[0]; } else { /* CIDR(slash) format (192.168.1.0/24) */ be_value = ~(~(guint32)0 >> mask_length); } mask_int = GUINT32_FROM_BE(be_value); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentSubnetMask", mask_int, &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } } if (G_IS_INET_ADDRESS (gateway)) { GError *local_error = NULL; const guint8 *gateway_bytes; guint32 be_value; guint32 gateway_int; /* GigEVision specification does not support IPv6 */ if (g_inet_address_get_family (gateway) != G_SOCKET_FAMILY_IPV4) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Gateway address is not IPv4 address"); return FALSE; } gateway_bytes = g_inet_address_to_bytes (gateway); be_value = ((guint32)gateway_bytes[3] << 24) | (gateway_bytes[2] << 16) | (gateway_bytes[1] << 8) | gateway_bytes[0]; gateway_int = GUINT32_FROM_BE(be_value); arv_device_set_integer_feature_value (ARV_DEVICE (gv_device), "ArvGevPersistentDefaultGateway", gateway_int, &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } } return arv_gv_device_set_ip_configuration_mode (gv_device, ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP, error); } /** * arv_gv_device_set_persistent_ip_from_string: * @gv_device: a #ArvGvDevice * @ip: (nullable): IPv4 address in string format * @mask: (nullable): netmask in string format * @gateway: (nullable): Gateway IPv4 address in string format * @error: a #GError placeholder, %NULL to ignore * * Sets the persistent IP address to device. * * Returns: %TRUE on success * * Since: 0.8.22 */ gboolean arv_gv_device_set_persistent_ip_from_string (ArvGvDevice *gv_device, const char *ip, const char *mask, const char *gateway, GError **error) { GError *local_error = NULL; GInetAddress *ip_gi = NULL; GInetAddressMask *mask_gi = NULL; GInetAddress *gateway_gi = NULL; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), FALSE); if (ip != NULL) ip_gi = g_inet_address_new_from_string (ip); if (mask != NULL) mask_gi = g_inet_address_mask_new_from_string (mask, NULL); if (gateway != NULL) gateway_gi = g_inet_address_new_from_string (gateway); if (ip != NULL && ip_gi == NULL) { local_error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "IP address could not be parsed: \"%s\"", ip); } else if (mask != NULL && mask_gi == NULL) { local_error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Netmask could not be parsed: \"%s\"", mask); } else if (gateway != NULL && gateway_gi == NULL) { local_error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Gateway address could not be parsed: \"%s\"", gateway); } if (local_error == NULL) arv_gv_device_set_persistent_ip (gv_device, ip_gi, mask_gi, gateway_gi, &local_error); g_clear_object (&ip_gi); g_clear_object (&mask_gi); g_clear_object (&gateway_gi); if (local_error != NULL){ g_propagate_error (error, local_error); return FALSE; } return TRUE; } /** * arv_gv_device_get_ip_configuration_mode: * @gv_device: a #ArvGvDevice * @error: a #GError placeholder, %NULL to ignore * * Get the IP address configuration mode. * * Returns: IP address configuration mode * * Since: 0.8.22 */ ArvGvIpConfigurationMode arv_gv_device_get_ip_configuration_mode (ArvGvDevice *gv_device, GError **error) { GError *local_error = NULL; gboolean dhcp_enabled; gboolean persistent_ip_enabled; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), 0); dhcp_enabled = arv_device_get_boolean_feature_value( ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationDHCP", &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return ARV_GV_IP_CONFIGURATION_MODE_NONE; } persistent_ip_enabled = arv_device_get_boolean_feature_value( ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationPersistentIP", &local_error); if (local_error != NULL) { g_propagate_error(error, local_error); return ARV_GV_IP_CONFIGURATION_MODE_NONE; } if (persistent_ip_enabled) return ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP; if (dhcp_enabled) return ARV_GV_IP_CONFIGURATION_MODE_DHCP; return ARV_GV_IP_CONFIGURATION_MODE_LLA; } /** * arv_gv_device_set_ip_configuration_mode: * @gv_device: a #ArvGvDevice * @mode: IP address configuration mode * @error: a #GError placeholder, %NULL to ignore * * Sets the IP address configuration mode. * Available modes are ARV_GV_IP_CONFIGURATION_MODE_DHCP, ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP, * ARV_GV_IP_CONFIGURATION_MODE_LLA * * Returns: %TRUE on success * * Since: 0.8.22 */ gboolean arv_gv_device_set_ip_configuration_mode (ArvGvDevice *gv_device, ArvGvIpConfigurationMode mode, GError **error) { GError *local_error = NULL; g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), FALSE); g_return_val_if_fail ((mode == ARV_GV_IP_CONFIGURATION_MODE_DHCP) || (mode == ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP) || (mode == ARV_GV_IP_CONFIGURATION_MODE_LLA), FALSE); if (mode == ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP) { /* Persistent IP: disable DHCP, enable persistent IP */ arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationDHCP", FALSE, &local_error); if (local_error == NULL) arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationPersistentIP", TRUE, &local_error); } else if (mode == ARV_GV_IP_CONFIGURATION_MODE_DHCP) { /* DHCP: enable DHCP, disable persistent IP */ arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationDHCP", TRUE, &local_error); if (local_error == NULL) arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationPersistentIP", FALSE, &local_error); } else { /* LLA: disable both */ arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationDHCP", FALSE, &local_error); if (local_error == NULL) arv_device_set_boolean_feature_value (ARV_DEVICE (gv_device), "ArvGevCurrentIPConfigurationPersistentIP", FALSE, &local_error); } if (local_error != NULL) { g_propagate_error(error, local_error); return FALSE; } return TRUE; } /** * arv_gv_device_is_controller: * @gv_device: a #ArvGvDevice * * Returns: value indicating whether the ArvGvDevice has control access to the camera * * Since: 0.8.0 */ gboolean arv_gv_device_is_controller (ArvGvDevice *gv_device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), 0); return priv->io_data->is_controller; } static char * _load_genicam (ArvGvDevice *gv_device, guint32 address, size_t *size, char **url, GError **error) { GError *local_error = NULL; char filename[ARV_GVBS_XML_URL_SIZE]; char *genicam = NULL; char *scheme = NULL; char *path = NULL; guint64 file_address; guint64 file_size; g_return_val_if_fail (size != NULL, NULL); g_return_val_if_fail (url != NULL, NULL); *size = 0; *url = NULL; if (!arv_gv_device_read_memory (ARV_DEVICE (gv_device), address, ARV_GVBS_XML_URL_SIZE, filename, error)) return NULL; filename[ARV_GVBS_XML_URL_SIZE - 1] = '\0'; arv_info_device ("[GvDevice::load_genicam] xml url = '%s' at 0x%x", filename, address); arv_parse_genicam_url (filename, -1, &scheme, NULL, &path, NULL, NULL, &file_address, &file_size); if (scheme != NULL) { if (g_ascii_strcasecmp (scheme, "file") == 0) { gsize len; g_file_get_contents (path, &genicam, &len, NULL); if (genicam) { *size = len; *url = g_strdup (filename); } } else if (g_ascii_strcasecmp (scheme, "local") == 0) { arv_info_device ("[GvDevice::load_genicam] Xml address = 0x%" G_GINT64_MODIFIER "x - " "size = 0x%" G_GINT64_MODIFIER "x - %s", file_address, file_size, path); if (file_size > 0) { genicam = g_malloc (file_size); if (arv_gv_device_read_memory (ARV_DEVICE (gv_device), file_address, file_size, genicam, &local_error)) { if (arv_debug_check (ARV_DEBUG_CATEGORY_MISC, ARV_DEBUG_LEVEL_DEBUG)) { GString *string = g_string_new (""); g_string_append_printf (string, "[GvDevice::load_genicam] Raw data size = 0x%" G_GINT64_MODIFIER "x\n", file_size); arv_g_string_append_hex_dump (string, genicam, file_size); arv_debug_misc ("%s", string->str); g_string_free (string, TRUE); } if (g_str_has_suffix (path, ".zip")) { ArvZip *zip; const GSList *zip_files; arv_info_device ("[GvDevice::load_genicam] Zipped xml data"); zip = arv_zip_new (genicam, file_size); zip_files = arv_zip_get_file_list (zip); if (zip_files != NULL) { const char *zip_filename; void *tmp_buffer; size_t tmp_buffer_size; zip_filename = arv_zip_file_get_name (zip_files->data); tmp_buffer = arv_zip_get_file (zip, zip_filename, &tmp_buffer_size); g_free (genicam); *size = tmp_buffer_size; genicam = tmp_buffer; } else { arv_warning_device ("[GvDevice::load_genicam] Invalid format"); g_clear_pointer (&genicam, g_free); } arv_zip_free (zip); } else { *size = file_size; } if (genicam != NULL) *url = g_strdup_printf ("%s:///%s;%" G_GINT64_MODIFIER "x;%" G_GINT64_MODIFIER "x", scheme, path, file_address, file_size); } else { g_clear_pointer (&genicam, g_free); } } } else if (g_ascii_strcasecmp (scheme, "http")) { GFile *file; GFileInputStream *stream; file = g_file_new_for_uri (filename); stream = g_file_read (file, NULL, NULL); if(stream) { GDataInputStream *data_stream; gsize len; data_stream = g_data_input_stream_new (G_INPUT_STREAM (stream)); genicam = g_data_input_stream_read_upto (data_stream, "", 0, &len, NULL, NULL); if (genicam) { *size = len; *url = g_strdup (filename); } g_object_unref (data_stream); g_object_unref (stream); } g_object_unref (file); } else { arv_warning_device ("Unkown GENICAM url scheme: '%s'", filename); } } if (local_error != NULL) { arv_warning_device("Failed to load GENICAM data: %s", local_error->message); g_propagate_error (error, local_error); } g_free (scheme); g_free (path); return genicam; } static const char * arv_gv_device_get_genicam_xml (ArvDevice *device, size_t *size) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); if (size != NULL) *size = priv->genicam_xml_size; return priv->genicam_xml; } void arv_gc_set_default_gv_features (ArvGc *genicam) { g_return_if_fail (ARV_IS_GC (genicam)); /* Shared features */ arv_gc_set_default_node_data (genicam, "DeviceVendorName", "" " Vendor Name" "
0x48
" " 32" " RO" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "DeviceModelName", "" " Model Name" "
0x68
" " 32" " RO" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "DeviceVersion", "" " Device Version" "
0x88
" " 32" " RO" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "DeviceManufacturerInfo", "" " Manufacturer Info" "
0xa8
" " 48" " RO" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "DeviceID", "" " Device ID" "
0xd8
" " 16" " RO" " Device" "
", NULL); /* Arv prefixed GigEVision features */ arv_gc_set_default_node_data (genicam, "ArvGevCurrentIPConfigurationLLA", "" " TLParamsLocked" " ArvGevCurrentIPConfigurationLLAReg" "", "" "
0x14
" " 4" " RW" " BigEndian" " Device" " NoCache" " 29" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevCurrentIPConfigurationDHCP", "" " TLParamsLocked" " ArvGevCurrentIPConfigurationDHCPReg" "", "" "
0x14
" " 4" " RW" " BigEndian" " Device" " NoCache" " 30" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevCurrentIPConfigurationPersistentIP", "" " TLParamsLocked" " ArvGevCurrentIPConfigurationPersistentIPReg" "", "" "
0x14
" " 4" " RW" " BigEndian" " Device" " NoCache" " 31" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevCurrentIPAddress", "" "
0x24
" " 4" " RO" " Unsigned" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevCurrentSubnetMask", "" "
0x34
" " 4" " RO" " Unsigned" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevCurrentDefaultGateway", "" "
0x44
" " 4" " RO" " Unsigned" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevNumberOfNetworkInterfaces", "" "
0x600
" " 4" " RO" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevPersistentIPAddress", "" "
0x64c
" " 4" " RW" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevPersistentSubnetMask", "" "
0x65c
" " 4" " RW" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevPersistentDefaultGateway", "" "
0x66c
" " 4" " RW" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevStreamChannelCount", "" "
0x904
" " 4" " RW" " BigEndian" " Device" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevStreamChannelSelector", "" " 0" " 0" " ArvGevStreamChannelMax" " 1" "", "" " ArvGevStreamChannelCount" " CNT - 1" "", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPAddrCalc", "" " ArvGevStreamChannelSelector" " SEL * 0x40" "", NULL); arv_gc_set_default_node_data (genicam, "ArvGevTimestampTickFrequency", "" " ArvGevTimestampTickFrequencyCalc" "", "" " ArvGevTimestampTickFrequencyHigh" " ArvGevTimestampTickFrequencyLow" " (HIGH<< 32) | LOW" "", "" " Invisible" "
0x93C
" " 4" " RO" " Device" " 31" " 0" " Unsigned" " BigEndian" "
", "" " Invisible" "
0x940
" " 4" " RO" " Device" " 31" " 0" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPHostPort", "" " Expert" " TLParamsLocked" " ArvGevSCPHostPortReg" "", "" "
0xd00
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 31" " 16" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPSFireTestPacket", "" " TLParamsLocked" " ArvGevSCPSFireTestPacketReg" " 1" "", "" "
0x0d04
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " 0" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPSDoNotFragment", "" " TLParamsLocked" " ArvGevSCPSDoNotFragmentReg" "", "" "
0x0d04
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 1" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPSBigEndian", "" " TLParamsLocked" " ArvGevSCPSBigEndianReg" "", " " "
0x0d04
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 2" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPSPacketSize", "" " Expert" " TLParamsLocked" " ArvGevSCPSPacketSizeReg" " 4" "", "" "
0xd04
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 31" " 16" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCPD", "" " Expert" " TLParamsLocked" " ArvGevSCPDReg" "", "" "
0xd08
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCDA", "" " Expert" " TLParamsLocked" " ArvGevSCDAReg" "", "" "
0xd18
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCSP", "" " Expert" " TLParamsLocked" " ArvGevSCSPReg" "", "" "
0xd1c
" " ArvGevSCPAddrCalc" " 4" " RO" " Device" " NoCache" " 31" " 16" " Unsigned" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "ArvGevSCCFGMultipart", "" " ArvGevSCCFGMultipartReg" " ArvGevSCCAPMultipartReg" " TLParamsLocked" "", "" "
0xd20
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 25" " BigEndian" "
", "" "
0xd24
" " ArvGevSCPAddrCalc" " 4" " RW" " Device" " NoCache" " 25" " BigEndian" "
", NULL); arv_gc_set_default_node_data (genicam, "TLParamsLocked", "" " Invisible" " 0" " 0" " 1" " ", NULL); /* GevSCPSPacketSize feature definition just for default boundaries */ arv_gc_set_default_node_data (genicam, "GevSCPSPacketSize", "" " Expert" " TLParamsLocked" " ArvGevSCPSPacketSizeReg" " 220" " 16404" " 4" "", NULL); arv_gc_set_default_node_data (genicam, "GevSCPD", "" " Expert" " TLParamsLocked" " ArvGevSCPDReg" " 0" " 1000" "", NULL); } static void arv_gv_device_load_genicam (ArvGvDevice *gv_device, GError **error) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); GError *local_error = NULL; char *url = NULL; char *xml; size_t size; size = 0; xml = _load_genicam (gv_device, ARV_GVBS_XML_URL_0_OFFSET, &size, &url, &local_error); if (xml == NULL && local_error == NULL) xml = _load_genicam (gv_device, ARV_GVBS_XML_URL_1_OFFSET, &size, &url, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); g_free (xml); g_free (url); return; } priv->genicam_xml = xml; priv->genicam_xml_size = size; priv->genicam = arv_gc_new (ARV_DEVICE (gv_device), xml, size); if (priv->genicam == NULL) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, "Invalid Genicam data"); g_free (url); return; } arv_gc_set_default_gv_features(priv->genicam); arv_dom_document_set_url (ARV_DOM_DOCUMENT(priv->genicam), url); g_free (url); } /* ArvDevice implemenation */ static ArvStream * arv_gv_device_create_stream (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error) { ArvGvDevice *gv_device = ARV_GV_DEVICE (device); ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); ArvStream *stream; guint32 n_stream_channels; GError *local_error = NULL; n_stream_channels = arv_device_get_integer_feature_value (device, "ArvGevStreamChannelCount", NULL); arv_info_device ("[GvDevice::create_stream] Number of stream channels = %d", n_stream_channels); if (n_stream_channels < 1) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NO_STREAM_CHANNEL, "No stream channel found"); return NULL; } if (!priv->io_data->is_controller) { arv_warning_device ("[GvDevice::create_stream] Can't create stream without control access"); g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_CONTROLLER, "Controller privilege required for streaming control"); return NULL; } if (priv->packet_size_adjustment != ARV_GV_PACKET_SIZE_ADJUSTMENT_NEVER && ((priv->packet_size_adjustment != ARV_GV_PACKET_SIZE_ADJUSTMENT_ONCE && priv->packet_size_adjustment != ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE) || !priv->first_stream_created)) { auto_packet_size (gv_device, priv->packet_size_adjustment == ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE || priv->packet_size_adjustment == ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return NULL; } } stream = arv_gv_stream_new (gv_device, callback, user_data, destroy, error); if (!ARV_IS_STREAM (stream)) return NULL; if (!priv->is_packet_resend_supported) g_object_set (stream, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL); priv->first_stream_created = TRUE; return stream; } static ArvGc * arv_gv_device_get_genicam (ArvDevice *device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (device)); return priv->genicam; } /** * arv_gv_device_get_stream_options: * @gv_device: a #ArvGvDevice * * Returns: options for stream creation * * Since: 0.6.0 */ ArvGvStreamOption arv_gv_device_get_stream_options (ArvGvDevice *gv_device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); g_return_val_if_fail (ARV_IS_GV_DEVICE (gv_device), ARV_GV_STREAM_OPTION_NONE); return priv->stream_options; } /** * arv_gv_device_set_stream_options: * @gv_device: a #ArvGvDevice * @options: options for stream creation * * Sets the option used during stream creation. It must be called before arv_device_create_stream(). * * Since: 0.6.0 */ void arv_gv_device_set_stream_options (ArvGvDevice *gv_device, ArvGvStreamOption options) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); g_return_if_fail (ARV_IS_GV_DEVICE (gv_device)); priv->stream_options = options; } /** * arv_gv_device_new: * @interface_address: address of the interface connected to the device * @device_address: device address * @error: a #GError placeholder, %NULL to ignore * * Returns: a newly created #ArvDevice using GigE protocol * * Since: 0.8.0 */ ArvDevice * arv_gv_device_new (GInetAddress *interface_address, GInetAddress *device_address, GError **error) { return g_initable_new (ARV_TYPE_GV_DEVICE, NULL, error, "interface-address", interface_address, "device-address", device_address, NULL); } static void arv_gv_device_constructed (GObject *object) { ArvGvDevice *gv_device = ARV_GV_DEVICE (object); ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); ArvGvDeviceIOData *io_data; ArvGvDeviceHeartbeatData *heartbeat_data; ArvGcRegisterDescriptionNode *register_description; ArvDomDocument *document; GError *local_error = NULL; char *address_string; guint32 capabilities; guint32 device_mode; G_OBJECT_CLASS (arv_gv_device_parent_class)->constructed (object); if (!G_IS_INET_ADDRESS (priv->interface_address) || !G_IS_INET_ADDRESS (priv->device_address)) { arv_device_take_init_error (ARV_DEVICE (object), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_INVALID_PARAMETER, "Invalid interface or device address")); return; } address_string = g_inet_address_to_string (priv->interface_address); arv_info_device ("[GvDevice::new] Interface address = %s", address_string); g_free (address_string); address_string = g_inet_address_to_string (priv->device_address); arv_info_device ("[GvDevice::new] Device address = %s", address_string); g_free (address_string); io_data = g_new0 (ArvGvDeviceIOData, 1); g_mutex_init (&io_data->mutex); io_data->packet_id = 65300; /* Start near the end of the circular counter */ io_data->interface_address = g_inet_socket_address_new (priv->interface_address, 0); io_data->device_address = g_inet_socket_address_new (priv->device_address, ARV_GVCP_PORT); io_data->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); if (!g_socket_bind (io_data->socket, io_data->interface_address, FALSE, &local_error)) { if (local_error == NULL) local_error = g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_UNKNOWN, "Unknown error trying to bind device interface"); arv_device_take_init_error (ARV_DEVICE (gv_device), local_error); return; } io_data->buffer = g_malloc (ARV_GV_DEVICE_BUFFER_SIZE); io_data->gvcp_n_retries = ARV_GV_DEVICE_GVCP_N_RETRIES_DEFAULT; io_data->gvcp_timeout_ms = ARV_GV_DEVICE_GVCP_TIMEOUT_MS_DEFAULT; io_data->poll_in_event.fd = g_socket_get_fd (io_data->socket); io_data->poll_in_event.events = G_IO_IN; io_data->poll_in_event.revents = 0; arv_gpollfd_prepare_all (&io_data->poll_in_event, 1); priv->io_data = io_data; arv_gv_device_load_genicam (gv_device, &local_error); if (local_error != NULL) { arv_device_take_init_error (ARV_DEVICE (gv_device), local_error); return; } if (!ARV_IS_GC (priv->genicam)) { arv_device_take_init_error (ARV_DEVICE (object), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, "Invalid Genicam data")); return; } arv_gv_device_take_control (gv_device, NULL); heartbeat_data = g_new (ArvGvDeviceHeartbeatData, 1); heartbeat_data->gv_device = gv_device; heartbeat_data->io_data = io_data; heartbeat_data->period_us = ARV_GV_DEVICE_HEARTBEAT_PERIOD_US; heartbeat_data->cancellable = g_cancellable_new (); priv->heartbeat_data = heartbeat_data; priv->heartbeat_thread = g_thread_new ("arv_gv_heartbeat", arv_gv_device_heartbeat_thread, priv->heartbeat_data); arv_gv_device_read_register (ARV_DEVICE (gv_device), ARV_GVBS_DEVICE_MODE_OFFSET, &device_mode, NULL); priv->is_big_endian_device = (device_mode & ARV_GVBS_DEVICE_MODE_BIG_ENDIAN) != 0; arv_gv_device_read_register (ARV_DEVICE (gv_device), ARV_GVBS_GVCP_CAPABILITY_OFFSET, &capabilities, NULL); priv->is_packet_resend_supported = (capabilities & ARV_GVBS_GVCP_CAPABILITY_PACKET_RESEND) != 0; priv->is_write_memory_supported = (capabilities & ARV_GVBS_GVCP_CAPABILITY_WRITE_MEMORY) != 0; arv_info_device ("[GvDevice::new] Device endianness = %s", priv->is_big_endian_device ? "big" : "little"); arv_info_device ("[GvDevice::new] Packet resend = %s", priv->is_packet_resend_supported ? "yes" : "no"); arv_info_device ("[GvDevice::new] Write memory = %s", priv->is_write_memory_supported ? "yes" : "no"); document = ARV_DOM_DOCUMENT (priv->genicam); register_description = ARV_GC_REGISTER_DESCRIPTION_NODE (arv_dom_document_get_document_element (document)); arv_info_device ("[GvDevice::new] Legacy endianness handling = %s", arv_gc_register_description_node_compare_schema_version (register_description, 1, 1, 0) < 0 ? "yes" : "no"); priv->init_success = TRUE; } static void arv_gv_device_init (ArvGvDevice *gv_device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); priv->genicam = NULL; priv->genicam_xml = NULL; priv->genicam_xml_size = 0; priv->stream_options = ARV_GV_STREAM_OPTION_NONE; } static void arv_gv_device_finalize (GObject *object) { ArvGvDevice *gv_device = ARV_GV_DEVICE (object); ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); ArvGvDeviceIOData *io_data; if (priv->heartbeat_thread != NULL) { ArvGvDeviceHeartbeatData *heartbeat_data; heartbeat_data = priv->heartbeat_data; g_cancellable_cancel (heartbeat_data->cancellable); g_thread_join (priv->heartbeat_thread); g_clear_object (&heartbeat_data->cancellable); g_clear_pointer (&heartbeat_data, g_free); priv->heartbeat_data = NULL; priv->heartbeat_thread = NULL; } if (priv->init_success) arv_gv_device_leave_control (gv_device, NULL); io_data = priv->io_data; if (io_data != NULL) { g_clear_object (&io_data->device_address); g_clear_object (&io_data->interface_address); g_clear_object (&io_data->socket); g_clear_pointer (&io_data->buffer, g_free); g_mutex_clear (&io_data->mutex); arv_gpollfd_finish_all (&io_data->poll_in_event, 1); g_clear_pointer (&io_data, g_free); } g_clear_object (&priv->genicam); g_clear_pointer (&priv->genicam_xml, g_free); g_clear_object (&priv->interface_address); g_clear_object (&priv->device_address); G_OBJECT_CLASS (arv_gv_device_parent_class)->finalize (object); } /** * arv_gv_device_get_interface_address: * @device: a #ArvGvDevice * * Returns: (transfer none): the device host interface IP address. * * Since: 0.2.0 */ GSocketAddress *arv_gv_device_get_interface_address(ArvGvDevice *gv_device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); return priv->io_data->interface_address; } /** * arv_gv_device_get_device_address: * @device: a #ArvGvDevice * * Returns: (transfer none): the device IP address. * * since: 0.2.0 */ GSocketAddress *arv_gv_device_get_device_address(ArvGvDevice *gv_device) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (gv_device); return priv->io_data->device_address; } static void arv_gv_device_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (self)); switch (prop_id) { case PROP_GV_DEVICE_INTERFACE_ADDRESS: g_clear_object (&priv->interface_address); priv->interface_address = g_value_dup_object (value); break; case PROP_GV_DEVICE_DEVICE_ADDRESS: g_clear_object (&priv->device_address); priv->device_address = g_value_dup_object (value); break; case PROP_GV_DEVICE_PACKET_SIZE_ADJUSTEMENT: priv->packet_size_adjustment = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; } } static void arv_gv_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ArvGvDevicePrivate *priv = arv_gv_device_get_instance_private (ARV_GV_DEVICE (object)); switch (prop_id) { case PROP_GV_DEVICE_PACKET_SIZE_ADJUSTEMENT: g_value_set_enum (value, priv->packet_size_adjustment); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_gv_device_class_init (ArvGvDeviceClass *gv_device_class) { GObjectClass *object_class = G_OBJECT_CLASS (gv_device_class); ArvDeviceClass *device_class = ARV_DEVICE_CLASS (gv_device_class); object_class->finalize = arv_gv_device_finalize; object_class->constructed = arv_gv_device_constructed; object_class->set_property = arv_gv_device_set_property; object_class->get_property = arv_gv_device_get_property; device_class->create_stream = arv_gv_device_create_stream; device_class->get_genicam_xml = arv_gv_device_get_genicam_xml; device_class->get_genicam = arv_gv_device_get_genicam; device_class->read_memory = arv_gv_device_read_memory; device_class->write_memory = arv_gv_device_write_memory; device_class->read_register = arv_gv_device_read_register; device_class->write_register = arv_gv_device_write_register; g_object_class_install_property (object_class, PROP_GV_DEVICE_INTERFACE_ADDRESS, g_param_spec_object ("interface-address", "Interface address", "The address of the interface connected to the device", G_TYPE_INET_ADDRESS, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_GV_DEVICE_DEVICE_ADDRESS, g_param_spec_object ("device-address", "Device address", "The device address", G_TYPE_INET_ADDRESS, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_GV_DEVICE_PACKET_SIZE_ADJUSTEMENT, g_param_spec_enum ("packet-size-adjustment", "Packet size adjustment", "Packet size adjustment option", ARV_TYPE_GV_PACKET_SIZE_ADJUSTMENT, ARV_GV_PACKET_SIZE_ADJUSTMENT_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); } aravis-0.8.34/src/arvgvdevice.h000066400000000000000000000137431475431451200163560ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #ifndef ARV_GV_DEVICE_H #define ARV_GV_DEVICE_H #include #include #include #include #include G_BEGIN_DECLS /** * ArvGvPacketSizeAdjustment: * @ARV_GV_PACKET_SIZE_ADJUSTMENT_NEVER: never adjust packet size * @ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE: adjust packet size if test packet check fails the with current * packet size, only on the first stream creation * @ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE: adjust packet size if test packet check fails with current packet size * @ARV_GV_PACKET_SIZE_ADJUSTMENT_ONCE: adjust packet size on the first stream creation * @ARV_GV_PACKET_SIZE_ADJUSTMENT_ALWAYS: always adjust the stream packet size * @ARV_GV_PACKET_SIZE_ADJUSTMENT_DEFAULT: default adjustment, which is ON_FAILURE_ONCE (Since 0.8.8) */ typedef enum { ARV_GV_PACKET_SIZE_ADJUSTMENT_NEVER, ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE, ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE, ARV_GV_PACKET_SIZE_ADJUSTMENT_ONCE, ARV_GV_PACKET_SIZE_ADJUSTMENT_ALWAYS, ARV_GV_PACKET_SIZE_ADJUSTMENT_DEFAULT = ARV_GV_PACKET_SIZE_ADJUSTMENT_ON_FAILURE_ONCE } ArvGvPacketSizeAdjustment; typedef enum { ARV_GV_IP_CONFIGURATION_MODE_NONE, ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP, ARV_GV_IP_CONFIGURATION_MODE_DHCP, ARV_GV_IP_CONFIGURATION_MODE_LLA, ARV_GV_IP_CONFIGURATION_MODE_FORCE_IP } ArvGvIpConfigurationMode; #define ARV_TYPE_GV_DEVICE (arv_gv_device_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGvDevice, arv_gv_device, ARV, GV_DEVICE, ArvDevice) ARV_API ArvDevice * arv_gv_device_new (GInetAddress *interface_address, GInetAddress *device_address, GError **error); ARV_API gboolean arv_gv_device_take_control (ArvGvDevice *gv_device, GError **error); ARV_API gboolean arv_gv_device_leave_control (ArvGvDevice *gv_device, GError **error); ARV_API guint64 arv_gv_device_get_timestamp_tick_frequency (ArvGvDevice *gv_device, GError **error); ARV_API GSocketAddress * arv_gv_device_get_interface_address (ArvGvDevice *device); ARV_API GSocketAddress * arv_gv_device_get_device_address (ArvGvDevice *device); ARV_API guint arv_gv_device_get_packet_size (ArvGvDevice *gv_device, GError **error); ARV_API void arv_gv_device_set_packet_size (ArvGvDevice *gv_device, gint packet_size, GError **error); ARV_API void arv_gv_device_set_packet_size_adjustment (ArvGvDevice *gv_device, ArvGvPacketSizeAdjustment adjustment); ARV_API guint arv_gv_device_auto_packet_size (ArvGvDevice *gv_device, GError **error); ARV_API ArvGvStreamOption arv_gv_device_get_stream_options (ArvGvDevice *gv_device); ARV_API void arv_gv_device_set_stream_options (ArvGvDevice *gv_device, ArvGvStreamOption options); ARV_API gboolean arv_gv_device_get_current_ip (ArvGvDevice *gv_device, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error); ARV_API gboolean arv_gv_device_get_persistent_ip (ArvGvDevice *gv_device, GInetAddress **ip, GInetAddressMask **mask, GInetAddress **gateway, GError **error); ARV_API gboolean arv_gv_device_set_persistent_ip (ArvGvDevice *gv_device, GInetAddress *ip, GInetAddressMask *mask, GInetAddress *gateway, GError **error); ARV_API gboolean arv_gv_device_set_persistent_ip_from_string (ArvGvDevice *gv_device, const char *ip, const char *mask, const char *gateway, GError **error); ARV_API ArvGvIpConfigurationMode arv_gv_device_get_ip_configuration_mode (ArvGvDevice *gv_device, GError **error); ARV_API gboolean arv_gv_device_set_ip_configuration_mode (ArvGvDevice *gv_device, ArvGvIpConfigurationMode mode, GError **error); ARV_API gboolean arv_gv_device_is_controller (ArvGvDevice *gv_device); G_END_DECLS #endif aravis-0.8.34/src/arvgvdeviceprivate.h000066400000000000000000000037031475431451200177440ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GV_DEVICE_PRIVATE_H #define ARV_GV_DEVICE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #if ARAVIS_HAS_FAST_HEARTBEAT #define ARV_GV_DEVICE_GVCP_N_RETRIES_DEFAULT 3 #define ARV_GV_DEVICE_GVCP_TIMEOUT_MS_DEFAULT 25 #define ARV_GV_DEVICE_HEARTBEAT_PERIOD_US 50000 #define ARV_GV_DEVICE_HEARTBEAT_RETRY_DELAY_US 1000 #define ARV_GV_DEVICE_HEARTBEAT_RETRY_TIMEOUT_S 0.25 #else #define ARV_GV_DEVICE_GVCP_N_RETRIES_DEFAULT 5 #define ARV_GV_DEVICE_GVCP_TIMEOUT_MS_DEFAULT 500 #define ARV_GV_DEVICE_HEARTBEAT_PERIOD_US 1000000 #define ARV_GV_DEVICE_HEARTBEAT_RETRY_DELAY_US 10000 #define ARV_GV_DEVICE_HEARTBEAT_RETRY_TIMEOUT_S 5.0 /* FIXME */ #endif #define ARV_GV_DEVICE_GVSP_PACKET_SIZE_DEFAULT 1500 #define ARV_GV_DEVICE_BUFFER_SIZE 1024 GRegex * arv_gv_device_get_url_regex (void); void arv_gc_set_default_gv_features (ArvGc *genicam); G_END_DECLS #endif aravis-0.8.34/src/arvgvfakecamera.c000066400000000000000000000650021475431451200171640ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include #include /** * SECTION: arvgvfakecamera * @short_description: GigE Vision Simulator * * #ArvGvFakeCamera is a class that simulates a real GigEVision camera. */ #define ARV_GV_FAKE_CAMERA_BUFFER_SIZE 65536 enum { ARV_GV_FAKE_CAMERA_INPUT_SOCKET_GVCP = 0, ARV_GV_FAKE_CAMERA_INPUT_SOCKET_GLOBAL_DISCOVERY, ARV_GV_FAKE_CAMERA_INPUT_SOCKET_SUBNET_DISCOVERY, ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS }; enum { PROP_0, PROP_INTERFACE_NAME, PROP_SERIAL_NUMBER, PROP_GENICAM_FILENAME, PROP_GVSP_LOST_PACKET_RATIO, PROP_CM_DOMAIN }; typedef struct { char *interface_name; char *serial_number; char *genicam_filename; ArvFakeCamera *camera; gboolean is_running; GPollFD socket_fds[ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS]; guint n_socket_fds; GSocketAddress *controller_address; gint64 controller_time; GSocket *input_sockets[ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS]; GSocket *gvsp_socket; GThread *thread; gboolean cancel; double gvsp_lost_packet_ratio; } ArvGvFakeCameraPrivate; struct _ArvGvFakeCamera { GObject object; ArvGvFakeCameraPrivate *priv; }; struct _ArvGvFakeCameraClass { GObjectClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvGvFakeCamera, arv_gv_fake_camera, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvGvFakeCamera)) static gboolean _g_inet_socket_address_is_equal (GInetSocketAddress *a, GInetSocketAddress *b) { if (!G_IS_INET_SOCKET_ADDRESS (a) || !G_IS_INET_SOCKET_ADDRESS (b)) return FALSE; if (g_inet_socket_address_get_port (a) != g_inet_socket_address_get_port (b)) return FALSE; return g_inet_address_equal (g_inet_socket_address_get_address (a), g_inet_socket_address_get_address (b)); } static gboolean _handle_control_packet (ArvGvFakeCamera *gv_fake_camera, GSocket *socket, GSocketAddress *remote_address, ArvGvcpPacket *packet, size_t size) { ArvGvcpPacket *ack_packet = NULL; size_t ack_packet_size; guint32 block_address; guint32 block_size; guint16 packet_id; guint16 packet_type; guint32 register_address; guint32 register_value; gboolean write_access; gboolean success = FALSE; if (gv_fake_camera->priv->controller_address != NULL) { gint64 time; guint64 elapsed_ms; time = g_get_real_time (); elapsed_ms = (time - gv_fake_camera->priv->controller_time) / 1000; if (elapsed_ms > arv_fake_camera_get_heartbeat_timeout (gv_fake_camera->priv->camera)) { g_object_unref (gv_fake_camera->priv->controller_address); gv_fake_camera->priv->controller_address = NULL; write_access = TRUE; arv_warning_device ("[GvFakeCamera::handle_control_packet] Heartbeat timeout"); arv_fake_camera_set_control_channel_privilege (gv_fake_camera->priv->camera, 0); } else write_access = _g_inet_socket_address_is_equal (G_INET_SOCKET_ADDRESS (remote_address), G_INET_SOCKET_ADDRESS (gv_fake_camera->priv->controller_address)); } else write_access = TRUE; arv_gvcp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); packet_id = arv_gvcp_packet_get_packet_id (packet); packet_type = arv_gvcp_packet_get_packet_type (packet); if (packet_type != ARV_GVCP_PACKET_TYPE_CMD) { arv_warning_device ("[GvFakeCamera::handle_control_packet] Unknown packet type"); return FALSE; } switch (g_ntohs (packet->header.command)) { case ARV_GVCP_COMMAND_DISCOVERY_CMD: ack_packet = arv_gvcp_packet_new_discovery_ack (packet_id, &ack_packet_size); arv_info_device ("[GvFakeCamera::handle_control_packet] Discovery command"); arv_fake_camera_read_memory (gv_fake_camera->priv->camera, 0, ARV_GVBS_DISCOVERY_DATA_SIZE, &ack_packet->data); break; case ARV_GVCP_COMMAND_READ_MEMORY_CMD: arv_gvcp_packet_get_read_memory_cmd_infos (packet, &block_address, &block_size); arv_info_device ("[GvFakeCamera::handle_control_packet] Read memory command %d (%d)", block_address, block_size); ack_packet = arv_gvcp_packet_new_read_memory_ack (block_address, block_size, packet_id, &ack_packet_size); arv_fake_camera_read_memory (gv_fake_camera->priv->camera, block_address, block_size, arv_gvcp_packet_get_read_memory_ack_data (ack_packet)); break; case ARV_GVCP_COMMAND_WRITE_MEMORY_CMD: arv_gvcp_packet_get_write_memory_cmd_infos (packet, &block_address, &block_size); if (!write_access) { arv_warning_device("[GvFakeCamera::handle_control_packet] Ignore Write memory command %d (%d) not controller", block_address, block_size); break; } arv_info_device ("[GvFakeCamera::handle_control_packet] Write memory command %d (%d)", block_address, block_size); arv_fake_camera_write_memory (gv_fake_camera->priv->camera, block_address, block_size, arv_gvcp_packet_get_write_memory_cmd_data (packet)); ack_packet = arv_gvcp_packet_new_write_memory_ack (block_address, packet_id, &ack_packet_size); break; case ARV_GVCP_COMMAND_READ_REGISTER_CMD: arv_gvcp_packet_get_read_register_cmd_infos (packet, ®ister_address); arv_fake_camera_read_register (gv_fake_camera->priv->camera, register_address, ®ister_value); arv_info_device ("[GvFakeCamera::handle_control_packet] Read register command %d -> %d", register_address, register_value); ack_packet = arv_gvcp_packet_new_read_register_ack (register_value, packet_id, &ack_packet_size); if (register_address == ARV_GVBS_CONTROL_CHANNEL_PRIVILEGE_OFFSET) gv_fake_camera->priv->controller_time = g_get_real_time (); break; case ARV_GVCP_COMMAND_WRITE_REGISTER_CMD: arv_gvcp_packet_get_write_register_cmd_infos (packet, ®ister_address, ®ister_value); if (!write_access) { arv_warning_device("[GvFakeCamera::handle_control_packet] Ignore Write register command %d (%d) not controller", register_address, register_value); break; } arv_fake_camera_write_register (gv_fake_camera->priv->camera, register_address, register_value); arv_info_device ("[GvFakeCamera::handle_control_packet] Write register command %d -> %d", register_address, register_value); ack_packet = arv_gvcp_packet_new_write_register_ack (1, packet_id, &ack_packet_size); break; default: arv_warning_device ("[GvFakeCamera::handle_control_packet] Unknown command"); } if (ack_packet != NULL) { g_socket_send_to (socket, remote_address, (char *) ack_packet, ack_packet_size, NULL, NULL); arv_gvcp_packet_debug (ack_packet, ARV_DEBUG_LEVEL_DEBUG); g_free (ack_packet); success = TRUE; } if (gv_fake_camera->priv->controller_address == NULL && arv_fake_camera_get_control_channel_privilege (gv_fake_camera->priv->camera) != 0) { g_object_ref (remote_address); arv_info_device("[GvFakeCamera::handle_control_packet] New controller"); gv_fake_camera->priv->controller_address = remote_address; gv_fake_camera->priv->controller_time = g_get_real_time (); } else if (gv_fake_camera->priv->controller_address != NULL && arv_fake_camera_get_control_channel_privilege (gv_fake_camera->priv->camera) == 0) { g_object_unref (gv_fake_camera->priv->controller_address); arv_info_device("[GvFakeCamera::handle_control_packet] Controller releases"); gv_fake_camera->priv->controller_address = NULL; gv_fake_camera->priv->controller_time = g_get_real_time (); } return success; } static void * _thread (void *user_data) { ArvGvFakeCamera *gv_fake_camera = user_data; ArvBuffer *image_buffer = NULL; GError *error = NULL; GSocketAddress *stream_address = NULL; void *packet_buffer; size_t packet_size; size_t payload = 0; guint16 block_id; ptrdiff_t offset; guint32 gv_packet_size; GInputVector input_vector; int n_events; gboolean is_streaming = FALSE; input_vector.buffer = g_malloc0 (ARV_GV_FAKE_CAMERA_BUFFER_SIZE); input_vector.size = ARV_GV_FAKE_CAMERA_BUFFER_SIZE; packet_buffer = g_malloc (ARV_GV_FAKE_CAMERA_BUFFER_SIZE); do { guint64 next_timestamp_us; if (is_streaming) { arv_fake_camera_get_sleep_time_for_next_frame (gv_fake_camera->priv->camera, &next_timestamp_us); } else { next_timestamp_us = g_get_real_time () + 100000; } do { gint timeout_ms; timeout_ms = (next_timestamp_us - g_get_real_time ()) / 1000LL; if (timeout_ms < 0) timeout_ms = 0; else if (timeout_ms > 100) timeout_ms = 100; n_events = g_poll (gv_fake_camera->priv->socket_fds, gv_fake_camera->priv->n_socket_fds, timeout_ms); if (n_events > 0) { unsigned int i; for (i = 0; i < ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS; i++) { GSocket *socket = gv_fake_camera->priv->input_sockets[i]; int count; if (G_IS_SOCKET (socket)) { GSocketAddress *remote_address = NULL; arv_gpollfd_clear_one (&gv_fake_camera->priv->socket_fds[i], socket); count = g_socket_receive_message (socket, &remote_address, &input_vector, 1, NULL, NULL, NULL, NULL, NULL); if (count > 0) { if (_handle_control_packet (gv_fake_camera, socket, remote_address, input_vector.buffer, count)) arv_info_device ("[GvFakeCamera::thread] Control packet received"); } g_clear_object (&remote_address); } } if (arv_fake_camera_get_control_channel_privilege (gv_fake_camera->priv->camera) == 0 || arv_fake_camera_get_acquisition_status (gv_fake_camera->priv->camera) == 0) { if (stream_address != NULL) { g_object_unref (stream_address); stream_address = NULL; g_object_unref (image_buffer); image_buffer = NULL; arv_info_stream_thread ("[GvFakeCamera::thread] Stop stream"); } is_streaming = FALSE; } } } while (!g_atomic_int_get (&gv_fake_camera->priv->cancel) && g_get_real_time () < next_timestamp_us); if (arv_fake_camera_get_control_channel_privilege (gv_fake_camera->priv->camera) != 0 && arv_fake_camera_get_acquisition_status (gv_fake_camera->priv->camera) != 0) { if (stream_address == NULL) { GInetAddress *inet_address; char *inet_address_string; stream_address = arv_fake_camera_get_stream_address (gv_fake_camera->priv->camera); inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (stream_address)); inet_address_string = g_inet_address_to_string (inet_address); arv_info_stream_thread ("[GvFakeCamera::thread] Start stream to %s (%d)", inet_address_string, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (stream_address))); g_free (inet_address_string); payload = arv_fake_camera_get_payload (gv_fake_camera->priv->camera); image_buffer = arv_buffer_new (payload, NULL); } if (arv_fake_camera_is_in_free_running_mode (gv_fake_camera->priv->camera) || (arv_fake_camera_is_in_software_trigger_mode (gv_fake_camera->priv->camera) && arv_fake_camera_check_and_acknowledge_software_trigger (gv_fake_camera->priv->camera))) { arv_fake_camera_fill_buffer (gv_fake_camera->priv->camera, image_buffer, &gv_packet_size); arv_info_stream_thread ("[GvFakeCamera::thread] Send frame %" G_GUINT64_FORMAT, image_buffer->priv->frame_id); block_id = 0; packet_size = ARV_GV_FAKE_CAMERA_BUFFER_SIZE; arv_gvsp_packet_new_image_leader (image_buffer->priv->frame_id, block_id, arv_buffer_get_timestamp(image_buffer), arv_buffer_get_image_pixel_format(image_buffer), arv_buffer_get_image_width(image_buffer), arv_buffer_get_image_height(image_buffer), arv_buffer_get_image_x(image_buffer), arv_buffer_get_image_y(image_buffer), 0, 0, packet_buffer, &packet_size); if (g_random_double () >= gv_fake_camera->priv->gvsp_lost_packet_ratio) g_socket_send_to (gv_fake_camera->priv->gvsp_socket, stream_address, packet_buffer, packet_size, NULL, &error); else arv_info_stream_thread ("Drop GVSP leader packet frame: %" G_GUINT64_FORMAT, image_buffer->priv->frame_id); if (error != NULL) { arv_warning_stream_thread ("[GvFakeCamera::thread] Failed to send leader for frame %" G_GUINT64_FORMAT ": %s", image_buffer->priv->frame_id, error->message); g_clear_error (&error); } block_id++; offset = 0; while (offset < payload) { size_t data_size; data_size = MIN (gv_packet_size - ARV_GVSP_PACKET_PROTOCOL_OVERHEAD (FALSE), payload - offset); packet_size = ARV_GV_FAKE_CAMERA_BUFFER_SIZE; arv_gvsp_packet_new_payload (image_buffer->priv->frame_id, block_id, data_size, ((char *) image_buffer->priv->data) + offset, packet_buffer, &packet_size); if (g_random_double () >= gv_fake_camera->priv->gvsp_lost_packet_ratio) g_socket_send_to (gv_fake_camera->priv->gvsp_socket, stream_address, packet_buffer, packet_size, NULL, &error); else arv_info_stream_thread ("Drop GVSP data packet frame:%" G_GUINT64_FORMAT ", block:%u", image_buffer->priv->frame_id, block_id); if (error != NULL) { arv_info_stream_thread ("[GvFakeCamera::thread] Failed to send frame block %d for frame" " %" G_GUINT64_FORMAT ": %s", block_id, image_buffer->priv->frame_id, error->message); g_clear_error (&error); } offset += data_size; block_id++; } packet_size = ARV_GV_FAKE_CAMERA_BUFFER_SIZE; arv_gvsp_packet_new_data_trailer (image_buffer->priv->frame_id, block_id, packet_buffer, &packet_size); if (g_random_double () >= gv_fake_camera->priv->gvsp_lost_packet_ratio) g_socket_send_to (gv_fake_camera->priv->gvsp_socket, stream_address, packet_buffer, packet_size, NULL, &error); else arv_info_stream_thread ("Drop GVSP trailer packet frame: %" G_GUINT64_FORMAT, image_buffer->priv->frame_id); if (error != NULL) { arv_info_stream_thread ("[GvFakeCamera::thread] Failed to send trailer for frame %" G_GUINT64_FORMAT ": %s", image_buffer->priv->frame_id, error->message); g_clear_error (&error); } is_streaming = TRUE; } } } while (!g_atomic_int_get (&gv_fake_camera->priv->cancel)); if (stream_address != NULL) g_object_unref (stream_address); if (image_buffer != NULL) g_object_unref (image_buffer); g_free (packet_buffer); g_free (input_vector.buffer); return NULL; } static gboolean _create_and_bind_input_socket (GSocket **socket_out, const char *socket_name, GInetAddress *inet_address, unsigned int port, gboolean allow_reuse, gboolean blocking) { GSocket *socket; GSocketAddress *socket_address; GError *error = NULL; gboolean success; char *address_string; address_string = g_inet_address_to_string (inet_address); if (port > 0) arv_info_device ("%s address = %s:%d",socket_name, address_string, port); else arv_info_device ("%s address = %s",socket_name, address_string); g_clear_pointer (&address_string, g_free); socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); if (!G_IS_SOCKET (socket)) { *socket_out = NULL; return FALSE; } socket_address = g_inet_socket_address_new (inet_address, port); success = g_socket_bind (socket, socket_address, allow_reuse, &error); if (error != NULL) { arv_warning_device ("Failed to bind %s socket: %s", socket_name, error->message); g_clear_error (&error); } g_clear_object (&socket_address); if (success) g_socket_set_blocking (socket, blocking); else g_clear_object (&socket); *socket_out = socket; return G_IS_SOCKET (socket); } static gboolean arv_gv_fake_camera_start (ArvGvFakeCamera *gv_fake_camera) { ArvNetworkInterface *iface; GSocketAddress *socket_address; GInetAddress *inet_address; GInetAddress *gvcp_inet_address; unsigned int i; unsigned int n_socket_fds; g_return_val_if_fail (ARV_IS_GV_FAKE_CAMERA (gv_fake_camera), FALSE); /* get the interface by name or address */ iface = arv_network_get_interface_by_address(gv_fake_camera->priv->interface_name); if (iface == NULL) iface = arv_network_get_interface_by_name(gv_fake_camera->priv->interface_name); #ifdef G_OS_WIN32 /* fake the default loopback interface in windows, in case not found */ if(iface == NULL && g_strcmp0 (gv_fake_camera->priv->interface_name,ARV_GV_FAKE_CAMERA_DEFAULT_INTERFACE) == 0) iface = arv_network_get_fake_ipv4_loopback(); #endif if (iface == NULL) { arv_warning_device ("[GvFakeCamera::start] No network interface with address or name '%s' found.",gv_fake_camera->priv->interface_name); return FALSE; } socket_address = g_socket_address_new_from_native (arv_network_interface_get_addr(iface), sizeof (struct sockaddr)); gvcp_inet_address = g_object_ref (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address))); arv_fake_camera_set_inet_address (gv_fake_camera->priv->camera, gvcp_inet_address); _create_and_bind_input_socket (&gv_fake_camera->priv->gvsp_socket, "GVSP", gvcp_inet_address, 0, FALSE, TRUE); _create_and_bind_input_socket (&gv_fake_camera->priv->input_sockets[ARV_GV_FAKE_CAMERA_INPUT_SOCKET_GVCP], "GVCP", gvcp_inet_address, ARV_GVCP_PORT, FALSE, FALSE); inet_address = g_inet_address_new_from_string ("255.255.255.255"); if (!g_inet_address_equal (gvcp_inet_address, inet_address)) _create_and_bind_input_socket (&gv_fake_camera->priv->input_sockets[ARV_GV_FAKE_CAMERA_INPUT_SOCKET_GLOBAL_DISCOVERY], "Global discovery", inet_address, ARV_GVCP_PORT, TRUE, FALSE); g_clear_object (&inet_address); g_clear_object (&socket_address); socket_address = g_socket_address_new_from_native (arv_network_interface_get_broadaddr(iface), sizeof (struct sockaddr)); inet_address = g_object_ref (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address))); if (!g_inet_address_equal (gvcp_inet_address, inet_address)) _create_and_bind_input_socket (&gv_fake_camera->priv->input_sockets[ARV_GV_FAKE_CAMERA_INPUT_SOCKET_SUBNET_DISCOVERY], "Subnet discovery", inet_address, ARV_GVCP_PORT, FALSE, FALSE); g_clear_object (&inet_address); g_clear_object (&socket_address); g_clear_object (&gvcp_inet_address); arv_network_interface_free(iface); n_socket_fds = 0; for (i = 0; i < ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS; i++) { GSocket *socket = gv_fake_camera->priv->input_sockets[i]; if (G_IS_SOCKET (socket)) { gv_fake_camera->priv->socket_fds[n_socket_fds].fd = g_socket_get_fd (socket); gv_fake_camera->priv->socket_fds[n_socket_fds].events = G_IO_IN; gv_fake_camera->priv->socket_fds[n_socket_fds].revents = 0; n_socket_fds++; } } arv_info_device ("Listening to %d sockets", n_socket_fds); gv_fake_camera->priv->n_socket_fds = n_socket_fds; arv_gpollfd_prepare_all (gv_fake_camera->priv->socket_fds, n_socket_fds); gv_fake_camera->priv->cancel = FALSE; gv_fake_camera->priv->thread = g_thread_new ("arv_fake_gv_fake_camera", _thread, gv_fake_camera); return TRUE; } static void arv_gv_fake_camera_stop (ArvGvFakeCamera *gv_fake_camera) { unsigned int i; g_return_if_fail (ARV_IS_GV_FAKE_CAMERA (gv_fake_camera)); if (gv_fake_camera->priv->thread != NULL) { g_atomic_int_set (&gv_fake_camera->priv->cancel, TRUE); g_thread_join (gv_fake_camera->priv->thread); gv_fake_camera->priv->thread = NULL; } arv_gpollfd_finish_all (gv_fake_camera->priv->socket_fds, gv_fake_camera->priv->n_socket_fds); for (i = 0; i < ARV_GV_FAKE_CAMERA_N_INPUT_SOCKETS; i++) { g_clear_object (&gv_fake_camera->priv->input_sockets[i]); } g_clear_object (&gv_fake_camera->priv->gvsp_socket); g_clear_object (&gv_fake_camera->priv->controller_address); } /** * arv_gv_fake_camera_new_full: * @interface_name: (nullable): listening network interface, by name or IP address, default is 127.0.0.1 * @serial_number: (nullable): fake device serial number, default is GV01 * @genicam_filename: (nullable): path to alternative genicam data * * Returns: a new #ArvGvFakeCamera * * Since: 0.8.0 */ ArvGvFakeCamera * arv_gv_fake_camera_new_full (const char *interface_name, const char *serial_number, const char *genicam_filename) { return g_object_new (ARV_TYPE_GV_FAKE_CAMERA, "interface-name", interface_name != NULL ? interface_name : ARV_GV_FAKE_CAMERA_DEFAULT_INTERFACE, "serial-number", serial_number != NULL ? serial_number : ARV_GV_FAKE_CAMERA_DEFAULT_SERIAL_NUMBER, "genicam-filename", genicam_filename, NULL); } /** * arv_gv_fake_camera_new: * @interface_name: (nullable): listening network interface ('lo' by default) * @serial_number: (nullable): fake device serial number ('GV01' by default) * * Returns: a new #ArvGvFakeCamera * * Since: 0.8.0 */ ArvGvFakeCamera * arv_gv_fake_camera_new (const char *interface_name, const char *serial_number) { return arv_gv_fake_camera_new_full (interface_name, serial_number, NULL); } static void _set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ArvGvFakeCamera *gv_fake_camera = ARV_GV_FAKE_CAMERA (object); switch (prop_id) { case PROP_INTERFACE_NAME: g_free (gv_fake_camera->priv->interface_name); gv_fake_camera->priv->interface_name = g_value_dup_string (value); break; case PROP_SERIAL_NUMBER: g_free (gv_fake_camera->priv->serial_number); gv_fake_camera->priv->serial_number = g_value_dup_string (value); break; case PROP_GENICAM_FILENAME: g_free (gv_fake_camera->priv->genicam_filename); gv_fake_camera->priv->genicam_filename = g_value_dup_string (value); break; case PROP_GVSP_LOST_PACKET_RATIO: gv_fake_camera->priv->gvsp_lost_packet_ratio = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * arv_gv_fake_camera_get_fake_camera: * @gv_fake_camera: a #ArvGvFakeCamera * * Retrieves the underlying #ArvFakeCamera object owned by @gv_fake_camera. * * Returns: (transfer none): underlying fake camera object. * * Since: 0.8.0 */ ArvFakeCamera * arv_gv_fake_camera_get_fake_camera (ArvGvFakeCamera *gv_fake_camera) { g_return_val_if_fail (ARV_IS_GV_FAKE_CAMERA (gv_fake_camera), NULL); return gv_fake_camera->priv->camera; } /** * arv_gv_fake_camera_is_running: * @gv_fake_camera: a #ArvGvFakeCamera * * Returns: %TRUE if the fake camera is correctly listening on the GVCP port * * Since: 0.8.0 */ gboolean arv_gv_fake_camera_is_running (ArvGvFakeCamera *gv_fake_camera) { g_return_val_if_fail (ARV_IS_GV_FAKE_CAMERA (gv_fake_camera), FALSE); return gv_fake_camera->priv->is_running; } static void arv_gv_fake_camera_init (ArvGvFakeCamera *gv_fake_camera) { gv_fake_camera->priv = arv_gv_fake_camera_get_instance_private (gv_fake_camera); } static void _constructed (GObject *gobject) { ArvGvFakeCamera *gv_fake_camera = ARV_GV_FAKE_CAMERA (gobject); G_OBJECT_CLASS (arv_gv_fake_camera_parent_class)->constructed (gobject); gv_fake_camera->priv->camera = arv_fake_camera_new_full (gv_fake_camera->priv->serial_number, gv_fake_camera->priv->genicam_filename); gv_fake_camera->priv->is_running = arv_gv_fake_camera_start (gv_fake_camera); } static void _finalize (GObject *object) { ArvGvFakeCamera *gv_fake_camera = ARV_GV_FAKE_CAMERA (object); gv_fake_camera->priv->is_running = FALSE; arv_gv_fake_camera_stop (gv_fake_camera); g_object_unref (gv_fake_camera->priv->camera); g_clear_pointer (&gv_fake_camera->priv->interface_name, g_free); g_clear_pointer (&gv_fake_camera->priv->serial_number, g_free); g_clear_pointer (&gv_fake_camera->priv->genicam_filename, g_free); G_OBJECT_CLASS (arv_gv_fake_camera_parent_class)->finalize (object); } static void arv_gv_fake_camera_class_init (ArvGvFakeCameraClass *this_class) { GObjectClass *object_class = G_OBJECT_CLASS (this_class); object_class->set_property = _set_property; object_class->constructed = _constructed; object_class->finalize = _finalize; g_object_class_install_property (object_class, PROP_INTERFACE_NAME, g_param_spec_string ("interface-name", "Interface name", "Interface name", ARV_GV_FAKE_CAMERA_DEFAULT_INTERFACE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_SERIAL_NUMBER, g_param_spec_string ("serial-number", "Serial number", "Serial number", ARV_GV_FAKE_CAMERA_DEFAULT_SERIAL_NUMBER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_GENICAM_FILENAME, g_param_spec_string ("genicam-filename", "Genicam filename", "Genicam filename", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_GVSP_LOST_PACKET_RATIO, g_param_spec_double ("gvsp-lost-ratio", "GVSP lost packet ratio", "GVSP lost packet ratio", 0.0, 1.0, 0.0, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); } aravis-0.8.34/src/arvgvfakecamera.h000066400000000000000000000034751475431451200171770ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #ifndef ARV_GV_FAKE_CAMERA_H #define ARV_GV_FAKE_CAMERA_H #include #include G_BEGIN_DECLS #define ARV_GV_FAKE_CAMERA_DEFAULT_SERIAL_NUMBER "GV01" #define ARV_GV_FAKE_CAMERA_DEFAULT_INTERFACE "127.0.0.1" #define ARV_TYPE_GV_FAKE_CAMERA (arv_gv_fake_camera_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGvFakeCamera, arv_gv_fake_camera, ARV, GV_FAKE_CAMERA, GObject) ARV_API ArvGvFakeCamera * arv_gv_fake_camera_new (const char *interface_name, const char *serial_number); ARV_API ArvGvFakeCamera * arv_gv_fake_camera_new_full (const char *interface_name, const char *serial_number, const char *genicam_filename); ARV_API gboolean arv_gv_fake_camera_is_running (ArvGvFakeCamera *gv_fake_camera); ARV_API ArvFakeCamera * arv_gv_fake_camera_get_fake_camera (ArvGvFakeCamera *gv_fake_camera); G_END_DECLS #endif aravis-0.8.34/src/arvgvinterface.c000066400000000000000000000746431475431451200170600ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgvinterface * @short_description: GigEVision interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ArvGvDiscoverSocket implementation */ typedef struct { GSocketAddress *interface_address; GSocketAddress *broadcast_address; GSocket *socket; } ArvGvDiscoverSocket; static gboolean arv_gv_discover_socket_set_broadcast (ArvGvDiscoverSocket *discover_socket, gboolean enable) { int socket_fd; int result; socket_fd = g_socket_get_fd (discover_socket->socket); result = setsockopt (socket_fd, SOL_SOCKET, SO_BROADCAST, (char*)&enable, sizeof (enable)); return result == 0; } typedef struct { unsigned int n_sockets; GSList *sockets; GPollFD *poll_fds; } ArvGvDiscoverSocketList; static ArvGvDiscoverSocketList * arv_gv_discover_socket_list_new (const char *discovery_interface) { ArvGvDiscoverSocketList *socket_list; GSList *iter; GList *ifaces; GList *iface_iter; int i; socket_list = g_new0 (ArvGvDiscoverSocketList, 1); ifaces = arv_enumerate_network_interfaces (); if (!ifaces) return socket_list; for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { ArvGvDiscoverSocket *discover_socket; GSocketAddress *socket_address; GSocketAddress *socket_broadcast; GInetAddress *inet_address; GInetAddress *inet_broadcast; char *inet_address_string; char *inet_broadcast_string; GError *error = NULL; gint buffer_size = ARV_GV_INTERFACE_DISCOVERY_SOCKET_BUFFER_SIZE; if (discovery_interface != NULL) if (g_strcmp0 (discovery_interface, arv_network_interface_get_name (iface_iter->data)) != 0) continue; discover_socket = g_new0 (ArvGvDiscoverSocket, 1); socket_address = g_socket_address_new_from_native (arv_network_interface_get_addr(iface_iter->data), sizeof (struct sockaddr)); socket_broadcast = g_socket_address_new_from_native (arv_network_interface_get_broadaddr(iface_iter->data), sizeof (struct sockaddr)); inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_address)); inet_broadcast = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (socket_broadcast)); inet_address_string = g_inet_address_to_string (inet_address); inet_broadcast_string = g_inet_address_to_string (inet_broadcast); arv_info_interface ("[GvDiscoverSocket::new] Add interface %s (%s)", inet_address_string, inet_broadcast_string); g_free (inet_address_string); g_free (inet_broadcast_string); discover_socket->interface_address = g_inet_socket_address_new (inet_address, 0); discover_socket->broadcast_address = g_inet_socket_address_new (inet_broadcast, ARV_GVCP_PORT); g_object_unref (socket_address); g_object_unref (socket_broadcast); discover_socket->socket = g_socket_new (g_inet_address_get_family (inet_address), G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); arv_socket_set_recv_buffer_size (g_socket_get_fd (discover_socket->socket), buffer_size); g_socket_bind (discover_socket->socket, discover_socket->interface_address, FALSE, &error); socket_list->sockets = g_slist_prepend (socket_list->sockets, discover_socket); socket_list->n_sockets++; } g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); socket_list->poll_fds = g_new (GPollFD, socket_list->n_sockets); for (i = 0, iter = socket_list->sockets; iter != NULL; i++, iter = iter->next) { ArvGvDiscoverSocket *discover_socket = iter->data; socket_list->poll_fds[i].fd = g_socket_get_fd (discover_socket->socket); socket_list->poll_fds[i].events = G_IO_IN; socket_list->poll_fds[i].revents = 0; } arv_gpollfd_prepare_all(socket_list->poll_fds, socket_list->n_sockets); return socket_list; } static void arv_gv_discover_socket_list_free (ArvGvDiscoverSocketList *socket_list) { GSList *iter; g_return_if_fail (socket_list != NULL); arv_gpollfd_finish_all (socket_list->poll_fds, socket_list->n_sockets); for (iter = socket_list->sockets; iter != NULL; iter = iter->next) { ArvGvDiscoverSocket *discover_socket = iter->data; g_object_unref (discover_socket->interface_address); g_object_unref (discover_socket->broadcast_address); g_object_unref (discover_socket->socket); g_free (discover_socket); } g_slist_free (socket_list->sockets); g_free (socket_list->poll_fds); socket_list->sockets = NULL; socket_list->n_sockets = 0; socket_list->poll_fds = NULL; g_free (socket_list); } static void arv_gv_discover_socket_list_send_discover_packet (ArvGvDiscoverSocketList *socket_list, gboolean allow_broadcast_discovery_ack) { GInetAddress *broadcast_address; GSocketAddress *broadcast_socket_address; ArvGvcpPacket *packet; GSList *iter; size_t size; packet = arv_gvcp_packet_new_discovery_cmd (allow_broadcast_discovery_ack, &size); broadcast_address = g_inet_address_new_from_string ("255.255.255.255"); broadcast_socket_address = g_inet_socket_address_new (broadcast_address, ARV_GVCP_PORT); g_object_unref (broadcast_address); for (iter = socket_list->sockets; iter != NULL; iter = iter->next) { ArvGvDiscoverSocket *discover_socket = iter->data; GError *error = NULL; arv_gv_discover_socket_set_broadcast (discover_socket, TRUE); g_socket_send_to (discover_socket->socket, broadcast_socket_address, (const char *) packet, size, NULL, &error); if (error != NULL) { arv_warning_interface ("[ArvGVInterface::send_discover_packet] " "Error sending packet using local broadcast: %s", error->message); g_clear_error (&error); g_socket_send_to (discover_socket->socket, discover_socket->broadcast_address, (const char *) packet, size, NULL, &error); if (error != NULL) { arv_warning_interface ("[ArvGVInterface::send_discover_packet] " "Error sending packet using directed broadcast: %s", error->message); g_clear_error (&error); } } arv_gv_discover_socket_set_broadcast (discover_socket, FALSE); } g_object_unref (broadcast_socket_address); arv_gvcp_packet_free (packet); } /* ArvGvInterfaceDeviceInfos implementation */ typedef struct { char *id; char *user_id; char *vendor_serial; char *vendor_alias_serial; char *vendor; char *manufacturer_info; char *model; char *serial; char *mac; GInetAddress *interface_address; guchar discovery_data[ARV_GVBS_DISCOVERY_DATA_SIZE]; volatile gint ref_count; } ArvGvInterfaceDeviceInfos; static ArvGvInterfaceDeviceInfos * arv_gv_interface_device_infos_new (GInetAddress *interface_address, void *discovery_data) { ArvGvInterfaceDeviceInfos *infos; g_return_val_if_fail (G_IS_INET_ADDRESS (interface_address), NULL); g_return_val_if_fail (discovery_data != NULL, NULL); g_object_ref (interface_address); infos = g_new0 (ArvGvInterfaceDeviceInfos, 1); memcpy (infos->discovery_data, discovery_data, ARV_GVBS_DISCOVERY_DATA_SIZE); infos->vendor = g_strndup ((char *) &infos->discovery_data[ARV_GVBS_MANUFACTURER_NAME_OFFSET], ARV_GVBS_MANUFACTURER_NAME_SIZE); infos->manufacturer_info = g_strndup ((char *) &infos->discovery_data[ARV_GVBS_MANUFACTURER_INFO_OFFSET], ARV_GVBS_MANUFACTURER_INFO_SIZE); infos->model = g_strndup ((char *) &infos->discovery_data[ARV_GVBS_MODEL_NAME_OFFSET], ARV_GVBS_MODEL_NAME_SIZE); infos->serial = g_strndup ((char *) &infos->discovery_data[ARV_GVBS_SERIAL_NUMBER_OFFSET], ARV_GVBS_SERIAL_NUMBER_SIZE); infos->user_id = g_strndup ((char *) &infos->discovery_data[ARV_GVBS_USER_DEFINED_NAME_OFFSET], ARV_GVBS_USER_DEFINED_NAME_SIZE); infos->mac = g_strdup_printf ("%02x:%02x:%02x:%02x:%02x:%02x", infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 2], infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 3], infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 4], infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 5], infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 6], infos->discovery_data[ARV_GVBS_DEVICE_MAC_ADDRESS_HIGH_OFFSET + 7]); /* Some devices return a zero length string as the serial identifier. * Use the MAC address as the serial number in this case */ if (infos->serial == NULL || infos->serial[0] == '\0') { g_free (infos->serial); infos->serial = g_strdup (infos->mac); } infos->id = g_strdup_printf ("%s-%s-%s", infos->vendor, infos->model, infos->serial); arv_str_strip (infos->id, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); infos->vendor_alias_serial = g_strdup_printf ("%s-%s", arv_vendor_alias_lookup (infos->vendor), infos->serial); arv_str_strip (infos->vendor_alias_serial, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); infos->vendor_serial = g_strdup_printf ("%s-%s", infos->vendor, infos->serial); arv_str_strip (infos->vendor_serial, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); infos->interface_address = interface_address; infos->ref_count = 1; return infos; } static ArvGvInterfaceDeviceInfos * arv_gv_interface_device_infos_ref (ArvGvInterfaceDeviceInfos *infos) { g_return_val_if_fail (infos != NULL, NULL); g_return_val_if_fail (g_atomic_int_get (&infos->ref_count) > 0, NULL); g_atomic_int_inc (&infos->ref_count); return infos; } static void arv_gv_interface_device_infos_unref (ArvGvInterfaceDeviceInfos *infos) { g_return_if_fail (infos != NULL); g_return_if_fail (g_atomic_int_get (&infos->ref_count) > 0); if (g_atomic_int_dec_and_test (&infos->ref_count)) { g_object_unref (infos->interface_address); g_free (infos->id); g_free (infos->user_id); g_free (infos->vendor_serial); g_free (infos->vendor_alias_serial); g_free (infos->vendor); g_free (infos->manufacturer_info); g_free (infos->model); g_free (infos->serial); g_free (infos->mac); g_free (infos); } } /* ArvGvInterface implementation */ typedef struct { GHashTable *devices; GMutex mutex; char *discovery_interface; } ArvGvInterfacePrivate; struct _ArvGvInterface { ArvInterface interface; ArvGvInterfacePrivate *priv; }; struct _ArvGvInterfaceClass { ArvInterfaceClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvGvInterface, arv_gv_interface, ARV_TYPE_INTERFACE, G_ADD_PRIVATE (ArvGvInterface)) static ArvGvInterfaceDeviceInfos * _discover (GHashTable *devices, const char *device_id, gboolean allow_broadcast_discovery_ack, const char *discovery_interface) { ArvGvDiscoverSocketList *socket_list; GSList *iter; char buffer[ARV_GV_INTERFACE_SOCKET_BUFFER_SIZE]; int count; int i; g_assert (devices == NULL || device_id == NULL); if (devices != NULL) g_hash_table_remove_all (devices); socket_list = arv_gv_discover_socket_list_new (discovery_interface); if (socket_list->n_sockets < 1) { arv_gv_discover_socket_list_free (socket_list); return NULL; } arv_gv_discover_socket_list_send_discover_packet (socket_list, allow_broadcast_discovery_ack); do { gint res; res = g_poll (socket_list->poll_fds, socket_list->n_sockets, ARV_GV_INTERFACE_DISCOVERY_TIMEOUT_MS); if (res <= 0) { arv_gv_discover_socket_list_free (socket_list); /* Timeout case */ if (res == 0) return NULL; g_critical ("g_poll returned %d (call was interrupted)", res); return NULL; } for (i = 0, iter = socket_list->sockets; iter != NULL; i++, iter = iter->next) { ArvGvDiscoverSocket *discover_socket = iter->data; arv_gpollfd_clear_one (&socket_list->poll_fds[i], discover_socket->socket); do { g_socket_set_blocking (discover_socket->socket, FALSE); count = g_socket_receive (discover_socket->socket, buffer, ARV_GV_INTERFACE_SOCKET_BUFFER_SIZE, NULL, NULL); g_socket_set_blocking (discover_socket->socket, TRUE); if (count > 0) { ArvGvcpPacket *packet = (ArvGvcpPacket *) buffer; if (g_ntohs (packet->header.command) == ARV_GVCP_COMMAND_DISCOVERY_ACK && g_ntohs (packet->header.id) == 0xffff) { ArvGvInterfaceDeviceInfos *device_infos; GInetAddress *interface_address; char *address_string; char *data = buffer + sizeof (ArvGvcpHeader); arv_gvcp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); interface_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (discover_socket->interface_address)); device_infos = arv_gv_interface_device_infos_new (interface_address, data); address_string = g_inet_address_to_string (interface_address); arv_info_interface ("[GvInterface::discovery] Device '%s' found " "(interface %s) user_id '%s' - MAC '%s'", device_infos->id, address_string, device_infos->user_id, device_infos->mac); g_free (address_string); if (devices != NULL) { if (device_infos->id != NULL && device_infos->id[0] != '\0') g_hash_table_replace (devices, device_infos->id, arv_gv_interface_device_infos_ref (device_infos)); if (device_infos->user_id != NULL && device_infos->user_id[0] != '\0') g_hash_table_replace (devices, device_infos->user_id, arv_gv_interface_device_infos_ref (device_infos)); if (device_infos->vendor_serial != NULL && device_infos->vendor_serial[0] != '\0') g_hash_table_replace (devices, device_infos->vendor_serial, arv_gv_interface_device_infos_ref (device_infos)); if (device_infos->vendor_alias_serial != NULL && device_infos->vendor_alias_serial[0] != '\0') g_hash_table_replace (devices, device_infos->vendor_alias_serial, arv_gv_interface_device_infos_ref (device_infos)); g_hash_table_replace (devices, device_infos->mac, arv_gv_interface_device_infos_ref (device_infos)); } else { if (device_id == NULL || g_strcmp0 (device_infos->id, device_id) == 0 || g_strcmp0 (device_infos->user_id, device_id) == 0 || g_strcmp0 (device_infos->vendor_serial, device_id) == 0 || g_strcmp0 (device_infos->vendor_alias_serial, device_id) == 0 || g_strcmp0 (device_infos->mac, device_id) == 0) { arv_gv_discover_socket_list_free (socket_list); return device_infos; } } arv_gv_interface_device_infos_unref (device_infos); } } } while (count > 0); } } while (1); } static void arv_gv_interface_discover (ArvGvInterface *gv_interface) { int flags = arv_interface_get_flags (ARV_INTERFACE(gv_interface)); char *discovery_interface; discovery_interface = arv_gv_interface_dup_discovery_interface_name(); _discover (gv_interface->priv->devices, NULL, flags & ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK, discovery_interface); g_free (discovery_interface); } static GInetAddress * _device_infos_to_ginetaddress (ArvGvInterfaceDeviceInfos *device_infos) { GInetAddress *device_address; device_address = g_inet_address_new_from_bytes (&device_infos->discovery_data[ARV_GVBS_CURRENT_IP_ADDRESS_OFFSET], G_SOCKET_FAMILY_IPV4); return device_address; } static void arv_gv_interface_update_device_list (ArvInterface *interface, GArray *device_ids) { ArvGvInterface *gv_interface; GHashTableIter iter; gpointer key, value; g_assert (device_ids->len == 0); gv_interface = ARV_GV_INTERFACE (interface); arv_gv_interface_discover (gv_interface); g_hash_table_iter_init (&iter, gv_interface->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { ArvGvInterfaceDeviceInfos *infos = value; if (g_strcmp0 (key, infos->id) == 0) { ArvInterfaceDeviceIds *ids; GInetAddress *device_address; device_address = _device_infos_to_ginetaddress (infos); ids = g_new0 (ArvInterfaceDeviceIds, 1); ids->device = g_strdup (key); ids->physical = g_strdup (infos->mac); ids->address = g_inet_address_to_string (device_address); ids->vendor = g_strdup (infos->vendor); ids->manufacturer_info = g_strdup (infos->manufacturer_info); ids->model = g_strdup (infos->model); ids->serial_nbr = g_strdup (infos->serial); g_array_append_val (device_ids, ids); g_object_unref (device_address); } } } static GInetAddress * arv_gv_interface_camera_locate (ArvGvInterface *gv_interface, GInetAddress *device_address) { ArvGvDiscoverSocketList *socket_list; ArvGvcpPacket *packet; char buffer[ARV_GV_INTERFACE_SOCKET_BUFFER_SIZE]; GSList *iter; GSocketAddress *device_socket_address; size_t size; int i, count; GList *ifaces; GList *iface_iter; struct sockaddr_in device_sockaddr; char *discovery_interface; device_socket_address = g_inet_socket_address_new(device_address, ARV_GVCP_PORT); ifaces = arv_enumerate_network_interfaces (); if (ifaces) { g_socket_address_to_native(device_socket_address, &device_sockaddr, sizeof(device_sockaddr), NULL); for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { struct sockaddr_in *sa = (struct sockaddr_in*)arv_network_interface_get_addr(iface_iter->data); struct sockaddr_in *mask = (struct sockaddr_in*)arv_network_interface_get_netmask(iface_iter->data); if ((sa->sin_addr.s_addr & mask->sin_addr.s_addr) == (device_sockaddr.sin_addr.s_addr & mask->sin_addr.s_addr)) { GSocketAddress *socket_address = g_socket_address_new_from_native (arv_network_interface_get_addr(iface_iter->data), sizeof(struct sockaddr)); GInetAddress *inet_address = g_object_ref(g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS(socket_address))); g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); g_object_unref(socket_address); g_object_unref(device_socket_address); return inet_address; } } g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); } discovery_interface = arv_gv_interface_dup_discovery_interface_name(); socket_list = arv_gv_discover_socket_list_new (discovery_interface); g_free (discovery_interface); if (socket_list->n_sockets < 1) { arv_gv_discover_socket_list_free (socket_list); return NULL; } /* Just read a random register from the camera socket */ packet = arv_gvcp_packet_new_read_register_cmd (ARV_GVBS_N_STREAM_CHANNELS_OFFSET, 0, &size); for (iter = socket_list->sockets; iter != NULL; iter = iter->next) { ArvGvDiscoverSocket *socket = iter->data; GError *error = NULL; g_socket_send_to (socket->socket, device_socket_address, (const char *) packet, size, NULL, &error); if (error != NULL) { arv_warning_interface ("[ArvGVInterface::arv_gv_interface_camera_locate] Error: %s", error->message); g_error_free (error); } } g_object_unref(device_socket_address); arv_gvcp_packet_free (packet); do { /* Now parse the result */ if (g_poll (socket_list->poll_fds, socket_list->n_sockets, ARV_GV_INTERFACE_DISCOVERY_TIMEOUT_MS) == 0) { arv_gv_discover_socket_list_free (socket_list); return NULL; } for (i = 0, iter = socket_list->sockets; iter != NULL; i++, iter = iter->next) { ArvGvDiscoverSocket *socket = iter->data; arv_gpollfd_clear_one (&socket_list->poll_fds[i], socket->socket); do { g_socket_set_blocking (socket->socket, FALSE); count = g_socket_receive (socket->socket, buffer, ARV_GV_INTERFACE_SOCKET_BUFFER_SIZE, NULL, NULL); g_socket_set_blocking (socket->socket, TRUE); if (count > 0) { ArvGvcpPacket *packet = (ArvGvcpPacket *) buffer; if (g_ntohs (packet->header.command) == ARV_GVCP_COMMAND_READ_REGISTER_CMD || g_ntohs (packet->header.command) == ARV_GVCP_COMMAND_READ_REGISTER_ACK) { GInetAddress *interface_address = g_inet_socket_address_get_address( G_INET_SOCKET_ADDRESS (socket->interface_address)); g_object_ref(interface_address); arv_gv_discover_socket_list_free (socket_list); return interface_address; } } } while (count > 0); } } while (1); arv_gv_discover_socket_list_free (socket_list); return NULL; } static ArvDevice * _open_device (ArvInterface *interface, GHashTable *devices, const char *device_id, GError **error) { ArvGvInterface *gv_interface; ArvDevice *device = NULL; ArvGvInterfaceDeviceInfos *device_infos; GInetAddress *device_address; gv_interface = ARV_GV_INTERFACE (interface); if (device_id == NULL) { GList *device_list; device_list = g_hash_table_get_values (devices); device_infos = device_list != NULL ? device_list->data : NULL; g_list_free (device_list); } else device_infos = g_hash_table_lookup (devices, device_id); if (device_infos == NULL) { struct addrinfo hints; struct addrinfo *servinfo, *endpoint; if (device_id == NULL) return NULL; /* Try if device_id is a hostname/IP address */ memset(&hints, 0, sizeof (hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; if (getaddrinfo(device_id, "3956", &hints, &servinfo) != 0) { return NULL; } for (endpoint=servinfo; endpoint!=NULL; endpoint=endpoint->ai_next) { char ipstr[INET_ADDRSTRLEN]; struct sockaddr_in *ip = (struct sockaddr_in *) endpoint->ai_addr; inet_ntop (endpoint->ai_family, &ip->sin_addr, ipstr, sizeof (ipstr)); device_address = g_inet_address_new_from_string (ipstr); if (device_address != NULL) { /* Try and find an interface that the camera will respond on */ GInetAddress *interface_address = arv_gv_interface_camera_locate (gv_interface, device_address); if (interface_address != NULL) { device = arv_gv_device_new (interface_address, device_address, NULL); g_object_unref (interface_address); } } g_object_unref (device_address); if (device != NULL) { break; } } freeaddrinfo (servinfo); if (device == NULL) g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "Can't connect to device at address '%s'", device_id); return device; } device_address = _device_infos_to_ginetaddress (device_infos); device = arv_gv_device_new (device_infos->interface_address, device_address, error); g_object_unref (device_address); return device; } static ArvDevice * arv_gv_interface_open_device (ArvInterface *interface, const char *device_id, GError **error) { ArvDevice *device; ArvGvInterfaceDeviceInfos *device_infos; char *discovery_interface; GError *local_error = NULL; int flags; device = _open_device (interface, ARV_GV_INTERFACE (interface)->priv->devices, device_id, &local_error); if (ARV_IS_DEVICE (device) || local_error != NULL) { if (local_error != NULL) g_propagate_error (error, local_error); return device; } flags = arv_interface_get_flags (interface); discovery_interface = arv_gv_interface_dup_discovery_interface_name(); device_infos = _discover (NULL, device_id, flags & ARV_GVCP_DISCOVERY_PACKET_FLAGS_ALLOW_BROADCAST_ACK, discovery_interface); g_free (discovery_interface); if (device_infos != NULL) { GInetAddress *device_address; device_address = _device_infos_to_ginetaddress (device_infos); device = arv_gv_device_new (device_infos->interface_address, device_address, error); g_object_unref (device_address); arv_gv_interface_device_infos_unref (device_infos); return device; } return NULL; } static ArvInterface *arv_gv_interface = NULL; static GMutex arv_gv_interface_mutex; static ArvInterface * _get_instance (void) { if (arv_gv_interface == NULL) arv_gv_interface = g_object_new (ARV_TYPE_GV_INTERFACE, NULL); return ARV_INTERFACE (arv_gv_interface); } /* * arv_gv_interface_set_discovery_interface_name: * @discovery_interface: (nullable): name of the discovery network interface * * Set the name of discovery network interface. If discovery_interface is %NULL, a discovery will be performed on every * interfaces, which is the default behaviour. * * A call to [func@Aravis.update_device_list] may be necessary after the discovery interface has changed, in order to * forget the previously discovered devices. * * Since: 0.8.34 */ void arv_gv_interface_set_discovery_interface_name (const char *discovery_interface) { ArvInterface *interface; g_mutex_lock (&arv_gv_interface_mutex); interface = _get_instance(); if (interface != NULL) { ArvGvInterfacePrivate *priv = ARV_GV_INTERFACE (interface)->priv; g_mutex_lock (&priv->mutex); g_clear_pointer (&priv->discovery_interface, g_free); priv->discovery_interface = g_strdup (discovery_interface); g_mutex_unlock (&priv->mutex); } g_mutex_unlock (&arv_gv_interface_mutex); } /* * arv_gv_interface_dup_discovery_interface_name: * * Returns: the name of the interface used for device discovery, %NULL if discovery is performed on all the available * interfaces. * * Since: 0.8.34 */ char * arv_gv_interface_dup_discovery_interface_name (void) { ArvInterface *interface; char *discovery_interface = NULL; g_mutex_lock (&arv_gv_interface_mutex); interface = _get_instance(); if (interface != NULL) { ArvGvInterfacePrivate *priv = ARV_GV_INTERFACE (interface)->priv; g_mutex_lock (&priv->mutex); discovery_interface = g_strdup (priv->discovery_interface); g_mutex_unlock (&priv->mutex); } g_mutex_unlock (&arv_gv_interface_mutex); return discovery_interface; } /** * arv_gv_interface_get_instance: * * Gets the unique instance of the GV interface. * * Returns: (transfer none): a #ArvInterface singleton. */ ArvInterface * arv_gv_interface_get_instance (void) { ArvInterface *gv_interface; g_mutex_lock (&arv_gv_interface_mutex); gv_interface = _get_instance(); g_mutex_unlock (&arv_gv_interface_mutex); return gv_interface; } void arv_gv_interface_destroy_instance (void) { g_mutex_lock (&arv_gv_interface_mutex); if (arv_gv_interface != NULL) { g_object_unref (arv_gv_interface); arv_gv_interface = NULL; } g_mutex_unlock (&arv_gv_interface_mutex); } static void arv_gv_interface_init (ArvGvInterface *gv_interface) { gv_interface->priv = arv_gv_interface_get_instance_private (gv_interface); gv_interface->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) arv_gv_interface_device_infos_unref); g_mutex_init(&gv_interface->priv->mutex); gv_interface->priv->discovery_interface = NULL; } static void arv_gv_interface_finalize (GObject *object) { ArvGvInterface *gv_interface = ARV_GV_INTERFACE (object); g_hash_table_unref (gv_interface->priv->devices); gv_interface->priv->devices = NULL; g_clear_pointer (&gv_interface->priv->discovery_interface, g_free); g_mutex_clear (&gv_interface->priv->mutex); G_OBJECT_CLASS (arv_gv_interface_parent_class)->finalize (object); } static void arv_gv_interface_class_init (ArvGvInterfaceClass *gv_interface_class) { GObjectClass *object_class = G_OBJECT_CLASS (gv_interface_class); ArvInterfaceClass *interface_class = ARV_INTERFACE_CLASS (gv_interface_class); object_class->finalize = arv_gv_interface_finalize; interface_class->update_device_list = arv_gv_interface_update_device_list; interface_class->open_device = arv_gv_interface_open_device; interface_class->protocol = "GigEVision"; } aravis-0.8.34/src/arvgvinterface.h000066400000000000000000000035451475431451200170560ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GV_INTERFACE_H #define ARV_GV_INTERFACE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS /** * ArvGvInterfaceFlags: * @ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK: allow gv devices to broadcast the discovery acknowledge packet * * Since: 0.8.23 */ typedef enum { ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK = 1 << 0 } ArvGvInterfaceFlags; #define ARV_TYPE_GV_INTERFACE (arv_gv_interface_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGvInterface, arv_gv_interface, ARV, GV_INTERFACE, ArvInterface) ARV_API ArvInterface * arv_gv_interface_get_instance (void); ARV_API void arv_gv_interface_set_discovery_interface_name (const char *discovery_interface); ARV_API char * arv_gv_interface_dup_discovery_interface_name (void); G_END_DECLS #endif aravis-0.8.34/src/arvgvinterfaceprivate.h000066400000000000000000000025561475431451200204520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GV_INTERFACE_PRIVATE_H #define ARV_GV_INTERFACE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_GV_INTERFACE_DISCOVERY_TIMEOUT_MS 1000 #define ARV_GV_INTERFACE_SOCKET_BUFFER_SIZE 1024 #define ARV_GV_INTERFACE_DISCOVERY_SOCKET_BUFFER_SIZE (256*1024) void arv_gv_interface_destroy_instance (void); G_END_DECLS #endif aravis-0.8.34/src/arvgvsp.c000066400000000000000000000256411475431451200155340ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /* * SECTION: arvgvsp * @short_description: GigEVision stream packet handling */ #include #include #include #include #include #include #include static ArvGvspPacket * arv_gvsp_packet_new (ArvGvspContentType content_type, guint16 frame_id, guint32 packet_id, size_t data_size, void *buffer, size_t *buffer_size) { ArvGvspPacket *packet; ArvGvspHeader *header; size_t packet_size; packet_size = sizeof (ArvGvspPacket) + sizeof (ArvGvspHeader) + data_size; if (packet_size == 0 || (buffer != NULL && (buffer_size == NULL || packet_size > *buffer_size))) return NULL; if (buffer_size != NULL) *buffer_size = packet_size; if (buffer != NULL) packet = buffer; else packet = g_malloc (packet_size); packet->packet_type = 0; header = (void *) &packet->header; header->frame_id = g_htons (frame_id); header->packet_infos = g_htonl ((packet_id & ARV_GVSP_PACKET_ID_MASK) | ((content_type << ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_POS) & ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_MASK)); return packet; } ArvGvspPacket * arv_gvsp_packet_new_image_leader (guint16 frame_id, guint32 packet_id, guint64 timestamp, ArvPixelFormat pixel_format, guint32 width, guint32 height, guint32 x_offset, guint32 y_offset, guint32 x_padding, guint32 y_padding, void *buffer, size_t *buffer_size) { ArvGvspPacket *packet; packet = arv_gvsp_packet_new (ARV_GVSP_CONTENT_TYPE_LEADER, frame_id, packet_id, sizeof (ArvGvspImageLeader), buffer, buffer_size); if (packet != NULL) { ArvGvspImageLeader *leader; leader = arv_gvsp_packet_get_data (packet); leader->flags = 0; leader->payload_type = g_htons (ARV_BUFFER_PAYLOAD_TYPE_IMAGE); leader->timestamp_high = g_htonl (((guint64) timestamp >> 32)); leader->timestamp_low = g_htonl ((guint64) timestamp & 0xffffffff); leader->infos.pixel_format = g_htonl (pixel_format); leader->infos.width = g_htonl (width); leader->infos.height = g_htonl (height); leader->infos.x_offset = g_htonl (x_offset); leader->infos.y_offset = g_htonl (y_offset); leader->infos.x_padding = g_htonl (x_padding); leader->infos.y_padding = g_htonl (y_padding); } return packet; } ArvGvspPacket * arv_gvsp_packet_new_data_trailer (guint16 frame_id, guint32 packet_id, void *buffer, size_t *buffer_size) { ArvGvspPacket *packet; packet = arv_gvsp_packet_new (ARV_GVSP_CONTENT_TYPE_TRAILER, frame_id, packet_id, sizeof (ArvGvspTrailer), buffer, buffer_size); if (packet != NULL) { ArvGvspTrailer *trailer; trailer = arv_gvsp_packet_get_data (packet); trailer->payload_type = g_htonl (ARV_BUFFER_PAYLOAD_TYPE_IMAGE); trailer->data0 = 0; } return packet; } ArvGvspPacket * arv_gvsp_packet_new_payload (guint16 frame_id, guint32 packet_id, size_t size, void *data, void *buffer, size_t *buffer_size) { ArvGvspPacket *packet; packet = arv_gvsp_packet_new (ARV_GVSP_CONTENT_TYPE_PAYLOAD, frame_id, packet_id, size, buffer, buffer_size); if (packet != NULL) memcpy (arv_gvsp_packet_get_data (packet), data, size); return packet; } static const char * arv_enum_to_string (GType type, guint enum_value) { GEnumClass *enum_class; GEnumValue *value; const char *retval = NULL; enum_class = g_type_class_ref (type); value = g_enum_get_value (enum_class, enum_value); if (value) retval = value->value_nick; g_type_class_unref (enum_class); return retval; } static const char * arv_gvsp_packet_type_to_string (ArvGvspPacketType value) { return arv_enum_to_string (ARV_TYPE_GVSP_PACKET_TYPE, value); } static const char * arv_gvsp_content_type_to_string (ArvGvspContentType value) { return arv_enum_to_string (ARV_TYPE_GVSP_CONTENT_TYPE, value); } char * arv_gvsp_packet_to_string (const ArvGvspPacket *packet, size_t packet_size) { ArvGvspPacketType packet_type; ArvBufferPayloadType payload_type; ArvGvspContentType content_type; guint part_id; ptrdiff_t offset; GString *string; string = g_string_new (""); packet_type = arv_gvsp_packet_get_packet_type (packet); content_type = arv_gvsp_packet_get_content_type (packet); g_string_append_printf (string, "packet_type = %8s (0x%04x)\n", arv_gvsp_packet_type_to_string (packet_type), packet_type); g_string_append_printf (string, "content_type = %8s (0x%04x)\n", arv_gvsp_content_type_to_string (content_type), content_type); g_string_append_printf (string, "frame_id = %8" G_GUINT64_FORMAT " %s\n", arv_gvsp_packet_get_frame_id (packet), arv_gvsp_packet_has_extended_ids (packet) ? " extended" : ""); g_string_append_printf (string, "packet_id = %8u\n", arv_gvsp_packet_get_packet_id (packet)); g_string_append_printf (string, "data_size = %8" G_GSIZE_FORMAT "\n", arv_gvsp_packet_get_data_size (packet, packet_size)); switch (content_type) { case ARV_GVSP_CONTENT_TYPE_LEADER: payload_type = arv_gvsp_leader_packet_get_buffer_payload_type (packet, NULL); switch (payload_type) { case ARV_BUFFER_PAYLOAD_TYPE_IMAGE: g_string_append (string, "payload_type = image\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA: g_string_append (string, "payload_type = chunk\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA: g_string_append (string, "payload_type = extended chunk\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_H264: g_string_append (string, "payload_type = h264\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_MULTIPART: g_string_append (string, "payload_type = multipart\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_JPEG: g_string_append (string, "payload_type = jpeg\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_JPEG2000: g_string_append (string, "payload_type = jpeg2000\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_RAWDATA: g_string_append (string, "payload_type = raw data\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_FILE: g_string_append (string, "payload_type = file\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE: g_string_append (string, "payload_type = multizone image\n"); break; default: g_string_append_printf (string, "payload_type = unknown (0x%08x)\n", payload_type); break; } if (payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA) { ArvPixelFormat pixel_format; guint32 width, height, x_offset, y_offset, x_padding, y_padding; if (arv_gvsp_leader_packet_get_image_infos (packet, &pixel_format, &width, &height, &x_offset, &y_offset, &x_padding, &y_padding)) { g_string_append_printf (string, "pixel format = %s\n", arv_pixel_format_to_gst_caps_string (pixel_format)); g_string_append_printf (string, "width = %8d\n", width); g_string_append_printf (string, "height = %8d\n", height); g_string_append_printf (string, "x_offset = %8d\n", x_offset); g_string_append_printf (string, "y_offset = %8d\n", y_offset); g_string_append_printf (string, "x_padding = %8d\n", x_padding); g_string_append_printf (string, "y_padding = %8d\n", y_padding); } } else if (payload_type == ARV_BUFFER_PAYLOAD_TYPE_MULTIPART) { g_string_append_printf (string, "n_parts = %8u\n", arv_gvsp_leader_packet_get_multipart_n_parts (packet)); } break; case ARV_GVSP_CONTENT_TYPE_TRAILER: break; case ARV_GVSP_CONTENT_TYPE_PAYLOAD: break; case ARV_GVSP_CONTENT_TYPE_H264: break; case ARV_GVSP_CONTENT_TYPE_MULTIZONE: break; case ARV_GVSP_CONTENT_TYPE_MULTIPART: if (arv_gvsp_multipart_packet_get_infos (packet, &part_id, &offset)) { g_string_append_printf (string, "part_id = %8d\n", part_id); g_string_append_printf (string, "offset = %8zu\n", offset); } break; case ARV_GVSP_CONTENT_TYPE_GENDC: break; case ARV_GVSP_CONTENT_TYPE_ALL_IN: break; } return arv_g_string_free_and_steal(string); } void arv_gvsp_packet_debug (const ArvGvspPacket *packet, size_t packet_size, ArvDebugLevel level) { char *string; if (!arv_debug_check (ARV_DEBUG_CATEGORY_SP, level)) return; string = arv_gvsp_packet_to_string (packet, packet_size); switch (level) { case ARV_DEBUG_LEVEL_TRACE: arv_trace_sp ("%s", string); break; case ARV_DEBUG_LEVEL_DEBUG: arv_debug_sp ("%s", string); break; case ARV_DEBUG_LEVEL_INFO: arv_info_sp ("%s", string); break; case ARV_DEBUG_LEVEL_WARNING: arv_warning_sp ("%s", string); break; default: break; } g_free (string); } aravis-0.8.34/src/arvgvspprivate.h000066400000000000000000000511611475431451200171300ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GVSP_PRIVATE_H #define ARV_GVSP_PRIVATE_H #include #include #include G_BEGIN_DECLS #define ARV_GVSP_PACKET_EXTENDED_ID_MODE_MASK 0x80 #define ARV_GVSP_PACKET_ID_MASK 0x00ffffff #define ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_MASK 0x7f000000 #define ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_POS 24 #define ARV_GVSP_PACKET_INFOS_N_PARTS_MASK 0x000000ff /** * ArvGvspPacketType: * @ARV_GVSP_PACKET_TYPE_OK: valid packet * @ARV_GVSP_PACKET_TYPE_RESEND: resent packet (BlackFly PointGrey camera support) * @ARV_GVSP_PACKET_TYPE_PACKET_UNAVAILABLE: error packet, indicating invalid resend request */ typedef enum { ARV_GVSP_PACKET_TYPE_OK = 0x0000, ARV_GVSP_PACKET_TYPE_RESEND = 0x0100, ARV_GVSP_PACKET_TYPE_PACKET_UNAVAILABLE = 0x800c } ArvGvspPacketType; /** * ArvGvspContentType: * @ARV_GVSP_CONTENT_TYPE_LEADER: leader packet * @ARV_GVSP_CONTENT_TYPE_TRAILER: trailer packet * @ARV_GVSP_CONTENT_TYPE_PAYLOAD: data packet * @ARV_GVSP_CONTENT_TYPE_ALL_IN: leader + data + trailer packet * @ARV_GVSP_CONTENT_TYPE_H264: h264 data packet * @ARV_GVSP_CONTENT_TYPE_MULTIZONE: multizone data packet * @ARV_GVSP_CONTENT_TYPE_MULTIPART: multipart data packet * @ARV_GVSP_CONTENT_TYPE_GENDC: GenDC data packet */ typedef enum { ARV_GVSP_CONTENT_TYPE_LEADER = 0x01, ARV_GVSP_CONTENT_TYPE_TRAILER = 0x02, ARV_GVSP_CONTENT_TYPE_PAYLOAD = 0x03, ARV_GVSP_CONTENT_TYPE_ALL_IN = 0x04, ARV_GVSP_CONTENT_TYPE_H264 = 0x05, ARV_GVSP_CONTENT_TYPE_MULTIZONE = 0x06, ARV_GVSP_CONTENT_TYPE_MULTIPART = 0x07, ARV_GVSP_CONTENT_TYPE_GENDC = 0x08 } ArvGvspContentType; #pragma pack(push,1) /** * ArvGvspHeader: * @frame_id: frame identifier * @packet_infos: #ArvGvspContentType and packet identifier in a 32 bit value * @data: data byte array * * GVSP packet header structure. */ typedef struct { guint16 frame_id; guint32 packet_infos; guint8 data[]; } ArvGvspHeader; typedef struct { guint16 flags; guint32 packet_infos; guint64 frame_id; guint32 packet_id; guint8 data[]; } ArvGvspExtendedHeader; /** * ArvGvspLeader: * @flags: generic flags * @payload_type: ID of the payload type */ typedef struct { guint16 flags; guint16 payload_type; guint32 timestamp_high; guint32 timestamp_low; } ArvGvspLeader; /** * ArvGvspImageInfos: * @pixel_format: a #ArvPixelFormat identifier * @width: frame width, in pixels * @height: frame height, in pixels * @x_offset: frame x offset, in pixels * @y_offset: frame y offset, in pixels */ typedef struct { guint32 pixel_format; guint32 width; guint32 height; guint32 x_offset; guint32 y_offset; guint16 x_padding; guint16 y_padding; } ArvGvspImageInfos; /** * ArvGvspImageLeader: * @flags: generic flags * @payload_type: ID of the payload type * @timestamp_high: most significant bits of frame timestamp * @timestamp_low: least significant bits of frame timestamp_low * @infos: image infos */ typedef struct { guint16 flags; guint16 payload_type; guint32 timestamp_high; guint32 timestamp_low; ArvGvspImageInfos infos; } ArvGvspImageLeader; typedef struct { guint16 data_type; guint16 part_length_high; guint32 part_length_low; guint32 pixel_format; guint16 reserved_0; guint8 source_id; guint8 additional_zones; guint32 zone_directions; guint16 data_purpose_id; guint16 region_id; guint32 width; guint32 height; guint32 x_offset; guint32 y_offset; guint16 x_padding; guint16 y_padding; guint32 reserved_1; } ArvGvspPartInfos; /* 48 bytes */ typedef struct { guint16 flags; guint16 payload_type; guint32 timestamp_high; guint32 timestamp_low; ArvGvspPartInfos parts[]; } ArvGvspMultipartLeader; typedef struct { guint8 part_id; guint8 zone_info; guint16 offset_high; guint32 offset_low; } ArvGvspMultipart; /** * ArvGvspTrailer: * @payload_type: ID of the payload type * @data0: unused * * GVSP data trailer packet data area. */ typedef struct { guint32 payload_type; guint32 data0; } ArvGvspTrailer; /** * ArvGvspPacket: * @packet_type: packet type, also known as status in wireshark dissector * @header: common GVSP packet header * * GVSP packet structure. */ typedef struct { guint16 packet_type; guint8 header[]; } ArvGvspPacket; /* Minimum ethernet frame size minus ethernet protocol overhead */ #define ARV_GVSP_MINIMUM_PACKET_SIZE (64 - 14 - 4) /* Maximum ethernet frame size minus ethernet protocol overhead */ #define ARV_GVSP_MAXIMUM_PACKET_SIZE (65536 - 14 - 4) /* IP + UDP */ #define ARV_GVSP_PACKET_UDP_OVERHEAD (20 + 8) /* IP + UDP + GVSP headers or IP + UDP + GVSP extended headers */ #define ARV_GVSP_PACKET_PROTOCOL_OVERHEAD(ext_ids) ((ext_ids) ? \ 20 + 8 + \ sizeof (ArvGvspPacket) + \ sizeof (ArvGvspExtendedHeader) : \ 20 + 8 + \ sizeof (ArvGvspPacket) + \ sizeof (ArvGvspHeader)) #define ARV_GVSP_PAYLOAD_PACKET_PROTOCOL_OVERHEAD(ext_ids) ARV_GVSP_PACKET_PROTOCOL_OVERHEAD(ext_ids) #define ARV_GVSP_MULTIPART_PACKET_PROTOCOL_OVERHEAD(ext_ids) ((ext_ids) ? \ 20 + 8 + \ sizeof (ArvGvspPacket) + \ sizeof (ArvGvspExtendedHeader) + \ sizeof (ArvGvspMultipart) : \ 20 + 8 + \ sizeof (ArvGvspPacket) + \ sizeof (ArvGvspHeader) + \ sizeof (ArvGvspMultipart)) #pragma pack(pop) ArvGvspPacket * arv_gvsp_packet_new_image_leader (guint16 frame_id, guint32 packet_id, guint64 timestamp, ArvPixelFormat pixel_format, guint32 width, guint32 height, guint32 x_offset, guint32 y_offset, guint32 x_padding, guint32 y_padding, void *buffer, size_t *buffer_size); ArvGvspPacket * arv_gvsp_packet_new_data_trailer (guint16 frame_id, guint32 packet_id, void *buffer, size_t *buffer_size); ArvGvspPacket * arv_gvsp_packet_new_payload (guint16 frame_id, guint32 packet_id, size_t size, void *data, void *buffer, size_t *buffer_size); char * arv_gvsp_packet_to_string (const ArvGvspPacket *packet, size_t packet_size); void arv_gvsp_packet_debug (const ArvGvspPacket *packet, size_t packet_size, ArvDebugLevel level); static inline ArvGvspPacketType arv_gvsp_packet_get_packet_type (const ArvGvspPacket *packet) { return (ArvGvspPacketType) g_ntohs (packet->packet_type); } static inline gboolean arv_gvsp_packet_type_is_error (const ArvGvspPacketType packet_type) { return (packet_type & 0x8000) != 0; } static inline gboolean arv_gvsp_packet_has_extended_ids (const ArvGvspPacket *packet) { return (packet->header[2] & ARV_GVSP_PACKET_EXTENDED_ID_MODE_MASK) != 0; } static inline ArvGvspContentType arv_gvsp_packet_get_content_type (const ArvGvspPacket *packet) { if (arv_gvsp_packet_has_extended_ids (packet)) { ArvGvspExtendedHeader *header = (ArvGvspExtendedHeader *) &packet->header; return (ArvGvspContentType) ((g_ntohl (header->packet_infos) & ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_MASK) >> ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_POS); } else { ArvGvspHeader *header = (ArvGvspHeader *) &packet->header; return (ArvGvspContentType) ((g_ntohl (header->packet_infos) & ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_MASK) >> ARV_GVSP_PACKET_INFOS_CONTENT_TYPE_POS); } } static inline gboolean arv_gvsp_packet_is_valid (const ArvGvspPacket *packet, size_t size) { size_t header_size; g_assert_not_reached (); /* Incomplete implementation */ if (G_UNLIKELY (size < 4)) return FALSE; header_size = arv_gvsp_packet_has_extended_ids (packet) ? sizeof (ArvGvspExtendedHeader) : sizeof (ArvGvspHeader); switch (arv_gvsp_packet_get_content_type (packet)) { case ARV_GVSP_CONTENT_TYPE_LEADER: if (G_UNLIKELY (size < sizeof (ArvGvspPacket) + header_size + sizeof (ArvGvspLeader))) return FALSE; break; case ARV_GVSP_CONTENT_TYPE_TRAILER: if (G_UNLIKELY (size < sizeof (ArvGvspPacket) + header_size + sizeof (ArvGvspTrailer))) return FALSE; break; case ARV_GVSP_CONTENT_TYPE_PAYLOAD: if (G_UNLIKELY (size < sizeof (ArvGvspPacket) + header_size + sizeof (ArvGvspTrailer))) return FALSE; break; case ARV_GVSP_CONTENT_TYPE_ALL_IN: return FALSE; case ARV_GVSP_CONTENT_TYPE_H264: return FALSE; case ARV_GVSP_CONTENT_TYPE_MULTIZONE: return FALSE; case ARV_GVSP_CONTENT_TYPE_MULTIPART: return FALSE; case ARV_GVSP_CONTENT_TYPE_GENDC: return FALSE; } return FALSE; } static inline guint32 arv_gvsp_packet_get_packet_id (const ArvGvspPacket *packet) { if (arv_gvsp_packet_has_extended_ids (packet)) { ArvGvspExtendedHeader *header = (ArvGvspExtendedHeader *) &packet->header; return g_ntohl (header->packet_id); } else { ArvGvspHeader *header = (ArvGvspHeader *) &packet->header; return g_ntohl (header->packet_infos) & ARV_GVSP_PACKET_ID_MASK; } } static inline guint64 arv_gvsp_packet_get_frame_id (const ArvGvspPacket *packet) { if (arv_gvsp_packet_has_extended_ids (packet)) { ArvGvspExtendedHeader *header = (ArvGvspExtendedHeader *) &packet->header; return GUINT64_FROM_BE(header->frame_id); } else { ArvGvspHeader *header = (ArvGvspHeader *) &packet->header; return g_ntohs (header->frame_id); } } static inline void * arv_gvsp_packet_get_data (const ArvGvspPacket *packet) { if (arv_gvsp_packet_has_extended_ids (packet)) { ArvGvspExtendedHeader *header = (ArvGvspExtendedHeader *) &packet->header; return &header->data; } else { ArvGvspHeader *header = (ArvGvspHeader *) &packet->header; return &header->data; } } static inline ArvBufferPayloadType arv_gvsp_leader_packet_get_buffer_payload_type (const ArvGvspPacket *packet, gboolean *has_chunks) { if (G_LIKELY (arv_gvsp_packet_get_content_type (packet) == ARV_GVSP_CONTENT_TYPE_LEADER)) { ArvGvspLeader *leader; guint16 payload_type; leader = (ArvGvspLeader *) arv_gvsp_packet_get_data (packet); payload_type = g_ntohs (leader->payload_type); if (has_chunks != NULL) *has_chunks = ((payload_type & 0x4000) != 0 || (payload_type == ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA) || (payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA)); return (ArvBufferPayloadType) (payload_type & 0x3fff); } return ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN; } static inline guint64 arv_gvsp_leader_packet_get_timestamp (const ArvGvspPacket *packet) { if (G_LIKELY (arv_gvsp_packet_get_content_type (packet) == ARV_GVSP_CONTENT_TYPE_LEADER)) { ArvGvspLeader *leader; leader = (ArvGvspLeader *) arv_gvsp_packet_get_data (packet); return ((guint64) g_ntohl (leader->timestamp_high) << 32) | g_ntohl (leader->timestamp_low); } return 0; } static inline guint8 arv_gvsp_leader_packet_get_multipart_n_parts (const ArvGvspPacket *packet) { if (arv_gvsp_leader_packet_get_buffer_payload_type (packet, NULL) == ARV_BUFFER_PAYLOAD_TYPE_MULTIPART) { if (arv_gvsp_packet_has_extended_ids (packet)) { ArvGvspExtendedHeader *header = (ArvGvspExtendedHeader *) &packet->header; return (g_ntohl (header->packet_infos) & ARV_GVSP_PACKET_INFOS_N_PARTS_MASK); } else { ArvGvspHeader *header = (ArvGvspHeader *) &packet->header; return (g_ntohl (header->packet_infos) & ARV_GVSP_PACKET_INFOS_N_PARTS_MASK); } } return 0; } static inline gboolean arv_gvsp_leader_packet_get_multipart_infos (const ArvGvspPacket *packet, guint part_id, guint *purpose_id, ArvBufferPartDataType *data_type, size_t *size, ArvPixelFormat *pixel_format, guint32 *width, guint32 *height, guint32 *x_offset, guint32 *y_offset, guint32 *x_padding, guint32 *y_padding) { unsigned int n_parts; ArvGvspMultipartLeader *leader; ArvGvspPartInfos *infos; n_parts = arv_gvsp_leader_packet_get_multipart_n_parts (packet); if (part_id >= n_parts) return FALSE; leader = (ArvGvspMultipartLeader *) arv_gvsp_packet_get_data (packet); infos = &leader->parts[part_id]; *purpose_id = g_ntohs(infos->data_purpose_id); *data_type = (ArvBufferPartDataType) g_ntohs (infos->data_type); *size = g_ntohl (infos->part_length_low) + (((guint64) g_ntohs (infos->part_length_high)) << 32); *pixel_format = g_ntohl (infos->pixel_format); *width = g_ntohl (infos->width); *height = g_ntohl (infos->height); *x_offset = g_ntohl (infos->x_offset); *y_offset = g_ntohl (infos->y_offset); *x_padding = g_ntohs(infos->x_padding); *y_padding = g_ntohs (infos->y_padding); return TRUE; } static inline guint64 arv_gvsp_leader_packet_get_multipart_size (const ArvGvspPacket *packet, unsigned int part_id) { unsigned int n_parts; ArvGvspMultipartLeader *leader; ArvGvspPartInfos *infos; n_parts = arv_gvsp_leader_packet_get_multipart_n_parts (packet); if (part_id >= n_parts) return 0; leader = (ArvGvspMultipartLeader *) arv_gvsp_packet_get_data (packet); infos = &leader->parts[part_id]; return g_ntohl (infos->part_length_low) + (((guint64) g_ntohs (infos->part_length_high)) << 32); } static inline gboolean arv_gvsp_leader_packet_get_image_infos (const ArvGvspPacket *packet, ArvPixelFormat *pixel_format, guint32 *width, guint32 *height, guint32 *x_offset, guint32 *y_offset, guint32 *x_padding, guint32 *y_padding) { ArvBufferPayloadType payload_type; payload_type = arv_gvsp_leader_packet_get_buffer_payload_type (packet, NULL); if (payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA) { ArvGvspImageLeader *leader; leader = (ArvGvspImageLeader *) arv_gvsp_packet_get_data (packet); *pixel_format = g_ntohl (leader->infos.pixel_format); *width = g_ntohl (leader->infos.width); *height = g_ntohl (leader->infos.height); *x_offset = g_ntohl (leader->infos.x_offset); *y_offset = g_ntohl (leader->infos.y_offset); *x_padding = g_ntohs (leader->infos.x_padding); *y_padding = g_ntohs (leader->infos.y_padding); return TRUE; } return FALSE; } static inline size_t arv_gvsp_payload_packet_get_data_size (const ArvGvspPacket *packet, size_t packet_size) { if (arv_gvsp_packet_get_content_type (packet) == ARV_GVSP_CONTENT_TYPE_PAYLOAD) { if (arv_gvsp_packet_has_extended_ids (packet)) return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspExtendedHeader); else return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspHeader); } return 0; } static inline gboolean arv_gvsp_multipart_packet_get_infos (const ArvGvspPacket *packet, guint *part_id, ptrdiff_t *offset) { ArvGvspMultipart *multipart; if (arv_gvsp_packet_get_content_type (packet) != ARV_GVSP_CONTENT_TYPE_MULTIPART) { *part_id = 0; *offset = 0; return FALSE; } multipart = (ArvGvspMultipart *) arv_gvsp_packet_get_data(packet); *part_id = multipart->part_id; *offset = ( (guint64) g_ntohs(multipart->offset_high) << 32) + g_ntohl(multipart->offset_low); return TRUE; } static inline size_t arv_gvsp_multipart_packet_get_data_size (const ArvGvspPacket *packet, size_t packet_size) { if (arv_gvsp_packet_get_content_type (packet) == ARV_GVSP_CONTENT_TYPE_MULTIPART) { if (arv_gvsp_packet_has_extended_ids (packet)) return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspExtendedHeader) - sizeof (ArvGvspMultipart); else return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspHeader) - sizeof (ArvGvspMultipart); } return 0; } static inline void * arv_gvsp_multipart_packet_get_data (const ArvGvspPacket *packet) { if (arv_gvsp_packet_get_content_type (packet) == ARV_GVSP_CONTENT_TYPE_MULTIPART) { if (arv_gvsp_packet_has_extended_ids (packet)) return (char *) packet + sizeof (ArvGvspPacket) + sizeof (ArvGvspExtendedHeader) + sizeof (ArvGvspMultipart); else return (char *) packet + sizeof (ArvGvspPacket) + sizeof (ArvGvspHeader) + sizeof (ArvGvspMultipart); } return NULL; } static inline guint64 arv_gvsp_timestamp_to_ns (guint64 timestamp, guint64 timestamp_tick_frequency) { guint64 timestamp_s; guint64 timestamp_ns; if (timestamp_tick_frequency < 1) return 0; timestamp_s = timestamp / timestamp_tick_frequency; timestamp_ns = ((timestamp % timestamp_tick_frequency) * 1000000000) / timestamp_tick_frequency; timestamp_ns += timestamp_s * 1000000000; return timestamp_ns; } static inline size_t arv_gvsp_packet_get_data_size (const ArvGvspPacket *packet, size_t packet_size) { if (arv_gvsp_packet_has_extended_ids (packet)) return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspExtendedHeader); else return packet_size - sizeof (ArvGvspPacket) - sizeof (ArvGvspHeader); } G_END_DECLS #endif aravis-0.8.34/src/arvgvstream.c000066400000000000000000002141221475431451200163770ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvgvstream * @short_description: GigEVision stream */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ARAVIS_HAS_PACKET_SOCKET #include #include #include #include #include #include #include #include #include #include #endif #define ARV_GV_STREAM_DISCARD_LATE_FRAME_THRESHOLD 100 #define ARV_GV_STREAM_BUFFER_SIZE_PROTOCOL_OVERHEAD 1024 /* Some room for protocol overhead (IP + UDP + GV) */ #define ARV_GV_STREAM_MIN_BUFFER_SIZE 20 * 1024 enum { ARV_GV_STREAM_PROPERTY_0, ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER, ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER_SIZE, ARV_GV_STREAM_PROPERTY_PACKET_RESEND, ARV_GV_STREAM_PROPERTY_PACKET_REQUEST_RATIO, ARV_GV_STREAM_PROPERTY_INITIAL_PACKET_TIMEOUT, ARV_GV_STREAM_PROPERTY_PACKET_TIMEOUT, ARV_GV_STREAM_PROPERTY_FRAME_RETENTION } ArvGvStreamProperties; typedef struct _ArvGvStreamThreadData ArvGvStreamThreadData; typedef struct { ArvGvDevice *gv_device; guint stream_channel; GThread *thread; ArvGvStreamThreadData *thread_data; } ArvGvStreamPrivate; struct _ArvGvStream { ArvStream stream; }; struct _ArvGvStreamClass { ArvStreamClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvGvStream, arv_gv_stream, ARV_TYPE_STREAM, G_ADD_PRIVATE (ArvGvStream)) /* Acquisition thread */ typedef struct { gboolean received; gboolean resend_requested; guint64 abs_timeout_us; } ArvGvStreamPacketData; typedef struct { ArvBuffer *buffer; guint64 frame_id; gboolean leader_received; gsize received_size; gint32 last_valid_packet; guint64 first_packet_time_us; guint64 last_packet_time_us; gboolean disable_resend_request; guint n_packets; ArvGvStreamPacketData *packet_data; guint n_packet_resend_requests; gboolean resend_ratio_reached; gboolean extended_ids; } ArvGvStreamFrameData; struct _ArvGvStreamThreadData { GCancellable *cancellable; ArvStream *stream; gboolean thread_started; GMutex thread_started_mutex; GCond thread_started_cond; ArvStreamCallback callback; void *callback_data; GSocket *socket; GInetAddress *interface_address; GSocketAddress *interface_socket_address; GInetAddress *device_address; GSocketAddress *device_socket_address; guint16 source_stream_port; guint16 stream_port; ArvGvStreamPacketResend packet_resend; double packet_request_ratio; guint initial_packet_timeout_us; guint packet_timeout_us; guint frame_retention_us; guint64 timestamp_tick_frequency; guint scps_packet_size; guint16 packet_id; GSList *frames; gboolean first_packet; guint64 last_frame_id; gboolean use_packet_socket; /* Statistics */ guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; guint64 n_timeouts; guint64 n_aborted; guint64 n_missing_frames; guint64 n_size_mismatch_errors; guint64 n_received_packets; guint64 n_missing_packets; guint64 n_error_packets; guint64 n_ignored_packets; guint64 n_resend_requests; guint64 n_resent_packets; guint64 n_resend_ratio_reached; guint64 n_resend_disabled; guint64 n_duplicated_packets; guint64 n_transferred_bytes; guint64 n_ignored_bytes; ArvHistogram *histogram; guint32 statistic_count; ArvGvStreamSocketBuffer socket_buffer_option; int socket_buffer_size; int current_socket_buffer_size; }; static void _send_packet_request (ArvGvStreamThreadData *thread_data, guint64 frame_id, guint32 first_block, guint32 last_block, gboolean extended_ids) { ArvGvcpPacket *packet; size_t packet_size; thread_data->packet_id = arv_gvcp_next_packet_id (thread_data->packet_id); packet = arv_gvcp_packet_new_packet_resend_cmd (frame_id, first_block, last_block, extended_ids, thread_data->packet_id, &packet_size); arv_debug_stream_thread ("[GvStream::send_packet_request] frame_id = %" G_GUINT64_FORMAT " (from packet %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT ")", frame_id, first_block, last_block); arv_gvcp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); g_socket_send_to (thread_data->socket, thread_data->device_socket_address, (const char *) packet, packet_size, NULL, NULL); arv_gvcp_packet_free (packet); } static void _update_socket (ArvGvStreamThreadData *thread_data, ArvBuffer *buffer) { int buffer_size = thread_data->current_socket_buffer_size; int fd; if (thread_data->socket_buffer_option == ARV_GV_STREAM_SOCKET_BUFFER_FIXED && thread_data->socket_buffer_size <= 0) return; fd = g_socket_get_fd (thread_data->socket); switch (thread_data->socket_buffer_option) { case ARV_GV_STREAM_SOCKET_BUFFER_FIXED: buffer_size = thread_data->socket_buffer_size; break; case ARV_GV_STREAM_SOCKET_BUFFER_AUTO: if (thread_data->socket_buffer_size <= 0) buffer_size = buffer->priv->allocated_size + ARV_GV_STREAM_BUFFER_SIZE_PROTOCOL_OVERHEAD; else buffer_size = MIN (buffer->priv->allocated_size + ARV_GV_STREAM_BUFFER_SIZE_PROTOCOL_OVERHEAD, thread_data->socket_buffer_size); break; } buffer_size = MAX (buffer_size, ARV_GV_STREAM_MIN_BUFFER_SIZE); if (buffer_size != thread_data->current_socket_buffer_size) { gboolean result; result = arv_socket_set_recv_buffer_size (fd, buffer_size); if (result) { thread_data->current_socket_buffer_size = buffer_size; arv_info_stream_thread ("[GvStream::update_socket] Socket buffer size set to %d", buffer_size); } else { arv_warning_stream_thread ("[GvStream::update_socket] Failed to set socket buffer size to %d (%d)", buffer_size, errno); } } } static unsigned int _compute_n_expected_packets (const ArvGvspPacket *packet, size_t allocated_size, size_t packet_size) { ArvGvspContentType content_type; ArvBufferPayloadType payload_type; gboolean extended_ids; guint32 block_size; extended_ids = arv_gvsp_packet_has_extended_ids (packet); content_type = arv_gvsp_packet_get_content_type (packet); switch (content_type) { case ARV_GVSP_CONTENT_TYPE_LEADER: payload_type = arv_gvsp_leader_packet_get_buffer_payload_type(packet, NULL); if (payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA || payload_type == ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA) { block_size = packet_size - ARV_GVSP_PAYLOAD_PACKET_PROTOCOL_OVERHEAD (extended_ids); return (allocated_size + block_size - 1) / block_size + (2 /* leader + trailer */); } else if (payload_type == ARV_BUFFER_PAYLOAD_TYPE_MULTIPART) { unsigned int n_parts; unsigned int n_packets = 0; unsigned int i; n_parts = arv_gvsp_leader_packet_get_multipart_n_parts(packet); block_size = packet_size - ARV_GVSP_MULTIPART_PACKET_PROTOCOL_OVERHEAD (extended_ids); for (i = 0; i < n_parts; i++) { n_packets += (arv_gvsp_leader_packet_get_multipart_size (packet, i) + block_size - 1) / block_size; } return n_packets + (2 /* leader + trailer */); } else { return 0; } break; case ARV_GVSP_CONTENT_TYPE_PAYLOAD: block_size = packet_size - ARV_GVSP_PAYLOAD_PACKET_PROTOCOL_OVERHEAD (extended_ids); return (allocated_size + block_size - 1) / block_size + (2 /* leader + trailer */); case ARV_GVSP_CONTENT_TYPE_MULTIPART: block_size = packet_size - ARV_GVSP_MULTIPART_PACKET_PROTOCOL_OVERHEAD (extended_ids); return (allocated_size + block_size - 1) / block_size + (2 /* leader + trailer */) + (255 /* n_parts_max) */); break; case ARV_GVSP_CONTENT_TYPE_TRAILER: return arv_gvsp_packet_get_packet_id (packet) + 1; break; case ARV_GVSP_CONTENT_TYPE_ALL_IN: return 1; case ARV_GVSP_CONTENT_TYPE_H264: case ARV_GVSP_CONTENT_TYPE_GENDC: case ARV_GVSP_CONTENT_TYPE_MULTIZONE: break; } return 0; } static ArvGvStreamFrameData * _find_frame_data (ArvGvStreamThreadData *thread_data, const ArvGvspPacket *packet, size_t packet_size, guint64 frame_id, guint32 packet_id, size_t read_count, guint64 time_us) { ArvGvStreamFrameData *frame = NULL; ArvBuffer *buffer; GSList *iter; guint n_packets = 0; gint64 frame_id_inc; gboolean extended_ids; extended_ids = arv_gvsp_packet_has_extended_ids (packet); for (iter = thread_data->frames; iter != NULL; iter = iter->next) { frame = iter->data; if (frame->frame_id == frame_id) { arv_histogram_fill (thread_data->histogram, 1, time_us - frame->first_packet_time_us); arv_histogram_fill (thread_data->histogram, 2, time_us - frame->last_packet_time_us); frame->last_packet_time_us = time_us; return frame; } } if (extended_ids) { frame_id_inc = (gint64) frame_id - (gint64) thread_data->last_frame_id; /* Frame id 0 is not a valid value */ if ((gint64) frame_id > 0 && (gint64) thread_data->last_frame_id < 0) frame_id_inc--; } else { frame_id_inc = (gint16) frame_id - (gint16) thread_data->last_frame_id; /* Frame id 0 is not a valid value */ if ((gint16) frame_id > 0 && (gint16) thread_data->last_frame_id < 0) frame_id_inc--; } if (frame_id_inc < 1 && frame_id_inc > -ARV_GV_STREAM_DISCARD_LATE_FRAME_THRESHOLD) { arv_info_stream_thread ("[GvStream::find_frame_data] Discard late frame %" G_GUINT64_FORMAT " (last: %" G_GUINT64_FORMAT ")", frame_id, thread_data->last_frame_id); arv_gvsp_packet_debug (packet, packet_size, ARV_DEBUG_LEVEL_INFO); return NULL; } buffer = arv_stream_pop_input_buffer (thread_data->stream); if (buffer == NULL) { thread_data->n_underruns++; return NULL; } n_packets = _compute_n_expected_packets (packet, buffer->priv->allocated_size, thread_data->scps_packet_size); if (n_packets < 1) { buffer->priv->status = ARV_BUFFER_STATUS_PAYLOAD_NOT_SUPPORTED; arv_stream_push_output_buffer(thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); return NULL; } frame = g_new0 (ArvGvStreamFrameData, 1); frame->disable_resend_request = FALSE; frame->frame_id = frame_id; frame->last_valid_packet = -1; frame->buffer = buffer; _update_socket (thread_data, frame->buffer); frame->buffer->priv->status = ARV_BUFFER_STATUS_FILLING; frame->first_packet_time_us = time_us; frame->last_packet_time_us = time_us; frame->packet_data = g_new0 (ArvGvStreamPacketData, n_packets); frame->n_packets = n_packets; if (thread_data->callback != NULL && frame->buffer != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_START_BUFFER, NULL); thread_data->last_frame_id = frame_id; if (frame_id_inc > 1) { thread_data->n_missing_frames++; arv_debug_stream_thread ("[GvStream::find_frame_data] Missed %" G_GINT64_FORMAT " frame(s) before %" G_GUINT64_FORMAT, frame_id_inc - 1, frame_id); } thread_data->frames = g_slist_append (thread_data->frames, frame); arv_debug_stream_thread ("[GvStream::find_frame_data] Start frame %" G_GUINT64_FORMAT, frame_id); frame->extended_ids = extended_ids; arv_histogram_fill (thread_data->histogram, 1, 0); return frame; } static void _process_data_leader (ArvGvStreamThreadData *thread_data, ArvGvStreamFrameData *frame, const ArvGvspPacket *packet, guint32 packet_id) { if (frame->buffer->priv->status != ARV_BUFFER_STATUS_FILLING) return; if (packet_id != 0) { frame->buffer->priv->status = ARV_BUFFER_STATUS_WRONG_PACKET_ID; return; } frame->leader_received = TRUE; frame->buffer->priv->payload_type = arv_gvsp_leader_packet_get_buffer_payload_type (packet, &frame->buffer->priv->has_chunks); frame->buffer->priv->frame_id = frame->frame_id; frame->buffer->priv->chunk_endianness = G_BIG_ENDIAN; frame->buffer->priv->system_timestamp_ns = g_get_real_time() * 1000LL; if (frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA) { guint64 timestamp; arv_buffer_set_n_parts (frame->buffer, 1); timestamp = arv_gvsp_leader_packet_get_timestamp(packet); frame->buffer->priv->parts[0].data_offset = 0; frame->buffer->priv->parts[0].component_id = 0; frame->buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; arv_gvsp_leader_packet_get_image_infos (packet, &frame->buffer->priv->parts[0].pixel_format, &frame->buffer->priv->parts[0].width, &frame->buffer->priv->parts[0].height, &frame->buffer->priv->parts[0].x_offset, &frame->buffer->priv->parts[0].y_offset, &frame->buffer->priv->parts[0].x_padding, &frame->buffer->priv->parts[0].y_padding); if (G_LIKELY (thread_data->timestamp_tick_frequency != 0)) frame->buffer->priv->timestamp_ns = arv_gvsp_timestamp_to_ns (timestamp, thread_data->timestamp_tick_frequency); else frame->buffer->priv->timestamp_ns = frame->buffer->priv->system_timestamp_ns; } else if (frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA) { guint64 timestamp; arv_buffer_set_n_parts (frame->buffer, 0); timestamp = arv_gvsp_leader_packet_get_timestamp(packet); if (G_LIKELY (thread_data->timestamp_tick_frequency != 0)) frame->buffer->priv->timestamp_ns = arv_gvsp_timestamp_to_ns (timestamp, thread_data->timestamp_tick_frequency); else frame->buffer->priv->timestamp_ns = frame->buffer->priv->system_timestamp_ns; } else if (frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_MULTIPART) { guint64 timestamp; unsigned int i; guint n_parts; ptrdiff_t offset = 0; n_parts = arv_gvsp_leader_packet_get_multipart_n_parts(packet); timestamp = arv_gvsp_leader_packet_get_timestamp(packet); arv_buffer_set_n_parts (frame->buffer, n_parts); for (i = 0; i < n_parts; i++) { frame->buffer->priv->parts[i].data_offset = offset; arv_gvsp_leader_packet_get_multipart_infos (packet, i, &frame->buffer->priv->parts[i].component_id, &frame->buffer->priv->parts[i].data_type, &frame->buffer->priv->parts[i].size, &frame->buffer->priv->parts[i].pixel_format, &frame->buffer->priv->parts[i].width, &frame->buffer->priv->parts[i].height, &frame->buffer->priv->parts[i].x_offset, &frame->buffer->priv->parts[i].y_offset, &frame->buffer->priv->parts[i].x_padding, &frame->buffer->priv->parts[i].y_padding); offset += frame->buffer->priv->parts[i].size; } if (G_LIKELY (thread_data->timestamp_tick_frequency != 0)) frame->buffer->priv->timestamp_ns = arv_gvsp_timestamp_to_ns (timestamp, thread_data->timestamp_tick_frequency); else frame->buffer->priv->timestamp_ns = frame->buffer->priv->system_timestamp_ns; } else { frame->buffer->priv->timestamp_ns = frame->buffer->priv->system_timestamp_ns; } if (frame->packet_data[packet_id].resend_requested) { thread_data->n_resent_packets++; arv_debug_stream_thread ("[GvStream::process_data_leader] Received resent packet %u for frame %" G_GUINT64_FORMAT, packet_id, frame->frame_id); } } static void _process_payload_block (ArvGvStreamThreadData *thread_data, ArvGvStreamFrameData *frame, const ArvGvspPacket *packet, guint32 packet_id, size_t read_count) { size_t block_size; ptrdiff_t block_offset; ptrdiff_t block_end; gboolean extended_ids; if (frame->buffer->priv->status != ARV_BUFFER_STATUS_FILLING) return; if (packet_id > frame->n_packets - 2 || packet_id < 1) { arv_gvsp_packet_debug (packet, read_count, ARV_DEBUG_LEVEL_INFO); frame->buffer->priv->status = ARV_BUFFER_STATUS_WRONG_PACKET_ID; return; } extended_ids = arv_gvsp_packet_has_extended_ids (packet); block_size = arv_gvsp_payload_packet_get_data_size (packet, read_count); block_offset = (packet_id - 1) * (thread_data->scps_packet_size - ARV_GVSP_PAYLOAD_PACKET_PROTOCOL_OVERHEAD (extended_ids)); block_end = block_size + block_offset; if (block_end > frame->buffer->priv->allocated_size) { arv_info_stream_thread ("[GvStream::process_data_block] %" G_GINTPTR_FORMAT " unexpected bytes in packet %u " " for frame %" G_GUINT64_FORMAT, block_end - frame->buffer->priv->allocated_size, packet_id, frame->frame_id); thread_data->n_size_mismatch_errors++; block_end = frame->buffer->priv->allocated_size; block_size = block_end - block_offset; } memcpy (((char *) frame->buffer->priv->data) + block_offset, arv_gvsp_packet_get_data (packet), block_size); frame->received_size += block_size; if (frame->packet_data[packet_id].resend_requested) { thread_data->n_resent_packets++; arv_debug_stream_thread ("[GvStream::process_data_block] Received resent packet %u for frame %" G_GUINT64_FORMAT, packet_id, frame->frame_id); } } static void _process_multipart_block (ArvGvStreamThreadData *thread_data, ArvGvStreamFrameData *frame, const ArvGvspPacket *packet, guint32 packet_id, size_t read_count) { guint part_id; ptrdiff_t block_offset; if (frame->buffer->priv->status != ARV_BUFFER_STATUS_FILLING) return; if (arv_gvsp_multipart_packet_get_infos (packet, &part_id, &block_offset)) { size_t block_size; ptrdiff_t block_end; void *data; block_size = arv_gvsp_multipart_packet_get_data_size (packet, read_count); block_end = block_offset + block_size; if (block_end > frame->buffer->priv->allocated_size) { arv_info_stream_thread ("[GvStream::process_multipart_block] %" G_GINTPTR_FORMAT " unexpected bytes in packet %u " " for frame %" G_GUINT64_FORMAT, block_end - frame->buffer->priv->allocated_size, packet_id, frame->frame_id); return; } data = arv_gvsp_multipart_packet_get_data (packet); memcpy ((char *) frame->buffer->priv->data + block_offset, data, block_size); frame->received_size += block_size; } } static void _process_data_trailer (ArvGvStreamThreadData *thread_data, ArvGvStreamFrameData *frame, guint32 packet_id) { if (frame->buffer->priv->status != ARV_BUFFER_STATUS_FILLING) return; if (packet_id > frame->n_packets - 1) { frame->buffer->priv->status = ARV_BUFFER_STATUS_WRONG_PACKET_ID; return; } /* Trailer packet received before expected, because the actual payload size is smaller than the buffer size */ if (frame->n_packets != packet_id + 1) { arv_debug_stream_thread ("[GvStream::process_data_trailer] Update expected number of packets (%u → %u)", frame->n_packets, packet_id + 1); frame->n_packets = packet_id + 1; } if (frame->packet_data[packet_id].resend_requested) { thread_data->n_resent_packets++; arv_debug_stream_thread ("[GvStream::process_data_trailer] Received resent packet %u for frame %" G_GUINT64_FORMAT, packet_id, frame->frame_id); } } static void _missing_packet_check (ArvGvStreamThreadData *thread_data, ArvGvStreamFrameData *frame, guint32 packet_id, guint64 time_us) { int i; if (thread_data->packet_resend == ARV_GV_STREAM_PACKET_RESEND_NEVER || frame->disable_resend_request || frame->resend_ratio_reached) return; if ((int) (frame->n_packets * thread_data->packet_request_ratio) <= 0) return; if (packet_id < frame->n_packets) { int first_missing = -1; for (i = frame->last_valid_packet + 1; i <= packet_id + 1; i++) { gboolean need_resend; if (i <= packet_id && !frame->packet_data[i].received) { if (frame->packet_data[i].abs_timeout_us == 0) frame->packet_data[i].abs_timeout_us = time_us + thread_data->initial_packet_timeout_us; need_resend = time_us > frame->packet_data[i].abs_timeout_us; } else need_resend = FALSE; if (need_resend) { if (first_missing < 0) first_missing = i; } if (i > packet_id || !need_resend) { if (first_missing >= 0) { int last_missing; int n_missing_packets; int j; last_missing = i - 1; n_missing_packets = last_missing - first_missing + 1; if (frame->n_packet_resend_requests + n_missing_packets > (frame->n_packets * thread_data->packet_request_ratio)) { frame->n_packet_resend_requests += n_missing_packets; arv_info_stream_thread ("[GvStream::missing_packet_check]" " Maximum number of requests " "reached at dt = %" G_GINT64_FORMAT ", n_packet_requests = %u (%u packets/frame), frame_id = %" G_GUINT64_FORMAT, time_us - frame->first_packet_time_us, frame->n_packet_resend_requests, frame->n_packets, frame->frame_id); thread_data->n_resend_ratio_reached++; frame->resend_ratio_reached = TRUE; return; } arv_debug_stream_thread ("[GvStream::missing_packet_check]" " Resend request at dt = %" G_GINT64_FORMAT ", packet id = %u (%u packets/frame)", time_us - frame->first_packet_time_us, packet_id, frame->n_packets); _send_packet_request (thread_data, frame->frame_id, first_missing, last_missing, frame->extended_ids); for (j = first_missing; j <= last_missing; j++) { frame->packet_data[j].abs_timeout_us = time_us + thread_data->packet_timeout_us; frame->packet_data[j].resend_requested = TRUE; } thread_data->n_resend_requests += n_missing_packets; first_missing = -1; } } } } } static void _close_frame (ArvGvStreamThreadData *thread_data, guint64 time_us, ArvGvStreamFrameData *frame) { if (frame->buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS) thread_data->n_completed_buffers++; else if (frame->buffer->priv->status != ARV_BUFFER_STATUS_ABORTED) thread_data->n_failures++; if (frame->buffer->priv->status == ARV_BUFFER_STATUS_TIMEOUT) thread_data->n_timeouts++; if (frame->buffer->priv->status == ARV_BUFFER_STATUS_ABORTED) thread_data->n_aborted++; if (frame->buffer->priv->status != ARV_BUFFER_STATUS_SUCCESS && frame->buffer->priv->status != ARV_BUFFER_STATUS_ABORTED) thread_data->n_missing_packets += (int) frame->n_packets - (frame->last_valid_packet + 1); arv_stream_push_output_buffer (thread_data->stream, frame->buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, frame->buffer); arv_histogram_fill (thread_data->histogram, 0, time_us - frame->first_packet_time_us); arv_debug_stream_thread ("[GvStream::close_frame] Close frame %" G_GUINT64_FORMAT, frame->frame_id); frame->buffer = NULL; frame->frame_id = 0; g_free (frame->packet_data); g_free (frame); } static void _check_frame_completion (ArvGvStreamThreadData *thread_data, guint64 time_us, ArvGvStreamFrameData *current_frame) { GSList *iter; ArvGvStreamFrameData *frame; gboolean can_close_frame = TRUE; for (iter = thread_data->frames; iter != NULL;) { frame = iter->data; if (can_close_frame && thread_data->packet_resend == ARV_GV_STREAM_PACKET_RESEND_NEVER && iter->next != NULL) { frame->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; arv_info_stream_thread ("[GvStream::check_frame_completion] Incomplete frame %" G_GUINT64_FORMAT, frame->frame_id); _close_frame (thread_data, time_us, frame); thread_data->frames = iter->next; g_slist_free_1 (iter); iter = thread_data->frames; continue; } if (can_close_frame && frame->last_valid_packet == frame->n_packets - 1) { frame->buffer->priv->status = ARV_BUFFER_STATUS_SUCCESS; frame->buffer->priv->received_size = frame->received_size; if (frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || frame->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA) { frame->buffer->priv->parts[0].size = frame->received_size; } arv_debug_stream_thread ("[GvStream::check_frame_completion] Completed frame %" G_GUINT64_FORMAT, frame->frame_id); _close_frame (thread_data, time_us, frame); thread_data->frames = iter->next; g_slist_free_1 (iter); iter = thread_data->frames; continue; } if (can_close_frame && /* Do not timeout on the most recent frame if the LEADER packet is so far the ONLY * valid packet received. This is needed by some devices sending the leader packet early, at * acquisition start. */ (frame->frame_id != thread_data->last_frame_id || frame->last_valid_packet != 0) && time_us - frame->last_packet_time_us >= thread_data->frame_retention_us) { frame->buffer->priv->status = ARV_BUFFER_STATUS_TIMEOUT; arv_warning_stream_thread ("[GvStream::check_frame_completion] Timeout for frame %" G_GUINT64_FORMAT " at dt = %" G_GUINT64_FORMAT, frame->frame_id, time_us - frame->first_packet_time_us); #if 0 if (arv_debug_check (&arv_debug_category_stream_thread, ARV_DEBUG_LEVEL_LOG)) { int i; arv_debug_stream_thread ("frame_id = %Lu", frame->frame_id); arv_debug_stream_thread ("last_valid_packet = %d", frame->last_valid_packet); for (i = 0; i < frame->n_packets; i++) { arv_debug_stream_thread ("%d - time = %Lu%s", i, frame->packet_data[i].time_us, frame->packet_data[i].received ? " - OK" : ""); } } #endif _close_frame (thread_data, time_us, frame); thread_data->frames = iter->next; g_slist_free_1 (iter); iter = thread_data->frames; continue; } can_close_frame = FALSE; if (frame != current_frame && time_us - frame->last_packet_time_us >= thread_data->packet_timeout_us) { _missing_packet_check (thread_data, frame, frame->n_packets - 1, time_us); iter = iter->next; continue; } iter = iter->next; } } static void _flush_frames (ArvGvStreamThreadData *thread_data, guint64 time_us) { GSList *iter; ArvGvStreamFrameData *frame; for (iter = thread_data->frames; iter != NULL; iter = iter->next) { frame = iter->data; frame->buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; _close_frame (thread_data, time_us, frame); } g_slist_free (thread_data->frames); thread_data->frames = NULL; } static ArvGvStreamFrameData * _process_packet (ArvGvStreamThreadData *thread_data, const ArvGvspPacket *packet, size_t packet_size, guint64 time_us) { ArvGvStreamFrameData *frame; guint32 packet_id; guint64 frame_id; int i; thread_data->n_received_packets++; frame_id = arv_gvsp_packet_get_frame_id (packet); packet_id = arv_gvsp_packet_get_packet_id (packet); if (thread_data->first_packet) { thread_data->last_frame_id = frame_id - 1; thread_data->first_packet = FALSE; } frame = _find_frame_data (thread_data, packet, packet_size, frame_id, packet_id, packet_size, time_us); if (frame != NULL) { ArvGvspPacketType packet_type = arv_gvsp_packet_get_packet_type (packet); if (arv_gvsp_packet_type_is_error (packet_type)) { ArvGvcpError error = packet_type & 0xff; arv_info_stream_thread ("[GvStream::process_packet]" " Error packet at dt = %" G_GINT64_FORMAT ", packet id = %u" " frame id = %" G_GUINT64_FORMAT, time_us - frame->first_packet_time_us, packet_id, frame->frame_id); arv_gvsp_packet_debug (packet, packet_size, ARV_DEBUG_LEVEL_INFO); if (error == ARV_GVCP_ERROR_PACKET_AND_PREVIOUS_REMOVED_FROM_MEMORY || error == ARV_GVCP_ERROR_PACKET_REMOVED_FROM_MEMORY || error == ARV_GVCP_ERROR_PACKET_UNAVAILABLE) { frame->disable_resend_request = TRUE; thread_data->n_resend_disabled++; } thread_data->n_error_packets++; thread_data->n_transferred_bytes += packet_size; } else if (packet_id < frame->n_packets && frame->packet_data[packet_id].received) { /* Ignore duplicate packet */ thread_data->n_duplicated_packets++; arv_debug_stream_thread ("[GvStream::process_packet] Duplicated packet %d for frame %" G_GUINT64_FORMAT, packet_id, frame->frame_id); arv_gvsp_packet_debug (packet, packet_size, ARV_DEBUG_LEVEL_DEBUG); thread_data->n_transferred_bytes += packet_size; } else { ArvGvspContentType content_type; if (packet_id < frame->n_packets) { frame->packet_data[packet_id].received = TRUE; } /* Keep track of last packet of a continuous block starting from packet 0 */ for (i = frame->last_valid_packet + 1; i < frame->n_packets; i++) if (!frame->packet_data[i].received) break; frame->last_valid_packet = i - 1; content_type = arv_gvsp_packet_get_content_type (packet); arv_gvsp_packet_debug (packet, packet_size, content_type == ARV_GVSP_CONTENT_TYPE_LEADER || content_type == ARV_GVSP_CONTENT_TYPE_TRAILER ? ARV_DEBUG_LEVEL_DEBUG : ARV_DEBUG_LEVEL_TRACE); switch (content_type) { case ARV_GVSP_CONTENT_TYPE_LEADER: _process_data_leader (thread_data, frame, packet, packet_id); thread_data->n_transferred_bytes += packet_size; break; case ARV_GVSP_CONTENT_TYPE_PAYLOAD: _process_payload_block (thread_data, frame, packet, packet_id, packet_size); thread_data->n_transferred_bytes += packet_size; break; case ARV_GVSP_CONTENT_TYPE_MULTIPART: _process_multipart_block (thread_data, frame, packet, packet_id, packet_size); thread_data->n_transferred_bytes += packet_size; break; case ARV_GVSP_CONTENT_TYPE_TRAILER: _process_data_trailer (thread_data, frame, packet_id); thread_data->n_transferred_bytes += packet_size; break; default: thread_data->n_ignored_packets++; thread_data->n_ignored_bytes += packet_size; break; } _missing_packet_check (thread_data, frame, packet_id, time_us); } } else { thread_data->n_ignored_packets++; thread_data->n_ignored_bytes += packet_size; } return frame; } static void _loop (ArvGvStreamThreadData *thread_data) { ArvGvStreamFrameData *frame; ArvGvspPacket *packet_buffers; GPollFD poll_fd[2]; guint64 time_us; gboolean use_poll; int i; GInputVector packet_iv[ARV_GV_STREAM_NUM_BUFFERS] = { {NULL, 0}, }; GInputMessage packet_im[ARV_GV_STREAM_NUM_BUFFERS] = { {NULL, NULL, 0, 0, 0, NULL, NULL}, }; // we don't need to consider the IP and UDP header size guint packet_buffer_size = thread_data->scps_packet_size - 20 - 8; arv_info_stream ("[GvStream::loop] Standard socket method"); poll_fd[0].fd = g_socket_get_fd (thread_data->socket); poll_fd[0].events = G_IO_IN; poll_fd[0].revents = 0; arv_gpollfd_prepare_all(poll_fd,1); packet_buffers = g_malloc0 (packet_buffer_size * ARV_GV_STREAM_NUM_BUFFERS); for (i = 0; i < ARV_GV_STREAM_NUM_BUFFERS; i++) { packet_iv[i].buffer = (char *) packet_buffers + i * packet_buffer_size; packet_iv[i].size = packet_buffer_size; packet_im[i].vectors = &packet_iv[i]; packet_im[i].num_vectors = 1; } use_poll = g_cancellable_make_pollfd (thread_data->cancellable, &poll_fd[1]); g_mutex_lock (&thread_data->thread_started_mutex); thread_data->thread_started = TRUE; g_cond_signal (&thread_data->thread_started_cond); g_mutex_unlock (&thread_data->thread_started_mutex); do { int timeout_ms; int n_events; int errsv; if (thread_data->frames != NULL) timeout_ms = thread_data->packet_timeout_us / 1000; else timeout_ms = ARV_GV_STREAM_POLL_TIMEOUT_US / 1000; do { poll_fd[0].revents = 0; n_events = g_poll (poll_fd, use_poll ? 2 : 1, timeout_ms); errsv = errno; } while (n_events < 0 && errsv == EINTR); if (poll_fd[0].revents != 0) { GError *error = NULL; int n_msgs; arv_gpollfd_clear_one (&poll_fd[0], thread_data->socket); n_msgs = g_socket_receive_messages (thread_data->socket, packet_im, ARV_GV_STREAM_NUM_BUFFERS, G_SOCKET_MSG_NONE, NULL, &error); if (G_LIKELY(n_msgs > 0)) { time_us = g_get_monotonic_time (); for (i = 0; i < n_msgs; i++) { frame = _process_packet (thread_data, packet_iv[i].buffer, packet_im[i].bytes_received, time_us); _check_frame_completion (thread_data, time_us, frame); } } else { arv_warning_stream_thread ("[GvStream::loop] receive_messages failed: %s", error != NULL ? error->message : "Unknown reason"); g_clear_error (&error); } } else { time_us = g_get_monotonic_time (); _check_frame_completion (thread_data, time_us, NULL); } } while (!g_cancellable_is_cancelled (thread_data->cancellable)); if (use_poll) g_cancellable_release_fd (thread_data->cancellable); arv_gpollfd_finish_all (poll_fd,1); g_free (packet_buffers); } #if ARAVIS_HAS_PACKET_SOCKET static void _set_socket_filter (int socket, guint32 source_ip, guint32 source_port, guint32 destination_ip, guint32 destination_port) { #if 0 /* * sudo tcpdump -i lo -e -nn "udp and src host 192.168.0.1 and src port 10 and dst host 192.168.0.2 and dst port 20" -d * * (000) ldh [12] * (001) jeq #0x86dd jt 17 jf 2 * (002) jeq #0x800 jt 3 jf 17 * (003) ldb [23] * (004) jeq #0x11 jt 5 jf 17 * (005) ld [26] * (006) jeq #0xc0a80001 jt 7 jf 17 Source host * (007) ldh [20] * (008) jset #0x1fff jt 17 jf 9 * (009) ldxb 4*([14]&0xf) * (010) ldh [x + 14] * (011) jeq #0xa jt 12 jf 17 Source port * (012) ld [30] * (013) jeq #0xc0a80002 jt 14 jf 17 Destination host * (014) ldh [x + 16] * (015) jeq #0x14 jt 16 jf 17 Destination port * (016) ret #262144 * (017) ret #0 */ struct sock_filter bpf[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 15, 0, 0x000086dd }, { 0x15, 0, 14, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 12, 0x00000011 }, { 0x20, 0, 0, 0x0000001a }, { 0x15, 0, 10, source_ip }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 8, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x0000000e }, { 0x15, 0, 5, source_port }, { 0x20, 0, 0, 0x0000001e }, { 0x15, 0, 3, destination_ip }, { 0x48, 0, 0, 0x00000010 }, { 0x15, 0, 1, destination_port }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 } }; #else /* Variant without source port check. */ /* * sudo tcpdump -i lo -e -nn "udp and src host 192.168.0.1 and dst host 192.168.0.2 and dst port 20" -d * * (000) ldh [12] * (001) jeq #0x86dd jt 15 jf 2 * (002) jeq #0x800 jt 3 jf 15 * (003) ldb [23] * (004) jeq #0x11 jt 5 jf 15 * (005) ld [26] * (006) jeq #0xc0a80001 jt 7 jf 15 * (007) ld [30] * (008) jeq #0xc0a80002 jt 9 jf 15 * (009) ldh [20] * (010) jset #0x1fff jt 15 jf 11 * (011) ldxb 4*([14]&0xf) * (012) ldh [x + 16] * (013) jeq #0x14 jt 14 jf 15 * (014) ret #262144 * (015) ret #0 */ struct sock_filter bpf[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 13, 0, 0x000086dd }, { 0x15, 0, 12, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 10, 0x00000011 }, { 0x20, 0, 0, 0x0000001a }, { 0x15, 0, 8, source_ip }, { 0x20, 0, 0, 0x0000001e }, { 0x15, 0, 6, destination_ip }, { 0x28, 0, 0, 0x00000014}, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x00000010 }, { 0x15, 0, 1, destination_port }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 } }; #endif struct sock_fprog bpf_prog = {G_N_ELEMENTS (bpf), bpf}; arv_info_stream_thread ("[GvStream::set_socket_filter] source ip = 0x%08x - port = %d - dest ip = 0x%08x - port %d", source_ip, source_port, destination_ip, destination_port); if (setsockopt(socket, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, sizeof(bpf_prog)) != 0) arv_warning_stream_thread ("[GvStream::set_socket_filter] Failed to attach Beckerley Packet Filter to stream socket"); } static unsigned _interface_index_from_address (guint32 ip) { struct ifaddrs *ifaddr = NULL; struct ifaddrs *ifa; unsigned index = 0; if (getifaddrs(&ifaddr) == -1) { return index; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *sa; sa = (struct sockaddr_in *) (ifa->ifa_addr); if (ip == g_ntohl (sa->sin_addr.s_addr)) { index = if_nametoindex (ifa->ifa_name); break; } } } freeifaddrs (ifaddr); return index; } typedef struct { guint32 version; guint32 offset_to_priv; struct tpacket_hdr_v1 h1; } ArvGvStreamBlockDescriptor; static void _ring_buffer_loop (ArvGvStreamThreadData *thread_data) { GPollFD poll_fd[2]; char *buffer; struct tpacket_req3 req; struct sockaddr_ll local_address = {0}; enum tpacket_versions version; int fd; unsigned block_id; const guint8 *bytes; guint32 interface_address; guint32 device_address; gboolean use_poll; arv_info_stream ("[GvStream::loop] Packet socket method"); fd = socket (PF_PACKET, SOCK_RAW, g_htons (ETH_P_ALL)); if (fd < 0) { arv_warning_stream_thread ("[GvStream::loop] Failed to create AF_PACKET socket"); goto af_packet_error; } version = TPACKET_V3; if (setsockopt (fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)) < 0) { arv_warning_stream_thread ("[GvStream::loop] Failed to set packet version"); goto socket_option_error; } req.tp_block_size = 1 << 21; req.tp_frame_size = 1024; req.tp_block_nr = 16; req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size; req.tp_sizeof_priv = 0; req.tp_retire_blk_tov = 5; req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; if (setsockopt (fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { arv_warning_stream_thread ("[GvStream::loop] Failed to set packet rx ring"); goto socket_option_error; } buffer = mmap (NULL, req.tp_block_size * req.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, 0); if (buffer == MAP_FAILED) { arv_warning_stream_thread ("[GvStream::loop] Failed to map ring buffer"); goto map_error; } bytes = g_inet_address_to_bytes (thread_data->interface_address); interface_address = g_ntohl (*((guint32 *) bytes)); bytes = g_inet_address_to_bytes (thread_data->device_address); device_address = g_ntohl (*((guint32 *) bytes)); local_address.sll_family = AF_PACKET; local_address.sll_protocol = g_htons(ETH_P_IP); local_address.sll_ifindex = _interface_index_from_address (interface_address); local_address.sll_hatype = 0; local_address.sll_pkttype = 0; local_address.sll_halen = 0; if (bind (fd, (struct sockaddr *) &local_address, sizeof(local_address)) == -1) { arv_warning_stream_thread ("[GvStream::loop] Failed to bind packet socket"); goto bind_error; } _set_socket_filter (fd, device_address, thread_data->source_stream_port, interface_address, thread_data->stream_port); poll_fd[0].fd = fd; poll_fd[0].events = G_IO_IN; poll_fd[0].revents = 0; use_poll = g_cancellable_make_pollfd (thread_data->cancellable, &poll_fd[1]); g_mutex_lock (&thread_data->thread_started_mutex); thread_data->thread_started = TRUE; g_cond_signal (&thread_data->thread_started_cond); g_mutex_unlock (&thread_data->thread_started_mutex); block_id = 0; do { ArvGvStreamBlockDescriptor *descriptor; guint64 time_us; time_us = g_get_monotonic_time (); descriptor = (void *) (buffer + block_id * req.tp_block_size); if ((descriptor->h1.block_status & TP_STATUS_USER) == 0) { int timeout_ms; int n_events; int errsv; _check_frame_completion (thread_data, time_us, NULL); if (thread_data->frames != NULL) timeout_ms = thread_data->packet_timeout_us / 1000; else timeout_ms = ARV_GV_STREAM_POLL_TIMEOUT_US / 1000; do { n_events = g_poll (poll_fd, use_poll ? 2 : 1, timeout_ms); errsv = errno; } while (n_events < 0 && errsv == EINTR); } else { ArvGvStreamFrameData *frame; const struct tpacket3_hdr *header; unsigned i; header = (void *) (((char *) descriptor) + descriptor->h1.offset_to_first_pkt); for (i = 0; i < descriptor->h1.num_pkts; i++) { const struct iphdr *ip; const ArvGvspPacket *packet; size_t size; ip = (void *) (((char *) header) + header->tp_mac + ETH_HLEN); packet = (void *) (((char *) ip) + sizeof (struct iphdr) + sizeof (struct udphdr)); size = g_ntohs (ip->tot_len) - sizeof (struct iphdr) - sizeof (struct udphdr); frame = _process_packet (thread_data, packet, size, time_us); _check_frame_completion (thread_data, time_us, frame); header = (void *) (((char *) header) + header->tp_next_offset); } descriptor->h1.block_status = TP_STATUS_KERNEL; block_id = (block_id + 1) % req.tp_block_nr; } } while (!g_cancellable_is_cancelled (thread_data->cancellable)); if (use_poll) g_cancellable_release_fd (thread_data->cancellable); bind_error: munmap (buffer, req.tp_block_size * req.tp_block_nr); socket_option_error: map_error: close (fd); af_packet_error: g_mutex_lock (&thread_data->thread_started_mutex); thread_data->thread_started = TRUE; g_cond_signal (&thread_data->thread_started_cond); g_mutex_unlock (&thread_data->thread_started_mutex); } #endif /* ARAVIS_HAS_PACKET_SOCKET */ static void * arv_gv_stream_thread (void *data) { ArvGvStreamThreadData *thread_data = data; #if ARAVIS_HAS_PACKET_SOCKET int fd; #endif thread_data->frames = NULL; thread_data->last_frame_id = 0; thread_data->first_packet = TRUE; if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_INIT, NULL); #if ARAVIS_HAS_PACKET_SOCKET if (thread_data->use_packet_socket && (fd = socket (PF_PACKET, SOCK_RAW, g_htons (ETH_P_ALL))) >= 0) { close (fd); _ring_buffer_loop (thread_data); } else #endif _loop (thread_data); _flush_frames (thread_data, g_get_monotonic_time ()); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_EXIT, NULL); return NULL; } /* ArvGvStream implementation */ guint16 arv_gv_stream_get_port (ArvGvStream *gv_stream) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (gv_stream); g_return_val_if_fail (ARV_IS_GV_STREAM (gv_stream), 0); return priv->thread_data->stream_port; } static void arv_gv_stream_start_thread (ArvStream *stream) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (stream)); ArvGvStreamThreadData *thread_data; g_return_if_fail (priv->thread == NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; thread_data->thread_started = FALSE; thread_data->cancellable = g_cancellable_new (); priv->thread = g_thread_new ("arv_gv_stream", arv_gv_stream_thread, priv->thread_data); g_mutex_lock (&thread_data->thread_started_mutex); while (!thread_data->thread_started) g_cond_wait (&thread_data->thread_started_cond, &thread_data->thread_started_mutex); g_mutex_unlock (&thread_data->thread_started_mutex); } static void arv_gv_stream_stop_thread (ArvStream *stream) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (stream)); ArvGvStreamThreadData *thread_data; g_return_if_fail (priv->thread != NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; g_cancellable_cancel (thread_data->cancellable); g_thread_join (priv->thread); g_clear_object (&thread_data->cancellable); priv->thread = NULL; } /** * arv_gv_stream_new: (skip) * @gv_device: a #ArvGvDevice * @callback: (scope call): processing callback * @callback_data: (closure): user data for @callback * * Return value: (transfer full): a new #ArvStream. */ ArvStream * arv_gv_stream_new (ArvGvDevice *gv_device, ArvStreamCallback callback, void *callback_data, GDestroyNotify destroy, GError **error) { return g_initable_new (ARV_TYPE_GV_STREAM, NULL, error, "device", gv_device, "callback", callback, "callback-data", callback_data, "destroy-notify", destroy, NULL); } /* ArvStream implementation */ /** * arv_gv_stream_get_statistics: * @gv_stream: a #ArvGvStream * @n_resent_packets: (out) * @n_missing_packets: (out) */ void arv_gv_stream_get_statistics (ArvGvStream *gv_stream, guint64 *n_resent_packets, guint64 *n_missing_packets) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (gv_stream); ArvGvStreamThreadData *thread_data; g_return_if_fail (ARV_IS_GV_STREAM (gv_stream)); thread_data = priv->thread_data; if (n_resent_packets != NULL) *n_resent_packets = thread_data->n_resent_packets; if (n_missing_packets != NULL) *n_missing_packets = thread_data->n_missing_packets; } static void arv_gv_stream_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (object)); ArvGvStreamThreadData *thread_data; thread_data = priv->thread_data; switch (prop_id) { case ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER: thread_data->socket_buffer_option = g_value_get_enum (value); break; case ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER_SIZE: thread_data->socket_buffer_size = g_value_get_int (value); break; case ARV_GV_STREAM_PROPERTY_PACKET_RESEND: thread_data->packet_resend = g_value_get_enum (value); break; case ARV_GV_STREAM_PROPERTY_PACKET_REQUEST_RATIO: thread_data->packet_request_ratio = g_value_get_double (value); break; case ARV_GV_STREAM_PROPERTY_INITIAL_PACKET_TIMEOUT: thread_data->initial_packet_timeout_us = g_value_get_uint (value); break; case ARV_GV_STREAM_PROPERTY_PACKET_TIMEOUT: thread_data->packet_timeout_us = g_value_get_uint (value); break; case ARV_GV_STREAM_PROPERTY_FRAME_RETENTION: thread_data->frame_retention_us = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_gv_stream_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (object)); ArvGvStreamThreadData *thread_data; thread_data = priv->thread_data; switch (prop_id) { case ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER: g_value_set_enum (value, thread_data->socket_buffer_option); break; case ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER_SIZE: g_value_set_int (value, thread_data->socket_buffer_size); break; case ARV_GV_STREAM_PROPERTY_PACKET_RESEND: g_value_set_enum (value, thread_data->packet_resend); break; case ARV_GV_STREAM_PROPERTY_PACKET_REQUEST_RATIO: g_value_set_double (value, thread_data->packet_request_ratio); break; case ARV_GV_STREAM_PROPERTY_INITIAL_PACKET_TIMEOUT: g_value_set_uint (value, thread_data->initial_packet_timeout_us); break; case ARV_GV_STREAM_PROPERTY_PACKET_TIMEOUT: g_value_set_uint (value, thread_data->packet_timeout_us); break; case ARV_GV_STREAM_PROPERTY_FRAME_RETENTION: g_value_set_uint (value, thread_data->frame_retention_us); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_gv_stream_init (ArvGvStream *gv_stream) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (gv_stream); priv->thread_data = g_new0 (ArvGvStreamThreadData, 1); } static void arv_gv_stream_constructed (GObject *object) { ArvStream *stream = ARV_STREAM (object); ArvGvStream *gv_stream = ARV_GV_STREAM (object); ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (stream)); ArvGvStreamOption options; GError *error = NULL; GInetAddress *interface_address; GInetAddress *device_address; guint64 timestamp_tick_frequency; const guint8 *address_bytes; GInetSocketAddress *local_address; guint packet_size; G_OBJECT_CLASS (arv_gv_stream_parent_class)->constructed (object); g_object_get (object, "device", &priv->gv_device, NULL); priv->stream_channel = arv_device_get_integer_feature_value(ARV_DEVICE(priv->gv_device), "ArvGevStreamChannelSelector", &error); if (error != NULL) { arv_stream_take_init_error (stream, error); g_clear_object (&priv->gv_device); return; } arv_info_stream ("[GvStream::stream_new] Stream channel = %u", priv->stream_channel); timestamp_tick_frequency = arv_gv_device_get_timestamp_tick_frequency (priv->gv_device, NULL); options = arv_gv_device_get_stream_options (priv->gv_device); packet_size = arv_gv_device_get_packet_size (priv->gv_device, NULL); if (packet_size <= ARV_GVSP_PACKET_PROTOCOL_OVERHEAD(FALSE)) { arv_gv_device_set_packet_size (priv->gv_device, ARV_GV_DEVICE_GVSP_PACKET_SIZE_DEFAULT, NULL); arv_info_stream ("[GvStream::stream_new] Packet size set to default value (%d)", ARV_GV_DEVICE_GVSP_PACKET_SIZE_DEFAULT); } packet_size = arv_gv_device_get_packet_size (priv->gv_device, NULL); arv_info_stream ("[GvStream::stream_new] Packet size = %d byte(s)", packet_size); if (packet_size <= ARV_GVSP_PACKET_PROTOCOL_OVERHEAD(FALSE)) { arv_stream_take_init_error (stream, g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Invalid packet size (%d byte(s))", packet_size)); g_clear_object (&priv->gv_device); return; } priv->thread_data->stream = stream; g_object_get (object, "callback", &priv->thread_data->callback, "callback-data", &priv->thread_data->callback_data, NULL); priv->thread_data->timestamp_tick_frequency = timestamp_tick_frequency; priv->thread_data->scps_packet_size = packet_size; priv->thread_data->use_packet_socket = (options & ARV_GV_STREAM_OPTION_PACKET_SOCKET_DISABLED) == 0; priv->thread_data->packet_id = 65300; priv->thread_data->histogram = arv_histogram_new (3, 100, 2000, 0); arv_histogram_set_variable_name (priv->thread_data->histogram, 0, "frame_retention"); arv_histogram_set_variable_name (priv->thread_data->histogram, 1, "packet_time"); arv_histogram_set_variable_name (priv->thread_data->histogram, 2, "inter_packet"); interface_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (arv_gv_device_get_interface_address (priv->gv_device))); device_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (arv_gv_device_get_device_address (priv->gv_device))); priv->thread_data->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, NULL); priv->thread_data->device_address = g_object_ref (device_address); priv->thread_data->interface_address = g_object_ref (interface_address); priv->thread_data->interface_socket_address = g_inet_socket_address_new (interface_address, 0); priv->thread_data->device_socket_address = g_inet_socket_address_new (device_address, ARV_GVCP_PORT); g_socket_set_blocking (priv->thread_data->socket, FALSE); g_socket_bind (priv->thread_data->socket, priv->thread_data->interface_socket_address, FALSE, NULL); local_address = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (priv->thread_data->socket, NULL)); priv->thread_data->stream_port = g_inet_socket_address_get_port (local_address); g_object_unref (local_address); address_bytes = g_inet_address_to_bytes (interface_address); arv_device_set_integer_feature_value (ARV_DEVICE (priv->gv_device), "ArvGevSCDA", g_htonl (*((guint32 *) address_bytes)), NULL); arv_device_set_integer_feature_value (ARV_DEVICE (priv->gv_device), "ArvGevSCPHostPort", priv->thread_data->stream_port, NULL); priv->thread_data->source_stream_port = arv_device_get_integer_feature_value (ARV_DEVICE (priv->gv_device), "ArvGevSCSP", NULL); arv_info_stream ("[GvStream::stream_new] Destination stream port = %d", priv->thread_data->stream_port); arv_info_stream ("[GvStream::stream_new] Source stream port = %d", priv->thread_data->source_stream_port); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_completed_buffers", G_TYPE_UINT64, &priv->thread_data->n_completed_buffers); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_failures", G_TYPE_UINT64, &priv->thread_data->n_failures); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_underruns", G_TYPE_UINT64, &priv->thread_data->n_underruns); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_timeouts", G_TYPE_UINT64, &priv->thread_data->n_timeouts); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_aborted", G_TYPE_UINT64, &priv->thread_data->n_aborted); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_missing_frames", G_TYPE_UINT64, &priv->thread_data->n_missing_frames); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_size_mismatch_errors", G_TYPE_UINT64, &priv->thread_data->n_size_mismatch_errors); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_received_packets", G_TYPE_UINT64, &priv->thread_data->n_received_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_missing_packets", G_TYPE_UINT64, &priv->thread_data->n_missing_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_error_packets", G_TYPE_UINT64, &priv->thread_data->n_error_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_ignored_packets", G_TYPE_UINT64, &priv->thread_data->n_ignored_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_resend_requests", G_TYPE_UINT64, &priv->thread_data->n_resend_requests); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_resent_packets", G_TYPE_UINT64, &priv->thread_data->n_resent_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_resend_ratio_reached", G_TYPE_UINT64, &priv->thread_data->n_resend_ratio_reached); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_resend_disabled", G_TYPE_UINT64, &priv->thread_data->n_resend_disabled); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_duplicated_packets", G_TYPE_UINT64, &priv->thread_data->n_duplicated_packets); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_transferred_bytes", G_TYPE_UINT64, &priv->thread_data->n_transferred_bytes); arv_stream_declare_info (ARV_STREAM (gv_stream), "n_ignored_bytes", G_TYPE_UINT64, &priv->thread_data->n_ignored_bytes); arv_gv_stream_start_thread (ARV_STREAM (gv_stream)); } static void arv_gv_stream_finalize (GObject *object) { ArvGvStreamPrivate *priv = arv_gv_stream_get_instance_private (ARV_GV_STREAM (object)); GError *error = NULL; arv_gv_stream_stop_thread (ARV_STREAM (object)); /* Stop the stream channel. We use a raw register write here, as the Genicam based access rely on * ArvGevStreamSelector state, and we don't want to change it here. */ arv_device_write_register(ARV_DEVICE(priv->gv_device), 0xd00 + 0x40 * priv->stream_channel, 0x0000, &error); if (error != NULL) { arv_warning_stream ("Failed to stop stream channel %d (%s)", priv->stream_channel, error->message); g_clear_error(&error); } if (priv->thread_data != NULL) { ArvGvStreamThreadData *thread_data; char *histogram_string; thread_data = priv->thread_data; histogram_string = arv_histogram_to_string (thread_data->histogram); arv_info_stream ("%s", histogram_string); g_free (histogram_string); arv_histogram_unref (thread_data->histogram); arv_info_stream ("[GvStream::finalize] n_completed_buffers = %" G_GUINT64_FORMAT, thread_data->n_completed_buffers); arv_info_stream ("[GvStream::finalize] n_failures = %" G_GUINT64_FORMAT, thread_data->n_failures); arv_info_stream ("[GvStream::finalize] n_underruns = %" G_GUINT64_FORMAT, thread_data->n_underruns); arv_info_stream ("[GvStream::finalize] n_timeouts = %" G_GUINT64_FORMAT, thread_data->n_timeouts); arv_info_stream ("[GvStream::finalize] n_aborted = %" G_GUINT64_FORMAT, thread_data->n_aborted); arv_info_stream ("[GvStream::finalize] n_missing_frames = %" G_GUINT64_FORMAT, thread_data->n_missing_frames); arv_info_stream ("[GvStream::finalize] n_size_mismatch_errors = %" G_GUINT64_FORMAT, thread_data->n_size_mismatch_errors); arv_info_stream ("[GvStream::finalize] n_received_packets = %" G_GUINT64_FORMAT, thread_data->n_received_packets); arv_info_stream ("[GvStream::finalize] n_missing_packets = %" G_GUINT64_FORMAT, thread_data->n_missing_packets); arv_info_stream ("[GvStream::finalize] n_error_packets = %" G_GUINT64_FORMAT, thread_data->n_error_packets); arv_info_stream ("[GvStream::finalize] n_ignored_packets = %" G_GUINT64_FORMAT, thread_data->n_ignored_packets); arv_info_stream ("[GvStream::finalize] n_resend_requests = %" G_GUINT64_FORMAT, thread_data->n_resend_requests); arv_info_stream ("[GvStream::finalize] n_resent_packets = %" G_GUINT64_FORMAT, thread_data->n_resent_packets); arv_info_stream ("[GvStream::finalize] n_resend_ratio_reached = %" G_GUINT64_FORMAT, thread_data->n_resend_ratio_reached); arv_info_stream ("[GvStream::finalize] n_resend_disabled = %" G_GUINT64_FORMAT, thread_data->n_resend_disabled); arv_info_stream ("[GvStream::finalize] n_duplicated_packets = %" G_GUINT64_FORMAT, thread_data->n_duplicated_packets); arv_info_stream ("[GvStream::finalize] n_transferred_bytes = %" G_GUINT64_FORMAT, thread_data->n_transferred_bytes); arv_info_stream ("[GvStream::finalize] n_ignored_bytes = %" G_GUINT64_FORMAT, thread_data->n_ignored_bytes); g_clear_object (&thread_data->device_address); g_clear_object (&thread_data->interface_address); g_clear_object (&thread_data->device_socket_address); g_clear_object (&thread_data->interface_socket_address); g_clear_object (&thread_data->socket); g_clear_pointer (&thread_data, g_free); } g_clear_object (&priv->gv_device); G_OBJECT_CLASS (arv_gv_stream_parent_class)->finalize (object); } static void arv_gv_stream_class_init (ArvGvStreamClass *gv_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (gv_stream_class); ArvStreamClass *stream_class = ARV_STREAM_CLASS (gv_stream_class); object_class->constructed = arv_gv_stream_constructed; object_class->finalize = arv_gv_stream_finalize; object_class->set_property = arv_gv_stream_set_property; object_class->get_property = arv_gv_stream_get_property; stream_class->start_thread = arv_gv_stream_start_thread; stream_class->stop_thread = arv_gv_stream_stop_thread; /** * ArvGvStream:socket-buffer: * * Incoming socket buffer policy. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER, g_param_spec_enum ("socket-buffer", "Socket buffer", "Socket buffer behaviour", ARV_TYPE_GV_STREAM_SOCKET_BUFFER, ARV_GV_STREAM_SOCKET_BUFFER_FIXED, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:socket-buffer-size: * * Size in bytes of the incoming socket buffer. A greater value helps to lower the number of missing packets, * as the expense of an increased memory usage. If the value is not strictly positive, this setting is ignored. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_SOCKET_BUFFER_SIZE, g_param_spec_int ("socket-buffer-size", "Socket buffer size", "Socket buffer size, in bytes", -1, G_MAXINT, 0, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:packet-resend: * * Packet resend policy. This only applies if the device supports packet resend. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_PACKET_RESEND, g_param_spec_enum ("packet-resend", "Packet resend", "Packet resend behaviour", ARV_TYPE_GV_STREAM_PACKET_RESEND, ARV_GV_STREAM_PACKET_RESEND_ALWAYS, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:packet-request-ratio: * * Maximum number of packet resend requests for a given frame, as a percentage of the number of packets per * frame. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_PACKET_REQUEST_RATIO, g_param_spec_double ("packet-request-ratio", "Packet request ratio", "Packet resend request limit as a percentage of frame packet number", 0.0, 2.0, ARV_GV_STREAM_PACKET_REQUEST_RATIO_DEFAULT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:initial-packet-timeout: * * Delay before asking for a packet resend after the packet was detected missing for the first time. The reason * for this delay is, depending on the network topology, stream packets are not always received in increasing id * order. As the missing packet detection happens at each received packet, by verifying if each previous packet * has been received, we could emit useless packet resend requests if they are not ordered. * * Since: 0.8.15 */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_INITIAL_PACKET_TIMEOUT, g_param_spec_uint ("initial-packet-timeout", "Initial packet timeout", "Initial packet timeout, in µs", 0, G_MAXUINT, ARV_GV_STREAM_INITIAL_PACKET_TIMEOUT_US_DEFAULT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:packet-timeout: * * Timeout while waiting for a packet after a resend request, before asking again. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_PACKET_TIMEOUT, g_param_spec_uint ("packet-timeout", "Packet timeout", "Packet timeout, in µs", 0, G_MAXUINT, ARV_GV_STREAM_PACKET_TIMEOUT_US_DEFAULT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); /** * ArvGvStream:frame-retention: * * Amount of time Aravis is wating for frame completion after the last packet is received. A greater value will * also increase the maximum frame latency in case of missing packets. */ g_object_class_install_property ( object_class, ARV_GV_STREAM_PROPERTY_FRAME_RETENTION, g_param_spec_uint ("frame-retention", "Frame retention", "Packet retention, in µs", 0, G_MAXUINT, ARV_GV_STREAM_FRAME_RETENTION_US_DEFAULT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); } aravis-0.8.34/src/arvgvstream.h000066400000000000000000000050621475431451200164050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GV_STREAM_H #define ARV_GV_STREAM_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS /** * ArvGvStreamOption: * @ARV_GV_STREAM_OPTION_NONE: no option specified * @ARV_GV_STREAM_OPTION_PACKET_SOCKET_DISABLED: use of packet socket is disabled */ typedef enum { ARV_GV_STREAM_OPTION_NONE = 0, ARV_GV_STREAM_OPTION_PACKET_SOCKET_DISABLED = 1 << 0, } ArvGvStreamOption; /** * ArvGvStreamSocketBuffer: * @ARV_GV_STREAM_SOCKET_BUFFER_FIXED: socket buffer is set using [property@Aravis.GvStream:socket-buffer-size] value * @ARV_GV_STREAM_SOCKET_BUFFER_AUTO: socket buffer size is set to the payload size if * [property@Aravis.GvStream:socket-buffer-size] is not strictly positive, or the minimum of both values */ typedef enum { ARV_GV_STREAM_SOCKET_BUFFER_FIXED, ARV_GV_STREAM_SOCKET_BUFFER_AUTO } ArvGvStreamSocketBuffer; /** * ArvGvStreamPacketResend: * @ARV_GV_STREAM_PACKET_RESEND_NEVER: never request a packet resend * @ARV_GV_STREAM_PACKET_RESEND_ALWAYS: request a packet resend if a packet was missing */ typedef enum { ARV_GV_STREAM_PACKET_RESEND_NEVER, ARV_GV_STREAM_PACKET_RESEND_ALWAYS } ArvGvStreamPacketResend; #define ARV_TYPE_GV_STREAM (arv_gv_stream_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvGvStream, arv_gv_stream, ARV, GV_STREAM, ArvStream) ARV_API guint16 arv_gv_stream_get_port (ArvGvStream *gv_stream); ARV_API void arv_gv_stream_get_statistics (ArvGvStream *gv_stream, guint64 *n_resent_packets, guint64 *n_missing_packets); G_END_DECLS #endif aravis-0.8.34/src/arvgvstreamprivate.h000066400000000000000000000027121475431451200177770ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_GV_STREAM_PRIVATE_H #define ARV_GV_STREAM_PRIVATE_H #include #include #include G_BEGIN_DECLS #define ARV_GV_STREAM_POLL_TIMEOUT_US 1000000 #define ARV_GV_STREAM_INITIAL_PACKET_TIMEOUT_US_DEFAULT 1000 #define ARV_GV_STREAM_PACKET_TIMEOUT_US_DEFAULT 20000 #define ARV_GV_STREAM_FRAME_RETENTION_US_DEFAULT 100000 #define ARV_GV_STREAM_PACKET_REQUEST_RATIO_DEFAULT 0.25 ArvStream * arv_gv_stream_new (ArvGvDevice *gv_device, ArvStreamCallback callback, void *callback_data, GDestroyNotify destroy, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvinterface.c000066400000000000000000000265301475431451200165130ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvinterface * @short_description: Abstract base class for camera discovery * * #ArvInterface is an abstract base class for camera discovery. It maintains a * list of the available devices and helps to instantiate the corresponding * #ArvDevice objects. If the user already knows the device id of the device, he should * not worry about this class and just use arv_camera_new() or * arv_open_device(). */ #include typedef struct { GArray *device_ids; int flags; } ArvInterfacePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvInterface, arv_interface, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvInterface)) static void arv_interface_clear_device_ids (ArvInterface *iface) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); unsigned int i; for (i = 0; i < priv->device_ids->len; i++) { g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->device); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->physical); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->address); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->vendor); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->manufacturer_info); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->model); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)->serial_nbr); g_free (g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, i)); } g_array_set_size (priv->device_ids, 0); } static gint _compare_device_ids (ArvInterfaceDeviceIds **a, ArvInterfaceDeviceIds **b) { if (*a == NULL || (*a)->device == NULL) return -1; if (*b == NULL || (*b)->device == NULL) return 1; return g_ascii_strcasecmp ((*a)->device, (*b)->device); } /** * arv_interface_update_device_list: * @iface: a #ArvInterface * * Updates the internal list of available devices. This may change the * connection between a list index and a device ID. * * Since: 0.2.0 */ void arv_interface_update_device_list (ArvInterface *iface) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_if_fail (ARV_IS_INTERFACE (iface)); arv_interface_clear_device_ids (iface); ARV_INTERFACE_GET_CLASS (iface)->update_device_list (iface, priv->device_ids); g_array_sort (priv->device_ids, (GCompareFunc) _compare_device_ids); } void arv_interface_set_flags (ArvInterface *iface, int flags) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_if_fail (ARV_IS_INTERFACE (iface)); priv->flags = flags; } int arv_interface_get_flags (ArvInterface *iface) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); return priv->flags; } /** * arv_interface_get_n_devices: * @iface: a #ArvInterface * * Queries the number of available devices on this interface. Prior to this * call the @arv_interface_update_device_list function must be called. The list content will not * change until the next call of the update function. * * Returns: the number of available devices * * Since: 0.2.0 */ unsigned int arv_interface_get_n_devices (ArvInterface *iface) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); return priv->device_ids->len; } /** * arv_interface_get_device_id: * @iface: a #ArvInterface * @index: device index * * Queries the unique device id corresponding to index. Prior to this * call the arv_interface_update_device_list() function must be called. * * Returns: a unique device id * * Since: 0.2.0 */ const char * arv_interface_get_device_id (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->device; } /** * arv_interface_get_device_physical_id: * @iface: a #ArvInterface * @index: device index * * Queries the physical device id corresponding to index such * as the MAC address for Ethernet based devices, bus id for PCI, * USB or Firewire based devices. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: a physical device id * * Since: 0.2.0 */ const char * arv_interface_get_device_physical_id (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->physical; } /** * arv_interface_get_device_address: * @iface: a #ArvInterface * @index: device index * * queries the device address (IP address in the case of an ethernet camera). Useful * for constructing manual connections to devices using @arv_gv_device_new * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device address * * Since: 0.2.0 */ const char * arv_interface_get_device_address (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->address; } /** * arv_interface_get_device_vendor: * @iface: a #ArvInterface * @index: device index * * Queries the device vendor. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device vendor, NULL on error * * Since: 0.6.0 */ const char * arv_interface_get_device_vendor (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->vendor; } /** * arv_interface_get_device_manufacturer_info: * @iface: a #ArvInterface * @index: device index * * Queries the device manufacturer info. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device manufacturer info, NULL on error * * Since: 0.8.20 */ const char * arv_interface_get_device_manufacturer_info (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->manufacturer_info; } /** * arv_interface_get_device_model: * @iface: a #ArvInterface * @index: device index * * Queries the device model. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device model, NULL on error * * Since: 0.6.0 */ const char * arv_interface_get_device_model (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->model; } /** * arv_interface_get_device_serial_nbr: * @iface: a #ArvInterface * @index: device index * * Queries the device serial. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device serial, NULL on error * * Since: 0.6.0 */ const char * arv_interface_get_device_serial_nbr (ArvInterface *iface, unsigned int index) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); g_return_val_if_fail (ARV_IS_INTERFACE (iface), 0); g_return_val_if_fail (priv->device_ids != NULL, 0); if (index >= priv->device_ids->len) return NULL; return g_array_index (priv->device_ids, ArvInterfaceDeviceIds *, index)->serial_nbr; } /** * arv_interface_get_device_protocol: * @iface: a #ArvInterface * @index: device index * * Queries the device protocol. Possible values are 'USB3Vision', 'GigEVision' * and 'Fake'. * * Prior to this call the arv_interface_update_device_list() * function must be called. * * Returns: (transfer none): the device protocol as a string, NULL on error * * Since: 0.6.0 */ const char * arv_interface_get_device_protocol (ArvInterface *iface, unsigned int index) { g_return_val_if_fail (ARV_IS_INTERFACE (iface), NULL); return ARV_INTERFACE_GET_CLASS (iface)->protocol; } /** * arv_interface_open_device: * @iface: a #ArvInterface * @device_id: (allow-none): device unique id * @error: a #GError placeholder, %NULL to ignore * * Creates a new #ArvDevice object corresponding to the given device id string. * The first available device is returned if @device_id is %NULL. * * Returns: (transfer full): a new #ArvDevice * * Since: 0.2.0 */ ArvDevice * arv_interface_open_device (ArvInterface *iface, const char *device_id, GError **error) { g_return_val_if_fail (ARV_IS_INTERFACE (iface), NULL); return ARV_INTERFACE_GET_CLASS (iface)->open_device (iface, device_id, error); } static void arv_interface_init (ArvInterface *iface) { ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); priv->device_ids = g_array_new (FALSE, TRUE, sizeof (ArvInterfaceDeviceIds *)); } static void arv_interface_finalize (GObject *object) { ArvInterface *iface = ARV_INTERFACE (object); ArvInterfacePrivate *priv = arv_interface_get_instance_private (iface); G_OBJECT_CLASS (arv_interface_parent_class)->finalize (object); arv_interface_clear_device_ids (iface); g_array_free (priv->device_ids, TRUE); priv->device_ids = NULL; } static void arv_interface_class_init (ArvInterfaceClass *interface_class) { GObjectClass *object_class = G_OBJECT_CLASS (interface_class); object_class->finalize = arv_interface_finalize; } aravis-0.8.34/src/arvinterface.h000066400000000000000000000053051475431451200165150ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_INTERFACE_H #define ARV_INTERFACE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_INTERFACE (arv_interface_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvInterface, arv_interface, ARV, INTERFACE, GObject) struct _ArvInterfaceClass { GObjectClass parent_class; void (*update_device_list) (ArvInterface *iface, GArray *device_ids); ArvDevice * (*open_device) (ArvInterface *iface, const char *device_id, GError **error); const char * protocol; }; ARV_API void arv_interface_update_device_list (ArvInterface *iface); ARV_API unsigned int arv_interface_get_n_devices (ArvInterface *iface); ARV_API const char * arv_interface_get_device_id (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_physical_id (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_address (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_vendor (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_manufacturer_info (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_model (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_serial_nbr (ArvInterface *iface, unsigned int index); ARV_API const char * arv_interface_get_device_protocol (ArvInterface *iface, unsigned int index); ARV_API ArvDevice * arv_interface_open_device (ArvInterface *iface, const char *device_id, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvinterfaceprivate.h000066400000000000000000000032201475431451200201020ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_INTERFACE_PRIVATE_H #define ARV_INTERFACE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define ARV_DEVICE_NAME_ILLEGAL_CHARACTERS "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" #define ARV_DEVICE_NAME_REPLACEMENT_CHARACTER '\0' typedef struct { char *device; char *physical; char *address; char *vendor; char *manufacturer_info; char *model; char *serial_nbr; } ArvInterfaceDeviceIds; void arv_interface_set_flags (ArvInterface *iface, int flags); int arv_interface_get_flags (ArvInterface *iface); G_END_DECLS #endif aravis-0.8.34/src/arvmisc.c000066400000000000000000001006411475431451200155020ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include #ifdef G_OS_WIN32 #include #endif /** * arv_get_major_version: * * Returns the major version number of the Aravis library. * * For example, in Aravis version 0.8.20 this is 0. * * This function is in the library, so it represents the Aravis library * your code is running against. Contrast with the %ARAVIS_MAJOR_VERSION * macro, which represents the major version of the Aravis headers you * have included when compiling your code. * * Returns: the major version number of the Aravis library * * Since: 0.8.20 */ guint arv_get_major_version (void) { return ARAVIS_MAJOR_VERSION; } /** * arv_get_minor_version: * * Returns the minor version number of the Aravis library. * * For example, in Aravis version 0.8.20 this is 8. * * This function is in the library, so it represents the Aravis library * your code is running against. Contrast with the %ARAVIS_MINOR_VERSION * macro, which represents the minor version of the Aravis headers you * have included when compiling your code. * * Returns: the minor version number of the Aravis library * * Since: 0.8.20 */ guint arv_get_minor_version (void) { return ARAVIS_MINOR_VERSION; } /** * arv_get_micro_version: * * Returns the micro version number of the Aravis library. * * For example, in Aravis version 0.8.20 this is 20. * * This function is in the library, so it represents the Aravis library * your code is running against. Contrast with the %ARAVIS_MICRO_VERSION * macro, which represents the micro version of the Aravis headers you * have included when compiling your code. * * Returns: the micro version number of the Aravis library * * Since: 0.8.20 */ guint arv_get_micro_version (void) { return ARAVIS_MICRO_VERSION; } typedef struct _ArvHistogramVariable ArvHistogramVariable; struct _ArvHistogramVariable { char *name; guint64 counter; guint64 and_more; guint64 and_less; guint64 last_seen_maximum; double maximum; double minimum; guint64 *bins; }; struct _ArvHistogram { guint n_variables; guint n_bins; double bin_step; double offset; ArvHistogramVariable *variables; gint ref_count; }; /** * arv_histogram_new: (skip) * @n_variables: number of variables * @n_bins: number of bins for each histogram * @bin_step: bin step * @offset: offset of the first bin * Return value: a new #ArvHistogram structure */ ArvHistogram * arv_histogram_new (unsigned int n_variables, unsigned n_bins, double bin_step, double offset) { ArvHistogram *histogram; unsigned int i; g_return_val_if_fail (n_variables > 0, NULL); g_return_val_if_fail (n_bins > 0, NULL); g_return_val_if_fail (bin_step > 0, NULL); histogram = g_new0 (ArvHistogram, 1); histogram->ref_count = 1; histogram->n_variables = n_variables; histogram->n_bins = n_bins; histogram->bin_step = bin_step; histogram->offset = offset; histogram->variables = g_new0 (ArvHistogramVariable, n_variables); for (i = 0; i < histogram->n_variables; i++) { histogram->variables[i].name = g_strdup_printf ("var%d", i); histogram->variables[i].bins = g_new (guint64, histogram->n_bins); } arv_histogram_reset (histogram); return histogram; } ArvHistogram * arv_histogram_ref (ArvHistogram *histogram) { g_return_val_if_fail (histogram != NULL, NULL); g_atomic_int_inc (&histogram->ref_count); return histogram; } void arv_histogram_unref (ArvHistogram *histogram) { g_return_if_fail (histogram != NULL); if (g_atomic_int_dec_and_test (&histogram->ref_count)) { guint j; if (histogram->variables != NULL) { for (j = 0; j < histogram->n_variables && histogram->variables[j].bins != NULL; j++) { g_free (histogram->variables[j].name); g_free (histogram->variables[j].bins); } g_free (histogram->variables); } g_free (histogram); } } GType arv_histogram_get_type (void) { GType type_id = 0; if (type_id == 0) type_id = g_pointer_type_register_static ("ArvHistogram"); return type_id; } void arv_histogram_reset (ArvHistogram *histogram) { ArvHistogramVariable *variable; int i, j; g_return_if_fail (histogram != NULL); for (j = 0; j < histogram->n_variables; j++) { variable = &histogram->variables[j]; variable->minimum = G_MAXDOUBLE; variable->maximum = -G_MAXDOUBLE; variable->last_seen_maximum = 0; variable->and_more = variable->and_less = 0; variable->counter = 0; for (i = 0; i < histogram->n_bins; i++) variable->bins[i] = 0; } } void arv_histogram_set_variable_name (ArvHistogram *histogram, unsigned int id, char const *name) { g_return_if_fail (histogram != NULL); g_return_if_fail (id < histogram->n_variables); g_free (histogram->variables[id].name); histogram->variables[id].name = g_strdup (name); } gboolean arv_histogram_fill (ArvHistogram *histogram, guint id, int value) { ArvHistogramVariable *variable; unsigned int class; g_return_val_if_fail (histogram != NULL, FALSE); g_return_val_if_fail (id < histogram->n_variables, FALSE); variable = &histogram->variables[id]; if (variable->minimum > value) variable->minimum = value; if (variable->maximum < value) { variable->maximum = value; variable->last_seen_maximum = variable->counter; } class = (value - histogram->offset) / histogram->bin_step; if (value < histogram->offset) variable->and_less++; else if (class >= histogram->n_bins) variable->and_more++; else variable->bins[class]++; variable->counter++; return TRUE; } char * arv_histogram_to_string (const ArvHistogram *histogram) { int i, j, bin_max; gboolean max_found = FALSE; GString *string; g_return_val_if_fail (histogram != NULL, NULL); string = g_string_new (""); bin_max = 0; for (i = histogram->n_bins - 1; i > 0 && !max_found; i--) { for (j = 0; j < histogram->n_variables && !max_found; j++) { if (histogram->variables[j].bins[i] != 0) { bin_max = i; max_found = TRUE; } } } if (bin_max >= histogram->n_bins) bin_max = histogram->n_bins - 1; for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append (string, " bins "); g_string_append_printf (string, ";%12.12s", histogram->variables[j].name != NULL ? histogram->variables[j].name : " ---- "); } g_string_append (string, "\n"); for (i = 0; i <= bin_max; i++) { for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append_printf (string, "%12g", (double) i * histogram->bin_step + histogram->offset); g_string_append_printf (string, ";%12llu", (unsigned long long) histogram->variables[j].bins[i]); } g_string_append (string, "\n"); } g_string_append (string, "-------------\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append_printf (string, ">=%10g", (double) i * histogram->bin_step + histogram->offset); g_string_append_printf (string, ";%12llu", (unsigned long long) histogram->variables[j].and_more); } g_string_append (string, "\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append_printf (string, "< %10g", histogram->offset); g_string_append_printf (string, ";%12" G_GUINT64_FORMAT , histogram->variables[j].and_less); } g_string_append (string, "\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append (string, "min "); if (histogram->variables[j].minimum != G_MAXDOUBLE) g_string_append_printf (string, "%c%12g", j == 0 ? ':' : ';', histogram->variables[j].minimum); else g_string_append_printf (string, "%c%12s", j == 0 ? ':' : ';', "n/a"); } g_string_append (string, "\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append (string, "max "); if (histogram->variables[j].maximum != -G_MAXDOUBLE) g_string_append_printf (string, "%c%12g", j == 0 ? ':' : ';', histogram->variables[j].maximum); else g_string_append_printf (string, "%c%12s", j == 0 ? ':' : ';', "n/a"); } g_string_append (string, "\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append (string, "last max at "); g_string_append_printf (string, "%c%12" G_GUINT64_FORMAT, j == 0 ? ':' : ';', histogram->variables[j].last_seen_maximum); } g_string_append (string, "\n"); for (j = 0; j < histogram->n_variables; j++) { if (j == 0) g_string_append (string, "counter "); g_string_append_printf (string, ":%12llu", (unsigned long long) histogram->variables[j].counter); } return arv_g_string_free_and_steal(string); } ArvValue * arv_value_new_double (double v_double) { ArvValue *value = g_new (ArvValue, 1); value->type = G_TYPE_DOUBLE; value->data.v_double = v_double; return value; } ArvValue * arv_value_new_int64 (gint64 v_int64) { ArvValue *value = g_new (ArvValue, 1); value->type = G_TYPE_INT64; value->data.v_int64 = v_int64; return value; } void arv_value_free (ArvValue *value) { g_free (value); } void arv_value_copy (ArvValue *to, const ArvValue *from) { *to = *from; } static ArvValue * arv_value_duplicate (const ArvValue *from) { ArvValue *value; if (from == NULL) return NULL; value = g_new (ArvValue, 1); *value = *from; return value; } GType arv_value_get_type (void) { GType type_id = 0; if (type_id == 0) type_id = g_boxed_type_register_static ("ArvValue", (GBoxedCopyFunc) arv_value_duplicate, (GBoxedFreeFunc) arv_value_free); return type_id; } void arv_value_set_int64 (ArvValue *value, gint64 v_int64) { value->type = G_TYPE_INT64; value->data.v_int64 = v_int64; } void arv_value_set_double (ArvValue *value, double v_double) { value->type = G_TYPE_DOUBLE; value->data.v_double = v_double; } gint64 arv_value_get_int64 (ArvValue *value) { if (value->type == G_TYPE_INT64) return value->data.v_int64; else return (gint64) value->data.v_double; } double arv_value_get_double (ArvValue *value) { if (value->type == G_TYPE_INT64) return (double) value->data.v_int64; else return value->data.v_double; } gboolean arv_value_holds_int64 (ArvValue *value) { return value->type == G_TYPE_INT64; } double arv_value_holds_double (ArvValue *value) { return value->type == G_TYPE_DOUBLE; } void arv_copy_memory_with_endianness (void *to, size_t to_size, guint to_endianness, void *from, size_t from_size, guint from_endianness) { char *to_ptr; char *from_ptr; int i; g_return_if_fail (to != NULL); g_return_if_fail (from != NULL); if (to_endianness == G_LITTLE_ENDIAN && from_endianness == G_BIG_ENDIAN) { to_ptr = to; from_ptr = ((char *) from) + from_size - 1; if (to_size <= from_size) { for (i = 0; i < to_size; i++, to_ptr++, from_ptr--) *to_ptr = *from_ptr; } else { for (i = 0; i < from_size; i++, to_ptr++, from_ptr--) *to_ptr = *from_ptr; memset (((char *) to) + from_size, 0, to_size - from_size); } } else if (to_endianness == G_BIG_ENDIAN && from_endianness == G_LITTLE_ENDIAN) { to_ptr = ((char *) to) + to_size - 1; from_ptr = from; if (to_size <= from_size) { for (i = 0; i < to_size; i++, to_ptr--, from_ptr++) *to_ptr = *from_ptr; } else { for (i = 0; i < from_size; i++, to_ptr--, from_ptr++) *to_ptr = *from_ptr; memset (to, 0, to_size - from_size); } } else if (to_endianness == G_LITTLE_ENDIAN && from_endianness == G_LITTLE_ENDIAN) { if (to_size <= from_size) memcpy (to, from, to_size); else { memcpy (to, from, from_size); memset (((char *) to) + from_size, 0, to_size - from_size); } } else if (to_endianness == G_BIG_ENDIAN && from_endianness == G_BIG_ENDIAN) { if (to_size <= from_size) memcpy (to, ((char *) from) + from_size - to_size, to_size); else { memcpy (((char *) to) + to_size - from_size, from, from_size); memset (to, 0, to_size - from_size); } } else g_assert_not_reached (); } #define ARV_DECOMPRESS_CHUNK 16384 /** * arv_decompress: * @input_buffer: compressed data * @input_buffer: size of compressed data * @output_size: (out): placeholder for inflated data * Return value: (transfer full): a newly allocated buffer **/ void * arv_decompress (void *input_buffer, size_t input_size, size_t *output_size) { z_stream stream; GByteArray *output; guchar z_stream_output[ARV_DECOMPRESS_CHUNK]; unsigned have; int result; g_return_val_if_fail (input_buffer != NULL, NULL); g_return_val_if_fail (input_size > 0, NULL); /* allocate inflate state */ stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; stream.avail_in = 0; stream.next_in = Z_NULL; stream.data_type = Z_UNKNOWN; g_return_val_if_fail (inflateInit2(&stream, -MAX_WBITS) == Z_OK, NULL); output = g_byte_array_new (); /* decompress until deflate stream ends or end of file */ do { stream.avail_in = MIN (input_size, ARV_DECOMPRESS_CHUNK); stream.next_in = input_buffer; arv_info_misc ("[Decompress] Input ptr = 0x%p - Chunk size = %d - %c", (void *) stream.next_in, stream.avail_in, *stream.next_in); input_size -= stream.avail_in; input_buffer = ((char *) input_buffer) + stream.avail_in; /* run inflate() on input until output buffer not full */ do { stream.avail_out = ARV_DECOMPRESS_CHUNK; stream.next_out = z_stream_output; result = inflate(&stream, Z_NO_FLUSH); if (result == Z_STREAM_ERROR) { arv_warning_misc ("[Decompress] Z_STREAM_ERROR"); goto CLEANUP; } switch (result) { case Z_NEED_DICT: arv_warning_misc ("[Decompress] Z_NEED_DICT"); goto CLEANUP; case Z_DATA_ERROR: arv_warning_misc ("[Decompress] Z_DATA_ERROR"); goto CLEANUP; case Z_MEM_ERROR: arv_warning_misc ("[Decompress] Z_MEM_ERROR"); goto CLEANUP; } have = ARV_DECOMPRESS_CHUNK - stream.avail_out; g_byte_array_append (output, z_stream_output, have); } while (stream.avail_out == 0); /* done when inflate() says it's done */ } while (input_size > 0 && result != Z_STREAM_END); /* clean up and return */ inflateEnd(&stream); if (result != Z_STREAM_END) { arv_warning_misc ("[Decompress] !Z_STREAM_END"); g_byte_array_free (output, TRUE); if (output_size != NULL) *output_size = 0; return NULL; } if (output_size != NULL) *output_size = output->len; return g_byte_array_free (output, FALSE); CLEANUP: if (output_size != NULL) *output_size = 0; g_byte_array_free (output, TRUE); inflateEnd(&stream); return NULL; } /** * SECTION: arvgst * @short_description: Gstreamer utilities */ #define ARV_MAKE_FOURCC(a,b,c,d) ((guint32)((a)|(b)<<8|(c)<<16|(d)<<24)) typedef struct { ArvPixelFormat pixel_format; const char *gst_caps_string; const char *name; const char *format; const char *gst_0_10_caps_string; const char *name_0_10; int bpp; int depth; guint32 fourcc; } ArvGstCapsInfos; ArvGstCapsInfos arv_gst_caps_infos[] = { { ARV_PIXEL_FORMAT_MONO_8, "video/x-raw, format=(string)GRAY8", "video/x-raw", "GRAY8", "video/x-raw-gray, bpp=(int)8, depth=(int)8", "video/x-raw-gray", 8, 8, 0 }, { ARV_PIXEL_FORMAT_MONO_16, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)16", "video/x-raw-gray", 16, 16, 0 }, { ARV_PIXEL_FORMAT_MONO_12, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)12", "video/x-raw-gray", 16, 12, 0 }, { ARV_PIXEL_FORMAT_MONO_12_PACKED, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)12, depth=(int)12", "video/x-raw-gray", 12, 12, 0 }, { ARV_PIXEL_FORMAT_MONO_14, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)14", "video/x-raw-gray", 16, 14, 0 }, { ARV_PIXEL_FORMAT_MONO_10, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)10", "video/x-raw-gray", 16, 10, 0 }, { ARV_PIXEL_FORMAT_BAYER_GR_8, "video/x-bayer, format=(string)grbg", "video/x-bayer", "grbg", "video/x-raw-bayer, format=(string)grbg, bpp=(int)8, depth=(int)8", "video/x-raw-bayer", 8, 8, ARV_MAKE_FOURCC ('g','r','b','g') }, { ARV_PIXEL_FORMAT_BAYER_RG_8, "video/x-bayer, format=(string)rggb", "video/x-bayer", "rggb", "video/x-raw-bayer, format=(string)rggb, bpp=(int)8, depth=(int)8", "video/x-raw-bayer", 8, 8, ARV_MAKE_FOURCC ('r','g','g','b') }, { ARV_PIXEL_FORMAT_BAYER_GB_8, "video/x-bayer, format=(string)gbrg", "video/x-bayer", "gbrg", "video/x-raw-bayer, format=(string)gbrg, bpp=(int)8, depth=(int)8", "video/x-raw-bayer", 8, 8, ARV_MAKE_FOURCC ('g','b','r','g') }, { ARV_PIXEL_FORMAT_BAYER_BG_8, "video/x-bayer, format=(string)bggr", "video/x-bayer", "bggr", "video/x-raw-bayer, format=(string)bggr, bpp=(int)8, depth=(int)8", "video/x-raw-bayer", 8, 8, ARV_MAKE_FOURCC ('b','g','g','r') }, /* Non 8bit bayer formats are not supported by gstreamer bayer plugin. * This feature is discussed in bug https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/86 .*/ { ARV_PIXEL_FORMAT_YUV_422_PACKED, "video/x-raw, format=(string)UYVY", "video/x-raw", "UYVY", "video/x-raw-yuv, format=(fourcc)UYVY", "video/x-raw-yuv", 0, 0, ARV_MAKE_FOURCC ('U','Y','V','Y') }, { ARV_PIXEL_FORMAT_YUV_422_YUYV_PACKED, "video/x-raw, format=(string)YUY2", "video/x-raw", "YUY2", "video/x-raw-yuv, format=(fourcc)YUYU2", "video/x-raw-yuv", 0, 0, ARV_MAKE_FOURCC ('Y','U','Y','2') }, { ARV_PIXEL_FORMAT_RGB_8_PACKED, "video/x-raw, format=(string)RGB", "video/x-raw", "RGB", "video/x-raw-rgb, format=(string)RGB, bpp=(int)24, depth=(int)8", "video/x-raw-rgb", 24, 24, 0 }, { ARV_PIXEL_FORMAT_RGBA_8_PACKED, "video/x-raw, format=(string)RGBA", "video/x-raw", "RGBA", "video/x-raw-rgba, format=(string)RGBA, bpp=(int)32, depth=(int)8", "video/x-raw-rgba", 32, 8, 0 }, { ARV_PIXEL_FORMAT_CUSTOM_YUV_422_YUYV_PACKED, "video/x-raw, format=(string)YUY2", "video/x-raw", "YUY2", "video/x-raw-yuv, format=(fourcc)YUYU2", "video/x-raw-yuv", 0, 0, ARV_MAKE_FOURCC ('Y','U','Y','2') }, { ARV_PIXEL_FORMAT_COORD3D_A_8, "video/x-raw, format=(string)GRAY8", "video/x-raw", "GRAY8", "video/x-raw-gray, bpp=(int)8, depth=(int)8", "video/x-raw-gray", 8, 8, 0 }, { ARV_PIXEL_FORMAT_COORD3D_B_8, "video/x-raw, format=(string)GRAY8", "video/x-raw", "GRAY8", "video/x-raw-gray, bpp=(int)8, depth=(int)8", "video/x-raw-gray", 8, 8, 0 }, { ARV_PIXEL_FORMAT_COORD3D_C_8, "video/x-raw, format=(string)GRAY8", "video/x-raw", "GRAY8", "video/x-raw-gray, bpp=(int)8, depth=(int)8", "video/x-raw-gray", 8, 8, 0 }, { ARV_PIXEL_FORMAT_COORD3D_A_16, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)16", "video/x-raw-gray", 16, 16, 0 }, { ARV_PIXEL_FORMAT_COORD3D_B_16, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)16", "video/x-raw-gray", 16, 16, 0 }, { ARV_PIXEL_FORMAT_COORD3D_C_16, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", "video/x-raw-gray, bpp=(int)16, depth=(int)16", "video/x-raw-gray", 16, 16, 0 }, }; typedef struct { const char *vendor_selection; const char *model_selection; } ArvBigEndianPixelFormatInfos; /* Some cameras seem to incorrectly transmit multi bytes image data in big endian order, even if GevSCPSBigEndian is not * set. In this case, we need to convert the pixel format to a big endian GstCaps. We maintain a list of such cameras * here. */ static const ArvBigEndianPixelFormatInfos arv_big_endian_pixel_format_infos[] = { { .vendor_selection = "Point Grey Research", .model_selection = "Blackfly BFLY-PGE-31S4M" }, { .vendor_selection = "Point Grey Research", .model_selection = "Blackfly BFLY-PGE-14S2C" } }; /* Only Mono16 is handled here, as we did not seen other cases of misbehaving devices with other pixel format for now. */ ArvGstCapsInfos big_endian_gst_caps_infos[] = { { ARV_PIXEL_FORMAT_MONO_16, "video/x-raw, format=(string)GRAY16_BE", "video/x-raw", "GRAY16_BE", "video/x-raw-gray, bpp=(int)16, depth=(int)16", "video/x-raw-gray", 16, 16, 0 }, }; static gboolean _use_big_endian_pixel_format (const char *vendor_name, const char *model_name) { guint i; for (i = 0; i < G_N_ELEMENTS (arv_big_endian_pixel_format_infos); i++) { if ((g_pattern_match_simple (arv_big_endian_pixel_format_infos[i].vendor_selection, vendor_name) == TRUE) && (g_pattern_match_simple (arv_big_endian_pixel_format_infos[i].model_selection, model_name) == TRUE)) { return TRUE; } } return FALSE; } static const char * pixel_format_to_gst_caps_string (ArvPixelFormat pixel_format, const ArvGstCapsInfos *infos, guint n_elements) { while (n_elements > 0) { if (infos->pixel_format == pixel_format) break; infos ++; n_elements --; } if (n_elements == 0) { return NULL; } arv_debug_misc ("[PixelFormat::to_gst_caps_string] 0x%08x -> %s", pixel_format, infos->gst_caps_string); return infos->gst_caps_string; } /** * arv_pixel_format_to_gst_caps_string: * @pixel_format: a pixel format * Return value: a gstreamer caps string describing the given @pixel_format. */ const char * arv_pixel_format_to_gst_caps_string (ArvPixelFormat pixel_format) { const char *caps_string; caps_string = pixel_format_to_gst_caps_string(pixel_format, arv_gst_caps_infos, G_N_ELEMENTS (arv_gst_caps_infos)); if (caps_string == NULL) { arv_warning_misc ("[PixelFormat::to_gst_caps_string] 0x%08x not found", pixel_format); } return caps_string; } #include /** * arv_pixel_format_to_gst_caps_string_full: * @pixel_format: a pixel format * @vendor_name: vendor name * @model_name: model name * Return value: (nullable): a gstreamer caps string describing the given @pixel_format, NULL if not found. * * The returned caps string defines a little endian pixel format, unless an exception has been detected. In this case, a * big endian caps is returned. * * Since: 0.8.32 */ const char * arv_pixel_format_to_gst_caps_string_full (ArvPixelFormat pixel_format, const char *vendor_name, const char *model_name) { const char *caps_string; caps_string = NULL; if (_use_big_endian_pixel_format (vendor_name, model_name)) caps_string = pixel_format_to_gst_caps_string (pixel_format, big_endian_gst_caps_infos, G_N_ELEMENTS (big_endian_gst_caps_infos)); if (caps_string != NULL) { return caps_string; } return arv_pixel_format_to_gst_caps_string (pixel_format); } ArvPixelFormat arv_pixel_format_from_gst_caps (const char *name, const char *format, int bpp, int depth) { unsigned int i; g_return_val_if_fail (name != NULL, 0); for (i = 0; i < G_N_ELEMENTS (arv_gst_caps_infos); i++) { if (strcmp (name, arv_gst_caps_infos[i].name) != 0 || (depth > 0 && depth != arv_gst_caps_infos[i].depth) || (bpp > 0 && bpp != arv_gst_caps_infos[i].bpp)) continue; if (strcmp (name, "video/x-raw") == 0 && strcmp (format, arv_gst_caps_infos[i].format) == 0) return arv_gst_caps_infos[i].pixel_format; if (strcmp (name, "video/x-bayer") == 0 && strcmp (format, arv_gst_caps_infos[i].format) == 0) return arv_gst_caps_infos[i].pixel_format; } return 0; } const char * arv_pixel_format_to_gst_0_10_caps_string (ArvPixelFormat pixel_format) { int i; for (i = 0; i < G_N_ELEMENTS (arv_gst_caps_infos); i++) if (arv_gst_caps_infos[i].pixel_format == pixel_format) break; if (i == G_N_ELEMENTS (arv_gst_caps_infos)) { arv_warning_misc ("[PixelFormat::to_gst_0_10_caps_string] 0x%08x not found", pixel_format); return NULL; } arv_debug_misc ("[PixelFormat::to_gst_0_10_caps_string] 0x%08x -> %s", pixel_format, arv_gst_caps_infos[i].gst_0_10_caps_string); return arv_gst_caps_infos[i].gst_0_10_caps_string; } ArvPixelFormat arv_pixel_format_from_gst_0_10_caps (const char *name, int bpp, int depth, guint32 fourcc) { unsigned int i; g_return_val_if_fail (name != NULL, 0); for (i = 0; i < G_N_ELEMENTS (arv_gst_caps_infos); i++) { if (strcmp (name, arv_gst_caps_infos[i].name_0_10) != 0) continue; if (strcmp (name, "video/x-raw-yuv") == 0 && (fourcc <= 0 || fourcc == arv_gst_caps_infos[i].fourcc)) return arv_gst_caps_infos[i].pixel_format; if ((depth <= 0 || depth == arv_gst_caps_infos[i].depth) && (bpp <= 0 || bpp == arv_gst_caps_infos[i].bpp) && fourcc == arv_gst_caps_infos[i].fourcc) return arv_gst_caps_infos[i].pixel_format; } return 0; } static struct { const char *vendor; const char *alias; } vendor_aliases[] = { { "The Imaging Source Europe GmbH", "TIS"}, { "Point Grey Research", "PointGrey"}, { "Lucid Vision Labs", "LucidVision"}, { "New-Imaging-Technologies", "NIT"} }; /** * arv_vendor_alias_lookup: * @vendor: a vendor string * * Returns: vendor alias string if found, or @vendor if not found. * * Since: 0.6.0 */ const char * arv_vendor_alias_lookup (const char *vendor) { int i; if (vendor == NULL) return NULL; for (i = 0; i < G_N_ELEMENTS (vendor_aliases); i++) if (g_strcmp0 (vendor_aliases[i].vendor, vendor) == 0) return vendor_aliases[i].alias; return vendor; } /** * arv_parse_genicam_url: * @url: a genicam data URL * @url_length: length of the URL string, -1 if NULL terminated * @scheme: (allow-none): placeholder for scheme * @authority: (allow-none): placeholder for authority * @path: (allow-none): placeholder for path * @query: (allow-none): placeholder for query * @fragment: (allow-none): placeholder for fragment * @address: (allow-none): placeholder for data adress in case of local URL * @size: (allow-none): placeholder for data size in case of local URL * * Parse the Genicam data URL. The URL should at least contain a scheme and a path. If scheme is 'local', the path * should also define the Genicam data address and size in the device memory. * * The placeholder are at least set to %NULL or 0 if they are valid addresses, and set to the corresponding URL part if * found in the string. The returned strings must be freed using g_free(). * * Returns: %TRUE if the URL was successfully parsed. */ gboolean arv_parse_genicam_url (const char *url, gssize url_length, char **scheme, char **authority, char **path, char **query, char **fragment, guint64 *address, guint64 *size) { GRegex *regex = NULL; GRegex *local_regex = NULL; GStrv tokens = NULL; GStrv local_tokens = NULL; char *l_scheme = NULL; char *l_authority = NULL; char *l_path = NULL; char *l_query = NULL; char *l_fragment = NULL; if (scheme != NULL) *scheme = NULL; if (authority != NULL) *authority = NULL; if (path != NULL) *path = NULL; if (query != NULL) *query = NULL; if (fragment != NULL) *fragment = NULL; if (address != NULL) *address = 0; if (size != NULL) *size = 0; g_return_val_if_fail (url != NULL, FALSE); /* https://tools.ietf.org/html/rfc3986#appendix-B */ regex = g_regex_new ("^(([^:\\/?#]+):)?(\\/\\/([^\\/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?",G_REGEX_CASELESS, 0, NULL); if (regex == NULL) return FALSE; tokens = g_regex_split_full (regex, url, url_length, 0, 0, 10, NULL); g_clear_pointer (®ex, g_regex_unref); if (g_strv_length (tokens) < 6 || tokens[5][0] == '\0') { g_strfreev (tokens); return FALSE; } l_scheme = tokens[2][0] != '\0' ? tokens[2] : NULL; l_authority = tokens[4][0] != '\0' ? tokens[4] : NULL; if (g_ascii_strcasecmp (l_scheme, "local") == 0) { local_regex = g_regex_new ("(?:\\s*)?(.+);(?:\\s*)?(?:0x)?([0-9:a-f]*);(?:\\s*)?(?:0x)?([0-9:a-f]*)", G_REGEX_CASELESS, 0, NULL); if (local_regex == NULL) { g_strfreev (tokens); return FALSE; } local_tokens = g_regex_split (local_regex, tokens[5], 0); g_clear_pointer (&local_regex, g_regex_unref); if (g_strv_length (local_tokens) < 4) { g_strfreev (tokens); g_strfreev (local_tokens); return FALSE; } l_path = local_tokens[1]; if (address != NULL) *address = g_ascii_strtoll (local_tokens[2], NULL, 16); if (size != NULL) *size = g_ascii_strtoll (local_tokens[3], NULL, 16); } else { l_path = tokens[5]; } if (tokens[6] != NULL && tokens[7] != NULL) { l_query = tokens[7][0] != '\0' ? tokens[7] : NULL; if (tokens[8] != NULL && tokens[9] != NULL) l_fragment = tokens[9][0] != '\0' ? tokens[9] : NULL; } if (scheme != NULL) *scheme = g_strdup( l_scheme); if (authority != NULL) *authority = g_strdup( l_authority); if (path != NULL) *path = g_strdup( l_path); if (query != NULL) *query = g_strdup( l_query); if (fragment != NULL) *fragment = g_strdup( l_fragment); g_strfreev (tokens); g_strfreev (local_tokens); return TRUE; } gint64 arv_monotonic_time_us (void) { #ifdef G_OS_WIN32 static LARGE_INTEGER freq = { .QuadPart = 0 }; LARGE_INTEGER t; if (freq.QuadPart == 0) { QueryPerformanceFrequency(&freq); } QueryPerformanceCounter(&t); return (t.QuadPart*1000000) / freq.QuadPart; #else return g_get_monotonic_time(); #endif } GRegex * arv_regex_new_from_glob_pattern (const char *glob, gboolean caseless) { GRegex *regex; GString *regex_pattern; const char *iter; char **globs; gunichar character; unsigned int i; g_return_val_if_fail (g_utf8_validate (glob, -1, NULL), NULL); regex_pattern = g_string_new (""); globs = g_strsplit (glob, "|", -1); for (i = 0; globs[i] != NULL; i++) { /* Ignore empty strings */ if (globs[i][0] == '\0') continue; if (i > 0) g_string_append (regex_pattern, "|^"); else g_string_append (regex_pattern, "^"); iter = g_strstrip (globs[i]); while (iter != NULL && *iter != '\0') { character = g_utf8_get_char (iter); switch (character) { case '\\': g_string_append (regex_pattern, "\\\\"); break; case '^': g_string_append (regex_pattern, "\\^"); break; case '$': g_string_append (regex_pattern, "\\$"); break; case '.': g_string_append (regex_pattern, "\\."); break; case '[': g_string_append (regex_pattern, "\\["); break; case '|': g_string_append (regex_pattern, "\\|"); break; case '(': g_string_append (regex_pattern, "\\("); break; case ')': g_string_append (regex_pattern, "\\)"); break; case '?': g_string_append (regex_pattern, "."); break; case '*': g_string_append (regex_pattern, ".*"); break; case '+': g_string_append (regex_pattern, "\\+"); break; case '{': g_string_append (regex_pattern, "\\{"); break; default: g_string_append_unichar (regex_pattern, character); break; } iter = g_utf8_find_next_char (iter, NULL); } g_string_append (regex_pattern, "$"); } g_strfreev (globs); arv_debug_misc ("Regex '%s' created from glob '%s'", regex_pattern->str, glob); regex = g_regex_new (regex_pattern->str, G_REGEX_OPTIMIZE | (caseless ? G_REGEX_CASELESS : 0), 0, NULL); g_string_free (regex_pattern, TRUE); return regex; } char * arv_g_string_free_and_steal (GString *string) { #if GLIB_CHECK_VERSION(2,75,4) return g_string_free_and_steal(string); #else char *buffer = string->str; g_string_free (string, FALSE); return buffer; #endif } aravis-0.8.34/src/arvmisc.h000066400000000000000000000042671475431451200155160ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_MISC_H #define ARV_MISC_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API guint arv_get_major_version (void); ARV_API guint arv_get_minor_version (void); ARV_API guint arv_get_micro_version (void); ARV_API const char * arv_pixel_format_to_gst_caps_string (ArvPixelFormat pixel_format); ARV_API const char * arv_pixel_format_to_gst_caps_string_full (ArvPixelFormat pixel_format, const char *vendor_name, const char *model_name); ARV_API ArvPixelFormat arv_pixel_format_from_gst_caps (const char *name, const char *format, int bpp, int depth); ARV_API const char * arv_pixel_format_to_gst_0_10_caps_string (ArvPixelFormat pixel_format); ARV_API ArvPixelFormat arv_pixel_format_from_gst_0_10_caps (const char *name, int bpp, int depth, guint32 fourcc); G_END_DECLS #endif aravis-0.8.34/src/arvmiscprivate.h000066400000000000000000000120671475431451200171060ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_MISC_PRIVATE_H #define ARV_MISC_PRIVATE_H #include #include typedef struct _ArvHistogram ArvHistogram; #define ARV_TYPE_HISTOGRAM (arv_histogram_get_type()) GType arv_histogram_get_type (void); ArvHistogram * arv_histogram_new (guint n_variables, guint n_bins, double bin_step, double offset); ArvHistogram * arv_histogram_ref (ArvHistogram *histogram); void arv_histogram_unref (ArvHistogram *histogram); void arv_histogram_reset (ArvHistogram *histogram); gboolean arv_histogram_fill (ArvHistogram *histogram, guint histogram_id, int value); void arv_histogram_set_variable_name (ArvHistogram *histogram, guint histogram_id, char const *name); char * arv_histogram_to_string (const ArvHistogram *histogram); struct _ArvValue { GType type; union { gint64 v_int64; double v_double; } data; }; #define ARV_TYPE_VALUE (arv_value_get_type()) GType arv_value_get_type (void); typedef struct _ArvValue ArvValue; ArvValue * arv_value_new_double (double v_double); ArvValue * arv_value_new_int64 (gint64 v_int64); void arv_value_free (ArvValue *value); void arv_value_copy (ArvValue *to, const ArvValue *from); void arv_value_set_int64 (ArvValue *value, gint64 v_int64); void arv_value_set_double (ArvValue *value, double v_double); gint64 arv_value_get_int64 (ArvValue *value); double arv_value_get_double (ArvValue *value); gboolean arv_value_holds_int64 (ArvValue *value); double arv_value_holds_double (ArvValue *value); /* Compatibility functions */ char * arv_g_string_free_and_steal (GString *string) G_GNUC_WARN_UNUSED_RESULT; /* private, but used by tests */ ARV_API gboolean arv_parse_genicam_url (const char *url, gssize url_length, char **scheme, char **authority, char **path, char **query, char **fragment, guint64 *address, guint64 *size); void arv_copy_memory_with_endianness (void *to, size_t to_size, guint to_endianness, void *from, size_t from_size, guint from_endianness); void * arv_decompress (void *input_buffer, size_t input_size, size_t *output_size); /* private, but used by tests */ ARV_API const char * arv_vendor_alias_lookup (const char *vendor); /* this only wraps g_get_monotonic_time on non-windows platforms */ gint64 arv_monotonic_time_us (void); #if GLIB_CHECK_VERSION(2,68,0) #define arv_memdup(p,s) g_memdup2(p,s) #else #define arv_memdup(p,s) g_memdup(p,(size_t) s) #endif ARV_API GRegex * arv_regex_new_from_glob_pattern (const char *glob, gboolean caseless); /* See 'glib/gconstrutor.h' for some extra details on how the following constructor/destructor macros work */ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) #define ARV_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); #define ARV_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); #elif defined (_MSC_VER) && (_MSC_VER >= 1500) #include #ifdef _M_IX86 #define ARV_MSVC_SYMBOL_PREFIX "_" #else #define ARV_MSVC_SYMBOL_PREFIX "" #endif #define ARV_MSVC_CTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper; #define ARV_MSVC_DTOR(_func,_sym_prefix) \ static void _func(void); \ extern int (* _array ## _func)(void); \ int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \ __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor; #define ARV_DEFINE_CONSTRUCTOR(_func) ARV_MSVC_CTOR (_func, ARV_MSVC_SYMBOL_PREFIX) #define ARV_DEFINE_DESTRUCTOR(_func) ARV_MSVC_DTOR (_func, ARV_MSVC_SYMBOL_PREFIX) #else #error "Constructors/destructors are not supported on this compiler!" #endif #endif aravis-0.8.34/src/arvnetwork.c000066400000000000000000000445571475431451200162550ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Authors: Emmanuel Pacaud * Václav Šmilauer */ #include #include #include #include #ifndef G_OS_WIN32 #include #else #include #include #include /* For PWCHAR */ #endif struct _ArvNetworkInterface{ struct sockaddr *addr; struct sockaddr *netmask; struct sockaddr *broadaddr; char* name; }; #ifdef G_OS_WIN32 ARV_DEFINE_CONSTRUCTOR (arv_initialize_networking) static void arv_initialize_networking (void) { long res; WSADATA wsaData; /* not sure which version is really needed, just use 2.2 (latest) */ res = WSAStartup(MAKEWORD(2, 2), &wsaData); if (res != 0) { /* error description functions should not be used when WSAStartup failed (not sure), do manually */ char *desc = "[unknown error code]"; switch (res) { case WSASYSNOTREADY: desc = "The underlying network subsystem is not ready for network communication."; break; case WSAVERNOTSUPPORTED: desc = "The version of Windows Sockets support requested is not provided by this particular Windows Sockets implementation."; break; case WSAEINPROGRESS: desc = "A blocking Windows Sockets 1.1 operation is in progress."; break; case WSAEPROCLIM: desc = "A limit on the number of tasks supported by the Windows Sockets implementation has been reached."; break; case WSAEFAULT: desc = "The lpWSAData parameter is not a valid pointer."; break; }; g_critical ("WSAStartup failed with error %ld: %s", res, desc); } arv_info_interface ("WSAStartup done."); } ARV_DEFINE_DESTRUCTOR (arv_cleanup_networking) static void arv_cleanup_networking (void) { long res; res=WSACleanup(); if (res != 0){ char *desc = "[unknown error code]"; switch (res) { case WSANOTINITIALISED: desc = "A successful WSAStartup call must occur before using this function."; break; case WSAENETDOWN: desc = "The network subsystem has failed."; break; case WSAEINPROGRESS: desc = "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."; break; }; g_critical ("WSACleanup failed with error %ld: %s", res, desc); } } GList * arv_enumerate_network_interfaces (void) { /* * docs: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses * * example source: https://github.com/zeromq/czmq/blob/master/src/ziflist.c#L284 * question about a better solution: https://stackoverflow.com/q/64348510/761090 * * note: >= Vista code untested */ ULONG outBufLen = 15000; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pAddrIter = NULL; GList* ret; int iter = 0; ULONG dwRetVal; /* pre-Vista windows don't have PIP_ADAPTER_UNICAST_ADDRESS onLinePrefixLength field. * To get netmask, we build pIPAddrTable (IPv4-only) and find netmask associated to each addr. * This means IPv6 will only work with >= Vista. * See https://stackoverflow.com/a/64358443/761090 for thorough explanation. */ #if WINVER < _WIN32_WINNT_VISTA // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getipaddrtable PMIB_IPADDRTABLE pIPAddrTable; DWORD dwSize = 0; /* Variables used to return error message */ pIPAddrTable = (MIB_IPADDRTABLE *) g_malloc(sizeof (MIB_IPADDRTABLE)); if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { g_free(pIPAddrTable); pIPAddrTable = (MIB_IPADDRTABLE *) g_malloc(dwSize); } dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); if (dwRetVal != NO_ERROR ) { arv_warning_interface("GetIpAddrTable failed."); g_free(pIPAddrTable); } #endif do { pAddresses = (IP_ADAPTER_ADDRESSES*) g_malloc (outBufLen); /* change family to AF_UNSPEC for both IPv4 and IPv6, later */ dwRetVal = GetAdaptersAddresses( /* Family */ AF_INET, /* Flags */ GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, /* Reserved */ NULL, pAddresses, &outBufLen ); if (dwRetVal==ERROR_BUFFER_OVERFLOW) { g_free (pAddresses); } else { break; } iter++; } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (iter<3)); if (dwRetVal != ERROR_SUCCESS){ arv_warning_interface ("Failed to enumerate network interfaces (GetAdaptersAddresses returned %lu)", dwRetVal); return NULL; } ret = NULL; for(pAddrIter = pAddresses; pAddrIter != NULL; pAddrIter = pAddrIter->Next){ PIP_ADAPTER_UNICAST_ADDRESS pUnicast; for (pUnicast = pAddrIter->FirstUnicastAddress; pUnicast != NULL; pUnicast = pUnicast->Next){ // PIP_ADAPTER_PREFIX pPrefix = pAddrIter->FirstPrefix; struct sockaddr* lpSockaddr = pUnicast->Address.lpSockaddr; ArvNetworkInterface* a; gboolean ok = (pAddrIter->OperStatus == IfOperStatusUp) /* extend for IPv6 here, later */ && ((lpSockaddr->sa_family == AF_INET) #if 0 && WINVER >= _WIN32_WINNT_VISTA || (lpSockaddr->sa_family == AF_INET6) #endif ) ; if (!ok) continue; a = (ArvNetworkInterface*) g_malloc0(sizeof(ArvNetworkInterface)); if (lpSockaddr->sa_family == AF_INET){ struct sockaddr_in* mask; struct sockaddr_in* broadaddr; /* copy 3x so that sa_family is already set for netmask and broadaddr */ a->addr = arv_memdup (lpSockaddr, sizeof(struct sockaddr)); a->netmask = arv_memdup (lpSockaddr, sizeof(struct sockaddr)); a->broadaddr = arv_memdup (lpSockaddr, sizeof(struct sockaddr)); /* adjust mask & broadcast */ mask = (struct sockaddr_in*) a->netmask; #if WINVER >= _WIN32_WINNT_VISTA mask->sin_addr.s_addr = htonl ((0xffffffffU) << (32 - pUnicast->OnLinkPrefixLength)); #else { int i; gboolean match = FALSE; for (i=0; i < pIPAddrTable->dwNumEntries; i++){ MIB_IPADDRROW* row=&pIPAddrTable->table[i]; #if 0 fprintf(stderr,"row %d: %08lx: match with %08lx (mask %08lx): %d\n",i,((struct sockaddr_in*)a->addr)->sin_addr.s_addr,row->dwAddr,row->dwMask,row->dwAddr == ((struct sockaddr_in*)a->addr)->sin_addr.s_addr); #endif /* both are in network byte order, no need to convert */ if (row->dwAddr == ((struct sockaddr_in*)a->addr)->sin_addr.s_addr){ match = TRUE; mask->sin_addr.s_addr = row->dwMask; break; } } if (!match){ arv_warning_interface ("Failed to obtain netmask for %08lx (secondary address?), using 255.255.0.0.",((struct sockaddr_in*)a->addr)->sin_addr.s_addr); mask->sin_addr.s_addr = htonl(0xffff0000U); } } #endif broadaddr = (struct sockaddr_in*) a->broadaddr; broadaddr->sin_addr.s_addr |= ~(mask->sin_addr.s_addr); } #if WINVER >= _WIN32_WINNT_VISTA else if (lpSockaddr->sa_family == AF_INET6){ arv_warning_interface("IPv6 support not yet implemented."); } #endif /* name is common to IPv4 and IPv6 */ { PWCHAR name = pAddrIter->FriendlyName; size_t asciiSize = wcstombs (0, name, 0) + 1; a->name = (char *) g_malloc (asciiSize); wcstombs (a->name, name, asciiSize); } ret = g_list_prepend(ret, a); } } g_free (pAddresses); #if WINVER < _WIN32_WINNT_VISTA g_free(pIPAddrTable); #endif ret = g_list_reverse(ret); return ret; } /* * mingw only defines inet_ntoa (ipv4-only), inet_ntop (IPv4 & IPv6) is missing from it headers * for _WIN32_WINNT < 0x0600; therefore we define it ourselves * The code comes from https://www.mail-archive.com/users@ipv6.org/msg02107.html. */ #if _WIN32_WINNT < 0x0600 const char * inet_ntop (int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in; memset (&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy (&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in; memset (&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy (&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } #endif /* _WIN32_WINNT < 0x0600 */ /* * g_poll does not work with sockets under windows correctly. The reason is that * g_poll uses WaitForMultipleObjectEx API function which does not handle Winsock * sockets descriptors directly. Instead, one should * * (a) create a new event (WSAEVENT) associated with each socket polled, and its * descriptor passed to g_poll instead (WSACreateEvent, WSAEventSelect); * * (b) when g_poll returns, the event must be cleared so that it is not returned * again (WSAEnumNetworkEvents); * * (c) events created in (a) must be closed (WSACloseEvent). * * These three points are respectively handled by arv_gpollfd_prepare_all, * arv_gpollfd_clear_one and arv_gpollfd_finish_all. These functions are no-op * for non-Windows builds. * * https://discourse.gnome.org/t/g-poll-times-out-with-windows is the original * discussion of the issue. * * https://gitlab.gnome.org/GNOME/glib/-/issues/214 is GLib bug report * (g_poll does not work on win32 sockets) * */ void arv_gpollfd_prepare_all (GPollFD *fds, guint nfds){ guint i; int wsaRes; for (i = 0; i < nfds; i++) { gint64 fd = fds[i].fd; fds[i].fd = (gint64) WSACreateEvent(); g_assert ((WSAEVENT) fds[i].fd != WSA_INVALID_EVENT); wsaRes = WSAEventSelect (fd, (WSAEVENT) fds[i].fd, FD_READ); g_assert (wsaRes == 0); } } void arv_gpollfd_clear_one (GPollFD *fd, GSocket* socket){ WSANETWORKEVENTS wsaNetEvents; /* TODO: check return value, check wsaNetEvents for errors */ WSAEnumNetworkEvents (g_socket_get_fd(socket), (WSAEVENT) fd->fd, &wsaNetEvents); } void arv_gpollfd_finish_all (GPollFD *fds, guint nfds){ guint i; for (i = 0; i < nfds; i++){ WSACloseEvent( (WSAEVENT) fds[i].fd); } } #else /* not G_OS_WIN32 */ GList* arv_enumerate_network_interfaces (void) { struct ifaddrs *ifap = NULL; struct ifaddrs *ifap_iter; GList* ret=NULL; if (getifaddrs (&ifap) <0) return NULL; for (ifap_iter = ifap; ifap_iter != NULL; ifap_iter = ifap_iter->ifa_next) { if ((ifap_iter->ifa_flags & IFF_UP) != 0 && (ifap_iter->ifa_addr != NULL) && (ifap_iter->ifa_addr->sa_family == AF_INET)) { ArvNetworkInterface* a; a = g_new0 (ArvNetworkInterface, 1); a->addr = arv_memdup (ifap_iter->ifa_addr, sizeof(struct sockaddr)); if (ifap_iter->ifa_netmask) a->netmask = arv_memdup (ifap_iter->ifa_netmask, sizeof(struct sockaddr)); #if (defined(__APPLE__) && defined(__MACH__)) || defined(BSD) if (ifap_iter->ifa_broadaddr && ifap_iter->ifa_broadaddr->sa_len != 0) { a->broadaddr = arv_memdup(ifap_iter->ifa_broadaddr, ifap_iter->ifa_broadaddr->sa_len); } else { /* Interface have no broadcast address, * IFF_BROADCAST probably not set, * this workaround to pass fakecamera * test that uses 127.0.0.1 address on * loopback interface. */ a->broadaddr = arv_memdup(ifap_iter->ifa_addr, ifap_iter->ifa_addr->sa_len); } #else if (ifap_iter->ifa_ifu.ifu_broadaddr) a->broadaddr = arv_memdup(ifap_iter->ifa_ifu.ifu_broadaddr, sizeof(struct sockaddr)); #endif if (ifap_iter->ifa_name) a->name = g_strdup(ifap_iter->ifa_name); ret = g_list_prepend (ret, a); } } freeifaddrs (ifap); return g_list_reverse (ret); }; /* no-op functions for Win32 GLib bug workaround, see above */ void arv_gpollfd_prepare_all(GPollFD *fds, guint nfds) { return; } void arv_gpollfd_clear_one(GPollFD *fds, GSocket* socket) { return; } void arv_gpollfd_finish_all(GPollFD *fds, guint nfds) { return; } #endif /* G_OS_WIN32 */ struct sockaddr * arv_network_interface_get_addr(ArvNetworkInterface* a) { return a->addr; } struct sockaddr * arv_network_interface_get_netmask(ArvNetworkInterface* a) { return a->netmask; } struct sockaddr * arv_network_interface_get_broadaddr(ArvNetworkInterface* a) { return a->broadaddr; } const char * arv_network_interface_get_name(ArvNetworkInterface* a) { return a->name; } void arv_network_interface_free(ArvNetworkInterface *a) { g_clear_pointer (&a->addr, g_free); g_clear_pointer (&a->netmask, g_free); g_clear_pointer (&a->broadaddr, g_free); g_clear_pointer (&a->name, g_free); g_free (a); } gboolean arv_socket_set_recv_buffer_size (int socket_fd, gint buffer_size) { #ifdef G_OS_WIN32 DWORD _buffer_size; DWORD buffer_size_reported; #else gint _buffer_size; gint buffer_size_reported; #endif int result; socklen_t optlen; _buffer_size = buffer_size; optlen = sizeof(buffer_size_reported); result = setsockopt (socket_fd, SOL_SOCKET, SO_RCVBUF, (const char *) &_buffer_size, sizeof (_buffer_size)); if(result != 0) { arv_warning_interface ("[set_recv_buffer_size] Setting socket buffer to %d bytes failed (%s)", _buffer_size, strerror(errno)); return FALSE; } /* setsockopt() succeeded, but sometimes the requested size is not actually be set. Ask * to see the new setting to confirm. */ result = getsockopt (socket_fd, SOL_SOCKET, SO_RCVBUF, (char *) &buffer_size_reported, &optlen); if (result != 0) { arv_warning_interface ("[set_recv_buffer_size] Read of socket buffer size (SO_RCVBUF) failed (%s)", strerror(errno)); return FALSE; } g_assert (optlen == sizeof (buffer_size_reported)); if(buffer_size_reported < buffer_size) { #ifndef G_OS_WIN32 arv_warning_interface ("[set_recv_buffer_size] Unexpected socket buffer size (SO_RCVBUF):" " actual %d < expected %d bytes" "\nYou might see missing packets and timeouts" "\nMost likely /proc/sys/net/core/rmem_max is too low" "\nSee the socket(7) manpage\n", buffer_size_reported, buffer_size); #else arv_warning_interface ("[set_recv_buffer_size] Unexpected socket buffer size (SO_RCVBUF):" " actual %d < expected %d bytes" "\nYou might see missing packets and timeouts", buffer_size_reported, buffer_size); #endif return FALSE; } return TRUE; } ArvNetworkInterface* arv_network_get_interface_by_name (const char* name) { GList *ifaces; GList *iface_iter; ArvNetworkInterface *ret = NULL; ifaces = arv_enumerate_network_interfaces (); for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { if (g_strcmp0 (name, arv_network_interface_get_name (iface_iter->data)) == 0) break; } if(iface_iter != NULL){ /* remove the interface node from the list (deleted below) but don't delete its data */ ret = iface_iter->data; ifaces = g_list_remove_link(ifaces, iface_iter); g_list_free(iface_iter); } g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); return ret; } ArvNetworkInterface* arv_network_get_interface_by_address (const char* addr) { GInetSocketAddress *iaddr_s = NULL; GInetAddress *iaddr = NULL; GList *ifaces; GList *iface_iter; ArvNetworkInterface *ret = NULL; ifaces = arv_enumerate_network_interfaces (); if (!g_hostname_is_ip_address(addr)) return NULL; iaddr_s = G_INET_SOCKET_ADDRESS (g_inet_socket_address_new_from_string (addr, 0)); if (iaddr_s == NULL) return NULL; iaddr = g_inet_socket_address_get_address(iaddr_s); for (iface_iter = ifaces; iface_iter != NULL; iface_iter = iface_iter->next) { GSocketAddress *iface_sock_addr; GInetAddress *iface_inet_addr; iface_sock_addr = g_socket_address_new_from_native (arv_network_interface_get_addr (iface_iter->data), sizeof(struct sockaddr)); iface_inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (iface_sock_addr)); if (g_inet_address_equal (iaddr,iface_inet_addr)) { g_clear_object (&iface_sock_addr); break; } g_clear_object(&iface_sock_addr); } if (iface_iter != NULL) { ret = iface_iter->data; ifaces = g_list_remove_link (ifaces, iface_iter); g_list_free (iface_iter); } g_clear_object(&iaddr_s); g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); return ret; } ArvNetworkInterface* arv_network_get_fake_ipv4_loopback (void) { ArvNetworkInterface* ret = (ArvNetworkInterface*) g_malloc0(sizeof(ArvNetworkInterface)); ret->name = g_strdup (""); ret->addr = g_malloc0 (sizeof(struct sockaddr_in)); ret->addr->sa_family = AF_INET; ((struct sockaddr_in*)ret->addr)->sin_addr.s_addr = htonl(0x7f000001); // INADDR_LOOPBACK ret->netmask = g_malloc0 (sizeof(struct sockaddr_in)); ret->netmask->sa_family = AF_INET; ((struct sockaddr_in*)ret->netmask)->sin_addr.s_addr = htonl(0xff000000); ret->broadaddr = g_malloc0 (sizeof(struct sockaddr_in)); ret->broadaddr->sa_family = AF_INET; ((struct sockaddr_in*)ret->broadaddr)->sin_addr.s_addr = htonl(0x7fffffff); return ret; } gboolean arv_network_interface_is_loopback(ArvNetworkInterface *a) { if(!a) return FALSE; if (a->addr->sa_family==AF_INET) return (ntohl(((struct sockaddr_in*)a->addr)->sin_addr.s_addr)>>24)==0x7f; if (a->addr->sa_family==AF_INET6) { unsigned int pos; /* 16 unsigned chars in network byte order (big-endian), loopback is ::1, i.e. zeros and 1 at the end */ unsigned char* i6=(unsigned char*)(&(((struct sockaddr_in6*)a->addr)->sin6_addr)); for (pos=0; pos<16; pos++) { if (i6[pos]!=0) return FALSE; } if (i6[16]!=1) return FALSE; return TRUE; } return FALSE; } aravis-0.8.34/src/arvnetworkprivate.h000066400000000000000000000071061475431451200176420ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Authors: Emmanuel Pacaud * Václav Šmilauer */ #ifndef ARV_NETWORK_PRIVATE_H #define ARV_NETWORK_PRIVATE_H #include #include #include #ifndef G_OS_WIN32 #include #include #include #include #include #include #include #endif #ifdef __linux__ #include #endif #if defined(__APPLE__) || defined(BSD) #include #define iphdr ip #endif #ifndef G_OS_WIN32 #include #endif #ifdef G_OS_WIN32 #include typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; struct udphdr { uint16_t source; uint16_t dest; uint16_t len; uint16_t check; }; #endif #if defined(G_OS_WIN32) struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; #endif typedef struct _ArvNetworkInterface ArvNetworkInterface; /* private, but used by tests */ ARV_API GList * arv_enumerate_network_interfaces (void); ArvNetworkInterface* arv_network_get_interface_by_name (const char* name); ArvNetworkInterface* arv_network_get_interface_by_address (const char* addr); ArvNetworkInterface* arv_network_get_fake_ipv4_loopback (void); /* private, but used by tests */ ARV_API void arv_network_interface_free (ArvNetworkInterface *a); ARV_API struct sockaddr * arv_network_interface_get_addr (ArvNetworkInterface *a); ARV_API struct sockaddr * arv_network_interface_get_netmask (ArvNetworkInterface *a); ARV_API struct sockaddr * arv_network_interface_get_broadaddr (ArvNetworkInterface *a); ARV_API const char * arv_network_interface_get_name (ArvNetworkInterface *a); ARV_API gboolean arv_network_interface_is_loopback (ArvNetworkInterface *a); gboolean arv_socket_set_recv_buffer_size (int socket_fd, gint buffer_size); #ifdef G_OS_WIN32 /* mingw only defines with _WIN32_WINNT>=0x0600, see * https://github.com/AravisProject/aravis/issues/416#issuecomment-717220610 */ #if _WIN32_WINNT < 0x0600 const char * inet_ntop (int af, const void *src, char *dst, socklen_t cnt); #endif #endif /* Workaround for broken socket g_poll under Windows, otherwise no-op */ void arv_gpollfd_prepare_all (GPollFD *fds, guint nfds); void arv_gpollfd_clear_one (GPollFD *fd, GSocket* socket); void arv_gpollfd_finish_all (GPollFD *fds, guint nfds); #endif aravis-0.8.34/src/arvparamsprivate.h.in000066400000000000000000000021001475431451200200260ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_PARAMS_H #define ARV_PARAMS_H /** * ARV_GV_STREAM_NUM_BUFFERS * * Number of buffers used to receive GVSP packets * * Since: 0.8.21 */ #mesondefine ARV_GV_STREAM_NUM_BUFFERS #endif aravis-0.8.34/src/arvrealtime.c000066400000000000000000000234621475431451200163560ustar00rootroot00000000000000/* Copyright 2009 Lennart Poettering Copyright 2010 David Henningsson Copyright © 2009-2025 Emmanuel Pacaud Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This file is based on rtkit sources, ported from libdbus to glib gdbus API. */ #include #include #include #include #ifdef G_OS_WIN32 #include #else #include #include #include #include #endif #if !defined(__APPLE__) && !defined(G_OS_WIN32) && !defined(BSD) #define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1" #define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1" #define ARV_RTKIT_ERROR arv_rtkit_error_quark () typedef enum { ARV_RTKIT_ERROR_PERMISSION_DENIED, ARV_RTKIT_ERROR_WRONG_REPLY } ArvRtkitError; static GQuark arv_rtkit_error_quark (void) { return g_quark_from_static_string ("arv-rtkit-error-quark"); } static gint64 arv_rtkit_get_int_property (GDBusConnection *connection, const char* propname, GError **error) { GDBusMessage *message; GDBusMessage *reply; GError *local_error = NULL; GVariant *body; GVariant *parameter; GVariant *variant; const GVariantType *variant_type; gint64 value; message = g_dbus_message_new_method_call (RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.DBus.Properties", "Get"); g_dbus_message_set_body (message, g_variant_new ("(ss)", "org.freedesktop.RealtimeKit1", propname)); reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 1000, NULL, NULL, &local_error); g_object_unref (message); if (local_error != NULL) { g_propagate_error (error, local_error); return 0; } if (g_dbus_message_get_message_type (reply) != G_DBUS_MESSAGE_TYPE_METHOD_RETURN) { local_error = g_error_new (ARV_RTKIT_ERROR, ARV_RTKIT_ERROR_PERMISSION_DENIED, "%s", g_dbus_message_get_error_name (reply)); g_propagate_error (error, local_error); g_object_unref (reply); return 0; } if (!g_variant_type_equal ("v", g_dbus_message_get_signature (reply))) { local_error = g_error_new (ARV_RTKIT_ERROR, ARV_RTKIT_ERROR_WRONG_REPLY, "Invalid reply signature"); g_propagate_error (error, local_error); g_object_unref (reply); return 0; } body = g_dbus_message_get_body (reply); parameter = g_variant_get_child_value (body, 0); variant = g_variant_get_variant (parameter); variant_type = g_variant_get_type (variant); if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT32)) value = g_variant_get_int32 (variant); else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT64)) value = g_variant_get_int64 (variant); else value = 0; g_variant_unref (parameter); g_variant_unref (variant); g_object_unref (reply); return value; } int arv_rtkit_get_max_realtime_priority (GDBusConnection *connection, GError **error) { return arv_rtkit_get_int_property (connection, "MaxRealtimePriority", error); } int arv_rtkit_get_min_nice_level (GDBusConnection *connection, GError **error) { return arv_rtkit_get_int_property (connection, "MinNiceLevel", error); } gint64 arv_rtkit_get_rttime_usec_max (GDBusConnection *connection, GError **error) { GError *local_error = NULL; gint64 rttime_usec_max; rttime_usec_max = arv_rtkit_get_int_property (connection, "RTTimeUSecMax", &local_error); if (local_error == NULL) return rttime_usec_max; g_error_free (local_error); return arv_rtkit_get_int_property (connection, "RTTimeNSecMax", error) / 1000; } void arv_rtkit_make_realtime (GDBusConnection *connection, pid_t thread, int priority, GError **error) { GDBusMessage *message; GDBusMessage *reply; GError *local_error = NULL; message = g_dbus_message_new_method_call (RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.RealtimeKit1", "MakeThreadRealtime"); g_dbus_message_set_body (message, g_variant_new ("(tu)", thread, priority)); reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 1000, NULL, NULL, &local_error); g_object_unref (message); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (g_dbus_message_get_message_type (reply) != G_DBUS_MESSAGE_TYPE_METHOD_RETURN) { local_error = g_error_new (ARV_RTKIT_ERROR, 0, "%s", g_dbus_message_get_error_name (reply)); g_propagate_error (error, local_error); g_object_unref (reply); return; } g_object_unref (reply); } void arv_rtkit_make_high_priority (GDBusConnection *connection, pid_t thread, int nice_level, GError **error) { GDBusMessage *message; GDBusMessage *reply; GError *local_error = NULL; message = g_dbus_message_new_method_call (RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority"); g_dbus_message_set_body (message, g_variant_new ("(ti)", thread, nice_level)); reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 1000, NULL, NULL, &local_error); g_object_unref (message); if (local_error != NULL) { g_propagate_error (error, local_error); return; } if (g_dbus_message_get_message_type (reply) != G_DBUS_MESSAGE_TYPE_METHOD_RETURN) { local_error = g_error_new (ARV_RTKIT_ERROR, 0, "%s", g_dbus_message_get_error_name (reply)); g_propagate_error (error, local_error); g_object_unref (reply); return; } g_object_unref (reply); } #ifndef SCHED_RESET_ON_FORK #define SCHED_RESET_ON_FORK 0x40000000 #endif #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif #include #include static pid_t _gettid(void) { return (pid_t) syscall(SYS_gettid); } /** * arv_make_thread_realtime: * @priority: realtime priority * * Returns: %TRUE on success. * * Try to make current thread realtime. It first try to use sched_setscheduler, * and if it fails, use rtkit. * * Since: 0.4.0 */ gboolean arv_make_thread_realtime (int priority) { struct sched_param p; memset(&p, 0, sizeof(p)); p.sched_priority = priority; if (sched_setscheduler(_gettid (), SCHED_RR|SCHED_RESET_ON_FORK, &p) < 0 && errno == EPERM) { struct rlimit rlim; GDBusConnection *bus; GError *error = NULL; memset(&rlim, 0, sizeof(rlim)); rlim.rlim_cur = rlim.rlim_max = 100000000ULL; /* 100ms */ if ((setrlimit(RLIMIT_RTTIME, &rlim) < 0)) { arv_warning_misc ("Failed to set RLIMIT_RTTIME: %s", strerror (errno)); return FALSE; } bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (error != NULL) { arv_warning_misc ("Failed to connect to system bus: %s", error->message); g_error_free (error); return FALSE; } arv_rtkit_make_realtime(bus, _gettid (), p.sched_priority, &error); g_object_unref (bus); if (error != NULL) { arv_warning_misc ("Failed to connect make realtime: %s", error->message); g_error_free (error); return FALSE; } arv_info_misc ("Thread became realtime with priority %d", priority); return TRUE; } return TRUE; } /** * arv_make_thread_high_priority: * @nice_level: new nice level * * Returns: %TRUE on success. * * Try to set current thread nice level to high priority, using rtkit. * * Since: 0.4.0 */ gboolean arv_make_thread_high_priority (int nice_level) { GDBusConnection *bus; GError *error = NULL; bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (error != NULL) { arv_warning_misc ("Failed to connect to system bus: %s", error->message); g_error_free (error); return FALSE; } arv_rtkit_make_high_priority (bus, _gettid(), nice_level, &error); g_object_unref (bus); if (error != NULL) { arv_warning_misc ("Failed to connect high priority: %s", error->message); g_error_free (error); return FALSE; } arv_info_misc ("Nice level successfully changed to %d", nice_level); return TRUE; } #elif defined(G_OS_WIN32) gboolean arv_make_thread_realtime (int priority) { SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) { DWORD err = GetLastError(); arv_warning_misc ("SetPriorityClass(..., THREAD_PRIORITY_TIME_CRITICAL) failed (%lu)", err); return FALSE; } arv_info_misc ("Thread made realtime!"); return TRUE; } gboolean arv_make_thread_high_priority (int priority) { if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) { DWORD err = GetLastError(); arv_warning_misc ("SetPriorityClass(..., THREAD_PRIORITY_HIGHEST) failed (%lu)", err); return FALSE; } arv_info_misc ("Thread made high-priority!"); return TRUE; } #else gboolean arv_make_thread_realtime (int priority) { arv_info_misc ("SCHED API not supported on OSX"); return FALSE; } gboolean arv_make_thread_high_priority (int nice_level) { arv_info_misc ("RtKit not supported on OSX"); return FALSE; } #endif aravis-0.8.34/src/arvrealtime.h000066400000000000000000000023511475431451200163550ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_REALTIME_H #define ARV_REALTIME_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API gboolean arv_make_thread_realtime (int priority); ARV_API gboolean arv_make_thread_high_priority (int nice_level); G_END_DECLS #endif aravis-0.8.34/src/arvrealtimeprivate.h000066400000000000000000000031251475431451200177500ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_REALTIME_PRIVATE_H #define ARV_REALTIME_PRIVATE_H #include #include #ifndef G_OS_WIN32 #include /* for pid_t */ /* private, but used by tests */ ARV_API int arv_rtkit_get_max_realtime_priority (GDBusConnection *connection, GError **error); ARV_API int arv_rtkit_get_min_nice_level (GDBusConnection *connection, GError **error); ARV_API gint64 arv_rtkit_get_rttime_usec_max (GDBusConnection *connection, GError **error); ARV_API void arv_rtkit_make_realtime (GDBusConnection *connection, pid_t thread, int priority, GError **error); ARV_API void arv_rtkit_make_high_priority (GDBusConnection *connection, pid_t thread, int nice_level, GError **error); #endif #endif aravis-0.8.34/src/arvresources.xml000066400000000000000000000002311475431451200171310ustar00rootroot00000000000000 arv-fake-camera.xml aravis-0.8.34/src/arvstr.c000066400000000000000000000151671475431451200153670ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #define ARV_STR_MAX_ILLEGAL_CHAR_LENGTH 512 /** * SECTION: arvstr * @short_description: String utilities */ /** * arv_str_strip: * @str: (allow-none): a string * @illegal_chars: illegal characters * @replacement_char: replacement character * * Remove any @illegal_chars from @str, and replace them by @replacement_char if they are not at the end or at the beginning of @str. * Several consecutive @illegal_chars are replaced by only one @replacement_char. @illegal_chars at the beginnig or at the end of @str * are simply removed. * * If @replacement_char is '\0', all @illegal_chars are simply removed. * * Returns: @str * * Since: 0.4.0 */ char * arv_str_strip (char *str, const char *illegal_chars, char replacement_char) { char *last_char = NULL; char *ptr = str; char *out = str; unsigned int n_illegal_chars; unsigned int i; if (str == NULL || illegal_chars == NULL) return str; n_illegal_chars = strnlen (illegal_chars, ARV_STR_MAX_ILLEGAL_CHAR_LENGTH); if (n_illegal_chars == 0) return str; while (*ptr != '\0') { gboolean found = FALSE; for (i = 0; i < n_illegal_chars && !found; i++) found = illegal_chars[i] == *ptr; if (found) { if (last_char == out && replacement_char != '\0') { *out = replacement_char; out++; } } else { *out = *ptr; out++; last_char = out; } ptr++; } if (last_char != NULL) *last_char = '\0'; else *str = '\0'; return str; } /* http://www.ietf.org/rfc/rfc2396.txt - Implementation comes from librsvg (rsvg-base.c). */ gboolean arv_str_is_uri (const char *str) { char const *p; if (str == NULL) return FALSE; if (strnlen (str, 4) < 4) return FALSE; if ( (str[0] < 'a' || str[0] > 'z') && (str[0] < 'A' || str[0] > 'Z')) return FALSE; for (p = &str[1]; (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '+' || *p == '-' || *p == '.'; p++); if (strnlen (p, 3) < 3) return FALSE; return (p[0] == ':' && p[1] == '/' && p[2] == '/'); } char * arv_str_to_uri (const char *str) { gchar *current_dir; gchar *absolute_filename; gchar *uri; if (str == NULL) return NULL; if (arv_str_is_uri (str)) return g_strdup (str); if (g_path_is_absolute (str)) return g_filename_to_uri (str, NULL, NULL); current_dir = g_get_current_dir (); absolute_filename = g_build_filename (current_dir, str, NULL); uri = g_filename_to_uri (absolute_filename, NULL, NULL); g_free (absolute_filename); g_free (current_dir); return uri; } gboolean arv_str_parse_double (char **str, double *x) { char *end, *c; gboolean integer_part = FALSE; gboolean fractional_part = FALSE; gboolean exponent_part = FALSE; double mantissa = 0.0; double exponent =0.0; double divisor; gboolean mantissa_sign = 1.0; gboolean exponent_sign = 1.0; c = *str; if (*c == '-') { mantissa_sign = -1.0; c++; } else if (*c == '+') c++; if (*c >= '0' && *c <= '9') { integer_part = TRUE; mantissa = *c - '0'; c++; while (*c >= '0' && *c <= '9') { mantissa = mantissa * 10.0 + *c - '0'; c++; } } if (*c == '.') c++; else if (!integer_part) return FALSE; if (*c >= '0' && *c <= '9') { fractional_part = TRUE; mantissa += (*c - '0') * 0.1; divisor = 0.01; c++; while (*c >= '0' && *c <= '9') { mantissa += (*c - '0') * divisor; divisor *= 0.1; c++; } } if (!fractional_part && !integer_part) return FALSE; end = c; if (*c == 'E' || *c == 'e') { c++; if (*c == '-') { exponent_sign = -1.0; c++; } else if (*c == '+') c++; if (*c >= '0' && *c <= '9') { exponent_part = TRUE; exponent = *c - '0'; c++; while (*c >= '0' && *c <= '9') { exponent = exponent * 10.0 + *c - '0'; c++; } } } if (exponent_part) { end = c; *x = mantissa_sign * mantissa * pow (10.0, exponent_sign * exponent); } else *x = mantissa_sign * mantissa; *str = end; return TRUE; } unsigned int arv_str_parse_double_list (char **str, unsigned int n_values, double *values) { char *ptr = *str; unsigned int i; arv_str_skip_comma_and_spaces (str); for (i = 0; i < n_values; i++) { if (!arv_str_parse_double (str, &values[i])) { *str = ptr; return i; } arv_str_skip_comma_and_spaces (str); } return i; } /** * arv_g_string_append_hex_dump: * @string: a #GString * @data: binary data * @size: size of binary data * * Adds an hexadecimal dump of @data to @string, which consists in lines displaying the data adress, 16 8 bit values in hexadecimal representation, followed by their corresponding ASCII character (replaced by a dot for control ones). * * Here is an example of the output: * * 01e0 c8 b7 89 b0 45 fa 3d 9d 8c e9 a7 33 46 85 1f 2c ....E.=....3F.., * 01f0 3f 4c ba 8d 99 f3 ff d0 40 78 73 37 32 e5 4f 9f ?L......@xs72.O. * 0200 d0 d2 f2 ef 5a 2f fc 61 e3 64 36 21 ....Z/.a.d6! */ void arv_g_string_append_hex_dump (GString *string, const void *data, size_t size) { guint64 i, j, index; for (i = 0; i < (size + 15) / 16; i++) { for (j = 0; j < 16; j++) { index = i * 16 + j; if (j == 0) g_string_append_printf (string, "%08" G_GINT64_MODIFIER "x", i * 16); if (index < size) g_string_append_printf (string, " %02x", *((guint8 *) data + index)); else g_string_append (string, " "); } for (j = 0; j < 16; j++) { index = i * 16 + j; if (j == 0) g_string_append (string, " "); if (index < size) if (*((char *) data + index) >= ' ' && *((char *) data + index) < '\x7f') g_string_append_c (string, *((char *) data + index)); else g_string_append_c (string, '.'); else g_string_append_c (string, ' '); } if (index < size) g_string_append (string, "\n"); } } aravis-0.8.34/src/arvstr.h000066400000000000000000000041651475431451200153700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_STR_H #define ARV_STR_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API char * arv_str_strip (char *str, const char *illegal_chars, char replacement_char); ARV_API gboolean arv_str_is_uri (const char *str); ARV_API char * arv_str_to_uri (const char *str); ARV_API gboolean arv_str_parse_double (char **str, double *x); ARV_API unsigned int arv_str_parse_double_list (char **str, unsigned int n_values, double *values); static inline void arv_str_skip_spaces (char **str) { while (g_ascii_isspace (**str)) (*str)++; } static inline void arv_str_skip_char (char **str, char c) { while (**str == c) (*str)++; } static inline void arv_str_skip_comma_and_spaces (char **str) { while (g_ascii_isspace (**str) || **str == ',') (*str)++; } static inline void arv_str_skip_semicolon_and_spaces (char **str) { while (g_ascii_isspace (**str) || **str == ';') (*str)++; } static inline void arv_str_skip_colon_and_spaces (char **str) { while (g_ascii_isspace (**str) || **str == ':') (*str)++; } ARV_API void arv_g_string_append_hex_dump (GString *string, const void *data, size_t size); G_END_DECLS #endif aravis-0.8.34/src/arvstream.c000066400000000000000000000552551475431451200160540ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvstream * @short_description: Abstract base class for video stream reception * * #ArvStream provides an abstract base class for the implementation of video * stream reception threads. The interface between the reception thread and the * main thread is done using asynchronous queues, containing #ArvBuffer * objects. */ #include #include #include #include #include typedef struct { char *name; char *description; GType type; gpointer data; } ArvStreamInfo; enum { ARV_STREAM_SIGNAL_NEW_BUFFER, ARV_STREAM_SIGNAL_LAST } ArvStreamSignals; static guint arv_stream_signals[ARV_STREAM_SIGNAL_LAST] = {0}; enum { ARV_STREAM_PROPERTY_0, ARV_STREAM_PROPERTY_EMIT_SIGNALS, ARV_STREAM_PROPERTY_DEVICE, ARV_STREAM_PROPERTY_CALLBACK, ARV_STREAM_PROPERTY_CALLBACK_DATA, ARV_STREAM_PROPERTY_DESTROY_NOTIFY } ArvStreamProperties; typedef struct { GAsyncQueue *input_queue; GAsyncQueue *output_queue; GRecMutex mutex; gboolean emit_signals; ArvDevice *device; ArvStreamCallback callback; void *callback_data; GDestroyNotify destroy_notify; GError *init_error; GPtrArray *infos; } ArvStreamPrivate; static void arv_stream_initable_iface_init (GInitableIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ArvStream, arv_stream, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvStream) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, arv_stream_initable_iface_init)) /** * arv_stream_push_buffer: * @stream: a #ArvStream * @buffer: (transfer full): buffer to push * * Pushes a #ArvBuffer to the @stream thread. The @stream takes ownership of @buffer, * and will free all the buffers still in its queues when destroyed. * * This method is thread safe. * * Since: 0.2.0 */ void arv_stream_push_buffer (ArvStream *stream, ArvBuffer *buffer) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_if_fail (ARV_IS_STREAM (stream)); g_return_if_fail (ARV_IS_BUFFER (buffer)); g_async_queue_push (priv->input_queue, buffer); } /** * arv_stream_pop_buffer: * @stream: a #ArvStream * * Pops a buffer from the output queue of @stream. The retrieved buffer * may contain an invalid image. Caller should check the buffer status before using it. * This function blocks until a buffer is available. * * This method is thread safe. * * Returns: (transfer full): a #ArvBuffer * * Since: 0.2.0 */ ArvBuffer * arv_stream_pop_buffer (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); return g_async_queue_pop (priv->output_queue); } /** * arv_stream_try_pop_buffer: * @stream: a #ArvStream * * Pops a buffer from the output queue of @stream. The retrieved buffer * may contain an invalid image. Caller should check the buffer status before using it. * This is the non blocking version of pop_buffer. * * This method is thread safe. * * Returns: (transfer full): a #ArvBuffer, NULL if no buffer is available. * * Since: 0.2.0 */ ArvBuffer * arv_stream_try_pop_buffer (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); return g_async_queue_try_pop (priv->output_queue); } /** * arv_stream_timeout_pop_buffer: * @stream: a #ArvStream * @timeout: timeout, in µs * * Pops a buffer from the output queue of @stream, waiting no more than @timeout. The retrieved buffer * may contain an invalid image. Caller should check the buffer status before using it. * * This method is thread safe. * * Returns: (transfer full): a #ArvBuffer, NULL if no buffer is available until the timeout occurs. * * Since: 0.2.0 */ ArvBuffer * arv_stream_timeout_pop_buffer (ArvStream *stream, guint64 timeout) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); return g_async_queue_timeout_pop (priv->output_queue, timeout); } /** * arv_stream_pop_input_buffer: (skip) * @stream: (transfer full): a #ArvStream * * Pops a buffer from the input queue of @stream. * * Since: 0.2.0 */ ArvBuffer * arv_stream_pop_input_buffer (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); return g_async_queue_try_pop (priv->input_queue); } ArvBuffer * arv_stream_timeout_pop_input_buffer (ArvStream *stream, guint64 timeout) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); return g_async_queue_timeout_pop (priv->input_queue, timeout); } void arv_stream_push_output_buffer (ArvStream *stream, ArvBuffer *buffer) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_if_fail (ARV_IS_STREAM (stream)); g_return_if_fail (ARV_IS_BUFFER (buffer)); g_async_queue_push (priv->output_queue, buffer); g_rec_mutex_lock (&priv->mutex); if (priv->emit_signals) g_signal_emit (stream, arv_stream_signals[ARV_STREAM_SIGNAL_NEW_BUFFER], 0); g_rec_mutex_unlock (&priv->mutex); } /** * arv_stream_get_n_buffers: * @stream: a #ArvStream * @n_input_buffers: (out) (allow-none): input queue length * @n_output_buffers: (out) (allow-none): output queue length * * An accessor to the stream buffer queue lengths. * * Since: 0.2.0 */ void arv_stream_get_n_buffers (ArvStream *stream, gint *n_input_buffers, gint *n_output_buffers) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); if (!ARV_IS_STREAM (stream)) { if (n_input_buffers != NULL) *n_input_buffers = 0; if (n_output_buffers != NULL) *n_output_buffers = 0; return; } if (n_input_buffers != NULL) *n_input_buffers = g_async_queue_length (priv->input_queue); if (n_output_buffers != NULL) *n_output_buffers = g_async_queue_length (priv->output_queue); } /** * arv_stream_start_thread: * @stream: a #ArvStream * * Start the stream receiving thread. The thread is automatically started when * the #ArvStream object is instantiated, so this function is only useful if * the thread was stopped using @arv_stream_stop_thread. * * Since: 0.6.2 */ void arv_stream_start_thread (ArvStream *stream) { ArvStreamClass *stream_class; g_return_if_fail (ARV_IS_STREAM (stream)); stream_class = ARV_STREAM_GET_CLASS (stream); g_return_if_fail (stream_class->start_thread != NULL); stream_class->start_thread (stream); } /** * arv_stream_stop_thread: * @stream: a #ArvStream * @delete_buffers: enable buffer deletion * * Stop the stream receiving thread, and optionally delete all the #ArvBuffer * stored in the stream object queues. Main use of this function is to be able * to quickly change an acquisition parameter that changes the payload size, * without deleting/recreating the stream object. * * Returns: the number of deleted buffers if @delete_buffers == %TRUE, 0 otherwise. * * Since: 0.6.2 */ unsigned int arv_stream_stop_thread (ArvStream *stream, gboolean delete_buffers) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); ArvStreamClass *stream_class; ArvBuffer *buffer; unsigned int n_deleted = 0; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); stream_class = ARV_STREAM_GET_CLASS (stream); g_return_val_if_fail (stream_class->stop_thread != NULL, 0); stream_class->stop_thread (stream); if (!delete_buffers) return 0; g_async_queue_lock (priv->input_queue); do { buffer = g_async_queue_try_pop_unlocked (priv->input_queue); if (buffer != NULL) { g_object_unref (buffer); n_deleted++; } } while (buffer != NULL); g_async_queue_unlock (priv->input_queue); g_async_queue_lock (priv->output_queue); do { buffer = g_async_queue_try_pop_unlocked (priv->output_queue); if (buffer != NULL) { g_object_unref (buffer); n_deleted++; } } while (buffer != NULL); g_async_queue_unlock (priv->output_queue); arv_info_stream ("[Stream::reset] Deleted %u buffers\n", n_deleted); return n_deleted; } /** * arv_stream_get_statistics: * @stream: a #ArvStream * @n_completed_buffers: (out) (allow-none): number of complete received buffers * @n_failures: (out) (allow-none): number of reception failures * @n_underruns: (out) (allow-none): number of input buffer underruns * * An accessor to the stream statistics. * * Since: 0.2.0 */ void arv_stream_get_statistics (ArvStream *stream, guint64 *n_completed_buffers, guint64 *n_failures, guint64 *n_underruns) { guint64 dummy; if (n_completed_buffers == NULL) n_completed_buffers = &dummy; if (n_failures == NULL) n_failures = &dummy; if (n_underruns == NULL) n_underruns = &dummy; *n_completed_buffers = arv_stream_get_info_uint64_by_name (stream, "n_completed_buffers"); *n_failures = arv_stream_get_info_uint64_by_name (stream, "n_failures"); *n_underruns = arv_stream_get_info_uint64_by_name (stream, "n_underruns"); } /** * arv_stream_set_emit_signals: * @stream: a #ArvStream * @emit_signals: the new state * * Make @stream emit signals. This option is * by default disabled because signal emission is expensive and unneeded when * the application prefers to operate in pull mode. * * Since: 0.2.0 */ void arv_stream_set_emit_signals (ArvStream *stream, gboolean emit_signals) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_if_fail (ARV_IS_STREAM (stream)); g_rec_mutex_lock (&priv->mutex); priv->emit_signals = emit_signals; g_rec_mutex_unlock (&priv->mutex); } /** * arv_stream_get_emit_signals: * @stream: a #ArvStream * * Check if stream will emit its signals. * * Returns: %TRUE if @stream is emiting its signals. * * Since: 0.2.0 */ gboolean arv_stream_get_emit_signals (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); gboolean ret; g_return_val_if_fail (ARV_IS_STREAM (stream), FALSE); g_rec_mutex_lock (&priv->mutex); ret = priv->emit_signals; g_rec_mutex_unlock (&priv->mutex); return ret; } static void arv_stream_info_free (ArvStreamInfo *info) { if (info == NULL) return; g_free (info->name); g_free (info); } void arv_stream_declare_info (ArvStream *stream, const char *name, GType type, gpointer data) { ArvStreamInfo *info; ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_if_fail (ARV_IS_STREAM (stream)); g_return_if_fail (type == G_TYPE_DOUBLE || type == G_TYPE_UINT64); g_return_if_fail (data != NULL); info = g_new0 (ArvStreamInfo, 1); info->name = g_strdup (name); info->type = type; info->data = data; g_ptr_array_add (priv->infos, info); } /** * arv_stream_get_n_infos: * @stream: a #ArvStream * * Returns: the number of stream informations. These informations contain useful numbers about data transfer quality. * * Since: 0.8.11 */ guint arv_stream_get_n_infos (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_val_if_fail (ARV_IS_STREAM (stream), 0); return priv->infos->len; } /** * arv_stream_get_info_name: * @stream: a #ArvStream * @id: info id * * Returns: the name of the corresponding stream information. * * Since: 0.8.11 */ const char * arv_stream_get_info_name (ArvStream *stream, guint id) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), NULL); g_return_val_if_fail (id < priv->infos->len, NULL); info = g_ptr_array_index (priv->infos, id); if (info != NULL) return info->name; return NULL; } /** * arv_stream_get_info_type: * @stream: a #ArvStream * @id: info id * * Returns: the #GType of the corresponding stream information, which indicates what API to use to retrieve the * information value (arv_stream_get_info_uint64() or arv_stream_get_info_double()). * * Since: 0.8.11 */ GType arv_stream_get_info_type (ArvStream *stream, guint id) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); g_return_val_if_fail (id < priv->infos->len, 0); info = g_ptr_array_index (priv->infos, id); if (info != NULL) return info->type; return 0; } /** * arv_stream_get_info_uint64: * @stream: a #ArvStream * @id: info id * * Returns: the value of the corresponding stream information, as a 64 bit unsigned integer. * * Since: 0.8.11 */ guint64 arv_stream_get_info_uint64 (ArvStream *stream, guint id) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); g_return_val_if_fail (id < priv->infos->len, 0); info = g_ptr_array_index (priv->infos, id); g_return_val_if_fail (info->type == G_TYPE_UINT64, 0); if (info != NULL) return *((guint64 *) (info->data)); return 0; } /** * arv_stream_get_info_double: * @stream: a #ArvStream * @id: info id * * Returns: the value of the corresponding stream information, as a double. * * Since: 0.8.11 */ double arv_stream_get_info_double (ArvStream *stream, guint id) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); g_return_val_if_fail (id < priv->infos->len, 0); info = g_ptr_array_index (priv->infos, id); g_return_val_if_fail (info->type == G_TYPE_DOUBLE, 0); if (info != NULL) return *((double *) (info->data)); return 0; } static const ArvStreamInfo * _find_info_by_name (ArvStream *stream, const char *name) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); guint i; for (i = 0; i < priv->infos->len; i++) { ArvStreamInfo *info = g_ptr_array_index (priv->infos, i); if (info != NULL && g_strcmp0 (name, info->name) == 0) return info; } return NULL; } /** * arv_stream_get_info_uint64_by_name: * @stream: a #ArvStream * @name: info name * * Returns: the value of the corresponding stream information, as a 64 bit unsigned integer. * * Since: 0.8.11 */ guint64 arv_stream_get_info_uint64_by_name (ArvStream *stream, const char *name) { const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); g_return_val_if_fail (name != NULL, 0); info = _find_info_by_name (stream, name); g_return_val_if_fail (info != NULL, 0); g_return_val_if_fail (info->type == G_TYPE_UINT64, 0); return *((guint64 *) (info->data)); } /** * arv_stream_get_info_double_by_name: * @stream: a #ArvStream * @name: info name * * Returns: the value of the corresponding stream information, as a double. * * Since: 0.8.11 */ double arv_stream_get_info_double_by_name (ArvStream *stream, const char *name) { const ArvStreamInfo *info; g_return_val_if_fail (ARV_IS_STREAM (stream), 0); g_return_val_if_fail (name != NULL, 0); info = _find_info_by_name (stream, name); g_return_val_if_fail (info != NULL, 0); g_return_val_if_fail (info->type == G_TYPE_DOUBLE, 0); return *((double *) (info->data)); } static void arv_stream_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ArvStream *stream = ARV_STREAM (object); ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); switch (prop_id) { case ARV_STREAM_PROPERTY_EMIT_SIGNALS: arv_stream_set_emit_signals (stream, g_value_get_boolean (value)); break; case ARV_STREAM_PROPERTY_DEVICE: g_clear_object (&priv->device); priv->device = g_value_dup_object (value); break; case ARV_STREAM_PROPERTY_CALLBACK: priv->callback = g_value_get_pointer (value); break; case ARV_STREAM_PROPERTY_CALLBACK_DATA: priv->callback_data = g_value_get_pointer (value); break; case ARV_STREAM_PROPERTY_DESTROY_NOTIFY: priv->destroy_notify = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_stream_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { ArvStream *stream = ARV_STREAM (object); ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); switch (prop_id) { case ARV_STREAM_PROPERTY_EMIT_SIGNALS: g_value_set_boolean (value, arv_stream_get_emit_signals (stream)); break; case ARV_STREAM_PROPERTY_DEVICE: g_value_set_object (value, priv->device); break; case ARV_STREAM_PROPERTY_CALLBACK: g_value_set_pointer (value, priv->callback); break; case ARV_STREAM_PROPERTY_CALLBACK_DATA: g_value_set_pointer (value, priv->callback_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } void arv_stream_take_init_error (ArvStream *stream, GError *error) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); g_return_if_fail (ARV_IS_STREAM (stream)); g_clear_error (&priv->init_error); priv->init_error = error; } static void arv_stream_init (ArvStream *stream) { ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); priv->input_queue = g_async_queue_new (); priv->output_queue = g_async_queue_new (); priv->emit_signals = FALSE; priv->infos = g_ptr_array_new (); g_rec_mutex_init (&priv->mutex); } static void arv_stream_finalize (GObject *object) { ArvStream *stream = ARV_STREAM (object); ArvStreamPrivate *priv = arv_stream_get_instance_private (stream); ArvBuffer *buffer; arv_info_stream ("[Stream::finalize] Flush %d buffer[s] in input queue", g_async_queue_length (priv->input_queue)); arv_info_stream ("[Stream::finalize] Flush %d buffer[s] in output queue", g_async_queue_length (priv->output_queue)); if (priv->emit_signals) { g_warning ("Stream finalized with 'new-buffer' signal enabled"); g_warning ("Please call arv_stream_set_emit_signals (stream, FALSE) before ArvStream object finalization"); } do { buffer = g_async_queue_try_pop (priv->output_queue); if (buffer != NULL) g_object_unref (buffer); } while (buffer != NULL); do { buffer = g_async_queue_try_pop (priv->input_queue); if (buffer != NULL) g_object_unref (buffer); } while (buffer != NULL); g_async_queue_unref (priv->input_queue); g_async_queue_unref (priv->output_queue); g_rec_mutex_clear (&priv->mutex); g_clear_object (&priv->device); g_clear_error (&priv->init_error); g_ptr_array_foreach (priv->infos, (GFunc) arv_stream_info_free, NULL); g_clear_pointer (&priv->infos, g_ptr_array_unref); if (priv->destroy_notify != NULL) { priv->destroy_notify(priv->callback_data); } G_OBJECT_CLASS (arv_stream_parent_class)->finalize (object); } static void arv_stream_class_init (ArvStreamClass *node_class) { GObjectClass *object_class = G_OBJECT_CLASS (node_class); object_class->finalize = arv_stream_finalize; object_class->set_property = arv_stream_set_property; object_class->get_property = arv_stream_get_property; /** * ArvStream::new-buffer: * @stream: the stream that emited the signal * * Signal that a new buffer is available. * * This signal is emited from the stream receive thread and only when the * "emit-signals" property is %TRUE. * * The new buffer can be retrieved with arv_stream_pop_buffer(). * * Note that this signal is only emited when the "emit-signals" property is * set to %TRUE, which it is not by default for performance reasons. * * Since: 0.2.0 */ arv_stream_signals[ARV_STREAM_SIGNAL_NEW_BUFFER] = g_signal_new ("new-buffer", G_TYPE_FROM_CLASS (node_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ArvStreamClass, new_buffer), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); g_object_class_install_property ( object_class, ARV_STREAM_PROPERTY_EMIT_SIGNALS, g_param_spec_boolean ("emit-signals", "Emit signals", "Emit signals", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) ); g_object_class_install_property (object_class, ARV_STREAM_PROPERTY_DEVICE, g_param_spec_object ("device", "Paret device", "A ArvDevice parent object", ARV_TYPE_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, ARV_STREAM_PROPERTY_CALLBACK, g_param_spec_pointer ("callback", "Stream callback", "Optional user callback", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, ARV_STREAM_PROPERTY_CALLBACK_DATA, g_param_spec_pointer ("callback-data", "Stream callback data", "Optional user callback data", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, ARV_STREAM_PROPERTY_DESTROY_NOTIFY, g_param_spec_pointer ("destroy-notify", "Destroy notify", "Optional destroy notify", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static gboolean arv_stream_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { ArvStreamPrivate *priv = arv_stream_get_instance_private (ARV_STREAM (initable)); g_return_val_if_fail (ARV_IS_STREAM (initable), FALSE); if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialization not supported"); return FALSE; } if (priv->init_error) { if (error != NULL) *error = g_error_copy (priv->init_error); return FALSE; } return TRUE; } static void arv_stream_initable_iface_init (GInitableIface *iface) { iface->init = arv_stream_initable_init; } aravis-0.8.34/src/arvstream.h000066400000000000000000000112051475431451200160440ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_STREAM_H #define ARV_STREAM_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * ArvStreamCallbackType: * @ARV_STREAM_CALLBACK_TYPE_INIT: thread initialization, happens once * @ARV_STREAM_CALLBACK_TYPE_EXIT: thread end, happens once * @ARV_STREAM_CALLBACK_TYPE_START_BUFFER: buffer filling start, happens at each frame * @ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE: buffer filled, happens at each frame * * Describes when the reason the stream callback is called. You are probably more interested in * @ARV_STREAM_CALLBACK_TYPE_INIT and @ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE. */ typedef enum { ARV_STREAM_CALLBACK_TYPE_INIT, ARV_STREAM_CALLBACK_TYPE_EXIT, ARV_STREAM_CALLBACK_TYPE_START_BUFFER, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE } ArvStreamCallbackType; #define ARV_TYPE_STREAM (arv_stream_get_type ()) ARV_API G_DECLARE_DERIVABLE_TYPE (ArvStream, arv_stream, ARV, STREAM, GObject) struct _ArvStreamClass { GObjectClass parent_class; void (*start_thread) (ArvStream *stream); void (*stop_thread) (ArvStream *stream); /* signals */ void (*new_buffer) (ArvStream *stream); }; /** * ArvStreamCallback: * @user_data: a pointer to user data associated to this callback * @type: reason of the callback call * @buffer: a [class@ArvBuffer] object * * This is the signature of the callback passed on an #ArvStream instantiation, which will be called on the stream * receiving thread initialization and finalization, and on every received buffer, once when the buffer is pulled from * the buffer queue, and one more when the buffer is done (successfully or not). * * @buffer is assured to be a valid #ArvBuffer object only when type is @ARV_STREAM_CALLBACK_TYPE_START_BUFFER or * @ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE. * * The callback is awaken from the stream receiving thread, which means it is forbidden to access to the camera * instance, except if you take care to protect the instance access from concurrent access. It also means all the time * spent in the callback is less time available for the incoming data handling. CPU intensive image processing should * happen elsewhere. */ typedef void (*ArvStreamCallback) (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer); ARV_API void arv_stream_push_buffer (ArvStream *stream, ArvBuffer *buffer); ARV_API ArvBuffer * arv_stream_pop_buffer (ArvStream *stream); ARV_API ArvBuffer * arv_stream_try_pop_buffer (ArvStream *stream); ARV_API ArvBuffer * arv_stream_timeout_pop_buffer (ArvStream *stream, guint64 timeout); ARV_API void arv_stream_get_n_buffers (ArvStream *stream, gint *n_input_buffers, gint *n_output_buffers); ARV_API void arv_stream_start_thread (ArvStream *stream); ARV_API unsigned int arv_stream_stop_thread (ArvStream *stream, gboolean delete_buffers); ARV_API void arv_stream_get_statistics (ArvStream *stream, guint64 *n_completed_buffers, guint64 *n_failures, guint64 *n_underruns); ARV_API guint arv_stream_get_n_infos (ArvStream *stream); ARV_API const char * arv_stream_get_info_name (ArvStream *stream, guint id); ARV_API GType arv_stream_get_info_type (ArvStream *stream, guint id); ARV_API guint64 arv_stream_get_info_uint64 (ArvStream *stream, guint id); ARV_API double arv_stream_get_info_double (ArvStream *stream, guint id); ARV_API guint64 arv_stream_get_info_uint64_by_name (ArvStream *stream, const char *name); ARV_API double arv_stream_get_info_double_by_name (ArvStream *stream, const char *name); ARV_API void arv_stream_set_emit_signals (ArvStream *stream, gboolean emit_signals); ARV_API gboolean arv_stream_get_emit_signals (ArvStream *stream); G_END_DECLS #endif aravis-0.8.34/src/arvstreamprivate.h000066400000000000000000000030151475431451200174370ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_STREAM_PRIVATE_H #define ARV_STREAM_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS ArvBuffer * arv_stream_pop_input_buffer (ArvStream *stream); ArvBuffer * arv_stream_timeout_pop_input_buffer (ArvStream *stream, guint64 timeout); void arv_stream_push_output_buffer (ArvStream *stream, ArvBuffer *buffer); void arv_stream_take_init_error (ArvStream *device, GError *error); void arv_stream_declare_info (ArvStream *stream, const char *name, GType type, gpointer data); G_END_DECLS #endif aravis-0.8.34/src/arvsystem.c000066400000000000000000000257771475431451200161130ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #if ARAVIS_HAS_USB #include #endif #include #include #include #include #include #include static GMutex arv_system_mutex; /** * SECTION: arv * @short_description: Device discovery and instantiation * * This module contans a set of APIs that allows to list and enable/disable the available interfaces, * and list and instantiate devices. */ typedef struct { const char *interface_id; gboolean is_available; ArvInterface * (*get_interface_instance) (void); void (*destroy_interface_instance) (void); } ArvInterfaceInfos; ArvInterfaceInfos interfaces[] = { { .interface_id = "Fake", .is_available = FALSE, .get_interface_instance = arv_fake_interface_get_instance, .destroy_interface_instance = arv_fake_interface_destroy_instance }, #if ARAVIS_HAS_USB { .interface_id = "USB3Vision", .is_available = TRUE, .get_interface_instance = arv_uv_interface_get_instance, .destroy_interface_instance = arv_uv_interface_destroy_instance }, #endif { .interface_id = "GigEVision", .is_available = TRUE, .get_interface_instance = arv_gv_interface_get_instance, .destroy_interface_instance = arv_gv_interface_destroy_instance } }; /** * arv_get_n_interfaces: * * Gets the number of available interfaces, including the disabled ones. * * Returns: The number of available interfaces. */ unsigned int arv_get_n_interfaces (void) { return G_N_ELEMENTS (interfaces); } /** * arv_get_interface_id: * @index: interface index * * Retrieves the interface identifier. Possible values are 'Fake', 'USB3Vision' * and 'GigEVision'. * * Returns: The interfae identifier string. */ const char * arv_get_interface_id (unsigned int index) { if (index >= G_N_ELEMENTS (interfaces)) return NULL; return interfaces[index].interface_id; } /** * arv_enable_interface: * @interface_id: name of the interface * * Enable an interface by name. By default, all interfaces are enabled, except 'Fake'. */ void arv_enable_interface (const char *interface_id) { guint i; g_return_if_fail (interface_id != NULL); for (i = 0; i < G_N_ELEMENTS (interfaces) ; i++) if (strcmp (interface_id, interfaces[i].interface_id) == 0) { interfaces[i].is_available = TRUE; return; } g_warning ("[Arv::enable_interface] Unknown interface '%s'", interface_id); } /** * arv_disable_interface: * @interface_id: name of the interface * * Disable an interface by name. By default, all interfaces are enabled, except 'Fake'. */ void arv_disable_interface (const char *interface_id) { guint i; g_return_if_fail (interface_id != NULL); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) if (strcmp (interface_id, interfaces[i].interface_id) == 0) { interfaces[i].is_available = FALSE; return; } g_warning ("[Arv::enable_interface] Unknown interface '%s'", interface_id); } /** * arv_set_interface_flags: * @interface_id: name of the interface * @flags: interface flags * * Set the device specific flags. * * Since: 0.8.23 */ void arv_set_interface_flags(const char *interface_id, int flags) { guint i; g_return_if_fail (interface_id != NULL); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) if (strcmp (interface_id, interfaces[i].interface_id) == 0) { ArvInterface *iface; iface = interfaces[i].get_interface_instance (); arv_interface_set_flags (iface, flags); return; } g_warning ("[Arv::enable_interface] Unknown interface '%s'", interface_id); } /** * arv_update_device_list: * * Updates the list of currently online devices. **/ void arv_update_device_list (void) { unsigned int i; g_mutex_lock (&arv_system_mutex); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) { ArvInterface *interface; if (interfaces[i].is_available) { interface = interfaces[i].get_interface_instance (); arv_interface_update_device_list (interface); } } g_mutex_unlock (&arv_system_mutex); } /** * arv_get_n_devices: * * Retrieves the number of currently online devices. This value is valid until * the next call to arv_update_device_list(). * * Returns: The number of currently online devices. **/ unsigned int arv_get_n_devices (void) { unsigned int n_devices = 0; unsigned int i; g_mutex_lock (&arv_system_mutex); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) { ArvInterface *interface; if (interfaces[i].is_available) { interface = interfaces[i].get_interface_instance (); n_devices += arv_interface_get_n_devices (interface); } } g_mutex_unlock (&arv_system_mutex); return n_devices; } static const char * arv_get_info (unsigned int index, const char *get_info (ArvInterface *, guint)) { unsigned int offset = 0; unsigned int i; const char *info; g_mutex_lock (&arv_system_mutex); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) { ArvInterface *interface; unsigned int n_devices; if (interfaces[i].is_available) { interface = interfaces[i].get_interface_instance (); n_devices = arv_interface_get_n_devices (interface); if (index - offset < n_devices) { info = get_info (interface, index - offset); g_mutex_unlock (&arv_system_mutex); return info; } offset += n_devices; } } g_mutex_unlock (&arv_system_mutex); return NULL; } /** * arv_get_device_id: * @index: device index * * Queries the unique device id corresponding to index. Prior to this * call the arv_update_device_list() function must be called. * * Returns: a unique device id * * Since: 0.2.0 */ const char * arv_get_device_id (unsigned int index) { return arv_get_info (index, arv_interface_get_device_id); } /** * arv_get_device_physical_id: * @index: device index * * Queries the physical device id corresponding to index such * as the MAC address for Ethernet based devices, bus id for PCI, * USB or Firewire based devices. * * Prior to this call the arv_update_device_list() * function must be called. * * Returns: a physical device id * * Since: 0.2.0 */ const char * arv_get_device_physical_id (unsigned int index) { return arv_get_info (index, arv_interface_get_device_physical_id); } /** * arv_get_device_vendor: * @index: device index * * Queries the device vendor. * * Prior to this call the arv_update_device_list() * function must be called. * * Returns: (transfer none): the device vendor, NULL on error * * Since: 0.6.0 */ const char * arv_get_device_vendor (unsigned int index) { return arv_get_info (index, arv_interface_get_device_vendor); } /** * arv_get_device_manufacturer_info: * @index: device index * * Queries the device manufacturer info. * * Prior to this call the arv_update_device_list() * function must be called. * * Returns: (transfer none): the device manufacturer info, NULL on error * * Since: 0.8.20 */ const char * arv_get_device_manufacturer_info (unsigned int index) { return arv_get_info (index, arv_interface_get_device_manufacturer_info); } /** * arv_get_device_model: * @index: device index * * Queries the device model. * * Prior to this call the arv_update_device_list() * function must be called. * * Returns: (transfer none): the device model, NULL on error * * Since: 0.6.0 */ const char * arv_get_device_model (unsigned int index) { return arv_get_info (index, arv_interface_get_device_model); } /** * arv_get_device_serial_nbr: * @index: device index * * Queries the device serial. * * Prior to this call the arv_update_device_list() * function must be called. * * Returns: (transfer none): the device serial, NULL on error * * Since: 0.6.0 */ const char * arv_get_device_serial_nbr (unsigned int index) { return arv_get_info (index, arv_interface_get_device_serial_nbr); } /** * arv_get_device_address: * @index: device index * * The index of a device may change after a call to arv_update_device_list(). * * Returns: The address of the device corresponding to @index as a string. * * Since: 0.6.0 */ const char * arv_get_device_address (unsigned int index) { return arv_get_info (index, arv_interface_get_device_address); } /** * arv_get_device_protocol: * @index: device index * * The index of a device may change after a call to arv_update_device_list(). * * Returns: The device protocol as a string. * * Since: 0.6.0 */ const char * arv_get_device_protocol (unsigned int index) { return arv_get_info (index, arv_interface_get_device_protocol); } /** * arv_open_device: * @device_id: (allow-none): a device identifier string * @error: a #GError placeholder, %NULL to ignore * * Open a device corresponding to the given identifier. A %NULL string makes * this function return the first available device. * * Return value: (transfer full): A new #ArvDevice instance. * * Since: 0.8.0 */ ArvDevice * arv_open_device (const char *device_id, GError **error) { unsigned int i; g_mutex_lock (&arv_system_mutex); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) { ArvInterface *interface; ArvDevice *device; if (interfaces[i].is_available) { GError *local_error = NULL; interface = interfaces[i].get_interface_instance (); device = arv_interface_open_device (interface, device_id, &local_error); if (ARV_IS_DEVICE (device) || local_error != NULL) { if (local_error != NULL) g_propagate_error (error, local_error); g_mutex_unlock (&arv_system_mutex); return device; } } } g_mutex_unlock (&arv_system_mutex); if (device_id != NULL) g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "Device '%s' not found", device_id); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "No supported device found"); return NULL; } /** * arv_shutdown: * * Frees a number of ressources allocated by Aravis that would be otherwise * reported as memory leak by tools like Valgrind. The call to this function is * optional if you don't intend to check for memory leaks. */ void arv_shutdown (void) { unsigned int i; g_mutex_lock (&arv_system_mutex); for (i = 0; i < G_N_ELEMENTS (interfaces); i++) interfaces[i].destroy_interface_instance (); arv_dom_implementation_cleanup (); g_mutex_unlock (&arv_system_mutex); } aravis-0.8.34/src/arvsystem.h000066400000000000000000000044541475431451200161050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_SYSTEM_H #define ARV_SYSTEM_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API unsigned int arv_get_n_interfaces (void); ARV_API const char * arv_get_interface_id (unsigned int index); ARV_API void arv_enable_interface (const char *interface_id); ARV_API void arv_disable_interface (const char *interface_id); ARV_API void arv_set_interface_flags (const char *interface_id, int flags); ARV_API void arv_update_device_list (void); ARV_API unsigned int arv_get_n_devices (void); ARV_API const char * arv_get_device_id (unsigned int index); ARV_API const char * arv_get_device_physical_id (unsigned int index); ARV_API const char * arv_get_device_address (unsigned int index); ARV_API const char * arv_get_device_vendor (unsigned int index); ARV_API const char * arv_get_device_manufacturer_info (unsigned int index); ARV_API const char * arv_get_device_model (unsigned int index); ARV_API const char * arv_get_device_serial_nbr (unsigned int index); ARV_API const char * arv_get_device_protocol (unsigned int index); ARV_API ArvDevice * arv_open_device (const char *device_id, GError **error); ARV_API void arv_shutdown (void); G_END_DECLS #endif aravis-0.8.34/src/arvtest.c000066400000000000000000001437741475431451200155440ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #define STDOUT_FILENO _fileno(stdout) #else #include #endif typedef enum { ARV_TEST_STATUS_SUCCESS, ARV_TEST_STATUS_FAILURE, ARV_TEST_STATUS_IGNORED } ArvTestStatus; typedef struct { char *test_name; char *camera_name; ArvTestStatus status; char *comment; } ArvTestResult; #define ARV_TYPE_TEST_RESULT (arv_test_result_get_type()) GType arv_test_result_get_type(void); static ArvTestResult * arv_test_result_new (const char *test_name, const char *camera_name, ArvTestStatus status, const char *comment) { ArvTestResult *result = g_new0 (ArvTestResult, 1); result->test_name = g_strdup (test_name); result->camera_name = g_strdup (camera_name); result->status = status; result->comment = g_strdup (comment); return result; } static ArvTestResult * arv_test_result_copy (ArvTestResult *result) { return arv_test_result_new (result->test_name, result->camera_name, result->status, result->comment); } static void arv_test_result_free (ArvTestResult *result) { if (result != NULL) { g_free (result->test_name); g_free (result->camera_name); g_free (result->comment); g_free (result); } } G_DEFINE_BOXED_TYPE (ArvTestResult, arv_test_result, arv_test_result_copy, arv_test_result_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ArvTestResult, arv_test_result_free) typedef struct { char *id; ArvCamera *camera; char *vendor_model; GSList *results; gboolean cache_check; } ArvTestCamera; #define ARV_TYPE_TEST_CAMERA (arv_test_camera_get_type()) GType arv_test_camera_get_type(void); static ArvTestCamera * arv_test_camera_new (const char *camera_id, gboolean cache_check) { ArvTestCamera *test_camera; ArvCamera *camera = arv_camera_new (camera_id, NULL); if (!ARV_IS_CAMERA (camera)) return NULL; test_camera = g_new0 (ArvTestCamera, 1); test_camera->id = g_strdup (camera_id); test_camera->camera = camera; test_camera->vendor_model = g_strdup_printf ("%s:%s", arv_camera_get_vendor_name (test_camera->camera, NULL), arv_camera_get_model_name (test_camera->camera, NULL)); test_camera->cache_check = cache_check; if (cache_check) arv_camera_set_register_cache_policy (test_camera->camera, ARV_REGISTER_CACHE_POLICY_DEBUG); return test_camera; } static ArvTestCamera * arv_test_camera_copy (ArvTestCamera *self) { return arv_test_camera_new (self->id, self->cache_check); } static void arv_test_camera_free (ArvTestCamera *camera) { if (camera != NULL) { if (camera->results != NULL) g_slist_free_full (camera->results, (GDestroyNotify) arv_test_result_free); g_clear_pointer (&camera->id, g_free); g_clear_object (&camera->camera); g_clear_pointer (&camera->vendor_model, g_free); g_free (camera); } } static gboolean stdout_has_color_support (void) { #if GLIB_CHECK_VERSION(2,50,0) static int has_color_support = -1; if (has_color_support >= 0) return has_color_support > 0; has_color_support = g_log_writer_supports_color (STDOUT_FILENO) ? 1 : 0; return has_color_support; #else return FALSE; #endif } static void arv_test_camera_add_result (ArvTestCamera *test_camera, const char *test_name, const char *step_name, ArvTestStatus status, const char *comment) { char *title; const char *status_str; title = g_strdup_printf ("%s:%s", test_name, step_name); if (stdout_has_color_support ()) switch (status) { case ARV_TEST_STATUS_SUCCESS: status_str = "\033[1;32mSUCCESS\033[0m"; break; case ARV_TEST_STATUS_FAILURE: status_str = "\033[1;31mFAILURE\033[0m"; break; case ARV_TEST_STATUS_IGNORED: status_str = "\033[1;34mIGNORED\033[0m"; break; default: status_str = ""; } else switch (status) { case ARV_TEST_STATUS_SUCCESS: status_str = "SUCCESS"; break; case ARV_TEST_STATUS_FAILURE: status_str = "FAILURE"; break; case ARV_TEST_STATUS_IGNORED: status_str = "IGNORED"; break; default: status_str = ""; } g_fprintf (stdout, "%-35s %s %s\n", title, status_str, comment != NULL ? comment : ""); test_camera->results = g_slist_append (test_camera->results, arv_test_result_new (title, test_camera->vendor_model, status, comment)); g_clear_pointer (&title, g_free); } G_DEFINE_BOXED_TYPE (ArvTestCamera, arv_test_camera, arv_test_camera_copy, arv_test_camera_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ArvTestCamera, arv_test_camera_free) #define ARV_TYPE_TEST arv_test_get_type() G_DECLARE_FINAL_TYPE (ArvTest, arv_test, ARV, TEST, GObject) struct _ArvTest { GObject parent; GKeyFile *key_file; ArvXmlSchema *schema_1_1; ArvXmlSchema *schema_1_0; }; G_DEFINE_TYPE (ArvTest, arv_test, G_TYPE_OBJECT) static void arv_test_finalize (GObject *gobject) { ArvTest *self = ARV_TEST (gobject); g_clear_object (&self->schema_1_1); g_clear_object (&self->schema_1_0); g_clear_pointer (&self->key_file, g_key_file_unref); G_OBJECT_CLASS (arv_test_parent_class)->finalize (gobject); } static void arv_test_class_init (ArvTestClass *test_class) { GObjectClass *object_class = G_OBJECT_CLASS (test_class); object_class->finalize = arv_test_finalize; } static void arv_test_init (ArvTest *self) { GBytes *bytes = NULL; bytes = g_resources_lookup_data("/org/aravis/GenApiSchema_Version_1_0.xsd", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); self->schema_1_0 = arv_xml_schema_new_from_memory (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); g_clear_pointer (&bytes, g_bytes_unref); bytes = g_resources_lookup_data("/org/aravis/GenApiSchema_Version_1_0.xsd", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); self->schema_1_1 = arv_xml_schema_new_from_memory (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); g_clear_pointer (&bytes, g_bytes_unref); bytes = g_resources_lookup_data ("/org/aravis/arv-test.cfg", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); self->key_file = g_key_file_new (); g_key_file_load_from_data (self->key_file, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), G_KEY_FILE_KEEP_COMMENTS, NULL); g_clear_pointer (&bytes, g_bytes_unref); } static ArvTest * arv_test_new (void) { return g_object_new (ARV_TYPE_TEST, NULL); } static double arv_test_camera_get_key_file_double (ArvTestCamera *test_camera, ArvTest *test, const char *key, double default_value) { GError *error = NULL; double value; g_return_val_if_fail (test_camera != NULL, 0); g_return_val_if_fail (ARV_IS_TEST (test), 0); value = g_key_file_get_double (test->key_file, test_camera->vendor_model, key, &error); if (error != NULL) { g_clear_error (&error); return default_value; } return value; } static gint64 arv_test_camera_get_key_file_int64 (ArvTestCamera *test_camera, ArvTest *test, const char *key, gint64 default_value) { GError *error = NULL; gint64 value; g_return_val_if_fail (test_camera != NULL, 0); g_return_val_if_fail (ARV_IS_TEST (test), 0); value = g_key_file_get_int64 (test->key_file, test_camera->vendor_model, key, &error); if (error != NULL) { g_clear_error (&error); return default_value; } return value; } static gboolean arv_test_camera_get_key_file_boolean (ArvTestCamera *test_camera, ArvTest *test, const char *key, gboolean default_value) { GError* error = NULL; gboolean value; g_return_val_if_fail (test_camera != NULL, 0); g_return_val_if_fail (ARV_IS_TEST (test), 0); value = g_key_file_get_boolean (test->key_file, test_camera->vendor_model, key, &error); if (error != NULL) { g_clear_error (&error); return default_value; } return value; } static char * arv_test_camera_get_key_file_string (ArvTestCamera *test_camera, ArvTest *test, const char *key, const char *default_value) { GError *error = NULL; char *value; g_return_val_if_fail (test_camera != NULL, NULL); g_return_val_if_fail (ARV_IS_TEST (test), NULL); value = g_key_file_get_string (test->key_file, test_camera->vendor_model, key, &error); if (error != NULL) { g_clear_error (&error); g_free (value); return g_strdup (default_value); } return value; } static char * arv_test_camera_get_key_file_comment (ArvTestCamera *test_camera, ArvTest *test, const char *key) { g_return_val_if_fail (test_camera != NULL, NULL); g_return_val_if_fail (ARV_IS_TEST (test), NULL); return g_key_file_get_comment (test->key_file, test_camera->vendor_model, key, NULL); } static gint * arv_test_camera_get_key_file_integer_list (ArvTestCamera *test_camera, ArvTest *test, const char *key, gsize *size) { if (size != NULL) *size = 0; g_return_val_if_fail (test_camera != NULL, NULL); g_return_val_if_fail (ARV_IS_TEST (test), NULL); return g_key_file_get_integer_list (test->key_file, test_camera->vendor_model, key, size, NULL); } static guint64 arv_test_camera_get_n_register_cache_errors (ArvTestCamera *test_camera) { ArvGc *genicam; genicam = arv_device_get_genicam (arv_camera_get_device (test_camera->camera)); return arv_gc_register_cache_error_add (genicam, 0); } static void arv_test_genicam (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { ArvDevice *device; ArvTestStatus status; char *version = NULL; char *comment = NULL; const char *genicam; size_t size; g_return_if_fail (ARV_IS_TEST (test)); version = arv_test_camera_get_key_file_string (test_camera, test, "Schema", "Invalid"); device = arv_camera_get_device (test_camera->camera); genicam = arv_device_get_genicam_xml (device, &size); arv_test_camera_add_result (test_camera, test_name, "Load", genicam != NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, genicam != NULL ? "" : "Failed to retrieve Genicam data"); if (genicam == NULL) { g_free (version); return; } status = ARV_TEST_STATUS_IGNORED; if (g_strcmp0 (version, "1.1") == 0) { if (arv_xml_schema_validate (test->schema_1_1, genicam, size, NULL, NULL, NULL)) { status = ARV_TEST_STATUS_SUCCESS; comment = g_strdup_printf ("%s", version); } else { status = ARV_TEST_STATUS_FAILURE; } } else if (g_strcmp0 (version, "1.0") == 0) { if (arv_xml_schema_validate (test->schema_1_0, genicam, size, NULL, NULL, NULL)) { status = ARV_TEST_STATUS_SUCCESS; comment = g_strdup_printf ("%s", version); } else { status = ARV_TEST_STATUS_FAILURE; } } else if (version != NULL && g_ascii_strcasecmp (version, "Invalid") == 0) { status = ARV_TEST_STATUS_SUCCESS; comment = g_strdup ("Schema validation disabled"); } arv_test_camera_add_result (test_camera, test_name, "Schema", status, comment); g_free (version); g_free (comment); } static void arv_test_device_properties (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { GError *error = NULL; gint *sensor_size = NULL; char *comment = NULL; ArvTestStatus status; gint sensor_width, sensor_height; gsize size = 0; gboolean available; g_return_if_fail (ARV_IS_TEST (test)); sensor_size = arv_test_camera_get_key_file_integer_list (test_camera, test, "SensorSize", &size); arv_camera_get_sensor_size (test_camera->camera, &sensor_width, &sensor_height, &error); arv_test_camera_add_result (test_camera, test_name, "SensorSizeReadout", error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); if (error == NULL && size == 2) { if (sensor_size[0] != sensor_width || sensor_size[1] != sensor_height) { status = ARV_TEST_STATUS_FAILURE; comment = g_strdup_printf ("Found %dx%d instead of %dx%d", sensor_width, sensor_height, sensor_size[0], sensor_size[1]); } else { status = ARV_TEST_STATUS_SUCCESS; comment = g_strdup_printf ("%dx%d", sensor_size[0], sensor_size[1]); } } else { status = ARV_TEST_STATUS_IGNORED; } arv_test_camera_add_result (test_camera, test_name, "SensorSizeCheck", status, comment); g_clear_error (&error); available = arv_camera_is_gain_available(test_camera->camera, &error); arv_test_camera_add_result (test_camera, test_name, "GainAvailable", error == NULL && available == arv_test_camera_get_key_file_boolean(test_camera, test, "GainAvailable", TRUE) ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); if (available) { g_clear_error (&error); arv_camera_get_gain (test_camera->camera, &error); arv_test_camera_add_result (test_camera, test_name, "GainReadout", error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); } g_clear_error (&error); available = arv_camera_is_exposure_time_available (test_camera->camera, &error); arv_test_camera_add_result (test_camera, test_name, "ExposureTimeAvailable", error == NULL && available == arv_test_camera_get_key_file_boolean(test_camera, test, "ExposureTimeAvailable", TRUE) ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); if (available) { g_clear_error (&error); arv_camera_get_exposure_time (test_camera->camera, &error); arv_test_camera_add_result (test_camera, test_name, "ExposureTimeReadout", error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); } g_clear_error (&error); g_free (sensor_size); g_free (comment); } static void _single_acquisition (ArvTest *test, const char *test_name, ArvTestCamera *test_camera, gboolean chunk_test, gboolean multipart_test) { GError *error = NULL; ArvBuffer *buffer = NULL; char **chunk_list = NULL; ArvChunkParser *parser = NULL; char *component = NULL; guint n_parts = 1; g_return_if_fail (ARV_IS_TEST (test)); g_assert (!(multipart_test & chunk_test)); if (multipart_test) { char *multiparts = arv_test_camera_get_key_file_string (test_camera, test, "Multipart", NULL); char **parts; gboolean multipart_supported; gboolean multipart_expected; guint i; parts = multiparts != NULL ? g_strsplit(multiparts, " ", -1) : NULL; n_parts = parts != NULL ? g_strv_length(parts) : 0; multipart_supported = arv_camera_gv_is_multipart_supported (test_camera->camera, &error); multipart_expected = n_parts > 1; if (!multipart_expected || (multipart_supported != multipart_expected)) { arv_test_camera_add_result (test_camera, test_name, "NoSupport", error == NULL && (multipart_expected == multipart_supported) ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); g_clear_error (&error); return; } arv_camera_gv_set_multipart(test_camera->camera, TRUE, &error); arv_test_camera_add_result (test_camera, test_name, "Enable", error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); if (error != NULL) { g_clear_error (&error); return; } for (i = 0; i < n_parts && error == NULL; i++) { arv_camera_select_and_enable_component(test_camera->camera, parts[i], i == 0, &error); } component = g_strdup (parts[0]); g_clear_pointer (&parts, g_strfreev); g_free (multiparts); } if (chunk_test) { char *chunks; gboolean chunks_support = arv_test_camera_get_key_file_boolean (test_camera, test, "ChunksSupport", TRUE); n_parts = arv_test_camera_get_key_file_int64 (test_camera, test, "ChunksNParts", 1); if (!arv_camera_are_chunks_available (test_camera->camera, &error)) { arv_test_camera_add_result (test_camera, test_name, "NoSupport", error == NULL && ! chunks_support ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); g_clear_error (&error); return; } chunks = arv_test_camera_get_key_file_string (test_camera, test, "ChunkList", "OffsetX OffsetY"); chunk_list = g_strsplit_set (chunks, " ", -1); parser = arv_camera_create_chunk_parser (test_camera->camera); arv_camera_set_chunks (test_camera->camera, chunks, &error); g_free (chunks); } if (error == NULL) buffer = arv_camera_acquisition (test_camera->camera, 1000000, &error); if (error == NULL && (!ARV_IS_BUFFER (buffer) || arv_buffer_get_status(buffer) != ARV_BUFFER_STATUS_SUCCESS)) { g_set_error (&error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TRANSFER_ERROR, "Buffer transfer failure"); } if (error == NULL && ARV_IS_BUFFER (buffer)) { if (arv_buffer_get_n_parts(buffer) != n_parts) g_set_error (&error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TRANSFER_ERROR, "Invalid number of parts (found: %u - expected: %u)", arv_buffer_get_n_parts(buffer), n_parts); } if (error == NULL && chunk_test) { int n_chunks = g_strv_length (chunk_list); int i; if (arv_buffer_has_chunks(buffer)) { for (i = 0; i < n_chunks && error == NULL; i++) { char *chunk_name = g_strdup_printf ("Chunk%s", chunk_list[i]); arv_chunk_parser_get_integer_value (parser, buffer, chunk_name, &error); g_clear_pointer (&chunk_name, g_free); } } else { g_set_error (&error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TRANSFER_ERROR, "No chunk found in buffer"); } g_clear_object (&parser); g_strfreev (chunk_list); } if (chunk_test) arv_camera_set_chunks (test_camera->camera, NULL, NULL); if (error == NULL && multipart_test) { arv_camera_gv_set_multipart (test_camera->camera, FALSE, &error); if (error == NULL) arv_camera_select_and_enable_component(test_camera->camera, component, TRUE, &error); g_free (component); } arv_test_camera_add_result (test_camera, test_name, "BufferCheck", ARV_IS_BUFFER (buffer) && arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS && error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); g_clear_error (&error); g_clear_object (&buffer); } static void arv_test_single_acquisition (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { _single_acquisition (test, test_name, test_camera, FALSE, FALSE); } static void arv_test_multipart (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { if (!arv_camera_is_gv_device (test_camera->camera)) return; _single_acquisition (test, test_name, test_camera, FALSE, TRUE); } static void arv_test_chunks (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { _single_acquisition (test, test_name, test_camera, TRUE, FALSE); } typedef struct { unsigned int n_init; unsigned int n_start; unsigned int n_done; unsigned int n_success; unsigned int n_exit; } _StreamCallbackData; static void _stream_calback (gpointer data, ArvStreamCallbackType type, ArvBuffer *buffer) { _StreamCallbackData *callback_data = data; switch (type) { case ARV_STREAM_CALLBACK_TYPE_INIT: callback_data->n_init++; break; case ARV_STREAM_CALLBACK_TYPE_START_BUFFER: callback_data->n_start++; break; case ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE: callback_data->n_done++; if (buffer->priv->status == ARV_BUFFER_STATUS_SUCCESS) callback_data->n_success++; break; case ARV_STREAM_CALLBACK_TYPE_EXIT: callback_data->n_exit++; break; default: break; } } static void _multiple_acquisition (ArvTest *test, const char *test_name, ArvTestCamera *test_camera, double frame_rate, gboolean use_system_timestamp) { GError *error = NULL; char *message = NULL; ArvStream *stream; _StreamCallbackData callback_data; unsigned int i; size_t payload_size; gboolean success = TRUE; gint64 start_time = -1; gint64 end_time = -1; gboolean frame_rate_success; guint n_completed_buffers = 0; guint n_expected_buffers = 10; arv_camera_set_acquisition_mode (test_camera->camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error); if (error == NULL) arv_camera_set_frame_rate (test_camera->camera, frame_rate, &error); if (error != NULL) { arv_test_camera_add_result (test_camera, test_name, "BufferCheck", ARV_TEST_STATUS_FAILURE, error->message); g_clear_error (&error); return; } callback_data.n_init = 0; callback_data.n_start = 0; callback_data.n_done = 0; callback_data.n_success = 0; callback_data.n_exit = 0; stream = arv_camera_create_stream (test_camera->camera, _stream_calback, &callback_data, &error); if (error == NULL) payload_size = arv_camera_get_payload (test_camera->camera, &error); if (error == NULL) { for (i = 0 ; i < 2; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload_size, FALSE)); } if (error == NULL) arv_camera_start_acquisition (test_camera->camera, &error); if (error == NULL) { for (i = 0 ; i < n_expected_buffers; i++) { ArvBuffer *buffer; buffer = arv_stream_timeout_pop_buffer (stream, i == 0 ? 5000000 : 500000); if (buffer == NULL) success = FALSE; else { if (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) { n_completed_buffers++; if (start_time < 0) start_time = use_system_timestamp ? arv_buffer_get_system_timestamp (buffer): arv_buffer_get_timestamp (buffer); else end_time = use_system_timestamp ? arv_buffer_get_system_timestamp (buffer): arv_buffer_get_timestamp (buffer); } else { success = FALSE; } arv_stream_push_buffer (stream, buffer); } } } if (error == NULL) arv_camera_stop_acquisition (test_camera->camera, &error); g_clear_object (&stream); success = success && callback_data.n_init == 1 && callback_data.n_start == callback_data.n_done && callback_data.n_success >= n_expected_buffers && callback_data.n_exit == 1; message = g_strdup_printf ("%u/%u%s%s", n_completed_buffers, n_expected_buffers, error != NULL ? " " : "", error != NULL ? error->message : ""); arv_test_camera_add_result (test_camera, test_name, "BufferCheck", success && error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, message); g_clear_pointer (&message, g_free); frame_rate_success = FALSE; if (end_time > 0 && start_time > 0 && start_time != end_time) { double actual_frame_rate = 9 * 1e9 / (end_time - start_time); double frame_rate_error = fabs (actual_frame_rate - frame_rate) / frame_rate; if (frame_rate_error < 0.10) { frame_rate_success = TRUE; message = g_strdup_printf ("%.2f Hz", actual_frame_rate); } else message = g_strdup_printf ("%.2f Hz (expected:%.2f Hz)", actual_frame_rate, frame_rate); } arv_test_camera_add_result (test_camera, test_name, "FrameRate", frame_rate_success ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, message); g_clear_error (&error); g_clear_pointer (&message, g_free); } static void arv_test_multiple_acquisition_a (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { double frame_rate; gboolean use_system_timestamp; g_return_if_fail (ARV_IS_TEST (test)); use_system_timestamp = arv_test_camera_get_key_file_boolean (test_camera, test, "UseSystemTimestamp", FALSE); frame_rate = arv_test_camera_get_key_file_double (test_camera, test, "FrameRateA", 10.0); _multiple_acquisition (test, test_name, test_camera, frame_rate, use_system_timestamp); } static void arv_test_multiple_acquisition_b (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { double frame_rate; gboolean use_system_timestamp; g_return_if_fail (ARV_IS_TEST (test)); use_system_timestamp = arv_test_camera_get_key_file_boolean (test_camera, test, "UseSystemTimestamp", FALSE); frame_rate = arv_test_camera_get_key_file_double (test_camera, test, "FrameRateB", 5.0); _multiple_acquisition (test, test_name, test_camera, frame_rate, use_system_timestamp); } static void arv_test_software_trigger (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { GError* error = NULL; char *message = NULL; ArvStream *stream; _StreamCallbackData callback_data; unsigned int i; size_t payload_size; gboolean success = TRUE; gboolean software_trigger_support; guint n_completed_buffers = 0; guint n_expected_buffers = 5; double delay_s; g_return_if_fail (ARV_IS_TEST (test)); software_trigger_support = arv_test_camera_get_key_file_boolean (test_camera, test, "SoftwareTriggerSupport", TRUE); if (!arv_camera_is_software_trigger_supported (test_camera->camera, &error)) { arv_test_camera_add_result (test_camera, test_name, "NoSupport", error == NULL && ! software_trigger_support ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); g_clear_error (&error); return; } delay_s = arv_test_camera_get_key_file_double (test_camera, test, "SoftwareTriggerWait", 0); arv_camera_set_acquisition_mode (test_camera->camera, ARV_ACQUISITION_MODE_CONTINUOUS, &error); if (error == NULL) arv_camera_set_trigger (test_camera->camera, "Software", &error); if (error != NULL) { arv_test_camera_add_result (test_camera, test_name, "BufferCheck", ARV_TEST_STATUS_FAILURE, error->message); g_clear_error (&error); return; } callback_data.n_init = 0; callback_data.n_start = 0; callback_data.n_done = 0; callback_data.n_success = 0; callback_data.n_exit = 0; stream = arv_camera_create_stream (test_camera->camera, _stream_calback, &callback_data, &error); if (error == NULL) payload_size = arv_camera_get_payload (test_camera->camera, &error); if (error == NULL) { for (i = 0 ; i < 2; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload_size, FALSE)); } if (error == NULL) arv_camera_start_acquisition (test_camera->camera, &error); for (i = 0 ; i < n_expected_buffers && error == NULL && success; i++) { ArvBuffer *buffer; if (error == NULL) { if (i > 0) g_usleep (1000000.0 * delay_s); arv_camera_software_trigger (test_camera->camera, &error); } if (error == NULL) { buffer = arv_stream_timeout_pop_buffer (stream, 500000); if (buffer == NULL) success = FALSE; else { if (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) { n_completed_buffers++; } else { success = FALSE; } arv_stream_push_buffer (stream, buffer); } } } if (error == NULL) arv_camera_stop_acquisition (test_camera->camera, &error); g_clear_object (&stream); success = success && callback_data.n_init == 1 && callback_data.n_start == callback_data.n_done && callback_data.n_success == n_expected_buffers && callback_data.n_exit == 1; message = g_strdup_printf ("%u/%u%s%s", n_completed_buffers, n_expected_buffers, error != NULL ? " " : "", error != NULL ? error->message : ""); arv_test_camera_add_result (test_camera, test_name, "BufferCheck", success && error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, message); g_clear_pointer (&message, g_free); g_clear_error (&error); } static void arv_test_gige_vision (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { GError *error = NULL; gint expected_n_stream_channels; gint n_stream_channels; gint expected_n_network_interfaces; gint n_network_interfaces; char *message; g_return_if_fail (ARV_IS_TEST (test)); if (!arv_camera_is_gv_device (test_camera->camera)) return; expected_n_network_interfaces = arv_test_camera_get_key_file_int64 (test_camera, test, "NNetworkInterfaces", 1); n_network_interfaces = arv_camera_gv_get_n_network_interfaces (test_camera->camera, &error); if (error != NULL) message = g_strdup_printf("%s", error->message); else message = g_strdup_printf("%d", n_network_interfaces); arv_test_camera_add_result (test_camera, test_name, "NNetworkInterfaces", n_network_interfaces == expected_n_network_interfaces && error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, message); g_clear_pointer(&message, g_free); expected_n_stream_channels = arv_test_camera_get_key_file_int64 (test_camera, test, "NStreamChannels", 1); n_stream_channels = arv_camera_gv_get_n_stream_channels (test_camera->camera, &error); if (error != NULL) message = g_strdup_printf("%s", error->message); else message = g_strdup_printf("%d", n_stream_channels); arv_test_camera_add_result (test_camera, test_name, "NStreamChannels", n_stream_channels == expected_n_stream_channels && error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, message); g_clear_pointer(&message, g_free); g_clear_error (&error); if (expected_n_stream_channels > 0) { arv_camera_gv_get_current_stream_channel (test_camera->camera, &error); arv_test_camera_add_result (test_camera, test_name, "StreamChannel", error == NULL ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, error != NULL ? error->message : NULL); } else { arv_test_camera_add_result (test_camera, test_name, "StreamChannel", ARV_TEST_STATUS_IGNORED, NULL); } g_clear_error (&error); } static void arv_test_usb3_vision (ArvTest *test, const char *test_name, ArvTestCamera *test_camera) { g_return_if_fail (ARV_IS_TEST (test)); if (!arv_camera_is_uv_device (test_camera->camera)) return; } const struct { const char *name; void (*run) (ArvTest *test, const char *test_name, ArvTestCamera *test_camera); gboolean is_slow; } tests[] = { {"Genicam", arv_test_genicam, FALSE}, {"Properties", arv_test_device_properties, FALSE}, {"MultipleAcquisitionA", arv_test_multiple_acquisition_a,FALSE}, {"SingleAcquisition", arv_test_single_acquisition, FALSE}, {"SoftwareTrigger", arv_test_software_trigger, FALSE}, {"MultipleAcquisitionB", arv_test_multiple_acquisition_b,FALSE}, {"Multipart", arv_test_multipart, FALSE}, {"Chunks", arv_test_chunks, FALSE}, {"GigEVision", arv_test_gige_vision, FALSE}, {"USB3Vision", arv_test_usb3_vision, FALSE} }; static gboolean arv_test_run (ArvTest *test, unsigned int n_iterations, const char *camera_selection, const char *test_selection, ArvUvUsbMode usb_mode, gboolean cache_check, gboolean packet_socket) { GRegex *camera_regex; GRegex *test_regex; unsigned n_devices, i, j; gboolean success = TRUE; g_return_val_if_fail (ARV_IS_TEST (test), FALSE); arv_update_device_list (); n_devices = arv_get_n_devices (); printf ("Found %d device%s\n", n_devices, n_devices > 1 ? "s" : ""); camera_regex = arv_regex_new_from_glob_pattern (camera_selection != NULL ? camera_selection : "*", TRUE); test_regex = arv_regex_new_from_glob_pattern (test_selection != NULL ? test_selection : "*", TRUE); for (j = 0; j < n_iterations; j++) { for (i = 0; i < n_devices; i++) { const char *camera_id = arv_get_device_id (i); if (g_regex_match (camera_regex, camera_id, 0, NULL)) { ArvTestCamera* test_camera = NULL; unsigned int j; test_camera = arv_test_camera_new (camera_id, cache_check); if (test_camera == NULL) { printf ("Failed to connect to '%s:%s'\n", arv_get_device_vendor (i), arv_get_device_model (i)); } else { printf ("Testing '%s:%s'\n", arv_get_device_vendor (i), arv_get_device_model (i)); if (arv_camera_is_uv_device (test_camera->camera)) arv_camera_uv_set_usb_mode (test_camera->camera, usb_mode); if (arv_camera_is_gv_device(test_camera->camera)) arv_camera_gv_set_stream_options (test_camera->camera, packet_socket ? ARV_GV_STREAM_OPTION_NONE : ARV_GV_STREAM_OPTION_PACKET_SOCKET_DISABLED); for (j = 0; j < G_N_ELEMENTS (tests); j++) { if (g_regex_match (test_regex, tests[j].name, 0, NULL)) { if (arv_test_camera_get_key_file_boolean (test_camera, test, tests[j].name, TRUE)) { char *delay_name; double delay; delay_name = g_strdup_printf ("%sDelay", tests[j].name); delay = arv_test_camera_get_key_file_double (test_camera, test, delay_name, 0); g_usleep (1000000.0 * delay); tests[j].run (test, tests[j].name, test_camera); g_free (delay_name); } else { char *comment; arv_test_camera_add_result (test_camera, tests[j].name, "*", ARV_TEST_STATUS_IGNORED, NULL); comment = arv_test_camera_get_key_file_comment (test_camera, test, tests[j].name); if (comment != NULL) { printf ("%s\n", comment); g_free (comment); } } } } if (cache_check) { guint64 n_cache_errors; char *comment = NULL; n_cache_errors = arv_test_camera_get_n_register_cache_errors (test_camera); if (n_cache_errors > 0) comment = g_strdup_printf ("%" G_GUINT64_FORMAT " error(s)", n_cache_errors); arv_test_camera_add_result (test_camera, "Genicam", "RegisterCache", n_cache_errors == 0 ? ARV_TEST_STATUS_SUCCESS : ARV_TEST_STATUS_FAILURE, comment); g_free (comment); } } g_clear_pointer (&test_camera, arv_test_camera_free); } } } g_regex_unref (camera_regex); g_regex_unref (test_regex); return success; } static gboolean arv_test_set_configuration (ArvTest *test, const char *path, GError **error) { g_return_val_if_fail (ARV_IS_TEST (test), FALSE); g_return_val_if_fail (path != NULL, FALSE); g_key_file_free (test->key_file); test->key_file = g_key_file_new (); return g_key_file_load_from_file (test->key_file, path, G_KEY_FILE_KEEP_COMMENTS, error); } static char *arv_option_camera_selection = NULL; static char *arv_option_test_selection = NULL; static gint arv_option_n_iterations = 1; static char *arv_option_configuration = NULL; static char *arv_option_debug_domains = NULL; static char *arv_option_uv_usb_mode = NULL; static gboolean arv_option_cache_check = FALSE; static gboolean arv_option_packet_socket = FALSE; static gboolean arv_option_show_version = FALSE; static const GOptionEntry arv_option_entries[] = { { "name", 'n', 0, G_OPTION_ARG_STRING, &arv_option_camera_selection, "Device selection", "" }, { "test", 't', 0, G_OPTION_ARG_STRING, &arv_option_test_selection, "Test selection", "" }, { "configuration", 'c', 0, G_OPTION_ARG_STRING, &arv_option_configuration, "Alternative configuration", "" }, { "iterations", 'i', 0, G_OPTION_ARG_INT, &arv_option_n_iterations, "Number of test repetitions", "" }, { "usb-mode", 's', 0, G_OPTION_ARG_STRING, &arv_option_uv_usb_mode, "USB device I/O mode", "{sync|async}" }, { "cache-check", 'a', 0, G_OPTION_ARG_NONE, &arv_option_cache_check, "Register cache check", NULL }, { "packet-socket", '\0', 0, G_OPTION_ARG_NONE, &arv_option_packet_socket, "Enable use of packet socket", NULL }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, NULL, "{[:][,...]|help}" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &arv_option_show_version, "Show version", NULL }, { NULL } }; static const char *description_content = "arv-test is an automated test utility that tries to exercise most of the\n" "Aravis functionalities. By default it runs all the tests on all the detected\n" "devices, but devices and tests can be selected using a glob pattern.\n\n" "A default configuration file is bundled in the executable. An alternative\n" "one with entries specific to the camera you want to test can be specified."; int main (int argc, char **argv) { GOptionContext *context; GError *error = NULL; ArvTest *test = NULL; gboolean success = TRUE; ArvUvUsbMode usb_mode; context = g_option_context_new (NULL); g_option_context_set_summary (context, "Automated test utillity."); g_option_context_set_description (context, description_content); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); if (arv_option_show_version) { printf ("%u.%u.%u\n", arv_get_major_version (), arv_get_minor_version (), arv_get_micro_version ()); return EXIT_SUCCESS; } if (!arv_debug_enable (arv_option_debug_domains)) { if (g_strcmp0 (arv_option_debug_domains, "help") != 0) printf ("Invalid debug selection\n"); else arv_debug_print_infos (); return EXIT_FAILURE; } test = arv_test_new (); if (arv_option_configuration != NULL) { arv_test_set_configuration (test, arv_option_configuration, &error); if (error != NULL) { printf ("Failed to open configuration file '%s': %s\n", arv_option_configuration, error->message); return EXIT_FAILURE; } } if (arv_option_uv_usb_mode == NULL) usb_mode = ARV_UV_USB_MODE_DEFAULT; else if (g_strcmp0 (arv_option_uv_usb_mode, "sync") == 0) usb_mode = ARV_UV_USB_MODE_SYNC; else if (g_strcmp0 (arv_option_uv_usb_mode, "async") == 0) usb_mode = ARV_UV_USB_MODE_ASYNC; else { printf ("Invalid USB device I/O mode\n"); return EXIT_FAILURE; } if (!arv_test_run (test, arv_option_n_iterations, arv_option_camera_selection, arv_option_test_selection, usb_mode, arv_option_cache_check, arv_option_packet_socket)) success = FALSE; g_clear_object (&test); arv_shutdown (); return success ? EXIT_SUCCESS : EXIT_FAILURE; } aravis-0.8.34/src/arvtestresources.xml000066400000000000000000000003561475431451200200410ustar00rootroot00000000000000 GenApiSchema_Version_1_0.xsd GenApiSchema_Version_1_1.xsd arv-test.cfg aravis-0.8.34/src/arvtool.c000066400000000000000000001171451475431451200155330ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include static char *arv_option_device_selection = NULL; static char *arv_option_device_address = NULL; static char *arv_option_debug_domains = NULL; static char *arv_option_register_cache = NULL; static char *arv_option_range_check = NULL; static char *arv_option_access_check = NULL; static gboolean arv_option_gv_allow_broadcast_discovery_ack = FALSE; static gboolean arv_option_show_time = FALSE; static gboolean arv_option_show_version = FALSE; static char *arv_option_gv_discovery_interface = NULL; static const GOptionEntry arv_option_entries[] = { { "name", 'n', 0, G_OPTION_ARG_STRING, &arv_option_device_selection, NULL, "" }, { "address", 'a', 0, G_OPTION_ARG_STRING, &arv_option_device_address, NULL, "" }, { "register-cache", '\0', 0, G_OPTION_ARG_STRING, &arv_option_register_cache, "Register cache policy", "{disable|enable|debug}" }, { "range-check", '\0', 0, G_OPTION_ARG_STRING, &arv_option_range_check, "Range check policy", "{disable|enable|debug}" }, { "access-check", '\0', 0, G_OPTION_ARG_STRING, &arv_option_access_check, "Feature access check policy", "{disable|enable}" }, { "gv-allow-broadcast-discovery-ack", '\0', 0, G_OPTION_ARG_NONE, &arv_option_gv_allow_broadcast_discovery_ack, "Allow broadcast discovery ack", NULL }, { "time", 't', 0, G_OPTION_ARG_NONE, &arv_option_show_time, "Show execution time", NULL }, { "gv-discovery-interface", '\0', 0, G_OPTION_ARG_STRING, &arv_option_gv_discovery_interface, "Discovery using the interface", "" }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, NULL, "{[:][,...]|help}" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &arv_option_show_version, "Show version", NULL }, { NULL } }; static const char description_content[] = "Command may be one of the following possibilities:\n" "\n" " genicam: dump the content of the Genicam xml data\n" " features: list all features\n" " values: list all available feature values\n" " description [] ...: show the full feature description\n" " control [=] ...: read/write device features\n" " network [=]...: read/write network settings\n" "\n" "If no command is given, this utility will list all the available devices.\n" "For the control command, direct access to device registers is provided using a R[address] syntax" " in place of a feature name.\n" "\n" "Examples:\n" "\n" "arv-tool-" ARAVIS_API_VERSION " control Width=128 Height=128 Gain R[0x10000]=0x10\n" "arv-tool-" ARAVIS_API_VERSION " features\n" "arv-tool-" ARAVIS_API_VERSION " description Width Height\n" "arv-tool-" ARAVIS_API_VERSION " network mode=PersistentIP\n" "arv-tool-" ARAVIS_API_VERSION " network ip=192.168.0.1 mask=255.255.255.0 gateway=192.168.0.254\n" "arv-tool-" ARAVIS_API_VERSION " -n Basler-210ab4 genicam"; typedef enum { ARV_TOOL_LIST_MODE_FEATURES, ARV_TOOL_LIST_MODE_DESCRIPTIONS, ARV_TOOL_LIST_MODE_VALUES } ArvToolListMode; static void arv_tool_show_feature (ArvGcFeatureNode *node, ArvToolListMode list_mode, int level) { if (ARV_IS_GC_CATEGORY (node)) { printf ("%*s%-12s: '%s'\n", 4 * level, "", arv_dom_node_get_node_name (ARV_DOM_NODE (node)), arv_gc_feature_node_get_name (node)); } else { if (arv_gc_feature_node_is_available (node, NULL)) { char *value = NULL; GError *error = NULL; gboolean is_selector; const char *access_mode; access_mode = arv_gc_access_mode_to_string (arv_gc_feature_node_get_actual_access_mode (node)); if (list_mode == ARV_TOOL_LIST_MODE_VALUES) { const char *unit; if (ARV_IS_GC_STRING (node) || ARV_IS_GC_ENUMERATION (node)) { value = g_strdup_printf ("'%s'", arv_gc_string_get_value (ARV_GC_STRING (node), &error)); } else if (ARV_IS_GC_INTEGER (node)) { if (ARV_IS_GC_ENUMERATION (node)) { value = g_strdup_printf ("'%s'", arv_gc_string_get_value (ARV_GC_STRING (node), &error)); } else { unit = arv_gc_integer_get_unit (ARV_GC_INTEGER (node)); value = g_strdup_printf ("%" G_GINT64_FORMAT "%s%s", arv_gc_integer_get_value (ARV_GC_INTEGER (node), &error), unit != NULL ? " " : "", unit != NULL ? unit : ""); } } else if (ARV_IS_GC_FLOAT (node)) { unit = arv_gc_float_get_unit (ARV_GC_FLOAT (node)); value = g_strdup_printf ("%g%s%s", arv_gc_float_get_value (ARV_GC_FLOAT (node), &error), unit != NULL ? " " : "", unit != NULL ? unit : ""); } else if (ARV_IS_GC_BOOLEAN (node)) { value = g_strdup_printf ("%s", arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), &error) ? "true" : "false"); } else if (ARV_IS_GC_REGISTER (node)) { guint64 length; length = arv_gc_register_get_length(ARV_GC_REGISTER (node), &error); value = g_strdup_printf ("%" G_GUINT64_FORMAT, length); } } is_selector = ARV_IS_GC_SELECTOR (node) && arv_gc_selector_is_selector (ARV_GC_SELECTOR (node)); if (error != NULL) { g_clear_error (&error); } else { if (value != NULL && value[0] != '\0') printf ("%*s%-13s: [%s] '%s' = %s\n", 4 * level, "", arv_dom_node_get_node_name (ARV_DOM_NODE (node)), access_mode, arv_gc_feature_node_get_name (node), value); else printf ("%*s%-13s: [%s] '%s'\n", 4 * level, "", arv_dom_node_get_node_name (ARV_DOM_NODE (node)), access_mode, arv_gc_feature_node_get_name (node)); if (is_selector) { const GSList *iter; for (iter = arv_gc_selector_get_selected_features (ARV_GC_SELECTOR (node)); iter != NULL; iter = iter->next) { printf (" %*s * %s\n", 4 * level, " ", arv_gc_feature_node_get_name (iter->data)); } } } g_clear_pointer (&value, g_free); } else { if (list_mode == ARV_TOOL_LIST_MODE_FEATURES) printf ("%*s%-12s: '%s' (Not available)\n", 4 * level, "", arv_dom_node_get_node_name (ARV_DOM_NODE (node)), arv_gc_feature_node_get_name (node)); } } if (list_mode == ARV_TOOL_LIST_MODE_DESCRIPTIONS) { const char *description; description = arv_gc_feature_node_get_description (node); if (description) printf ("%s\n", description); } if (ARV_IS_GC_ENUMERATION (node) && list_mode == ARV_TOOL_LIST_MODE_FEATURES) { const GSList *childs; const GSList *iter; childs = arv_gc_enumeration_get_entries (ARV_GC_ENUMERATION (node)); for (iter = childs; iter != NULL; iter = iter->next) { if (arv_gc_feature_node_is_implemented (iter->data, NULL)) { printf ("%*s%-12s: '%s'%s\n", 4 * (level + 1), "", arv_dom_node_get_node_name (iter->data), arv_gc_feature_node_get_name (iter->data), arv_gc_feature_node_is_available (iter->data, NULL) ? "" : " (Not available)"); } } } } static void arv_tool_list_features (ArvGc *genicam, const char *feature, ArvToolListMode list_mode, GRegex *regex, int level) { ArvGcNode *node; node = arv_gc_get_node (genicam, feature); if (ARV_IS_GC_FEATURE_NODE (node) && arv_gc_feature_node_is_implemented (ARV_GC_FEATURE_NODE (node), NULL)) { gboolean match; match = regex == NULL || g_regex_match (regex, arv_gc_feature_node_get_name (ARV_GC_FEATURE_NODE (node)), 0, NULL); if (match) arv_tool_show_feature (ARV_GC_FEATURE_NODE (node), list_mode, level); if (ARV_IS_GC_CATEGORY (node)) { const GSList *features; const GSList *iter; features = arv_gc_category_get_features (ARV_GC_CATEGORY (node)); for (iter = features; iter != NULL; iter = iter->next) arv_tool_list_features (genicam, iter->data, list_mode, match ? NULL : regex, level + 1); } } } static void arv_tool_control (int argc, char **argv, ArvDevice *device) { int i; for (i = 2; i < argc; i++) { ArvGcNode *feature; char **tokens; tokens = g_strsplit (argv[i], "=", 2); feature = arv_device_get_feature (device, tokens[0]); if (ARV_IS_GC_FEATURE_NODE (feature)) { if (ARV_IS_GC_COMMAND (feature)) { GError *error = NULL; arv_gc_command_execute (ARV_GC_COMMAND (feature), &error); if (error != NULL) { printf ("%s execute error: %s\n", tokens[0], error->message); g_clear_error (&error); } else printf ("%s executed\n", tokens[0]); } else { const char *unit; GError *error = NULL; if (tokens[1] != NULL) arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (feature), tokens[1], &error); if (error == NULL) { if (ARV_IS_GC_STRING (feature) || ARV_IS_GC_ENUMERATION (feature)) { const char *value = arv_gc_string_get_value (ARV_GC_STRING (feature), &error); if (error == NULL) printf ("%s = %s\n", tokens[0], value); } else if (ARV_IS_GC_INTEGER (feature)) { gint64 max_int64, min_int64, inc_int64; gint64 value; min_int64 = arv_gc_integer_get_min (ARV_GC_INTEGER (feature), NULL); max_int64 = arv_gc_integer_get_max (ARV_GC_INTEGER (feature), NULL); inc_int64 = arv_gc_integer_get_inc (ARV_GC_INTEGER (feature), NULL); unit = arv_gc_integer_get_unit (ARV_GC_INTEGER (feature)); value = arv_gc_integer_get_value (ARV_GC_INTEGER (feature), &error); if (error == NULL) { GString *string = g_string_new (""); g_string_append_printf (string, "%s = %" G_GINT64_FORMAT, tokens[0], value); if (unit != NULL) g_string_append_printf (string, " %s", unit); if (min_int64 != G_MININT64) g_string_append_printf (string, " min:%" G_GINT64_FORMAT, min_int64); if (max_int64 != G_MAXINT64) g_string_append_printf (string, " max:%" G_GINT64_FORMAT, max_int64); if (inc_int64 != 1) g_string_append_printf (string, " inc:%" G_GINT64_FORMAT, inc_int64); printf ("%s\n", string->str); g_string_free (string, TRUE); } } else if (ARV_IS_GC_FLOAT (feature)) { double max_double, min_double, inc_double; GString *string = g_string_new (""); double value; min_double = arv_gc_float_get_min (ARV_GC_FLOAT (feature), NULL); max_double = arv_gc_float_get_max (ARV_GC_FLOAT (feature), NULL); inc_double = arv_gc_float_get_inc (ARV_GC_FLOAT (feature), NULL); unit = arv_gc_float_get_unit (ARV_GC_FLOAT (feature)); value = arv_gc_float_get_value (ARV_GC_FLOAT (feature), &error); if (error == NULL) { g_string_append_printf (string, "%s = %g", tokens[0], value); if (unit != NULL) g_string_append_printf (string, " %s", unit); if (min_double != -G_MAXDOUBLE) g_string_append_printf (string, " min:%g", min_double); if (max_double != G_MAXDOUBLE) g_string_append_printf (string, " max:%g", max_double); if (inc_double != G_MINDOUBLE) g_string_append_printf (string, " inc:%g", inc_double); printf ("%s\n", string->str); g_string_free (string, TRUE); } } else if (ARV_IS_GC_BOOLEAN (feature)) { gboolean value = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (feature), &error); if (error == NULL) printf ("%s = %s\n", tokens[0], value ? "true" : "false"); } else if (ARV_IS_GC_REGISTER (feature)) { unsigned char *buffer; guint64 length; buffer = arv_gc_register_dup (ARV_GC_REGISTER (feature), &length, &error); if (error == NULL && buffer != NULL) { GString *dump; dump = g_string_new(""); printf ("%s = %" G_GUINT64_FORMAT " byte(s)@0x%08" G_GINT64_MODIFIER "x\n", tokens[0], length, arv_gc_register_get_address (ARV_GC_REGISTER(feature), NULL)); arv_g_string_append_hex_dump(dump, buffer, length); printf ("%s\n", dump->str); g_string_free (dump, TRUE); } g_free(buffer); }else { const char *value = arv_gc_feature_node_get_value_as_string (ARV_GC_FEATURE_NODE (feature), &error); if (error == NULL) printf ("%s = %s\n", tokens[0], value); } } if (error != NULL) { printf ("%s %s error: %s\n", tokens[0], tokens[1] != NULL ? "write" : "read", error->message); g_clear_error (&error); } } } else { if (g_strrstr (tokens[0], "R[") == tokens[0]) { guint32 value; guint32 address; GError *error = NULL; address = g_ascii_strtoll(&tokens[0][2], NULL, 0); if (tokens[1] != NULL) { arv_device_write_register (device, address, g_ascii_strtoll (tokens[1], NULL, 0), &error); if (error != NULL) printf ("R[0x%08x] write error: %s\n", address, error->message); } if (error == NULL) { arv_device_read_register (device, address, &value, &error); if (error == NULL) { printf ("R[0x%08x] = 0x%08x\n", address, value); } else { printf ("R[0x%08x] read error: %s\n", address, error->message); } } g_clear_error(&error); } else printf ("Feature '%s' not found\n", tokens[0]); } g_strfreev (tokens); } } static void arv_tool_show_network_mode (ArvGvDevice* gv_device, GError** error) { GError *local_error = NULL; ArvGvIpConfigurationMode mode; mode = arv_gv_device_get_ip_configuration_mode (gv_device, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } switch (mode) { case ARV_GV_IP_CONFIGURATION_MODE_NONE: printf ("Mode: None\n"); break; case ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP: printf ("Mode: PersistentIP\n"); break; case ARV_GV_IP_CONFIGURATION_MODE_DHCP: printf ("Mode: DHCP\n"); break; case ARV_GV_IP_CONFIGURATION_MODE_LLA: printf ("Mode: LLA\n"); break; case ARV_GV_IP_CONFIGURATION_MODE_FORCE_IP: printf ("Mode: ForceIP\n"); break; } } static void arv_tool_show_current_ip (ArvGvDevice* gv_device, GError** error) { GError *local_error = NULL; GInetAddress* ip; GInetAddressMask* mask; GInetAddress* gateway; gchar* ip_str; gchar* mask_str; gchar* gateway_str; arv_gv_device_get_current_ip (gv_device, &ip, &mask, &gateway, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } ip_str = g_inet_address_to_string (ip); mask_str = g_inet_address_mask_to_string (mask); gateway_str = g_inet_address_to_string (gateway); g_object_unref(ip); g_object_unref(mask); g_object_unref(gateway); printf ("Current IP: %s\nCurrent Mask: %s\nCurrent Gateway: %s\n", ip_str, mask_str, gateway_str); g_free (ip_str); g_free (mask_str); g_free (gateway_str); } static void arv_tool_show_persistent_ip (ArvGvDevice* gv_device, gboolean show_ip, gboolean show_mask, gboolean show_gateway, GError** error) { GError *local_error = NULL; GInetAddress* ip; GInetAddressMask* mask; GInetAddress* gateway; gchar* ip_str; gchar* mask_str; gchar* gateway_str; arv_gv_device_get_persistent_ip (gv_device, &ip, &mask, &gateway, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); return; } ip_str = g_inet_address_to_string (ip); mask_str = g_inet_address_mask_to_string (mask); gateway_str = g_inet_address_to_string (gateway); g_object_unref(ip); g_object_unref(mask); g_object_unref(gateway); if (show_ip) printf ("Persistent IP: %s\n", ip_str); if (show_mask) printf ("Persistent Mask: %s\n", mask_str); if (show_gateway) printf ("Persistent Gateway: %s\n", gateway_str); g_free (ip_str); g_free (mask_str); g_free (gateway_str); } static void arv_tool_network (int argc, char **argv, ArvDevice *device) { ArvGvDevice* gv_device = NULL; if (!ARV_IS_GV_DEVICE (device)) { printf ("This is not a GV device\n"); return; } gv_device = ARV_GV_DEVICE (device); if (argv[2] == NULL) { GError *error = NULL; arv_tool_show_network_mode (gv_device, &error); if (error == NULL) { arv_tool_show_current_ip (gv_device, &error); } if (error == NULL) { arv_tool_show_persistent_ip (gv_device, TRUE, TRUE, TRUE, &error); } if (error != NULL) { printf ("%s error: %s\n", argv[2], error->message); g_clear_error (&error); } } else { int i; for (i = 2; i < argc; i++) { GError *error = NULL; char **tokens; tokens = g_strsplit (argv[i], "=", 2); if (g_strcmp0 (tokens[0], "mode") == 0) { if (tokens[1] != NULL) { ArvGvIpConfigurationMode mode; if (g_ascii_strcasecmp (tokens[1], "PersistentIP") == 0) mode = ARV_GV_IP_CONFIGURATION_MODE_PERSISTENT_IP; else if (g_ascii_strcasecmp (tokens[1], "DHCP") == 0) mode = ARV_GV_IP_CONFIGURATION_MODE_DHCP; else if (g_ascii_strcasecmp (tokens[1], "LLA") == 0) mode = ARV_GV_IP_CONFIGURATION_MODE_LLA; else { printf ("Unknown mode \"%s\". Avalaible modes: PersistentIP, DHCP and LLA\n", tokens[1]); return; } arv_gv_device_set_ip_configuration_mode (gv_device, mode, &error); } else { arv_tool_show_network_mode (gv_device, &error); } } else if (g_strcmp0 (tokens[0], "ip") == 0) { if (tokens[1] != NULL) { arv_gv_device_set_persistent_ip_from_string (gv_device, tokens[1], NULL, NULL, &error); } else { arv_tool_show_persistent_ip (gv_device, TRUE, FALSE, FALSE, &error); } } else if (g_strcmp0 (tokens[0], "mask") == 0) { if (tokens[1] != NULL) { arv_gv_device_set_persistent_ip_from_string (gv_device, NULL, tokens[1], NULL, &error); } else { arv_tool_show_persistent_ip (gv_device, FALSE, TRUE, FALSE, &error); } } else if (g_strcmp0 (tokens[0], "gateway") == 0) { if (tokens[1] != NULL) { arv_gv_device_set_persistent_ip_from_string (gv_device, NULL, NULL, tokens[1], &error); } else { arv_tool_show_persistent_ip (gv_device, FALSE, FALSE, TRUE, &error); } } if (error != NULL) { printf ("%s error: %s\n", argv[i], error->message); g_clear_error (&error); return; } g_strfreev (tokens); } } } static void arv_tool_execute_command (int argc, char **argv, ArvDevice *device, ArvRegisterCachePolicy register_cache_policy, ArvRangeCheckPolicy range_check_policy, ArvAccessCheckPolicy access_check_policy) { ArvGc *genicam; const char *command = argv[1]; gint64 start; if (device == NULL || argc < 2) return; arv_device_set_register_cache_policy (device, register_cache_policy); arv_device_set_range_check_policy (device, range_check_policy); arv_device_set_access_check_policy (device, access_check_policy); genicam = arv_device_get_genicam (device); start = g_get_monotonic_time (); if (g_strcmp0 (command, "genicam") == 0) { const char *xml; size_t size; xml = arv_device_get_genicam_xml (device, &size); if (xml != NULL) printf ("%*s\n", (int) size, xml); } else if (g_strcmp0 (command, "features") == 0) { if (argc > 3) printf ("features command takes at most one feature selection parameter\n"); else { GRegex *regex; regex = arv_regex_new_from_glob_pattern (argc == 3 ? argv[2] : "*", TRUE); arv_tool_list_features (genicam, "Root", ARV_TOOL_LIST_MODE_FEATURES, regex, 0); g_regex_unref (regex); } } else if (g_strcmp0 (command, "values") == 0) { if (argc > 3) printf ("features command takes at most one feature selection parameter\n"); else { GRegex *regex; regex = arv_regex_new_from_glob_pattern (argc == 3 ? argv[2] : "*", TRUE); arv_tool_list_features (genicam, "Root", ARV_TOOL_LIST_MODE_VALUES, regex, 0); g_regex_unref (regex); } } else if (g_strcmp0 (command, "description") == 0) { if (argc > 3) printf ("features command takes at most one feature selection parameter\n"); else { GRegex *regex; regex = arv_regex_new_from_glob_pattern (argc == 3 ? argv[2] : "*", TRUE); arv_tool_list_features (genicam, "Root", ARV_TOOL_LIST_MODE_DESCRIPTIONS, regex, 0); g_regex_unref (regex); } } else if (g_strcmp0 (command, "control") == 0) { arv_tool_control (argc, argv, device); } else if (g_strcmp0 (command, "network") == 0) { arv_tool_network (argc, argv, device); } else { printf ("Unknown command\n"); } if (arv_option_show_time) printf ("Executed in %g s\n", (g_get_monotonic_time () - start) / 1000000.0); } int main (int argc, char **argv) { ArvDevice *device; ArvRegisterCachePolicy register_cache_policy; ArvRangeCheckPolicy range_check_policy; ArvAccessCheckPolicy access_check_policy; GRegex *regex; const char *device_id; GOptionContext *context; GError *error = NULL; unsigned int n_devices; unsigned int n_found_devices = 0; unsigned int i; gboolean is_glob_pattern = FALSE; context = g_option_context_new (" command "); g_option_context_set_summary (context, "Small utility for basic control of a Genicam device."); g_option_context_set_description (context, description_content); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); if (arv_option_show_version) { printf ("%u.%u.%u\n", arv_get_major_version (), arv_get_minor_version (), arv_get_micro_version ()); return EXIT_SUCCESS; } if (arv_option_register_cache == NULL) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_register_cache, "disable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DISABLE; else if (g_strcmp0 (arv_option_register_cache, "enable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_ENABLE; else if (g_strcmp0 (arv_option_register_cache, "debug") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEBUG; else { printf ("Invalid register cache policy\n"); return EXIT_FAILURE; } if (arv_option_range_check == NULL) range_check_policy = ARV_RANGE_CHECK_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_range_check, "disable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DISABLE; else if (g_strcmp0 (arv_option_range_check, "enable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_ENABLE; else if (g_strcmp0 (arv_option_range_check, "debug") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DEBUG; else { printf ("Invalid range check policy\n"); return EXIT_FAILURE; } if (arv_option_access_check == NULL) access_check_policy = ARV_ACCESS_CHECK_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_access_check, "disable") == 0) access_check_policy = ARV_ACCESS_CHECK_POLICY_DISABLE; else if (g_strcmp0 (arv_option_access_check, "enable") == 0) access_check_policy = ARV_ACCESS_CHECK_POLICY_ENABLE; else { printf ("Invalid access check policy\n"); return EXIT_FAILURE; } if (!arv_debug_enable (arv_option_debug_domains)) { if (g_strcmp0 (arv_option_debug_domains, "help") != 0) printf ("Invalid debug selection\n"); else arv_debug_print_infos (); return EXIT_FAILURE; } for (i = 0; arv_option_device_selection != NULL && arv_option_device_selection[i] != '\0'; i++) if (arv_option_device_selection[i] == '*' || arv_option_device_selection[i] == '?' || arv_option_device_selection[i] == '|') is_glob_pattern = TRUE; if (arv_option_gv_allow_broadcast_discovery_ack) arv_set_interface_flags ("GigEVision", ARV_GV_INTERFACE_FLAGS_ALLOW_BROADCAST_DISCOVERY_ACK); if (arv_option_gv_discovery_interface) arv_gv_interface_set_discovery_interface_name (arv_option_gv_discovery_interface); device_id = arv_option_device_address != NULL ? arv_option_device_address : (is_glob_pattern ? NULL : arv_option_device_selection); if (device_id != NULL) { GError *error = NULL; device = arv_open_device (device_id, &error); if (ARV_IS_DEVICE (device)) { if (argc < 2) { printf ("%s\n", device_id); } else { arv_tool_execute_command (argc, argv, device, register_cache_policy, range_check_policy, access_check_policy); } g_object_unref (device); } else { if (error != NULL) { fprintf (stderr, "%s\n", error->message); g_clear_error (&error); } else { fprintf (stderr, "Device '%s' not found", device_id); } } arv_shutdown (); return EXIT_SUCCESS; } arv_update_device_list (); n_devices = arv_get_n_devices (); regex = arv_regex_new_from_glob_pattern (arv_option_device_selection != NULL ? arv_option_device_selection : "*", TRUE); if (n_devices > 0) { for (i = 0; i < n_devices; i++) { GError *error = NULL; device_id = arv_get_device_id (i); if (g_regex_match (regex, device_id, 0, NULL)) { n_found_devices++; printf ("%s (%s)\n", device_id, arv_get_device_address (i)); if (argc >= 2) { device = arv_open_device (device_id, &error); if (ARV_IS_DEVICE (device)) { arv_tool_execute_command (argc, argv, device, register_cache_policy, range_check_policy, access_check_policy); g_object_unref (device); } else { fprintf (stderr, "Failed to open device '%s'%s%s\n", device_id, error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } } } } } if (n_found_devices < 1) { if (n_devices > 0) fprintf (stderr, "No matching device found (%d filtered out)\n", n_devices); else fprintf (stderr, "No device found\n"); } g_regex_unref (regex); arv_shutdown (); return EXIT_SUCCESS; } aravis-0.8.34/src/arvtypes.h000066400000000000000000000071201475431451200157160ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_TYPES_H #define ARV_TYPES_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _ArvCamera ArvCamera; typedef struct _ArvEvaluator ArvEvaluator; typedef struct _ArvDomNode ArvDomNode; typedef struct _ArvDomNodeList ArvDomNodeList; typedef struct _ArvDomNamedNodeMap ArvDomNamedNodeMap; typedef struct _ArvDomElement ArvDomElement; typedef struct _ArvDomDocument ArvDomDocument; typedef struct _ArvDomDocumentFragment ArvDomDocumentFragment; typedef struct _ArvDomCharacterData ArvDomCharacterData; typedef struct _ArvDomText ArvDomText; typedef struct _ArvGc ArvGc; typedef struct _ArvGcNode ArvGcNode; typedef struct _ArvGcPropertyNode ArvGcPropertyNode; typedef struct _ArvGcIndexNode ArvGcIndexNode; typedef struct _ArvGcValueIndexedNode ArvGcValueIndexedNode; typedef struct _ArvGcInvalidatorNode ArvGcInvalidatorNode; typedef struct _ArvGcFeatureNode ArvGcFeatureNode; typedef struct _ArvGcRegisterDescriptionNode ArvGcRegisterDescriptionNode; typedef struct _ArvGcGroupNode ArvGcGroupNode; typedef struct _ArvGcCategory ArvGcCategory; typedef struct _ArvGcBoolean ArvGcBoolean; typedef struct _ArvGcEnumeration ArvGcEnumeration; typedef struct _ArvGcEnumEntry ArvGcEnumEntry; typedef struct _ArvGcIntegerNode ArvGcIntegerNode; typedef struct _ArvGcFloatNode ArvGcFloatNode; typedef struct _ArvGcRegisterNode ArvGcRegisterNode; typedef struct _ArvGcStructEntryNode ArvGcStructEntryNode; typedef struct _ArvGcCommand ArvGcCommand; typedef struct _ArvGcSwissKnife ArvGcSwissKnife; typedef struct _ArvGcConverter ArvGcConverter; typedef struct _ArvGcConverterNode ArvGcConverterNode; typedef struct _ArvGcIntConverterNode ArvGcIntConverterNode; typedef struct _ArvGcPort ArvGcPort; typedef struct _ArvGcRegister ArvGcRegister; typedef struct _ArvGcInteger ArvGcInteger; typedef struct _ArvGcFloat ArvGcFloat; typedef struct _ArvGcString ArvGcString; typedef struct _ArvGcSelector ArvGcSelector; typedef struct _ArvInterface ArvInterface; typedef struct _ArvDevice ArvDevice; typedef struct _ArvStream ArvStream; typedef struct _ArvChunkParser ArvChunkParser; typedef struct _ArvGvInterface ArvGvInterface; typedef struct _ArvGvDevice ArvGvDevice; typedef struct _ArvGvStream ArvGvStream; #if ARAVIS_HAS_USB typedef struct _ArvUvInterface ArvUvInterface; typedef struct _ArvUvDevice ArvUvDevice; typedef struct _ArvUvStream ArvUvStream; #endif typedef struct _ArvFakeCamera ArvFakeCamera; typedef struct _ArvGvFakeCamera ArvGvFakeCamera; typedef struct _ArvZip ArvZip; typedef struct _ArvZipFile ArvZipFile; G_END_DECLS #endif aravis-0.8.34/src/arvuvcp.c000066400000000000000000000154551475431451200155340ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /* * SECTION: arvuvcp * @short_description: USB3Vision control packet handling */ #include #include #include #include #include #include void arv_uvcp_packet_free (ArvUvcpPacket *packet) { g_free (packet); } /** * arv_uvcp_packet_new_read_memory_cmd: (skip) * @address: read address * @size: read size, in bytes * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvUvcpPacket * * Create a uvcp packet for a memory read command. */ ArvUvcpPacket * arv_uvcp_packet_new_read_memory_cmd (guint64 address, guint32 size, guint16 packet_id, size_t *packet_size) { ArvUvcpReadMemoryCmd *packet; g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvUvcpReadMemoryCmd); packet = g_malloc (*packet_size); packet->header.magic = GUINT32_TO_LE (ARV_UVCP_MAGIC); packet->header.flags = GUINT16_TO_LE (ARV_UVCP_FLAGS_REQUEST_ACK); packet->header.command = GUINT16_TO_LE (ARV_UVCP_COMMAND_READ_MEMORY_CMD); packet->header.size = GUINT16_TO_LE (sizeof (ArvUvcpReadMemoryCmdInfos)); packet->header.id = GUINT16_TO_LE (packet_id); packet->infos.address = GUINT64_TO_LE (address); packet->infos.unknown = 0; packet->infos.size = GUINT16_TO_LE (size); return (ArvUvcpPacket *) packet; } /** * arv_uvcp_packet_new_write_memory_cmd: (skip) * @address: write address * @size: write size, in bytes * @packet_id: packet id * @packet_size: (out): packet size, in bytes * Return value: (transfer full): a new #ArvUvcpPacket * * Create a uvcp packet for a memory write command. */ ArvUvcpPacket * arv_uvcp_packet_new_write_memory_cmd (guint64 address, guint32 size, guint16 packet_id, size_t *packet_size) { ArvUvcpWriteMemoryCmd *packet; g_return_val_if_fail (packet_size != NULL, NULL); *packet_size = sizeof (ArvUvcpWriteMemoryCmd) + size; packet = g_malloc (*packet_size); packet->header.magic = GUINT32_TO_LE (ARV_UVCP_MAGIC); packet->header.flags = GUINT16_TO_LE (ARV_UVCP_FLAGS_REQUEST_ACK); packet->header.command = GUINT16_TO_LE (ARV_UVCP_COMMAND_WRITE_MEMORY_CMD); packet->header.size = GUINT16_TO_LE (sizeof (ArvUvcpWriteMemoryCmdInfos) + size); packet->header.id = GUINT16_TO_LE (packet_id); packet->infos.address = GUINT64_TO_LE (address); return (ArvUvcpPacket *) packet; } static const char * arv_enum_to_string (GType type, guint enum_value) { GEnumClass *enum_class; GEnumValue *value; const char *retval = NULL; enum_class = g_type_class_ref (type); value = g_enum_get_value (enum_class, enum_value); if (value) retval = value->value_nick; g_type_class_unref (enum_class); return retval; } const char * arv_uvcp_status_to_string (ArvUvcpStatus value) { return arv_enum_to_string (ARV_TYPE_UVCP_STATUS, value); } const char * arv_uvcp_command_to_string (ArvUvcpCommand value) { return arv_enum_to_string (ARV_TYPE_UVCP_COMMAND, value); } /** * arv_uvcp_packet_to_string: * @packet: a #ArvUvcpPacket * * Converts @packet into a human readable string. * * return value: (transfer full): A newly allocated string. */ char * arv_uvcp_packet_to_string (const ArvUvcpPacket *packet) { ArvUvcpCommand command; GString *string; int packet_size; guint64 value; g_return_val_if_fail (packet != NULL, NULL); string = g_string_new (""); command = arv_uvcp_packet_get_command (packet); if ((command & 0x0001) != 0) g_string_append_printf (string, "status = %s\n", arv_uvcp_status_to_string (arv_uvcp_packet_get_status (packet))); else g_string_append_printf (string, "flags = 0x%04x\n", arv_uvcp_packet_get_flags (packet)); g_string_append_printf (string, "command = %s\n", arv_uvcp_command_to_string (command)); g_string_append_printf (string, "size = %d\n", GUINT16_FROM_LE (packet->header.size)); g_string_append_printf (string, "id = %d\n", GUINT16_FROM_LE (packet->header.id)); switch (GUINT16_FROM_LE (packet->header.command)) { case ARV_UVCP_COMMAND_READ_MEMORY_CMD: { ArvUvcpReadMemoryCmd *cmd_packet = (void *) packet; value = GUINT64_FROM_LE (cmd_packet->infos.address); g_string_append_printf (string, "address = 0x%016" G_GINT64_MODIFIER "x\n", value); value = GUINT16_FROM_LE (cmd_packet->infos.size); g_string_append_printf (string, "size = %10" G_GINT64_MODIFIER "u (0x%08" G_GINT64_MODIFIER "x)\n", value, value); break; } case ARV_UVCP_COMMAND_READ_MEMORY_ACK: { break; } case ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: { ArvUvcpWriteMemoryCmd *cmd_packet = (void *) packet; value = GUINT64_FROM_LE (cmd_packet->infos.address); g_string_append_printf (string, "address = 0x%016" G_GINT64_MODIFIER "x\n", value); break; } case ARV_UVCP_COMMAND_WRITE_MEMORY_ACK: { ArvUvcpWriteMemoryAck *cmd_packet = (void *) packet; value = GUINT64_FROM_LE (cmd_packet->infos.bytes_written); g_string_append_printf (string, "written = %10" G_GINT64_MODIFIER "u (0x%08" G_GINT64_MODIFIER "x)\n", value, value); break; } } packet_size = sizeof (ArvUvcpHeader) + GUINT16_FROM_LE (packet->header.size); arv_g_string_append_hex_dump (string, packet, packet_size); return arv_g_string_free_and_steal(string); } /** * arv_uvcp_packet_debug: * @packet: a #ArvUvcpPacket * @level: debug level * * Dumps the content of @packet if level is lower or equal to the current debug level for the cp debug category. See arv_debug_enable(). */ void arv_uvcp_packet_debug (const ArvUvcpPacket *packet, ArvDebugLevel level) { char *string; if (!arv_debug_check (ARV_DEBUG_CATEGORY_CP, level)) return; string = arv_uvcp_packet_to_string (packet); switch (level) { case ARV_DEBUG_LEVEL_DEBUG: arv_debug_cp ("%s", string); break; case ARV_DEBUG_LEVEL_INFO: arv_info_cp ("%s", string); break; case ARV_DEBUG_LEVEL_WARNING: arv_warning_cp ("%s", string); break; default: break; } g_free (string); } aravis-0.8.34/src/arvuvcpprivate.h000066400000000000000000000263571475431451200171370ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UVCP_PRIVATE_H #define ARV_UVCP_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define ARV_UVCP_MAGIC 0x43563355 #define ARV_UVCP_DEFAULT_RESPONSE_TIME_MS 5 #define ARV_ABRM_GENCP_VERSION 0x0000 #define ARV_ABRM_MANUFACTURER_NAME 0x0004 #define ARV_ABRM_MODEL_NAME 0x0044 #define ARV_ABRM_FAMILY_NAME 0x0084 #define ARV_ABRM_DEVICE_VERSION 0x00c4 #define ARV_ABRM_MANUFACTURER_INFO 0x0104 #define ARV_ABRM_SERIAL_NUMBER 0x0144 #define ARV_ABRM_USER_DEFINED_NAME 0x0184 #define ARV_ABRM_DEVICE_CAPABILITY 0x01c4 #define ARV_ABRM_MAX_DEVICE_RESPONSE_TIME 0x01cc /* in ms */ #define ARV_ABRM_MANIFEST_TABLE_ADDRESS 0x01d0 #define ARV_ABRM_SBRM_ADDRESS 0x01d8 #define ARV_ABRM_DEVICE_CONFIGURATION 0x01e0 #define ARV_ABRM_HEARTBEAT_TIMEOUT 0x01e8 #define ARV_ABRM_MESSAGE_CHANNEL_ID 0x01ec #define ARV_ABRM_TIMESTAMP 0x01f0 #define ARV_ABRM_TIMESTAMP_LATCH 0x01f8 #define ARV_ABRM_TIMESTAMP_INCREMENT 0x01fc #define ARV_ABRM_ACCESS_PRIVILEGE 0x0204 #define ARV_ABRM_PROTOCOL_ENDIANNESS 0x0208 #define ARV_ABRM_IMPLEMENTATION_ENDIANNESS 0x020c #define ARV_ABRM_RESERVED 0x0210 #define ARV_SBRM_U3V_VERSION 0x0000 #define ARV_SBRM_U3VCP_CAPABILITY 0x0004 #define ARV_SBRM_U3VCP_CONFIGURATION 0x000c #define ARV_SBRM_MAX_CMD_TRANSFER 0x0014 #define ARV_SBRM_MAX_ACK_TRANSFER 0x0018 #define ARV_SBRM_NUM_STREAM_CHANNELS 0x001c #define ARV_SBRM_SIRM_ADDRESS 0x0020 #define ARV_SBRM_SIRM_LENGTH 0x0028 #define ARV_SBRM_EIRM_ADDRESS 0x002c #define ARV_SBRM_EIRM_LENGTH 0x0034 #define ARV_SBRM_IIDC2_ADDRESS 0x0038 #define ARV_SBRM_CURRENT_SPEED 0x0040 #define ARV_SBRM_RESERVED 0x0044 #define ARV_SIRM_INFO 0x0000 #define ARV_SIRM_CONTROL 0x0004 #define ARV_SIRM_REQ_PAYLOAD_SIZE 0x0008 #define ARV_SIRM_REQ_LEADER_SIZE 0x0010 #define ARV_SIRM_REQ_TRAILER_SIZE 0x0014 #define ARV_SIRM_MAX_LEADER_SIZE 0x0018 #define ARV_SIRM_PAYLOAD_SIZE 0x001C #define ARV_SIRM_PAYLOAD_COUNT 0x0020 #define ARV_SIRM_TRANSFER1_SIZE 0x0024 #define ARV_SIRM_TRANSFER2_SIZE 0x0028 #define ARV_SIRM_MAX_TRAILER_SIZE 0x002C #define ARV_SIRM_INFO_ALIGNMENT_MASK 0xFF000000 #define ARV_SIRM_INFO_ALIGNMENT_SHIFT 0x0018 #define ARV_SIRM_CONTROL_STREAM_ENABLE 0x00000001 /** * ArvUvcpStatus: * @ARV_UCVP_STATUS_SUCCESS: success * @ARV_UCVP_STATUS_NOT_IMPLEMENTED: command not implemented in the device * @ARV_UCVP_STATUS_INVALID_PARAMETER: at least one command parameter of CCD or SCD is invalid or out of range * @ARV_UVCP_STATUS_INVALID_ADDRESS: attempt to access a not existing register address * @ARV_UVCP_STATUS_WRITE_PROTECT: attempt to write to a read only register * @ARV_UVCP_STATUS_BAD_ALIGNMENT: attempt to access registers with an address which is not aligned according to the * underlying technology * @ARV_UVCP_STATUS_ACCESS_DENIED: attempt toread a non-readable or write a non-writable register address * @ARV_UVCP_STATUS_BUSY: the command receiver is currently busy * @ARV_UVCP_STATUS_MSG_TIMEOUT: timeout waiting for an acknowledge * @ARV_UVCP_STATUS_INVALID_HEADER: the header of the received command is invalid. This includes CCD and SCD fields but * not the command payload * @ARV_UVCP_STATUS_WRONG_CONFIG: the current receiver configuration does not allow the execution of the sent command * @ARV_UVCP_STATUS_ERROR: generic error * @ARV_UVCP_STATUS_RESEND_NOT_SUPPORTED: * @ARV_UVCP_STATUS_DSI_ENDPOINT_HALTED: * @ARV_UVCP_STATUS_SI_PAYLOAD_SIZE_NOT_ALIGNED: * @ARV_UVCP_STATUS_SI_REGISTERS_INCONSISTENT: * @ARV_UVCP_STATUS_DATA_DISCARDED: * @ARV_UVCP_STATUS_DATA_OVERRUN: */ typedef enum { ARV_UVCP_STATUS_SUCCESS = 0x0000, ARV_UVCP_STATUS_NOT_IMPLEMENTED = 0x8001, ARV_UVCP_STATUS_INVALID_PARAMETER = 0x8002, ARV_UVCP_STATUS_INVALID_ADDRESS = 0x8003, ARV_UVCP_STATUS_WRITE_PROTECT = 0x8004, ARV_UVCP_STATUS_BAD_ALIGNMENT = 0x8005, ARV_UVCP_STATUS_ACCESS_DENIED = 0x8006, ARV_UVCP_STATUS_BUSY = 0x8007, ARV_UVCP_STATUS_MSG_TIMEOUT = 0x800B, ARV_UVCP_STATUS_INVALID_HEADER = 0x800E, ARV_UVCP_STATUS_WRONG_CONFIG = 0x800F, ARV_UVCP_STATUS_ERROR = 0x8FFF, ARV_UVCP_STATUS_RESEND_NOT_SUPPORTED = 0xA001, ARV_UVCP_STATUS_DSI_ENDPOINT_HALTED = 0xA002, ARV_UVCP_STATUS_SI_PAYLOAD_SIZE_NOT_ALIGNED = 0xA003, ARV_UVCP_STATUS_SI_REGISTERS_INCONSISTENT = 0xA004, ARV_UVCP_STATUS_DATA_DISCARDED = 0xA100, ARV_UVCP_STATUS_DATA_OVERRUN = 0xA101 } ArvUvcpStatus; /** * ArvUvcpFlags: * @ARV_UVCP_FLAGS_REQUEST_ACK: if set the sender requests an acknowledge packet from the command receiver * @ARV_UVCP_FLAGS_COMMAND_RESEND: if set the command is sent as a retry of a previous sent that failed */ typedef enum { ARV_UVCP_FLAGS_REQUEST_ACK = 1 << 14, ARV_UVCP_FLAGS_COMMAND_RESEND = 1 << 15 } ArvUvcpFlags; /** * ArvUvcpCommand: * @ARV_UVCP_COMMAND_READ_MEMORY_CMD: read memory command * @ARV_UVCP_COMMAND_READ_MEMORY_ACK: read memory acknowledge * @ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: write memory command * @ARV_UVCP_COMMAND_WRITE_MEMORY_ACK: write memory acknowledge * @ARV_UVCP_COMMAND_PENDING_ACK: pending command acknowledge * @ARV_UVCP_COMMAND_EVENT_CMD: event command */ typedef enum { ARV_UVCP_COMMAND_READ_MEMORY_CMD = 0x0800, ARV_UVCP_COMMAND_READ_MEMORY_ACK = 0x0801, ARV_UVCP_COMMAND_WRITE_MEMORY_CMD = 0x0802, ARV_UVCP_COMMAND_WRITE_MEMORY_ACK = 0x0803, ARV_UVCP_COMMAND_PENDING_ACK = 0x0805, ARV_UVCP_COMMAND_EVENT_CMD = 0x0c00, ARV_UVCP_COMMAND_EVENT_ACK = 0x0c01 } ArvUvcpCommand; #pragma pack(push,1) /** * ArvUvcpHeader: * * UVCP packet header structure. */ typedef struct { guint32 magic; union { guint16 status; guint16 flags; }; guint16 command; guint16 size; guint16 id; } ArvUvcpHeader; typedef struct { guint64 address; guint16 unknown; /* Listed as reserved, always 0 */ guint16 size; } ArvUvcpReadMemoryCmdInfos; typedef struct { ArvUvcpHeader header; ArvUvcpReadMemoryCmdInfos infos; } ArvUvcpReadMemoryCmd; typedef struct { guint64 address; } ArvUvcpWriteMemoryCmdInfos; typedef struct { ArvUvcpHeader header; ArvUvcpWriteMemoryCmdInfos infos; } ArvUvcpWriteMemoryCmd; typedef struct { guint16 unknown; guint16 bytes_written; } ArvUvcpWriteMemoryAckInfos; typedef struct { ArvUvcpHeader header; ArvUvcpWriteMemoryAckInfos infos; } ArvUvcpWriteMemoryAck; typedef struct { guint16 unknown; guint16 timeout; } ArvUvcpPendingAckInfos; typedef struct { ArvUvcpHeader header; ArvUvcpPendingAckInfos infos; } ArvUvcpPendingAck; /** * ArvUvcpPacket: * @header: packet header * @data: variable size byte array * * UVCP packet structure. */ typedef struct { ArvUvcpHeader header; unsigned char data[]; } ArvUvcpPacket; typedef struct { guint16 file_version_subminor; guint8 file_version_minor; guint8 file_version_major; guint32 schema; guint64 address; guint64 size; guint64 unknown3; guint64 unknown4; guint64 unknown5; guint64 unknown6; guint64 unknown7; } ArvUvcpManifestEntry; #pragma pack(pop) /** * ArvUvcpManifestSchemaType: * @ARV_UVCP_SCHEMA_RAW: uncompressed genicam data * @ARV_UVCP_SCHEMA_ZIP: zipped genicam data * * This is packed into the 32-bit schema type as bits 10-15 */ typedef enum { ARV_UVCP_SCHEMA_RAW = 0x0, ARV_UVCP_SCHEMA_ZIP = 0x1 } ArvUvcpManifestSchemaType; static inline ArvUvcpManifestSchemaType arv_uvcp_manifest_entry_get_schema_type (ArvUvcpManifestEntry *entry) { g_return_val_if_fail (entry != NULL, ARV_UVCP_SCHEMA_RAW); return (ArvUvcpManifestSchemaType) ((entry->schema >> 10) & 0x0000001f); } void arv_uvcp_packet_free (ArvUvcpPacket *packet); ArvUvcpPacket * arv_uvcp_packet_new_read_memory_cmd (guint64 address, guint32 size, guint16 packet_id, size_t *packet_size); ArvUvcpPacket * arv_uvcp_packet_new_write_memory_cmd (guint64 address, guint32 size, guint16 packet_id, size_t *packet_size); char * arv_uvcp_packet_to_string (const ArvUvcpPacket *packet); void arv_uvcp_packet_debug (const ArvUvcpPacket *packet, ArvDebugLevel level); const char * arv_uvcp_status_to_string (ArvUvcpStatus value); const char * arv_uvcp_command_to_string (ArvUvcpCommand value); /** * arv_uvcp_packet_get_status: * @packet: a #ArvUvcpPacket * * Return value: The #ArvUvcpStatus code of @packet. */ static inline ArvUvcpStatus arv_uvcp_packet_get_status (const ArvUvcpPacket *packet) { if (packet == NULL) return ARV_UVCP_STATUS_ERROR; return (ArvUvcpStatus) GUINT16_FROM_LE (packet->header.status); } /** * arv_uvcp_packet_get_flags: * @packet: a #ArvUvcpFlags * * Return value: The #ArvUvcpFlags of @packet. */ static inline ArvUvcpFlags arv_uvcp_packet_get_flags (const ArvUvcpPacket *packet) { if (packet == NULL) return (ArvUvcpFlags) 0; return (ArvUvcpFlags) GUINT16_FROM_LE (packet->header.flags); } /** * arv_uvcp_packet_get_command: * @packet: a #ArvUvcpPacket * * Return value: The #ArvUvcpCommand code of @packet. */ static inline ArvUvcpCommand arv_uvcp_packet_get_command (const ArvUvcpPacket *packet) { if (packet == NULL) return (ArvUvcpCommand) 0; return (ArvUvcpCommand) GUINT16_FROM_LE (packet->header.command); } static inline void arv_uvcp_packet_set_packet_id (ArvUvcpPacket *packet, guint16 id) { if (packet != NULL) packet->header.id = GUINT16_TO_LE (id); } static inline guint16 arv_uvcp_packet_get_packet_id (const ArvUvcpPacket *packet) { if (packet == NULL) return 0; return GUINT16_FROM_LE (packet->header.id); } static inline void * arv_uvcp_packet_get_read_memory_ack_data (const ArvUvcpPacket *packet) { return (char *) packet + sizeof (ArvUvcpHeader); } static inline size_t arv_uvcp_packet_get_read_memory_ack_size (size_t data_size) { return sizeof (ArvUvcpHeader) + data_size; } static inline void * arv_uvcp_packet_get_write_memory_cmd_data (const ArvUvcpPacket *packet) { return (char *) packet + sizeof (ArvUvcpWriteMemoryCmd); } static inline size_t arv_uvcp_packet_get_write_memory_ack_size (void) { return sizeof (ArvUvcpWriteMemoryAck); } static inline guint16 arv_uvcp_packet_get_pending_ack_timeout (ArvUvcpPacket *packet) { return GUINT16_FROM_LE (((ArvUvcpPendingAck *) packet)->infos.timeout); } static inline guint16 arv_uvcp_next_packet_id (guint16 packet_id) { /* packet_id == 0 is an error value */ if (packet_id == 0xffff) return 1; return packet_id + 1; } G_END_DECLS #endif aravis-0.8.34/src/arvuvdevice.c000066400000000000000000001267661475431451200164010ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvuvdevice * @short_description: USB3Vision device */ #include #include #include #include #include #include #include #include #include #include #include #include enum { PROP_0, PROP_UV_DEVICE_VENDOR, PROP_UV_DEVICE_PRODUCT, PROP_UV_DEVICE_SERIAL_NUMBER, PROP_UV_DEVICE_GUID }; #define ARV_UV_DEVICE_N_TRIES_MAX 5 typedef struct { char *vendor; char *product; char *serial_number; char *guid; libusb_context *usb; libusb_device_handle *usb_device; libusb_hotplug_callback_handle hotplug_cb_handle; ArvGc *genicam; char *genicam_xml; size_t genicam_xml_size; guint16 packet_id; guint timeout_ms; guint cmd_packet_size_max; guint ack_packet_size_max; guint control_interface; guint data_interface; guint8 control_endpoint; guint8 data_endpoint; gboolean disconnected; ArvUvUsbMode usb_mode; int event_thread_run; GThread* event_thread; GMutex transfer_mutex; } ArvUvDevicePrivate; struct _ArvUvDevice { ArvDevice device; }; struct _ArvUvDeviceClass { ArvDeviceClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvUvDevice, arv_uv_device, ARV_TYPE_DEVICE, G_ADD_PRIVATE (ArvUvDevice)) static ArvDeviceError arv_uvcp_status_to_device_error (ArvUvcpStatus status) { switch (status) { case ARV_UVCP_STATUS_NOT_IMPLEMENTED: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_NOT_IMPLEMENTED; case ARV_UVCP_STATUS_INVALID_PARAMETER: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_PARAMETER; case ARV_UVCP_STATUS_INVALID_ADDRESS: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_INVALID_ADDRESS; case ARV_UVCP_STATUS_WRITE_PROTECT: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_WRITE_PROTECT; case ARV_UVCP_STATUS_BAD_ALIGNMENT: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_BAD_ALIGNMENT; case ARV_UVCP_STATUS_ACCESS_DENIED: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_ACCESS_DENIED; case ARV_UVCP_STATUS_BUSY: return ARV_DEVICE_ERROR_PROTOCOL_ERROR_BUSY; default: break; } return ARV_DEVICE_ERROR_PROTOCOL_ERROR; } /* ArvDevice implementation */ /* ArvUvDevice implementation */ gboolean arv_uv_device_is_connected (ArvUvDevice *uv_device) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); return !priv->disconnected; } void arv_uv_device_fill_bulk_transfer (struct libusb_transfer* transfer, ArvUvDevice *uv_device, ArvUvEndpointType endpoint_type, unsigned char endpoint_flags, void *data, size_t size, libusb_transfer_cb_fn callback, void* callback_data, unsigned int timeout) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); guint8 endpoint; endpoint = (endpoint_type == ARV_UV_ENDPOINT_CONTROL) ? priv->control_endpoint : priv->data_endpoint; libusb_fill_bulk_transfer (transfer, priv->usb_device, endpoint | endpoint_flags, data, size, callback, callback_data, timeout ); } gboolean arv_uv_device_bulk_transfer (ArvUvDevice *uv_device, ArvUvEndpointType endpoint_type, unsigned char endpoint_flags, void *data, size_t size, size_t *transferred_size, guint32 timeout_ms, GError **error) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); gboolean success; guint8 endpoint; int transferred = 0; int result; g_return_val_if_fail (ARV_IS_UV_DEVICE (uv_device), FALSE); g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (size > 0, FALSE); if (priv->disconnected) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_CONNECTED, "Not connected"); return FALSE; } endpoint = (endpoint_type == ARV_UV_ENDPOINT_CONTROL) ? priv->control_endpoint : priv->data_endpoint; result = libusb_bulk_transfer (priv->usb_device, endpoint | endpoint_flags, data, size, &transferred, timeout_ms > 0 ? timeout_ms : priv->timeout_ms); success = result >= 0; if (!success) g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TRANSFER_ERROR, "%s", libusb_error_name (result)); if (transferred_size != NULL) *transferred_size = transferred; if (result == LIBUSB_ERROR_NO_DEVICE) { if (!priv->disconnected) { priv->disconnected = TRUE; arv_device_emit_control_lost_signal (ARV_DEVICE (uv_device)); } } return success; } static ArvStream * arv_uv_device_create_stream (ArvDevice *device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, GError **error) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (device)); return arv_uv_stream_new (ARV_UV_DEVICE (device), callback, user_data, destroy, priv->usb_mode, error); } static gboolean _send_cmd_and_receive_ack (ArvUvDevice *uv_device, ArvUvcpCommand command, guint64 address, guint32 size, void *buffer, GError **error) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); ArvUvcpCommand expected_ack_command; ArvUvcpPacket *ack_packet; ArvUvcpPacket *packet; const char *operation; size_t packet_size; size_t ack_size; unsigned n_tries = 0; gboolean success = FALSE; ArvUvcpStatus status = ARV_UVCP_STATUS_SUCCESS; switch (command) { case ARV_UVCP_COMMAND_READ_MEMORY_CMD: operation = "read_memory"; expected_ack_command = ARV_UVCP_COMMAND_READ_MEMORY_ACK; ack_size = arv_uvcp_packet_get_read_memory_ack_size (size); break; case ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: operation = "write_memory"; expected_ack_command = ARV_UVCP_COMMAND_WRITE_MEMORY_ACK; ack_size = arv_uvcp_packet_get_write_memory_ack_size (); break; default: g_assert_not_reached (); } if (ack_size > priv->ack_packet_size_max) { arv_info_device ("Invalid uv %s acknowledge packet size (%" G_GSIZE_FORMAT " / max: %d)", operation, ack_size, priv->ack_packet_size_max); return FALSE; } switch (command) { case ARV_UVCP_COMMAND_READ_MEMORY_CMD: packet = arv_uvcp_packet_new_read_memory_cmd (address, size, 0, &packet_size); break; case ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: packet = arv_uvcp_packet_new_write_memory_cmd (address, size, 0, &packet_size); break; default: g_assert_not_reached (); } if (packet_size > priv->cmd_packet_size_max) { arv_info_device ("Invalid us %s command packet size (%" G_GSIZE_FORMAT " / max: %d)", operation, packet_size, priv->cmd_packet_size_max); arv_uvcp_packet_free (packet); return FALSE; } switch (command) { case ARV_UVCP_COMMAND_READ_MEMORY_CMD: break; case ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: memcpy (arv_uvcp_packet_get_write_memory_cmd_data (packet), buffer, size); break; default: g_assert_not_reached (); } ack_packet = g_malloc (ack_size); g_mutex_lock (&priv->transfer_mutex); do { GError *local_error = NULL; size_t transferred; priv->packet_id = arv_uvcp_next_packet_id (priv->packet_id); arv_uvcp_packet_set_packet_id (packet, priv->packet_id); arv_uvcp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); success = TRUE; success = success && arv_uv_device_bulk_transfer (uv_device, ARV_UV_ENDPOINT_CONTROL, LIBUSB_ENDPOINT_OUT, packet, packet_size, NULL, 0, &local_error); if (success) { gint timeout_ms; gint64 timeout_stop_ms; gboolean pending_ack; gboolean expected_answer; timeout_stop_ms = g_get_monotonic_time () / 1000 + priv->timeout_ms; do { pending_ack = FALSE; timeout_ms = timeout_stop_ms - g_get_monotonic_time () / 1000; if (timeout_ms < 0) timeout_ms = 0; success = TRUE; success = success && arv_uv_device_bulk_transfer (uv_device, ARV_UV_ENDPOINT_CONTROL, LIBUSB_ENDPOINT_IN, ack_packet, ack_size, &transferred, timeout_ms, &local_error); if (success) { ArvUvcpCommand ack_command; guint16 packet_id; arv_uvcp_packet_debug (ack_packet, ARV_DEBUG_LEVEL_DEBUG); status = arv_uvcp_packet_get_status (ack_packet); ack_command = arv_uvcp_packet_get_command (ack_packet); packet_id = arv_uvcp_packet_get_packet_id (ack_packet); if (ack_command == ARV_UVCP_COMMAND_PENDING_ACK) { gint64 pending_ack_timeout_ms; pending_ack = TRUE; expected_answer = FALSE; pending_ack_timeout_ms = arv_uvcp_packet_get_pending_ack_timeout (ack_packet); timeout_stop_ms = g_get_monotonic_time () / 1000 + pending_ack_timeout_ms; arv_debug_device ("[UvDevice::%s] Try %d/%d: " "pending ack timeout = %" G_GINT64_FORMAT, operation, n_tries + 1, ARV_UV_DEVICE_N_TRIES_MAX, pending_ack_timeout_ms); } if (status != ARV_UVCP_STATUS_SUCCESS) { expected_answer = ack_command == expected_ack_command && packet_id == priv->packet_id; if (!expected_answer) { arv_info_device ("[[UvDevice::%s] Try %d/%d: " "unexpected answer (0x%04x)", operation, n_tries + 1, ARV_UV_DEVICE_N_TRIES_MAX, status); } } else { expected_answer = status == ARV_UVCP_STATUS_SUCCESS && ack_command == expected_ack_command && packet_id == priv->packet_id; if (!expected_answer) arv_info_device ("[[UvDevice::%s] Try %d/%d: " "unexpected answer (0x%04x)", operation, n_tries + 1, ARV_UV_DEVICE_N_TRIES_MAX, status); } } else { expected_answer = FALSE; if (local_error != NULL) arv_warning_device ("[UvDevice::%s] Try %d/%d: ack reception error: %s", operation, n_tries + 1, ARV_UV_DEVICE_N_TRIES_MAX, local_error->message); g_clear_error (&local_error); } } while (pending_ack || (!expected_answer && timeout_ms)); success = success && expected_answer; if (success && status == ARV_UVCP_STATUS_SUCCESS) { switch (command) { case ARV_UVCP_COMMAND_READ_MEMORY_CMD: memcpy (buffer, arv_uvcp_packet_get_read_memory_ack_data (ack_packet), size); break; case ARV_UVCP_COMMAND_WRITE_MEMORY_CMD: break; default: g_assert_not_reached(); } } } else { if (local_error != NULL) arv_warning_device ("[UvDevice::%s] Try %d/%d: command sending error: %s", operation, n_tries + 1, ARV_UV_DEVICE_N_TRIES_MAX, local_error->message); g_clear_error (&local_error); } n_tries++; } while (!success && n_tries < ARV_UV_DEVICE_N_TRIES_MAX); g_mutex_unlock (&priv->transfer_mutex); g_free (ack_packet); arv_uvcp_packet_free (packet); success = success && status == ARV_UVCP_STATUS_SUCCESS; if (!success) { if (status != ARV_UVCP_STATUS_SUCCESS) g_set_error (error, ARV_DEVICE_ERROR, arv_uvcp_status_to_device_error (status), "USB3Vision %s error (%s)", operation, arv_uvcp_status_to_string (status)); else g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_TIMEOUT, "USB3Vision %s timeout", operation); } return success; } static gboolean arv_uv_device_read_memory (ArvDevice *device, guint64 address, guint32 size, void *buffer, GError **error) { ArvUvDevice *uv_device = ARV_UV_DEVICE (device); ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); int i; gint32 block_size; guint data_size_max; data_size_max = priv->ack_packet_size_max - sizeof (ArvUvcpHeader); for (i = 0; i < (size + data_size_max - 1) / data_size_max; i++) { block_size = MIN (data_size_max, size - i * data_size_max); if (!_send_cmd_and_receive_ack (uv_device, ARV_UVCP_COMMAND_READ_MEMORY_CMD, address + i * data_size_max, block_size, ((char *) buffer) + i * data_size_max, error)) return FALSE; } return TRUE; } static gboolean arv_uv_device_write_memory (ArvDevice *device, guint64 address, guint32 size, const void *buffer, GError **error) { ArvUvDevice *uv_device = ARV_UV_DEVICE (device); ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); int i; gint32 block_size; guint data_size_max; data_size_max = priv->ack_packet_size_max - sizeof (ArvUvcpHeader); for (i = 0; i < (size + data_size_max - 1) / data_size_max; i++) { block_size = MIN (data_size_max, size - i * data_size_max); if (!_send_cmd_and_receive_ack (uv_device, ARV_UVCP_COMMAND_WRITE_MEMORY_CMD, address + i * data_size_max, block_size, ((char *) buffer) + i * data_size_max, error)) return FALSE; } return TRUE; } static gboolean arv_uv_device_read_register (ArvDevice *device, guint64 address, guint32 *value, GError **error) { return arv_uv_device_read_memory (device, address, sizeof (guint32), value, error); } static gboolean arv_uv_device_write_register (ArvDevice *device, guint64 address, guint32 value, GError **error) { return arv_uv_device_write_memory (device, address, sizeof (guint32), &value, error); } static gboolean _bootstrap (ArvUvDevice *uv_device) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); ArvDevice *device = ARV_DEVICE (uv_device); guint64 offset; guint32 response_time; guint64 manifest_table_address; guint64 device_capability; guint32 max_cmd_transfer; guint32 max_ack_transfer; guint64 u3vcp_capability; guint64 sirm_offset; guint32 si_info; guint32 si_control; guint64 si_req_payload_size; guint32 si_req_leader_size; guint32 si_req_trailer_size; guint32 si_max_leader_size; guint32 si_payload_size; guint32 si_payload_count; guint32 si_transfer1_size; guint32 si_transfer2_size; guint32 si_max_trailer_size; guint64 manifest_n_entries; ArvUvcpManifestEntry entry; ArvUvcpManifestSchemaType schema_type; GString *string; void *data; char manufacturer[64]; gboolean success = TRUE; char *genicam_url = NULL; arv_info_device ("Get genicam"); success = success && arv_device_read_memory(device, ARV_ABRM_MANUFACTURER_NAME, 64, &manufacturer, NULL); if (!success) { arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); return FALSE; } manufacturer[63] = 0; arv_info_device ("MANUFACTURER_NAME = '%s'", manufacturer); success = success && arv_device_read_memory (device, ARV_ABRM_SBRM_ADDRESS, sizeof (offset), &offset, NULL); success = success && arv_device_read_memory (device, ARV_ABRM_MAX_DEVICE_RESPONSE_TIME, sizeof (response_time), &response_time, NULL); success = success && arv_device_read_memory (device, ARV_ABRM_DEVICE_CAPABILITY, sizeof (device_capability), &device_capability, NULL); success = success && arv_device_read_memory (device, ARV_ABRM_MANIFEST_TABLE_ADDRESS, sizeof (manifest_table_address), &manifest_table_address, NULL); if (!success) { arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); return FALSE; } arv_info_device ("MAX_DEVICE_RESPONSE_TIME = 0x%08x", response_time); arv_info_device ("DEVICE_CAPABILITY = 0x%016" G_GINT64_MODIFIER "x", device_capability); arv_info_device ("SRBM_ADDRESS = 0x%016" G_GINT64_MODIFIER "x", offset); arv_info_device ("MANIFEST_TABLE_ADDRESS = 0x%016" G_GINT64_MODIFIER "x", manifest_table_address); priv->timeout_ms = MAX (ARV_UVCP_DEFAULT_RESPONSE_TIME_MS, response_time); success = success && arv_device_read_memory (device, offset + ARV_SBRM_U3VCP_CAPABILITY, sizeof (u3vcp_capability), &u3vcp_capability, NULL); success = success && arv_device_read_memory (device, offset + ARV_SBRM_MAX_CMD_TRANSFER, sizeof (max_cmd_transfer), &max_cmd_transfer, NULL); success = success && arv_device_read_memory (device, offset + ARV_SBRM_MAX_ACK_TRANSFER, sizeof (max_ack_transfer), &max_ack_transfer, NULL); success = success && arv_device_read_memory (device, offset + ARV_SBRM_SIRM_ADDRESS, sizeof (sirm_offset), &sirm_offset, NULL); if (!success) { arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); return FALSE; } arv_info_device ("U3VCP_CAPABILITY = 0x%016" G_GINT64_MODIFIER "x", u3vcp_capability); arv_info_device ("MAX_CMD_TRANSFER = 0x%08x", max_cmd_transfer); arv_info_device ("MAX_ACK_TRANSFER = 0x%08x", max_ack_transfer); arv_info_device ("SIRM_OFFSET = 0x%016" G_GINT64_MODIFIER "x", sirm_offset); priv->cmd_packet_size_max = MIN (priv->cmd_packet_size_max, max_cmd_transfer); priv->ack_packet_size_max = MIN (priv->ack_packet_size_max, max_ack_transfer); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_INFO, sizeof (si_info), &si_info, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_CONTROL, sizeof (si_control), &si_control, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_REQ_PAYLOAD_SIZE, sizeof (si_req_payload_size), &si_req_payload_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_REQ_LEADER_SIZE, sizeof (si_req_leader_size), &si_req_leader_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_REQ_TRAILER_SIZE, sizeof (si_req_trailer_size), &si_req_trailer_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_MAX_LEADER_SIZE, sizeof (si_max_leader_size), &si_max_leader_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_PAYLOAD_SIZE, sizeof (si_payload_size), &si_payload_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_PAYLOAD_COUNT, sizeof (si_payload_count), &si_payload_count, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_TRANSFER1_SIZE, sizeof (si_transfer1_size), &si_transfer1_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_TRANSFER2_SIZE, sizeof (si_transfer2_size), &si_transfer2_size, NULL); success = success && arv_device_read_memory (device, sirm_offset + ARV_SIRM_MAX_TRAILER_SIZE, sizeof (si_max_trailer_size), &si_max_trailer_size, NULL); if (!success) { arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); return FALSE; } arv_info_device ("SIRM_INFO = 0x%08x", si_info); arv_info_device ("SIRM_CONTROL = 0x%08x", si_control); arv_info_device ("SIRM_REQ_PAYLOAD_SIZE = 0x%016" G_GINT64_MODIFIER "x", si_req_payload_size); arv_info_device ("SIRM_REQ_LEADER_SIZE = 0x%08x", si_req_leader_size); arv_info_device ("SIRM_REQ_TRAILER_SIZE = 0x%08x", si_req_trailer_size); arv_info_device ("SIRM_MAX_LEADER_SIZE = 0x%08x", si_max_leader_size); arv_info_device ("SIRM_PAYLOAD_SIZE = 0x%08x", si_payload_size); arv_info_device ("SIRM_PAYLOAD_COUNT = 0x%08x", si_payload_count); arv_info_device ("SIRM_TRANSFER1_SIZE = 0x%08x", si_transfer1_size); arv_info_device ("SIRM_TRANSFER2_SIZE = 0x%08x", si_transfer2_size); arv_info_device ("SIRM_MAX_TRAILER_SIZE = 0x%08x", si_max_trailer_size); success = success && arv_device_read_memory (device, manifest_table_address, sizeof (manifest_n_entries), &manifest_n_entries, NULL); success = success && arv_device_read_memory (device, manifest_table_address + 0x08, sizeof (entry), &entry, NULL); if (!success) { arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); return FALSE; } arv_info_device ("MANIFEST_N_ENTRIES = 0x%016" G_GINT64_MODIFIER "x", manifest_n_entries); string = g_string_new (""); arv_g_string_append_hex_dump (string, &entry, sizeof (entry)); arv_info_device ("MANIFEST ENTRY\n%s", string->str); g_string_free (string, TRUE); arv_info_device ("genicam address = 0x%016" G_GINT64_MODIFIER "x", entry.address); arv_info_device ("genicam size = 0x%016" G_GINT64_MODIFIER "x", entry.size); data = g_malloc0 (entry.size); success = success && arv_device_read_memory (device, entry.address, entry.size, data, NULL); if (!success){ arv_warning_device ("[UvDevice::_bootstrap] Error during memory read"); g_free(data); return FALSE; } #if 0 string = g_string_new (""); arv_g_string_append_hex_dump (string, data, entry.size); arv_info_device ("GENICAM\n%s", string->str); g_string_free (string, TRUE); #endif schema_type = arv_uvcp_manifest_entry_get_schema_type (&entry); switch (schema_type) { case ARV_UVCP_SCHEMA_ZIP: { ArvZip *zip; const GSList *zip_files; zip = arv_zip_new (data, entry.size); zip_files = arv_zip_get_file_list (zip); if (zip_files != NULL) { const char *zip_filename; zip_filename = arv_zip_file_get_name (zip_files->data); priv->genicam_xml = arv_zip_get_file (zip, zip_filename, &priv->genicam_xml_size); arv_info_device ("zip file = %s", zip_filename); #if 0 string = g_string_new (""); arv_g_string_append_hex_dump (string, priv->genicam_xml, priv->genicam_xml_size); arv_info_device ("GENICAM\n%s", string->str); g_string_free (string, TRUE); #endif priv->genicam = arv_gc_new (ARV_DEVICE (uv_device), priv->genicam_xml, priv->genicam_xml_size); genicam_url = g_strdup_printf("local:///DeviceU3V.zip;%" G_GINT64_MODIFIER "x;%" G_GINT64_MODIFIER "x", entry.address, entry.size); arv_dom_document_set_url(ARV_DOM_DOCUMENT(priv->genicam), genicam_url); g_free (genicam_url); } arv_zip_free (zip); g_free (data); } break; case ARV_UVCP_SCHEMA_RAW: { priv->genicam_xml = data; priv->genicam_xml_size = entry.size; priv->genicam = arv_gc_new (ARV_DEVICE (uv_device), priv->genicam_xml, priv->genicam_xml_size); genicam_url = g_strdup_printf("local:///DeviceU3V.xml;%" G_GINT64_MODIFIER "x;%" G_GINT64_MODIFIER "x", entry.address, entry.size); arv_dom_document_set_url(ARV_DOM_DOCUMENT(priv->genicam), genicam_url); g_free (genicam_url); } break; default: arv_warning_device ("Unknown USB3Vision manifest schema type (%d)", schema_type); } #if 0 arv_info_device("GENICAM\n:%s", priv->genicam_xml); #endif return TRUE; } static ArvGc * arv_uv_device_get_genicam (ArvDevice *device) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (device)); return priv->genicam; } static const char * arv_uv_device_get_genicam_xml (ArvDevice *device, size_t *size) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (device)); if (size != NULL) *size = priv->genicam_xml_size; return priv->genicam_xml; } static void reset_endpoint (libusb_device_handle *usb_device, guint8 endpoint, guint8 endpoint_flags) { int errcode; /* Set endpoint in halt condition */ errcode = libusb_control_transfer(usb_device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_ENDPOINT, LIBUSB_REQUEST_SET_FEATURE, 0, /* Value: 0=endpoint_halt */ endpoint | endpoint_flags, 0, 0, 1000); if (errcode < 0) { arv_warning_device("Failed to set endpoint %x in halt condition: %s", endpoint|endpoint_flags, libusb_error_name (errcode)); return; } /* Clear halt condtion on the endpoint, effectivelly resetting the pipe */ errcode = libusb_clear_halt(usb_device, endpoint | endpoint_flags); if (errcode < 0) { arv_warning_device("Failed to clear halt contidion on endpoint: %s", libusb_error_name (errcode)); return; } } gboolean arv_uv_device_reset_stream_endpoint (ArvUvDevice *device) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (device)); g_return_val_if_fail(ARV_IS_UV_DEVICE(device), FALSE); reset_endpoint (priv->usb_device, priv->data_endpoint, LIBUSB_ENDPOINT_IN); return TRUE; } static int get_guid_index(libusb_device * device) { struct libusb_config_descriptor *config; const struct libusb_interface *inter; const struct libusb_interface_descriptor *interdesc; int guid_index = -1; int i, j; libusb_get_config_descriptor (device, 0, &config); for (i = 0; i < (int) config->bNumInterfaces; i++) { inter = &config->interface[i]; for (j = 0; j < inter->num_altsetting; j++) { interdesc = &inter->altsetting[j]; if (interdesc->bInterfaceClass == ARV_UV_INTERFACE_INTERFACE_CLASS && interdesc->bInterfaceSubClass == ARV_UV_INTERFACE_INTERFACE_SUBCLASS) { if (interdesc->bInterfaceProtocol == ARV_UV_INTERFACE_CONTROL_PROTOCOL && interdesc->extra && interdesc->extra_length >= ARV_UV_INTERFACE_GUID_INDEX_OFFSET + sizeof(unsigned char)) { guid_index = (int) (*(interdesc->extra + ARV_UV_INTERFACE_GUID_INDEX_OFFSET)); } } } } libusb_free_config_descriptor (config); return guid_index; } static gboolean _open_usb_device (ArvUvDevice *uv_device, GError **error) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); libusb_device **devices; unsigned i, j, k; ssize_t count; count = libusb_get_device_list (priv->usb, &devices); if (count < 0) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Failed to get USB device list: %s", libusb_error_name (count)); return FALSE; } for (i = 0; i < count && priv->usb_device == NULL; i++) { libusb_device_handle *usb_device; struct libusb_device_descriptor desc; if (libusb_get_device_descriptor (devices[i], &desc) >= 0 && libusb_open (devices[i], &usb_device) == LIBUSB_SUCCESS) { unsigned char *manufacturer; unsigned char *product; unsigned char *serial_number; unsigned char *guid; int index; manufacturer = g_malloc0 (256); product = g_malloc0 (256); serial_number = g_malloc0 (256); guid = g_malloc0 (256); index = desc.iManufacturer; if (index > 0) libusb_get_string_descriptor_ascii (usb_device, index, manufacturer, 256); index = desc.iProduct; if (index > 0) libusb_get_string_descriptor_ascii (usb_device, index, product, 256); index = desc.iSerialNumber; if (index > 0) libusb_get_string_descriptor_ascii (usb_device, index, serial_number, 256); index = get_guid_index(devices[i]); if (index > 0) libusb_get_string_descriptor_ascii (usb_device, index, guid, 256); if ((priv->vendor != NULL && g_strcmp0 ((char * ) manufacturer, priv->vendor) == 0 && priv->product != NULL && g_strcmp0 ((char * ) product, priv->product) == 0 && priv->serial_number != NULL && g_strcmp0 ((char * ) serial_number, priv->serial_number) == 0) || (priv->guid != NULL && g_strcmp0 ((char * ) guid, priv->guid) == 0)) { struct libusb_config_descriptor *config; struct libusb_endpoint_descriptor endpoint; const struct libusb_interface *inter; const struct libusb_interface_descriptor *interdesc; int result; priv->usb_device = usb_device; result = libusb_set_auto_detach_kernel_driver (usb_device, 1); if (result != 0) { arv_warning_device ("Failed to set auto kernel detach feature " "for USB device '%s-%s-%s': %s", priv->vendor, priv->product, priv->serial_number, libusb_error_name (result)); } libusb_get_config_descriptor (devices[i], 0, &config); for (j = 0; j < (int) config->bNumInterfaces; j++) { inter = &config->interface[j]; for (k = 0; k < inter->num_altsetting; k++) { interdesc = &inter->altsetting[k]; if (interdesc->bInterfaceClass == ARV_UV_INTERFACE_INTERFACE_CLASS && interdesc->bInterfaceSubClass == ARV_UV_INTERFACE_INTERFACE_SUBCLASS) { if (interdesc->bInterfaceProtocol == ARV_UV_INTERFACE_CONTROL_PROTOCOL) { endpoint = interdesc->endpoint[0]; priv->control_endpoint = endpoint.bEndpointAddress & 0x0f; priv->control_interface = interdesc->bInterfaceNumber; } if (interdesc->bInterfaceProtocol == ARV_UV_INTERFACE_DATA_PROTOCOL) { endpoint = interdesc->endpoint[0]; priv->data_endpoint = endpoint.bEndpointAddress & 0x0f; priv->data_interface = interdesc->bInterfaceNumber; } } } } libusb_free_config_descriptor (config); } else libusb_close (usb_device); g_free (manufacturer); g_free (product); g_free (serial_number); g_free (guid); } } libusb_free_device_list (devices, 1); if (priv->usb_device == NULL) { g_set_error (error, ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_NOT_FOUND, "USB device '%s:%s:%s' not found", priv->vendor, priv->product, priv->serial_number); return FALSE; } return TRUE; } static gpointer event_thread_func(void *p) { ArvUvDevicePrivate *priv = (ArvUvDevicePrivate *)p; struct timeval tv = { 0, 100000 }; while (priv->event_thread_run) { libusb_handle_events_timeout(priv->usb, &tv); } return NULL; } /** * arv_uv_device_set_usb_mode: * @uv_device: a #ArvUvDevice * @usb_mode: a #ArvUvUsbMode option * * Sets the option to utilize the USB synchronous or asynchronous device I/O API. The default mode is * @ARV_UV_USB_MODE_SYNC, which means USB bulk transfer will be synchronously executed. This mode is qualified to work, * but it has the performance issue with some high framerate device. Using @ARV_UV_USB_MODE_ASYNC possibly improves the * bandwidth. * * Since: 0.8.17 */ void arv_uv_device_set_usb_mode (ArvUvDevice *uv_device, ArvUvUsbMode usb_mode) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); g_return_if_fail (ARV_IS_UV_DEVICE (uv_device)); priv->usb_mode = usb_mode; } /** * arv_uv_device_new: * @vendor: USB3 vendor string * @product: USB3 product string * @serial_number: device serial number * @error: a #GError placeholder, %NULL to ignore * * Returns: a newly created #ArvDevice using USB3 based protocol * * Since: 0.8.0 */ ArvDevice * arv_uv_device_new (const char *vendor, const char *product, const char *serial_number, GError **error) { return g_initable_new (ARV_TYPE_UV_DEVICE, NULL, error, "vendor", vendor, "product", product, "serial-number", serial_number, NULL); } /** * arv_uv_device_new_from_guid: * @guid: device GUID * @error: a #GError placeholder, %NULL to ignore * * Returns: a newly created #ArvDevice using USB3 based protocol * * Since: 0.8.17 */ ArvDevice * arv_uv_device_new_from_guid (const char *guid, GError **error) { return g_initable_new (ARV_TYPE_UV_DEVICE, NULL, error, "guid", guid, NULL); } static int LIBUSB_CALL _disconnect_event (libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (user_data)); if (device == libusb_get_device (priv->usb_device)) { if (!priv->disconnected) { priv->disconnected = TRUE; arv_device_emit_control_lost_signal (ARV_DEVICE (user_data)); } } return 0; } static void arv_uv_device_constructed (GObject *object) { ArvUvDevice *uv_device = ARV_UV_DEVICE (object); ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); GError *error = NULL; int result; G_OBJECT_CLASS (arv_uv_device_parent_class)->constructed (object); g_mutex_init (&priv->transfer_mutex); result = libusb_init (&priv->usb); if (result != 0) { arv_device_take_init_error (ARV_DEVICE (uv_device), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Failed to initialize USB library: %s", libusb_error_name (result))); return; } if (priv->vendor != NULL) arv_info_device ("[UvDevice::new] Vendor = %s", priv->vendor); if (priv->product != NULL) arv_info_device ("[UvDevice::new] Product = %s", priv->product); if (priv->serial_number != NULL) arv_info_device ("[UvDevice::new] S/N = %s", priv->serial_number); if (priv->guid != NULL) arv_info_device ("[UvDevice::new] GUID = %s", priv->guid); priv->packet_id = 65300; /* Start near the end of the circular counter */ priv->timeout_ms = 32; if (!_open_usb_device (uv_device, &error)) { arv_device_take_init_error (ARV_DEVICE (uv_device), error); return; } arv_info_device("[UvDevice::new] Using control endpoint %d, interface %d", priv->control_endpoint, priv->control_interface); arv_info_device("[UvDevice::new] Using data endpoint %d, interface %d", priv->data_endpoint, priv->data_interface); result = libusb_claim_interface (priv->usb_device, priv->control_interface); if (result != 0) { arv_device_take_init_error (ARV_DEVICE (uv_device), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Failed to claim USB control interface to '%s-%s-%s-%s': %s", priv->vendor, priv->product, priv->serial_number, priv->guid, libusb_error_name (result))); return; } result = libusb_claim_interface (priv->usb_device, priv->data_interface); if (result != 0) { arv_device_take_init_error (ARV_DEVICE (uv_device), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Failed to claim USB data interface to '%s-%s-%s-%s': %s", priv->vendor, priv->product, priv->serial_number, priv->guid, libusb_error_name (result))); return; } if ( !_bootstrap (uv_device)){ arv_device_take_init_error (ARV_DEVICE (uv_device), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_PROTOCOL_ERROR, "Failed to bootstrap USB device '%s-%s-%s-%s'", priv->vendor, priv->product, priv->serial_number, priv->guid)); return; } if (!ARV_IS_GC (priv->genicam)) { arv_device_take_init_error (ARV_DEVICE (uv_device), g_error_new (ARV_DEVICE_ERROR, ARV_DEVICE_ERROR_GENICAM_NOT_FOUND, "Failed to load Genicam data for USB device '%s-%s-%s-%s'", priv->vendor, priv->product, priv->serial_number, priv->guid)); return; } libusb_hotplug_register_callback (priv->usb, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, _disconnect_event, uv_device, &priv->hotplug_cb_handle); priv->usb_mode = ARV_UV_USB_MODE_DEFAULT; priv->event_thread_run = 1; priv->event_thread = g_thread_new ( "arv_libusb", event_thread_func, priv); } static void arv_uv_device_init (ArvUvDevice *uv_device) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); priv->cmd_packet_size_max = 65536 + sizeof (ArvUvcpHeader); priv->ack_packet_size_max = 65536 + sizeof (ArvUvcpHeader); priv->disconnected = FALSE; } static void arv_uv_device_finalize (GObject *object) { ArvUvDevice *uv_device = ARV_UV_DEVICE (object); ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (uv_device); libusb_hotplug_deregister_callback (priv->usb, priv->hotplug_cb_handle); priv->event_thread_run = 0; if (priv->event_thread) g_thread_join (priv->event_thread); g_clear_object (&priv->genicam); g_clear_pointer (&priv->vendor, g_free); g_clear_pointer (&priv->product, g_free); g_clear_pointer (&priv->serial_number, g_free); g_clear_pointer (&priv->guid, g_free); g_clear_pointer (&priv->genicam_xml, g_free); if (priv->usb_device != NULL) { libusb_release_interface (priv->usb_device, priv->control_interface); libusb_release_interface (priv->usb_device, priv->data_interface); libusb_close (priv->usb_device); } if (priv->usb != NULL) libusb_exit (priv->usb); g_mutex_clear (&priv->transfer_mutex); G_OBJECT_CLASS (arv_uv_device_parent_class)->finalize (object); } static void arv_uv_device_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec) { ArvUvDevicePrivate *priv = arv_uv_device_get_instance_private (ARV_UV_DEVICE (self)); switch (prop_id) { case PROP_UV_DEVICE_VENDOR: g_free (priv->vendor); priv->vendor = g_value_dup_string (value); break; case PROP_UV_DEVICE_PRODUCT: g_free (priv->product); priv->product = g_value_dup_string (value); break; case PROP_UV_DEVICE_SERIAL_NUMBER: g_free (priv->serial_number); priv->serial_number = g_value_dup_string (value); break; case PROP_UV_DEVICE_GUID: g_free (priv->guid); priv->guid = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; } } static void arv_uv_device_class_init (ArvUvDeviceClass *uv_device_class) { GObjectClass *object_class = G_OBJECT_CLASS (uv_device_class); ArvDeviceClass *device_class = ARV_DEVICE_CLASS (uv_device_class); object_class->finalize = arv_uv_device_finalize; object_class->constructed = arv_uv_device_constructed; object_class->set_property = arv_uv_device_set_property; device_class->create_stream = arv_uv_device_create_stream; device_class->get_genicam_xml = arv_uv_device_get_genicam_xml; device_class->get_genicam = arv_uv_device_get_genicam; device_class->read_memory = arv_uv_device_read_memory; device_class->write_memory = arv_uv_device_write_memory; device_class->read_register = arv_uv_device_read_register; device_class->write_register = arv_uv_device_write_register; g_object_class_install_property (object_class, PROP_UV_DEVICE_VENDOR, g_param_spec_string ("vendor", "Vendor", "USB3 device vendor string", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_UV_DEVICE_PRODUCT, g_param_spec_string ("product", "Product", "USB3 device product string", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_UV_DEVICE_SERIAL_NUMBER, g_param_spec_string ("serial-number", "Serial number", "USB3 device serial number", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_UV_DEVICE_GUID, g_param_spec_string ("guid", "GUID", "USB3 device GUID", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } aravis-0.8.34/src/arvuvdevice.h000066400000000000000000000032051475431451200163640ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_DEVICE_H #define ARV_UV_DEVICE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_UV_DEVICE (arv_uv_device_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvUvDevice, arv_uv_device, ARV, UV_DEVICE, ArvDevice) ARV_API ArvDevice * arv_uv_device_new (const char *vendor, const char *product, const char *serial_number, GError **error); ARV_API ArvDevice * arv_uv_device_new_from_guid (const char *guid, GError **error); ARV_API void arv_uv_device_set_usb_mode (ArvUvDevice *uv_device, ArvUvUsbMode usb_mode); G_END_DECLS #endif aravis-0.8.34/src/arvuvdeviceprivate.h000066400000000000000000000042211475431451200177560ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_DEVICE_PRIVATE_H #define ARV_UV_DEVICE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS typedef enum { ARV_UV_ENDPOINT_CONTROL, ARV_UV_ENDPOINT_DATA } ArvUvEndpointType; gboolean arv_uv_device_bulk_transfer (ArvUvDevice *uv_device, ArvUvEndpointType endpoint_type, unsigned char endpoint_flags, void *data, size_t size, size_t *transferred_size, guint32 timeout_ms, GError **error); void arv_uv_device_fill_bulk_transfer (struct libusb_transfer* transfer, ArvUvDevice *uv_device, ArvUvEndpointType endpoint_type, unsigned char endpoint_flags, void *data, size_t size, libusb_transfer_cb_fn callback, void* callback_data, unsigned int timeout); gboolean arv_uv_device_is_connected (ArvUvDevice *uv_device); gboolean arv_uv_device_reset_stream_endpoint (ArvUvDevice *device); G_END_DECLS #endif aravis-0.8.34/src/arvuvinterface.c000066400000000000000000000332241475431451200170640ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvuvinterface * @short_description: USB3Vision interface */ #include #include #include #include #include #include #include #include /* ArvUvInterface implementation */ typedef struct { char *id; char *name; char *full_name; char *manufacturer; char *product; char *serial_nbr; char *guid; volatile gint ref_count; } ArvUvInterfaceDeviceInfos; static ArvUvInterfaceDeviceInfos * arv_uv_interface_device_infos_new (const char *manufacturer, const char *product, const char *serial_nbr, const char *guid) { ArvUvInterfaceDeviceInfos *infos; g_return_val_if_fail (manufacturer != NULL, NULL); g_return_val_if_fail (product != NULL, NULL); g_return_val_if_fail (serial_nbr != NULL, NULL); g_return_val_if_fail (guid != NULL, NULL); infos = g_new (ArvUvInterfaceDeviceInfos, 1); infos->id = g_strdup_printf ("%s-%s-%s", manufacturer, guid, serial_nbr); infos->manufacturer = g_strdup (manufacturer); infos->name = g_strdup_printf ("%s-%s", arv_vendor_alias_lookup (manufacturer), serial_nbr); infos->full_name = g_strdup_printf ("%s-%s", manufacturer, serial_nbr); infos->product = g_strdup (product); infos->serial_nbr = g_strdup (serial_nbr); infos->guid = g_strdup (guid); infos->ref_count = 1; arv_str_strip (infos->id, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); arv_str_strip (infos->name, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); arv_str_strip (infos->full_name, ARV_DEVICE_NAME_ILLEGAL_CHARACTERS, ARV_DEVICE_NAME_REPLACEMENT_CHARACTER); return infos; } static ArvUvInterfaceDeviceInfos * arv_uv_interface_device_infos_ref (ArvUvInterfaceDeviceInfos *infos) { g_return_val_if_fail (infos != NULL, NULL); g_return_val_if_fail (g_atomic_int_get (&infos->ref_count) > 0, NULL); g_atomic_int_inc (&infos->ref_count); return infos; } static void arv_uv_interface_device_infos_unref (ArvUvInterfaceDeviceInfos *infos) { g_return_if_fail (infos != NULL); g_return_if_fail (g_atomic_int_get (&infos->ref_count) > 0); if (g_atomic_int_dec_and_test (&infos->ref_count)) { g_clear_pointer (&infos->id, g_free); g_clear_pointer (&infos->name, g_free); g_clear_pointer (&infos->full_name, g_free); g_clear_pointer (&infos->manufacturer, g_free); g_clear_pointer (&infos->product, g_free); g_clear_pointer (&infos->serial_nbr, g_free); g_clear_pointer (&infos->guid, g_free); g_clear_pointer (&infos, g_free); } } typedef struct { GHashTable *devices; libusb_context *usb; } ArvUvInterfacePrivate; struct _ArvUvInterface { ArvInterface interface; ArvUvInterfacePrivate *priv; }; struct _ArvUvInterfaceClass { ArvInterfaceClass parent_class; }; #if 0 static void printdev (libusb_device *device) { struct libusb_device_descriptor desc; struct libusb_config_descriptor *config; const struct libusb_interface *inter; const struct libusb_interface_descriptor *interdesc; int r, i, j; r = libusb_get_device_descriptor (device, &desc); if (r < 0) { printf ("Failed to get device descriptor\n"); return; } printf ("VendorID: 0x%04x\n", desc.idVendor); printf ("ProductID: 0x%04x\n", desc.idProduct); printf ("Device Class: 0x%02x\n", (int) desc.bDeviceClass); printf ("Device SubClass: 0x%02x\n", (int) desc.bDeviceSubClass); printf ("Protocol: 0x%02x\n", (int) desc.bDeviceProtocol); libusb_get_config_descriptor (device, 0, &config); printf ("Nbr of Interfaces: %d\n", (int)config->bNumInterfaces); for (i = 0; i< (int) config->bNumInterfaces; i++) { inter = &config->interface[i]; for (j = 0; j < inter->num_altsetting; j++) { interdesc = &inter->altsetting[j]; printf (" Interface Class: 0x%02x\n", (int) interdesc->bInterfaceClass); printf (" Interface SubClass: 0x%02x\n", (int) interdesc->bInterfaceSubClass); printf (" Interface Protocol: 0x%02x\n", (int) interdesc->bInterfaceProtocol); } } libusb_free_config_descriptor (config); } #endif static ArvInterfaceDeviceIds * _usb_device_to_device_ids (ArvUvInterface *uv_interface, libusb_device *device) { ArvInterfaceDeviceIds *device_ids = NULL; libusb_device_handle *device_handle; struct libusb_device_descriptor desc; struct libusb_config_descriptor *config; const struct libusb_interface *inter; const struct libusb_interface_descriptor *interdesc; gboolean control_protocol_found; gboolean data_protocol_found; int guid_index = -1; int result, i, j; result = libusb_get_device_descriptor (device, &desc); if (result < 0) { arv_warning_interface ("Failed to get device descriptor: %s", libusb_error_name (result)); return NULL; } if (desc.bDeviceClass != ARV_UV_INTERFACE_DEVICE_CLASS || desc.bDeviceSubClass != ARV_UV_INTERFACE_DEVICE_SUBCLASS || desc.bDeviceProtocol != ARV_UV_INTERFACE_DEVICE_PROTOCOL) return NULL; control_protocol_found = FALSE; data_protocol_found = FALSE; libusb_get_config_descriptor (device, 0, &config); for (i = 0; i< (int) config->bNumInterfaces; i++) { inter = &config->interface[i]; for (j = 0; j < inter->num_altsetting; j++) { interdesc = &inter->altsetting[j]; if (interdesc->bInterfaceClass == ARV_UV_INTERFACE_INTERFACE_CLASS && interdesc->bInterfaceSubClass == ARV_UV_INTERFACE_INTERFACE_SUBCLASS) { if (interdesc->bInterfaceProtocol == ARV_UV_INTERFACE_CONTROL_PROTOCOL) { control_protocol_found = TRUE; if (interdesc->extra && interdesc->extra_length >= ARV_UV_INTERFACE_GUID_INDEX_OFFSET + sizeof(unsigned char)) { guid_index = (int) (*(interdesc->extra + ARV_UV_INTERFACE_GUID_INDEX_OFFSET)); } } if (interdesc->bInterfaceProtocol == ARV_UV_INTERFACE_DATA_PROTOCOL) data_protocol_found = TRUE; } } } libusb_free_config_descriptor (config); if (!control_protocol_found || !data_protocol_found) return NULL; result = libusb_open (device, &device_handle); if (result == LIBUSB_SUCCESS) { ArvUvInterfaceDeviceInfos *device_infos; unsigned char *manufacturer; unsigned char *product; unsigned char *serial_nbr; unsigned char *guid; int index; device_ids = g_new0 (ArvInterfaceDeviceIds, 1); manufacturer = g_malloc0 (256); product = g_malloc0 (256); serial_nbr = g_malloc0 (256); guid = g_malloc0 (256); index = desc.iManufacturer; if (index > 0) libusb_get_string_descriptor_ascii (device_handle, index, manufacturer, 256); index = desc.iProduct; if (index > 0) libusb_get_string_descriptor_ascii (device_handle, index, product, 256); index = desc.iSerialNumber; if (index > 0) libusb_get_string_descriptor_ascii (device_handle, index, serial_nbr, 256); index = guid_index; if (index > 0) libusb_get_string_descriptor_ascii (device_handle, index, guid, 256); device_infos = arv_uv_interface_device_infos_new ((char *) manufacturer, (char *) product, (char *) serial_nbr, (char *) guid); g_hash_table_replace (uv_interface->priv->devices, device_infos->id, arv_uv_interface_device_infos_ref (device_infos)); g_hash_table_replace (uv_interface->priv->devices, device_infos->name, arv_uv_interface_device_infos_ref (device_infos)); g_hash_table_replace (uv_interface->priv->devices, device_infos->full_name, arv_uv_interface_device_infos_ref (device_infos)); g_hash_table_replace (uv_interface->priv->devices, device_infos->guid, arv_uv_interface_device_infos_ref (device_infos)); arv_uv_interface_device_infos_unref (device_infos); device_ids->device = g_strdup (device_infos->id); device_ids->physical = g_strdup (device_infos->guid); device_ids->address = g_strdup ("USB3"); /* FIXME */ device_ids->vendor = g_strdup (device_infos->manufacturer); device_ids->manufacturer_info = g_strdup ("none"); device_ids->model = g_strdup (device_infos->product); device_ids->serial_nbr = g_strdup (device_infos->serial_nbr); g_free (manufacturer); g_free (product); g_free (serial_nbr); g_free (guid); libusb_close (device_handle); } else arv_warning_interface ("Failed to open USB device: %s", libusb_error_name (result)); return device_ids; } static void _discover (ArvUvInterface *uv_interface, GArray *device_ids) { libusb_device **devices; unsigned uv_count = 0; ssize_t result; unsigned i; if (uv_interface->priv->usb == NULL) return; result = libusb_get_device_list(uv_interface->priv->usb, &devices); if (result < 0) { arv_warning_interface ("Failed to get USB device list: %s", libusb_error_name (result)); return; } g_hash_table_remove_all (uv_interface->priv->devices); for (i = 0; i < result; i++) { ArvInterfaceDeviceIds *ids; ids = _usb_device_to_device_ids (uv_interface, devices[i]); if (ids != NULL) { uv_count++; if (device_ids != NULL) g_array_append_val (device_ids, ids); else { g_free (ids->device); g_free (ids->physical); g_free (ids->address); g_free (ids->vendor); g_free (ids->manufacturer_info); g_free (ids->model); g_free (ids->serial_nbr); g_free (ids); } } } arv_info_interface ("Found %d USB3Vision device%s (among %" G_GSSIZE_FORMAT " USB device%s)", uv_count , uv_count > 1 ? "s" : "", result, result > 1 ? "s" : ""); libusb_free_device_list (devices, 1); } static void arv_uv_interface_update_device_list (ArvInterface *interface, GArray *device_ids) { ArvUvInterface *uv_interface = ARV_UV_INTERFACE (interface); g_assert (device_ids->len == 0); _discover (uv_interface, device_ids); } static ArvDevice * _open_device (ArvInterface *interface, const char *device_id, GError **error) { ArvUvInterface *uv_interface; ArvUvInterfaceDeviceInfos *device_infos; uv_interface = ARV_UV_INTERFACE (interface); if (device_id == NULL) { GList *device_list; device_list = g_hash_table_get_values (uv_interface->priv->devices); device_infos = device_list != NULL ? device_list->data : NULL; g_list_free (device_list); } else device_infos = g_hash_table_lookup (uv_interface->priv->devices, device_id); if (device_infos == NULL) return NULL; return arv_uv_device_new_from_guid (device_infos->guid, error); } static ArvDevice * arv_uv_interface_open_device (ArvInterface *interface, const char *device_id, GError **error) { ArvDevice *device; GError *local_error = NULL; device = _open_device (interface, device_id, error); if (ARV_IS_DEVICE (device) || local_error != NULL) { if (local_error != NULL) g_propagate_error (error, local_error); return device; } _discover (ARV_UV_INTERFACE (interface), NULL); return _open_device (interface, device_id, error); } static ArvInterface *arv_uv_interface = NULL; static GMutex arv_uv_interface_mutex; /** * arv_uv_interface_get_instance: * * Gets the unique instance of the GV interface. * * Returns: (transfer none): a #ArvInterface singleton. */ ArvInterface * arv_uv_interface_get_instance (void) { g_mutex_lock (&arv_uv_interface_mutex); if (arv_uv_interface == NULL) arv_uv_interface = g_object_new (ARV_TYPE_UV_INTERFACE, NULL); g_mutex_unlock (&arv_uv_interface_mutex); return ARV_INTERFACE (arv_uv_interface); } void arv_uv_interface_destroy_instance (void) { g_mutex_lock (&arv_uv_interface_mutex); g_clear_object (&arv_uv_interface); g_mutex_unlock (&arv_uv_interface_mutex); } G_DEFINE_TYPE_WITH_CODE (ArvUvInterface, arv_uv_interface, ARV_TYPE_INTERFACE, G_ADD_PRIVATE (ArvUvInterface)) static void arv_uv_interface_init (ArvUvInterface *uv_interface) { int result; uv_interface->priv = arv_uv_interface_get_instance_private (uv_interface); result = libusb_init (&uv_interface->priv->usb); if (result != 0) arv_warning_interface ("Failed to initialize USB library: %s", libusb_error_name (result)); uv_interface->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) arv_uv_interface_device_infos_unref); } static void arv_uv_interface_finalize (GObject *object) { ArvUvInterface *uv_interface = ARV_UV_INTERFACE (object); g_hash_table_unref (uv_interface->priv->devices); G_OBJECT_CLASS (arv_uv_interface_parent_class)->finalize (object); if (uv_interface->priv->usb != NULL) libusb_exit (uv_interface->priv->usb); } static void arv_uv_interface_class_init (ArvUvInterfaceClass *uv_interface_class) { GObjectClass *object_class = G_OBJECT_CLASS (uv_interface_class); ArvInterfaceClass *interface_class = ARV_INTERFACE_CLASS (uv_interface_class); object_class->finalize = arv_uv_interface_finalize; interface_class->update_device_list = arv_uv_interface_update_device_list; interface_class->open_device = arv_uv_interface_open_device; interface_class->protocol = "USB3Vision"; } aravis-0.8.34/src/arvuvinterface.h000066400000000000000000000025661475431451200170760ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_INTERFACE_H #define ARV_UV_INTERFACE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_UV_INTERFACE (arv_uv_interface_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvUvInterface, arv_uv_interface, ARV, UV_INTERFACE, ArvInterface) ARV_API ArvInterface * arv_uv_interface_get_instance (void); G_END_DECLS #endif aravis-0.8.34/src/arvuvinterfaceprivate.h000066400000000000000000000032141475431451200204600ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_INTERFACE_PRIVATE_H #define ARV_UV_INTERFACE_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #define ARV_UV_INTERFACE_DEVICE_CLASS 0xef /* Miscellaneous device */ #define ARV_UV_INTERFACE_DEVICE_SUBCLASS 0x02 #define ARV_UV_INTERFACE_DEVICE_PROTOCOL 0x01 #define ARV_UV_INTERFACE_INTERFACE_CLASS 0xef #define ARV_UV_INTERFACE_INTERFACE_SUBCLASS 0x05 #define ARV_UV_INTERFACE_CONTROL_PROTOCOL 0x00 #define ARV_UV_INTERFACE_EVENT_PROTOCOL 0x01 #define ARV_UV_INTERFACE_DATA_PROTOCOL 0x02 #define ARV_UV_INTERFACE_GUID_INDEX_OFFSET 11 G_BEGIN_DECLS void arv_uv_interface_destroy_instance (void); G_END_DECLS #endif aravis-0.8.34/src/arvuvsp.c000066400000000000000000000103531475431451200155440ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include /* * SECTION: arvuvsp * @short_description: USB3Vision stream packet handling */ /** * arv_uvsp_packet_to_string: * @packet: a #ArvUvspPacket * * Converts @packet into a human readable string. * * return value: (transfer full): A newly allocated string. */ char * arv_uvsp_packet_to_string (const ArvUvspPacket *packet) { ArvUvspLeader *leader = (ArvUvspLeader *) packet; ArvUvspTrailer *trailer = (ArvUvspTrailer *) packet; GString *string; g_return_val_if_fail (packet != NULL, NULL); string = g_string_new (""); switch (GUINT32_FROM_LE (packet->header.magic)) { case ARV_UVSP_LEADER_MAGIC: g_string_append (string, "packet_type = leader\n"); g_string_append_printf (string, "size = %d\n", GUINT16_FROM_LE (packet->header.size)); g_string_append_printf (string, "frame id = %" G_GUINT64_FORMAT "\n", GUINT64_FROM_LE (packet->header.frame_id)); switch (GUINT16_FROM_LE (leader->infos.payload_type)) { case ARV_BUFFER_PAYLOAD_TYPE_NO_DATA: g_string_append (string, "payload_type = no data\n"); break; case ARV_BUFFER_PAYLOAD_TYPE_IMAGE: g_string_append (string, "payload_type = image\n"); break; default: g_string_append (string, "payload_type = unknown\n"); break; } g_string_append_printf (string, "pixel format = %s\n", arv_pixel_format_to_gst_caps_string (GUINT32_FROM_LE (leader->infos.pixel_format))); g_string_append_printf (string, "width = %d\n", GUINT16_FROM_LE (leader->infos.width)); g_string_append_printf (string, "height = %d\n", GUINT16_FROM_LE (leader->infos.height)); g_string_append_printf (string, "x_offset = %d\n", GUINT16_FROM_LE (leader->infos.x_offset)); g_string_append_printf (string, "y_offset = %d", GUINT16_FROM_LE (leader->infos.y_offset)); break; case ARV_UVSP_TRAILER_MAGIC: g_string_append (string, "packet_type = trailer\n"); g_string_append_printf (string, "size = %d\n", GUINT16_FROM_LE (packet->header.size)); g_string_append_printf (string, "frame id = %" G_GUINT64_FORMAT "\n", GUINT64_FROM_LE (packet->header.frame_id)); g_string_append_printf (string, "payload_size = %" G_GUINT64_FORMAT "", GUINT64_FROM_LE (trailer->infos.payload_size)); break; default: g_string_append (string, "packet_type = image"); break; } #if 0 { size_t packet_size; packet_size = sizeof (ArvUvspHeader) + GUINT16_FROM_LE (packet->header.size); arv_g_string_append_hex_dump (string, packet, packet_size); } #endif return arv_g_string_free_and_steal(string); } /** * arv_uvsp_packet_debug: * @packet: a #ArvUvspPacket * @level: debug level * * Dumps the content of @packet if level is lower or equal to the current debug level for the sp debug category. See arv_debug_enable(). */ void arv_uvsp_packet_debug (const ArvUvspPacket *packet, ArvDebugLevel level) { char *string; if (!arv_debug_check (ARV_DEBUG_CATEGORY_SP, level)) return; string = arv_uvsp_packet_to_string (packet); switch (level) { case ARV_DEBUG_LEVEL_DEBUG: arv_debug_sp ("%s", string); break; case ARV_DEBUG_LEVEL_INFO: arv_info_sp ("%s", string); break; case ARV_DEBUG_LEVEL_WARNING: arv_warning_sp ("%s", string); break; default: break; } g_free (string); } aravis-0.8.34/src/arvuvspprivate.h000066400000000000000000000237031475431451200171470ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UVSP_PRIVATE_H #define ARV_UVSP_PRIVATE_H #include #include #include G_BEGIN_DECLS #define ARV_UVSP_LEADER_MAGIC 0x4C563355 #define ARV_UVSP_TRAILER_MAGIC 0x54563355 /** * ArvUvspPacketType: * @ARV_UVSP_PACKET_TYPE_UNKNOWN: unknown packet * @ARV_UVSP_PACKET_TYPE_LEADER: leader packet * @ARV_UVSP_PACKET_TYPE_TRAILER: trailer packet * @ARV_UVSP_PACKET_TYPE_DATA: data packet */ typedef enum { ARV_UVSP_PACKET_TYPE_UNKNOWN, ARV_UVSP_PACKET_TYPE_LEADER, ARV_UVSP_PACKET_TYPE_TRAILER, ARV_UVSP_PACKET_TYPE_DATA } ArvUvspPacketType; typedef enum { ARV_UVSP_PAYLOAD_TYPE_UNKNOWN = 0x0000, ARV_UVSP_PAYLOAD_TYPE_IMAGE = 0x0001, ARV_UVSP_PAYLOAD_TYPE_IMAGE_EXTENDED_CHUNK = 0x4001, ARV_UVSP_PAYLOAD_TYPE_CHUNK = 0x4000, ARV_UVSP_PAYLOAD_TYPE_GENDC_CONTAINER = 0x1001, ARV_UVSP_PAYLOAD_TYPE_GENDC_COMPONENT_DATA = 0x1002 } ArvUvspPayloadType; #pragma pack(push,1) typedef struct { guint32 magic; guint16 unknown0; guint16 size; guint64 frame_id; } ArvUvspHeader; typedef struct { ArvUvspHeader header; void *data; } ArvUvspPacket; typedef struct { guint16 unknown0; guint16 payload_type; guint64 timestamp; guint32 pixel_format; guint32 width; guint32 height; guint32 x_offset; guint32 y_offset; guint16 x_padding; guint16 unknown1; } ArvUvspLeaderInfos; typedef struct { ArvUvspHeader header; ArvUvspLeaderInfos infos; } ArvUvspLeader; typedef struct { guint32 unknown0; guint64 payload_size; } ArvUvspTrailerInfos; typedef struct { ArvUvspHeader header; ArvUvspTrailerInfos infos; } ArvUvspTrailer; typedef struct { guint32 signature; guint32 version_with_0; guint16 header_type; guint16 flags; guint32 header_size; guint64 id; guint64 variable_field_with_0; guint64 datasize; guint64 dataoffset; guint32 descriptorsize; guint32 component_count; } ArvUvspGenDCContainerHeader; typedef struct { guint16 header_type; guint16 flags; guint32 header_size; guint16 reserved; guint16 groupid; guint16 sourceid; guint16 regionid; guint32 region_offset_x; guint32 region_offset_y; guint64 timestamp; guint64 type_id; guint32 format; guint16 reserved2; guint16 part_count; } ArvUvspGenDCComponentHeader; typedef struct { guint16 header_type; guint16 flags; guint32 header_size; guint32 format; guint16 reserved; guint16 flow_id; guint64 flowoffset; guint64 datasize; guint64 dataoffset; guint32 dimension_x; guint32 dimension_y; guint16 padding_x; guint16 padding_y; guint32 info_reserved; } ArvUvspGenDCPartHeader; #pragma pack(pop) char * arv_uvsp_packet_to_string (const ArvUvspPacket *packet); void arv_uvsp_packet_debug (const ArvUvspPacket *packet, ArvDebugLevel level); static inline ArvUvspPacketType arv_uvsp_packet_get_packet_type (const ArvUvspPacket *packet) { if (packet == NULL) return ARV_UVSP_PACKET_TYPE_UNKNOWN; else if (GUINT32_FROM_LE (packet->header.magic) == ARV_UVSP_LEADER_MAGIC) return ARV_UVSP_PACKET_TYPE_LEADER; else if (GUINT32_FROM_LE (packet->header.magic) == ARV_UVSP_TRAILER_MAGIC) return ARV_UVSP_PACKET_TYPE_TRAILER; else return ARV_UVSP_PACKET_TYPE_DATA; } static inline ArvBufferPayloadType arv_uvsp_packet_get_buffer_payload_type (ArvUvspPacket *packet, gboolean *has_chunks) { ArvUvspLeader *leader; guint16 payload_type; if (packet == NULL) return ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN; leader = (ArvUvspLeader *) packet; payload_type = GUINT16_FROM_LE (leader->infos.payload_type); if (has_chunks != NULL) *has_chunks = (payload_type & 0x4000) != 0; if (payload_type == ARV_UVSP_PAYLOAD_TYPE_GENDC_CONTAINER){ return ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER; } return (ArvBufferPayloadType) (payload_type & 0x3fff); } static inline guint64 arv_uvsp_packet_get_frame_id (ArvUvspPacket *packet) { if (packet == NULL) return 0; return (GUINT64_FROM_LE (packet->header.frame_id)); } static inline void arv_uvsp_packet_get_region (ArvUvspPacket *packet, guint32 *width, guint32 *height, guint32 *x_offset, guint32 *y_offset, guint32 *x_padding, guint32 *y_padding) { ArvUvspLeader *leader; if (packet == NULL) return; leader = (ArvUvspLeader *)packet; *width = GUINT32_FROM_LE (leader->infos.width); *height = GUINT32_FROM_LE (leader->infos.height); *x_offset = GUINT32_FROM_LE (leader->infos.x_offset); *y_offset = GUINT32_FROM_LE (leader->infos.y_offset); *x_padding = GUINT32_FROM_LE (leader->infos.x_padding); *y_padding = 0; } static inline ArvPixelFormat arv_uvsp_packet_get_pixel_format (ArvUvspPacket *packet) { ArvUvspLeader *leader; if (packet == NULL) return 0; leader = (ArvUvspLeader *)packet; return GUINT32_FROM_LE (leader->infos.pixel_format); } static inline guint64 arv_uvsp_packet_get_timestamp (ArvUvspPacket *packet) { ArvUvspLeader *leader; if (packet == NULL) return 0; leader = (ArvUvspLeader *)packet; return GUINT64_FROM_LE (leader->infos.timestamp); } static inline gboolean arv_uvsp_packet_is_gendc(unsigned char *packet) { ArvUvspGenDCContainerHeader *gendc_container_header; if (packet == NULL) return FALSE; gendc_container_header = (ArvUvspGenDCContainerHeader*)packet; return gendc_container_header->signature == 0x43444E47; } static inline guint64 arv_uvsp_packet_get_gendc_dataoffset(unsigned char *packet) { ArvUvspGenDCContainerHeader *gendc_container_header; if (packet == NULL) return 0; gendc_container_header = (ArvUvspGenDCContainerHeader*)packet; return gendc_container_header->dataoffset; } static inline guint32 arv_uvsp_packet_get_gendc_descriptorsize(unsigned char *packet) { ArvUvspGenDCContainerHeader *gendc_container_header; if (packet == NULL) return 0; gendc_container_header = (ArvUvspGenDCContainerHeader*)packet; return gendc_container_header->descriptorsize; } static inline guint64 arv_uvsp_packet_get_gendc_datasize(unsigned char *packet) { ArvUvspGenDCContainerHeader *gendc_container_header; if (packet == NULL) return 0; gendc_container_header = (ArvUvspGenDCContainerHeader*)packet; return gendc_container_header->datasize; } static inline guint32 arv_uvsp_packet_get_gendc_componentcount(unsigned char *packet) { ArvUvspGenDCContainerHeader *gendc_container_header; if (packet == NULL) return 0; gendc_container_header = (ArvUvspGenDCContainerHeader*)packet; return gendc_container_header->component_count; } static inline guint64 arv_uvsp_packet_get_gendc_componentoffset(unsigned char *packet, int ith) { guint64 ith_component_offset = 0; if ( !(ith < arv_uvsp_packet_get_gendc_componentcount(packet))) return 0; memcpy (&ith_component_offset, ((char *) packet + 56 + 8 * ith), 8); return ith_component_offset; } static inline gboolean arv_uvsp_packet_get_gendc_iscomponentvalid(unsigned char *packet) { ArvUvspGenDCComponentHeader *gendc_component_header; if (packet == NULL) return 0; gendc_component_header = (ArvUvspGenDCComponentHeader*)packet; return gendc_component_header->flags == 0; } static inline guint64 arv_uvsp_packet_get_gendc_componenttypeid(unsigned char *packet) { ArvUvspGenDCComponentHeader* gendc_component_header; if (packet == NULL) return 0; gendc_component_header = (ArvUvspGenDCComponentHeader*)packet; return gendc_component_header->type_id; } static inline guint64 arv_uvsp_packet_get_gendc_partoffset(unsigned char *packet, int jth) { guint64 jth_part_offset = 0; memcpy (&jth_part_offset, ((char *) packet + 48 + 8 * jth), 8); return jth_part_offset; } static inline guint64 arv_uvsp_packet_get_gendc_partdatapffset(unsigned char *packet){ ArvUvspGenDCPartHeader *gendc_part_header; if (packet == NULL){ return 0; } gendc_part_header = (ArvUvspGenDCPartHeader*)packet; return gendc_part_header->dataoffset; } static inline guint64 arv_uvsp_packet_get_gendc_componentpixelformat(unsigned char *packet){ ArvUvspGenDCComponentHeader* gendc_component_header; if (packet == NULL){ return 0; } gendc_component_header = (ArvUvspGenDCComponentHeader*)packet; return gendc_component_header->format; } static inline guint32 arv_uvsp_packet_get_gendc_partdimension_x(unsigned char *packet){ ArvUvspGenDCPartHeader *gendc_part_header; if (packet == NULL){ return 0; } gendc_part_header = (ArvUvspGenDCPartHeader*)packet; return gendc_part_header->dimension_x; } static inline guint32 arv_uvsp_packet_get_gendc_partdimension_y(unsigned char *packet){ ArvUvspGenDCPartHeader *gendc_part_header; if (packet == NULL){ return 0; } gendc_part_header = (ArvUvspGenDCPartHeader*)packet; return gendc_part_header->dimension_y; } static inline guint16 arv_uvsp_packet_get_gendc_partpadding_x(unsigned char *packet){ ArvUvspGenDCPartHeader *gendc_part_header; if (packet == NULL){ return 0; } gendc_part_header = (ArvUvspGenDCPartHeader*)packet; return gendc_part_header->padding_x; } static inline guint16 arv_uvsp_packet_get_gendc_partpadding_y(unsigned char *packet){ ArvUvspGenDCPartHeader *gendc_part_header; if (packet == NULL){ return 0; } gendc_part_header = (ArvUvspGenDCPartHeader*)packet; return gendc_part_header->padding_y; } G_END_DECLS #endif aravis-0.8.34/src/arvuvstream.c000066400000000000000000001431741475431451200164250ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ /** * SECTION: arvuvstream * @short_description: USB3Vision video stream */ #include #include #include #include #include #include #include #include #include #include #define ARV_UV_STREAM_MAXIMUM_TRANSFER_SIZE (1024*1024*1) #define ARV_UV_STREAM_MAXIMUM_SUBMIT_TOTAL (8*1024*1024) #define ARV_UV_STREAM_POP_INPUT_BUFFER_TIMEOUT_MS 10 #define ARV_UV_STREAM_TRANSFER_WAIT_TIMEOUT_MS 10 enum { ARV_UV_STREAM_PROPERTY_0, ARV_UV_STREAM_PROPERTY_USB_MODE } ArvUvStreamProperties; /* Acquisition thread */ typedef struct { guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; guint64 n_aborted; guint64 n_transferred_bytes; guint64 n_ignored_bytes; } ArvStreamStatistics; typedef struct { ArvStream *stream; gboolean thread_started; GMutex thread_started_mutex; GCond thread_started_cond; ArvUvDevice *uv_device; ArvStreamCallback callback; void *callback_data; size_t expected_size; size_t leader_size; size_t payload_size; guint32 payload_count; size_t transfer1_size; size_t trailer_size; gboolean cancel; /* Notification for completed transfers and cancellation */ GMutex stream_mtx; GCond stream_event; /* Statistics */ ArvStreamStatistics statistics; gint n_buffer_in_use; } ArvUvStreamThreadData; typedef struct { GThread *thread; ArvUvStreamThreadData *thread_data; ArvUvUsbMode usb_mode; guint64 sirm_address; } ArvUvStreamPrivate; struct _ArvUvStream { ArvStream stream; }; struct _ArvUvStreamClass { ArvStreamClass parent_class; }; typedef struct { ArvBuffer *buffer; ArvStream *stream; ArvStreamCallback callback; gpointer callback_data; GMutex* transfer_completed_mtx; GCond* transfer_completed_event; size_t total_payload_transferred; size_t expected_size; guint8 *leader_buffer, *trailer_buffer; int num_payload_transfers; struct libusb_transfer *leader_transfer, *trailer_transfer, **payload_transfers; guint num_submitted; gint *total_submitted_bytes; gboolean is_aborting; ArvStreamStatistics *statistics; gint *n_buffer_in_use; } ArvUvStreamBufferContext; G_DEFINE_TYPE_WITH_CODE (ArvUvStream, arv_uv_stream, ARV_TYPE_STREAM, G_ADD_PRIVATE (ArvUvStream)) static void arv_uv_stream_buffer_context_wait_transfer_completed (ArvUvStreamBufferContext* ctx, gint64 timeout_ms) { g_mutex_lock( ctx->transfer_completed_mtx ); if (timeout_ms > 0) { gint64 end_time; end_time = g_get_monotonic_time() + timeout_ms * G_TIME_SPAN_MILLISECOND; g_cond_wait_until (ctx->transfer_completed_event, ctx->transfer_completed_mtx, end_time); } else { g_cond_wait( ctx->transfer_completed_event, ctx->transfer_completed_mtx ); } g_mutex_unlock( ctx->transfer_completed_mtx ); } static void arv_uv_stream_buffer_context_notify_transfer_completed (ArvUvStreamBufferContext* ctx) { g_mutex_lock( ctx->transfer_completed_mtx ); g_cond_broadcast( ctx->transfer_completed_event ); g_mutex_unlock( ctx->transfer_completed_mtx ); } static void LIBUSB_CALL arv_uv_stream_leader_cb (struct libusb_transfer *transfer) { ArvUvStreamBufferContext *ctx = transfer->user_data; ArvUvspPacket *packet = (ArvUvspPacket*)transfer->buffer; if (ctx->buffer != NULL) { if (ctx->is_aborting) { ctx->buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; } else { switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: arv_uvsp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); if (arv_uvsp_packet_get_packet_type (packet) != ARV_UVSP_PACKET_TYPE_LEADER) { arv_warning_stream_thread ("Unexpected packet type (was expecting leader packet)"); ctx->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; break; } ctx->buffer->priv->system_timestamp_ns = g_get_real_time () * 1000LL; ctx->buffer->priv->payload_type = arv_uvsp_packet_get_buffer_payload_type (packet, &ctx->buffer->priv->has_chunks); ctx->buffer->priv->chunk_endianness = G_LITTLE_ENDIAN; if (ctx->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || ctx->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA || ctx->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER ) { arv_buffer_set_n_parts(ctx->buffer, 1); ctx->buffer->priv->parts[0].data_offset = 0; ctx->buffer->priv->parts[0].component_id = 0; ctx->buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; ctx->buffer->priv->parts[0].pixel_format = arv_uvsp_packet_get_pixel_format (packet); arv_uvsp_packet_get_region (packet, &ctx->buffer->priv->parts[0].width, &ctx->buffer->priv->parts[0].height, &ctx->buffer->priv->parts[0].x_offset, &ctx->buffer->priv->parts[0].y_offset, &ctx->buffer->priv->parts[0].x_padding, &ctx->buffer->priv->parts[0].y_padding); } ctx->buffer->priv->frame_id = arv_uvsp_packet_get_frame_id (packet); ctx->buffer->priv->timestamp_ns = arv_uvsp_packet_get_timestamp (packet); break; default: arv_warning_stream_thread ("Leader transfer failed (%s)", libusb_error_name (transfer->status)); ctx->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; break; } } } g_atomic_int_dec_and_test (&ctx->num_submitted); g_atomic_int_add (ctx->total_submitted_bytes, -transfer->length); ctx->statistics->n_transferred_bytes += transfer->length; arv_uv_stream_buffer_context_notify_transfer_completed (ctx); } static void LIBUSB_CALL arv_uv_stream_payload_cb (struct libusb_transfer *transfer) { ArvUvStreamBufferContext *ctx = transfer->user_data; int component_count; if (ctx->buffer != NULL) { if (ctx->is_aborting) { ctx->buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; } else { switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: ctx->total_payload_transferred += transfer->actual_length; if (ctx->buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER){ if(!arv_uvsp_packet_is_gendc (ctx->buffer->priv->data)){ arv_warning_sp ("Invalid GenDC Container: Signature shows %.4s which is supposed to be GNDC", ctx->buffer->priv->data); }else{ ctx->buffer->priv->has_gendc = TRUE; ctx->buffer->priv->gendc_data_offset = arv_uvsp_packet_get_gendc_dataoffset(ctx->buffer->priv->data); ctx->buffer->priv->gendc_descriptor_size = arv_uvsp_packet_get_gendc_descriptorsize(ctx->buffer->priv->data); ctx->buffer->priv->gendc_data_size = arv_uvsp_packet_get_gendc_datasize(ctx->buffer->priv->data); component_count = (int) arv_uvsp_packet_get_gendc_componentcount(ctx->buffer->priv->data); for(int ith_component = 0; ith_component < component_count; ++ith_component){ int64_t ith_component_offset = arv_uvsp_packet_get_gendc_componentoffset(ctx->buffer->priv->data, ith_component); if (arv_uvsp_packet_get_gendc_iscomponentvalid(ctx->buffer->priv->data + ith_component_offset) && arv_uvsp_packet_get_gendc_componenttypeid(ctx->buffer->priv->data + ith_component_offset) == 0x1 ){ guint64 partoffset = arv_uvsp_packet_get_gendc_partoffset(ctx->buffer->priv->data + ith_component_offset, 0); ctx->buffer->priv->parts[0].data_offset = arv_uvsp_packet_get_gendc_partdatapffset(ctx->buffer->priv->data + partoffset); ctx->buffer->priv->parts[0].component_id = ith_component; ctx->buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; ctx->buffer->priv->parts[0].pixel_format = arv_uvsp_packet_get_gendc_componentpixelformat(ctx->buffer->priv->data + ith_component_offset); ctx->buffer->priv->parts[0].width = arv_uvsp_packet_get_gendc_partdimension_x(ctx->buffer->priv->data + partoffset); ctx->buffer->priv->parts[0].width = arv_uvsp_packet_get_gendc_partdimension_y(ctx->buffer->priv->data + partoffset); ctx->buffer->priv->parts[0].x_offset = 0; ctx->buffer->priv->parts[0].y_offset = 0; ctx->buffer->priv->parts[0].x_padding = arv_uvsp_packet_get_gendc_partpadding_x(ctx->buffer->priv->data + partoffset); ctx->buffer->priv->parts[0].y_padding = arv_uvsp_packet_get_gendc_partpadding_y(ctx->buffer->priv->data + partoffset); break; } } } } break; default: arv_warning_stream_thread ("Payload transfer failed (%s)", libusb_error_name (transfer->status)); ctx->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; break; } } } g_atomic_int_dec_and_test( &ctx->num_submitted ); g_atomic_int_add (ctx->total_submitted_bytes, -transfer->length); ctx->statistics->n_transferred_bytes += transfer->length; arv_uv_stream_buffer_context_notify_transfer_completed (ctx); } static void LIBUSB_CALL arv_uv_stream_trailer_cb (struct libusb_transfer *transfer) { ArvUvStreamBufferContext *ctx = transfer->user_data; ArvUvspPacket *packet = (ArvUvspPacket*)transfer->buffer; if (ctx->buffer != NULL) { if (ctx->is_aborting) { ctx->buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; ctx->statistics->n_aborted += 1; } else { switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: arv_uvsp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); if (arv_uvsp_packet_get_packet_type (packet) != ARV_UVSP_PACKET_TYPE_TRAILER) { arv_warning_stream_thread ("Unexpected packet type (was expecting trailer packet)"); ctx->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; break; } arv_debug_stream_thread ("Total payload: %zu bytes", ctx->total_payload_transferred); if (ctx->total_payload_transferred != ctx->expected_size) { arv_warning_stream_thread ("Unexpected total payload size (received %" G_GSIZE_FORMAT " - expected %" G_GSIZE_FORMAT")", ctx->total_payload_transferred, ctx->expected_size); ctx->buffer->priv->status = ARV_BUFFER_STATUS_SIZE_MISMATCH; break; } break; default: arv_warning_stream_thread ("Trailer transfer failed (%s)", libusb_error_name(transfer->status)); ctx->buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; break; } switch (ctx->buffer->priv->status) { case ARV_BUFFER_STATUS_FILLING: ctx->buffer->priv->status = ARV_BUFFER_STATUS_SUCCESS; ctx->buffer->priv->received_size = ctx->total_payload_transferred; ctx->buffer->priv->parts[0].size = ctx->total_payload_transferred; ctx->statistics->n_completed_buffers += 1; break; default: ctx->statistics->n_failures += 1; break; } } arv_stream_push_output_buffer (ctx->stream, ctx->buffer); if (ctx->callback != NULL) ctx->callback (ctx->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, ctx->buffer); g_atomic_int_dec_and_test(ctx->n_buffer_in_use); ctx->buffer = NULL; } g_atomic_int_dec_and_test( &ctx->num_submitted ); g_atomic_int_add (ctx->total_submitted_bytes, -transfer->length); ctx->statistics->n_transferred_bytes += transfer->length; arv_uv_stream_buffer_context_notify_transfer_completed (ctx); } static ArvUvStreamBufferContext* arv_uv_stream_buffer_context_new (ArvBuffer *buffer, ArvUvStreamThreadData *thread_data, gint *total_submitted_bytes) { ArvUvStreamBufferContext* ctx = g_malloc0 (sizeof(ArvUvStreamBufferContext)); int i; size_t offset = 0; ctx->buffer = NULL; ctx->stream = thread_data->stream; ctx->callback = thread_data->callback; ctx->callback_data = thread_data->callback_data; ctx->transfer_completed_mtx = &thread_data->stream_mtx; ctx->transfer_completed_event = &thread_data->stream_event; ctx->n_buffer_in_use = &thread_data->n_buffer_in_use; ctx->leader_buffer = g_malloc (thread_data->leader_size); ctx->leader_transfer = libusb_alloc_transfer (0); arv_uv_device_fill_bulk_transfer (ctx->leader_transfer, thread_data->uv_device, ARV_UV_ENDPOINT_DATA, LIBUSB_ENDPOINT_IN, ctx->leader_buffer, thread_data->leader_size, arv_uv_stream_leader_cb, ctx, 0); ctx->num_payload_transfers = (buffer->priv->allocated_size - 1) / thread_data->payload_size + 1; ctx->payload_transfers = g_malloc (ctx->num_payload_transfers * sizeof(struct libusb_transfer*)); for (i = 0; i < ctx->num_payload_transfers; ++i) { size_t size = MIN (thread_data->payload_size, buffer->priv->allocated_size - offset); ctx->payload_transfers[i] = libusb_alloc_transfer(0); arv_uv_device_fill_bulk_transfer (ctx->payload_transfers[i], thread_data->uv_device, ARV_UV_ENDPOINT_DATA, LIBUSB_ENDPOINT_IN, buffer->priv->data + offset, size, arv_uv_stream_payload_cb, ctx, 0); offset += size; } ctx->trailer_buffer = g_malloc (thread_data->trailer_size); ctx->trailer_transfer = libusb_alloc_transfer (0); arv_uv_device_fill_bulk_transfer (ctx->trailer_transfer, thread_data->uv_device, ARV_UV_ENDPOINT_DATA, LIBUSB_ENDPOINT_IN, ctx->trailer_buffer, thread_data->trailer_size, arv_uv_stream_trailer_cb, ctx, 0); ctx->num_submitted = 0; ctx->total_submitted_bytes = total_submitted_bytes; ctx->statistics = &thread_data->statistics; return ctx; } static void arv_uv_stream_buffer_context_free (gpointer data) { ArvUvStreamBufferContext* ctx = data; int i; g_return_if_fail (ctx->num_submitted == 0); libusb_free_transfer (ctx->leader_transfer); for (i = 0; i < ctx->num_payload_transfers; ++i) { libusb_free_transfer (ctx->payload_transfers[i]); } libusb_free_transfer (ctx->trailer_transfer ); g_free (ctx->leader_buffer); g_free (ctx->payload_transfers); g_free (ctx->trailer_buffer); if (ctx->buffer != NULL) { ctx->buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; arv_stream_push_output_buffer (ctx->stream, ctx->buffer); if (ctx->callback != NULL) ctx->callback (ctx->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, ctx->buffer); g_atomic_int_dec_and_test(ctx->n_buffer_in_use); ctx->buffer = NULL; } g_free (ctx); } static void _submit_transfer (ArvUvStreamBufferContext* ctx, struct libusb_transfer* transfer, gboolean* cancel) { while (!g_atomic_int_get (cancel) && ((g_atomic_int_get(ctx->total_submitted_bytes) + transfer->length) > ARV_UV_STREAM_MAXIMUM_SUBMIT_TOTAL)) { arv_uv_stream_buffer_context_wait_transfer_completed (ctx, ARV_UV_STREAM_TRANSFER_WAIT_TIMEOUT_MS); } while (!g_atomic_int_get (cancel)) { int status = libusb_submit_transfer (transfer); switch (status) { case LIBUSB_SUCCESS: g_atomic_int_inc (&ctx->num_submitted); g_atomic_int_add (ctx->total_submitted_bytes, transfer->length); return; case LIBUSB_ERROR_IO: /* * arv_debug_stream_thread ("libusb_submit_transfer failed (%d)", status); * * The kernel USB memory buffer limit has been reached (default 16MBytes) * * In order to allow more memory to be used for submitted buffers, increase usbfs_memory_mb: * sudo modprobe usbcore usbfs_memory_mb=1000 */ arv_uv_stream_buffer_context_wait_transfer_completed (ctx, ARV_UV_STREAM_TRANSFER_WAIT_TIMEOUT_MS); break; default: arv_warning_stream_thread ("libusb_submit_transfer failed (%d)", status); return; } } } static void arv_uv_stream_buffer_context_submit (ArvUvStreamBufferContext* ctx, ArvBuffer *buffer, ArvUvStreamThreadData *thread_data) { int i; if (ctx->callback != NULL) ctx->callback (ctx->callback_data, ARV_STREAM_CALLBACK_TYPE_START_BUFFER, buffer); ctx->buffer = buffer; ctx->total_payload_transferred = 0; buffer->priv->status = ARV_BUFFER_STATUS_FILLING; ctx->expected_size = thread_data->expected_size; _submit_transfer (ctx, ctx->leader_transfer, &thread_data->cancel); for (i = 0; i < ctx->num_payload_transfers; ++i) { _submit_transfer (ctx, ctx->payload_transfers[i], &thread_data->cancel); } _submit_transfer (ctx, ctx->trailer_transfer, &thread_data->cancel); } static void arv_uv_stream_buffer_context_cancel (gpointer key, gpointer value, gpointer user_data) { ArvUvStreamBufferContext* ctx = value; int i; ctx->is_aborting = TRUE; libusb_cancel_transfer (ctx->leader_transfer ); for (i = 0; i < ctx->num_payload_transfers; ++i) { libusb_cancel_transfer (ctx->payload_transfers[i]); } libusb_cancel_transfer (ctx->trailer_transfer); while (ctx->num_submitted > 0) { arv_uv_stream_buffer_context_wait_transfer_completed (ctx, ARV_UV_STREAM_TRANSFER_WAIT_TIMEOUT_MS); } } static void * arv_uv_stream_thread_async (void *data) { ArvUvStreamThreadData *thread_data = data; ArvBuffer *buffer = NULL; GHashTable *ctx_lookup; gint total_submitted_bytes = 0; arv_info_stream_thread ("Start async USB3Vision stream thread"); arv_debug_stream_thread ("leader_size = %zu", thread_data->leader_size ); arv_debug_stream_thread ("payload_size = %zu", thread_data->payload_size ); arv_debug_stream_thread ("trailer_size = %zu", thread_data->trailer_size ); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_INIT, NULL); ctx_lookup = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, arv_uv_stream_buffer_context_free ); g_mutex_lock (&thread_data->thread_started_mutex); thread_data->thread_started = TRUE; g_cond_signal (&thread_data->thread_started_cond); g_mutex_unlock (&thread_data->thread_started_mutex); while (!g_atomic_int_get (&thread_data->cancel) && arv_uv_device_is_connected (thread_data->uv_device)) { ArvUvStreamBufferContext* ctx; buffer = arv_stream_timeout_pop_input_buffer (thread_data->stream, ARV_UV_STREAM_POP_INPUT_BUFFER_TIMEOUT_MS * 1000); if( buffer == NULL ) { if (thread_data->n_buffer_in_use == 0) thread_data->statistics.n_underruns += 1; /* NOTE: n_ignored_bytes is not accumulated because it doesn't submit next USB transfer if * buffer is shortage. It means back pressure might be hanlded by USB slave side. */ continue; } else { g_atomic_int_inc(&thread_data->n_buffer_in_use); } ctx = g_hash_table_lookup( ctx_lookup, buffer ); if (!ctx) { arv_debug_stream_thread ("Stream buffer context not found for buffer %p, creating...", buffer); ctx = arv_uv_stream_buffer_context_new (buffer, thread_data, &total_submitted_bytes); g_hash_table_insert (ctx_lookup, buffer, ctx); } arv_uv_stream_buffer_context_submit (ctx, buffer, thread_data); } g_hash_table_foreach (ctx_lookup, arv_uv_stream_buffer_context_cancel, NULL); g_hash_table_destroy (ctx_lookup); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_EXIT, NULL); arv_info_stream_thread ("Stop USB3Vision async stream thread"); return NULL; } static void * arv_uv_stream_thread_sync (void *data) { ArvUvStreamThreadData *thread_data = data; ArvUvspPacket *packet; ArvBuffer *buffer = NULL; void *incoming_buffer; guint64 offset; size_t transferred; int component_count; arv_info_stream_thread ("Start sync USB3Vision stream thread"); incoming_buffer = g_malloc (ARV_UV_STREAM_MAXIMUM_TRANSFER_SIZE); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_INIT, NULL); offset = 0; g_mutex_lock (&thread_data->thread_started_mutex); thread_data->thread_started = TRUE; g_cond_signal (&thread_data->thread_started_cond); g_mutex_unlock (&thread_data->thread_started_mutex); while (!g_atomic_int_get (&thread_data->cancel)) { GError *error = NULL; size_t size; transferred = 0; if (buffer == NULL) size = ARV_UV_STREAM_MAXIMUM_TRANSFER_SIZE; else { if (offset < buffer->priv->allocated_size) size = MIN (thread_data->payload_size, buffer->priv->allocated_size - offset); else size = thread_data->trailer_size; } /* Avoid unnecessary memory copy by transferring data directly to the image buffer */ if (buffer != NULL && buffer->priv->status == ARV_BUFFER_STATUS_FILLING && offset + size <= buffer->priv->allocated_size) packet = (ArvUvspPacket *) (buffer->priv->data + offset); else packet = incoming_buffer; arv_debug_sp ("Asking for %" G_GSIZE_FORMAT " bytes", size); arv_uv_device_bulk_transfer (thread_data->uv_device, ARV_UV_ENDPOINT_DATA, LIBUSB_ENDPOINT_IN, packet, size, &transferred, 0, &error); if (error != NULL) { arv_warning_sp ("USB transfer error: %s", error->message); g_clear_error (&error); } else if (transferred < 1) { arv_warning_sp ("No data transferred"); } else { ArvUvspPacketType packet_type; arv_debug_sp ("Received %" G_GSIZE_FORMAT " bytes", transferred); arv_uvsp_packet_debug (packet, ARV_DEBUG_LEVEL_DEBUG); packet_type = arv_uvsp_packet_get_packet_type (packet); switch (packet_type) { case ARV_UVSP_PACKET_TYPE_LEADER: if (buffer != NULL) { arv_info_stream_thread ("New leader received while a buffer is still open"); buffer->priv->status = ARV_BUFFER_STATUS_MISSING_PACKETS; arv_stream_push_output_buffer (thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); thread_data->statistics.n_failures++; g_atomic_int_dec_and_test(&thread_data->n_buffer_in_use); buffer = NULL; } buffer = arv_stream_pop_input_buffer (thread_data->stream); if (buffer != NULL) { g_atomic_int_inc(&thread_data->n_buffer_in_use); buffer->priv->system_timestamp_ns = g_get_real_time () * 1000LL; buffer->priv->status = ARV_BUFFER_STATUS_FILLING; buffer->priv->received_size = 0; buffer->priv->payload_type = arv_uvsp_packet_get_buffer_payload_type (packet, &buffer->priv->has_chunks); buffer->priv->chunk_endianness = G_LITTLE_ENDIAN; if (buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_IMAGE || buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA || buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER) { arv_buffer_set_n_parts(buffer, 1); buffer->priv->parts[0].data_offset = 0; buffer->priv->parts[0].component_id = 0; buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; buffer->priv->parts[0].pixel_format = arv_uvsp_packet_get_pixel_format (packet); arv_uvsp_packet_get_region (packet, &buffer->priv->parts[0].width, &buffer->priv->parts[0].height, &buffer->priv->parts[0].x_offset, &buffer->priv->parts[0].y_offset, &buffer->priv->parts[0].x_padding, &buffer->priv->parts[0].y_padding); } buffer->priv->frame_id = arv_uvsp_packet_get_frame_id (packet); buffer->priv->timestamp_ns = arv_uvsp_packet_get_timestamp (packet); offset = 0; if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_START_BUFFER, NULL); thread_data->statistics.n_transferred_bytes += transferred; } else { thread_data->statistics.n_underruns++; thread_data->statistics.n_ignored_bytes += transferred; } break; case ARV_UVSP_PACKET_TYPE_TRAILER: if (buffer != NULL) { arv_debug_stream_thread ("Received %" G_GUINT64_FORMAT " bytes", offset); if (offset != thread_data->expected_size) { arv_info_stream_thread ("Incomplete image received, dropping " "(received %" G_GUINT64_FORMAT " / expected %" G_GSIZE_FORMAT ")", offset, thread_data->expected_size); buffer->priv->status = ARV_BUFFER_STATUS_SIZE_MISMATCH; arv_stream_push_output_buffer (thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); thread_data->statistics.n_failures++; thread_data->statistics.n_ignored_bytes += transferred; g_atomic_int_dec_and_test(&thread_data->n_buffer_in_use); buffer = NULL; } else { buffer->priv->status = ARV_BUFFER_STATUS_SUCCESS; buffer->priv->received_size = offset; buffer->priv->parts[0].size = offset; arv_stream_push_output_buffer (thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); thread_data->statistics.n_completed_buffers++; thread_data->statistics.n_transferred_bytes += transferred; g_atomic_int_dec_and_test(&thread_data->n_buffer_in_use); buffer = NULL; } } break; case ARV_UVSP_PACKET_TYPE_DATA: if (buffer != NULL && buffer->priv->status == ARV_BUFFER_STATUS_FILLING) { if (offset + transferred <= buffer->priv->allocated_size) { if (packet == incoming_buffer) memcpy (((char *) buffer->priv->data) + offset, packet, transferred); offset += transferred; thread_data->statistics.n_transferred_bytes += transferred; if (buffer->priv->payload_type == ARV_BUFFER_PAYLOAD_TYPE_GENDC_CONTAINER){ if(!arv_uvsp_packet_is_gendc (buffer->priv->data)){ arv_warning_sp ("Invalid GenDC Container: Signature shows %.4s which is supposed to be GNDC", buffer->priv->data); }else{ buffer->priv->has_gendc = TRUE; buffer->priv->gendc_data_offset = arv_uvsp_packet_get_gendc_dataoffset(buffer->priv->data); buffer->priv->gendc_descriptor_size = arv_uvsp_packet_get_gendc_descriptorsize(buffer->priv->data); buffer->priv->gendc_data_size = arv_uvsp_packet_get_gendc_datasize(buffer->priv->data); component_count = (int) arv_uvsp_packet_get_gendc_componentcount(buffer->priv->data); for(int ith_component = 0; ith_component < component_count; ++ith_component){ int64_t ith_component_offset = arv_uvsp_packet_get_gendc_componentoffset(buffer->priv->data, ith_component); // only if the component is valid and have an image data (GDC_INTENSITY from SFNC) if (arv_uvsp_packet_get_gendc_iscomponentvalid(buffer->priv->data + ith_component_offset) && arv_uvsp_packet_get_gendc_componenttypeid(buffer->priv->data + ith_component_offset) == 0x1 ){ guint64 partoffset = arv_uvsp_packet_get_gendc_partoffset(buffer->priv->data + ith_component_offset, 0); buffer->priv->parts[0].data_offset = arv_uvsp_packet_get_gendc_partdatapffset(buffer->priv->data + partoffset); buffer->priv->parts[0].component_id = ith_component; buffer->priv->parts[0].data_type = ARV_BUFFER_PART_DATA_TYPE_2D_IMAGE; buffer->priv->parts[0].pixel_format = arv_uvsp_packet_get_gendc_componentpixelformat(buffer->priv->data + ith_component_offset); buffer->priv->parts[0].width = arv_uvsp_packet_get_gendc_partdimension_x(buffer->priv->data + partoffset); buffer->priv->parts[0].width = arv_uvsp_packet_get_gendc_partdimension_y(buffer->priv->data + partoffset); buffer->priv->parts[0].x_offset = 0; buffer->priv->parts[0].y_offset = 0; buffer->priv->parts[0].x_padding = arv_uvsp_packet_get_gendc_partpadding_x(buffer->priv->data + partoffset); buffer->priv->parts[0].y_padding = arv_uvsp_packet_get_gendc_partpadding_y(buffer->priv->data + partoffset); break; } } } } } else { buffer->priv->status = ARV_BUFFER_STATUS_SIZE_MISMATCH; thread_data->statistics.n_ignored_bytes += transferred; } } else { thread_data->statistics.n_ignored_bytes += transferred; } break; default: arv_info_stream_thread ("Unknown packet type"); break; } } } if (buffer != NULL) { buffer->priv->status = ARV_BUFFER_STATUS_ABORTED; thread_data->statistics.n_aborted++; arv_stream_push_output_buffer (thread_data->stream, buffer); if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE, buffer); g_atomic_int_dec_and_test(&thread_data->n_buffer_in_use); } if (thread_data->callback != NULL) thread_data->callback (thread_data->callback_data, ARV_STREAM_CALLBACK_TYPE_EXIT, NULL); g_free (incoming_buffer); arv_info_stream_thread ("Stop USB3Vision sync stream thread"); return NULL; } /* ArvUvStream implementation */ static guint32 align (guint32 val, guint32 alignment) { /* Alignment must be a power of two, otherwise the used alignment algorithm does not work. */ g_assert (alignment > 0 && (alignment & (alignment - 1)) == 0); return (val + (alignment - 1)) & ~(alignment - 1); } static void arv_uv_stream_start_thread (ArvStream *stream) { ArvUvStream *uv_stream = ARV_UV_STREAM (stream); ArvUvStreamPrivate *priv = arv_uv_stream_get_instance_private (uv_stream); ArvUvStreamThreadData *thread_data; ArvDevice *device; GError *error = NULL; guint64 sbrm_address; guint32 si_info; guint64 si_req_payload_size; guint32 si_req_leader_size; guint32 si_req_trailer_size; guint32 si_payload_size; guint32 si_payload_count; guint32 si_transfer1_size; guint32 si_transfer2_size; guint32 si_leader_size; guint32 si_trailer_size; guint32 si_control; guint32 alignment; guint32 aligned_maximum_transfer_size; g_return_if_fail (priv->thread == NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; device = ARV_DEVICE (thread_data->uv_device); arv_device_read_memory (device, ARV_ABRM_SBRM_ADDRESS, sizeof (guint64), &sbrm_address, NULL); arv_device_read_memory (device, sbrm_address + ARV_SBRM_SIRM_ADDRESS, sizeof (guint64), &priv->sirm_address, NULL); arv_device_read_memory (device, priv->sirm_address + ARV_SIRM_INFO, sizeof (si_info), &si_info, NULL); arv_device_read_memory (device, priv->sirm_address + ARV_SIRM_REQ_PAYLOAD_SIZE, sizeof (si_req_payload_size), &si_req_payload_size, NULL); arv_device_read_memory (device, priv->sirm_address + ARV_SIRM_REQ_LEADER_SIZE, sizeof (si_req_leader_size), &si_req_leader_size, NULL); arv_device_read_memory (device, priv->sirm_address + ARV_SIRM_REQ_TRAILER_SIZE, sizeof (si_req_trailer_size), &si_req_trailer_size, NULL); alignment = 1 << ((si_info & ARV_SIRM_INFO_ALIGNMENT_MASK) >> ARV_SIRM_INFO_ALIGNMENT_SHIFT); arv_info_stream ("SIRM_INFO = 0x%08x", si_info); arv_info_stream ("SIRM_REQ_PAYLOAD_SIZE = 0x%016" G_GINT64_MODIFIER "x", si_req_payload_size); arv_info_stream ("SIRM_REQ_LEADER_SIZE = 0x%08x", si_req_leader_size); arv_info_stream ("SIRM_REQ_TRAILER_SIZE = 0x%08x", si_req_trailer_size); arv_info_stream ("Required alignment = %d", alignment); aligned_maximum_transfer_size = ARV_UV_STREAM_MAXIMUM_TRANSFER_SIZE / alignment * alignment; if (si_req_leader_size < 1) { arv_warning_stream ("Wrong SI_REQ_LEADER_SIZE value, using %d instead", aligned_maximum_transfer_size); si_leader_size = aligned_maximum_transfer_size; } else { si_leader_size = align (si_req_leader_size, alignment); } if (si_req_trailer_size < 1) { arv_warning_stream ("Wrong SI_REQ_TRAILER_SIZE value, using %d instead", aligned_maximum_transfer_size); si_trailer_size = aligned_maximum_transfer_size; } else { si_trailer_size = align (si_req_trailer_size, alignment); } si_payload_size = MIN(si_req_payload_size , aligned_maximum_transfer_size); si_payload_count= si_req_payload_size / si_payload_size; si_transfer1_size = align(si_req_payload_size % si_payload_size, alignment); si_transfer2_size = 0; arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_MAX_LEADER_SIZE, sizeof (si_leader_size), &si_leader_size, NULL); arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_MAX_TRAILER_SIZE, sizeof (si_trailer_size), &si_trailer_size, NULL); arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_PAYLOAD_SIZE, sizeof (si_payload_size), &si_payload_size, NULL); arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_PAYLOAD_COUNT, sizeof (si_payload_count), &si_payload_count, NULL); arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_TRANSFER1_SIZE, sizeof (si_transfer1_size), &si_transfer1_size, NULL); arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_TRANSFER2_SIZE, sizeof (si_transfer2_size), &si_transfer2_size, NULL); arv_info_stream ("SIRM_PAYLOAD_SIZE = 0x%08x", si_payload_size); arv_info_stream ("SIRM_PAYLOAD_COUNT = 0x%08x", si_payload_count); arv_info_stream ("SIRM_TRANSFER1_SIZE = 0x%08x", si_transfer1_size); arv_info_stream ("SIRM_TRANSFER2_SIZE = 0x%08x", si_transfer2_size); arv_info_stream ("SIRM_MAX_LEADER_SIZE = 0x%08x", si_leader_size); arv_info_stream ("SIRM_MAX_TRAILER_SIZE = 0x%08x", si_trailer_size); thread_data->expected_size = si_req_payload_size; thread_data->leader_size = si_leader_size; thread_data->payload_size = si_payload_size; thread_data->payload_count = si_payload_count; thread_data->transfer1_size = si_transfer1_size; thread_data->trailer_size = si_trailer_size; thread_data->n_buffer_in_use = 0; thread_data->cancel = FALSE; switch (priv->usb_mode) { case ARV_UV_USB_MODE_SYNC: priv->thread = g_thread_new ("arv_uv_stream", arv_uv_stream_thread_sync, priv->thread_data); break; case ARV_UV_USB_MODE_ASYNC: priv->thread = g_thread_new ("arv_uv_stream", arv_uv_stream_thread_async, priv->thread_data); break; default: g_assert_not_reached (); } g_mutex_lock (&thread_data->thread_started_mutex); while (!thread_data->thread_started) g_cond_wait (&thread_data->thread_started_cond, &thread_data->thread_started_mutex); g_mutex_unlock (&thread_data->thread_started_mutex); arv_uv_device_reset_stream_endpoint (thread_data->uv_device); si_control = ARV_SIRM_CONTROL_STREAM_ENABLE; arv_device_write_memory (device, priv->sirm_address + ARV_SIRM_CONTROL, sizeof (si_control), &si_control, &error); if (error != NULL) { arv_warning_stream ("Failed to enable stream (%s)", error->message); g_clear_error(&error); } } static void arv_uv_stream_stop_thread (ArvStream *stream) { ArvUvStream *uv_stream = ARV_UV_STREAM (stream); ArvUvStreamPrivate *priv = arv_uv_stream_get_instance_private (uv_stream); ArvUvStreamThreadData *thread_data; guint32 si_control; GError *error = NULL; g_return_if_fail (priv->thread != NULL); g_return_if_fail (priv->thread_data != NULL); thread_data = priv->thread_data; g_atomic_int_set (&priv->thread_data->cancel, TRUE); g_cond_broadcast (&priv->thread_data->stream_event); g_thread_join (priv->thread); priv->thread = NULL; si_control = 0x0; arv_device_write_memory (ARV_DEVICE (thread_data->uv_device), priv->sirm_address + ARV_SIRM_CONTROL, sizeof (si_control), &si_control, &error); if (error != NULL) { arv_warning_stream ("Failed to disable stream (%s)", error->message); g_clear_error(&error); } } /** * arv_uv_stream_new: (skip) * @uv_device: a #ArvUvDevice * @callback: (scope call): image processing callback * @callback_data: (closure): user data for @callback * @error: a #GError placeholder, %NULL to ignore * * Return Value: (transfer full): a new #ArvStream. */ ArvStream * arv_uv_stream_new (ArvUvDevice *uv_device, ArvStreamCallback callback, void *callback_data, GDestroyNotify destroy, ArvUvUsbMode usb_mode, GError **error) { return g_initable_new (ARV_TYPE_UV_STREAM, NULL, error, "device", uv_device, "callback", callback, "callback-data", callback_data, "destroy-notify", destroy, "usb-mode", usb_mode, NULL); } static void arv_uv_stream_constructed (GObject *object) { ArvUvStream *uv_stream = ARV_UV_STREAM (object); ArvStream *stream = ARV_STREAM (uv_stream); ArvUvStreamPrivate *priv = arv_uv_stream_get_instance_private (uv_stream); ArvUvStreamThreadData *thread_data; G_OBJECT_CLASS (arv_uv_stream_parent_class)->constructed (object); thread_data = g_new0 (ArvUvStreamThreadData, 1); thread_data->stream = stream; g_cond_init( &thread_data->stream_event ); g_mutex_init( &thread_data->stream_mtx ); thread_data->statistics.n_completed_buffers = 0; thread_data->statistics.n_failures = 0; thread_data->statistics.n_underruns = 0; thread_data->statistics.n_aborted = 0; thread_data->statistics.n_transferred_bytes = 0; thread_data->statistics.n_ignored_bytes = 0; g_object_get (object, "device", &thread_data->uv_device, "callback", &thread_data->callback, "callback-data", &thread_data->callback_data, NULL); priv->thread_data = thread_data; arv_stream_declare_info (ARV_STREAM (uv_stream), "n_completed_buffers", G_TYPE_UINT64, &thread_data->statistics.n_completed_buffers); arv_stream_declare_info (ARV_STREAM (uv_stream), "n_failures", G_TYPE_UINT64, &thread_data->statistics.n_failures); arv_stream_declare_info (ARV_STREAM (uv_stream), "n_underruns", G_TYPE_UINT64, &thread_data->statistics.n_underruns); arv_stream_declare_info (ARV_STREAM (uv_stream), "n_aborted", G_TYPE_UINT64, &thread_data->statistics.n_aborted); arv_stream_declare_info (ARV_STREAM (uv_stream), "n_transferred_bytes", G_TYPE_UINT64, &thread_data->statistics.n_transferred_bytes); arv_stream_declare_info (ARV_STREAM (uv_stream), "n_ignored_bytes", G_TYPE_UINT64, &thread_data->statistics.n_ignored_bytes); arv_uv_stream_start_thread (ARV_STREAM (uv_stream)); } /* ArvStream implementation */ static void arv_uv_stream_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ArvUvStreamPrivate *priv = arv_uv_stream_get_instance_private (ARV_UV_STREAM (object)); switch (prop_id) { case ARV_UV_STREAM_PROPERTY_USB_MODE: priv->usb_mode = g_value_get_enum(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void arv_uv_stream_init (ArvUvStream *uv_stream) { } static void arv_uv_stream_finalize (GObject *object) { ArvUvStream *uv_stream = ARV_UV_STREAM (object); ArvUvStreamPrivate *priv = arv_uv_stream_get_instance_private (uv_stream); arv_uv_stream_stop_thread (ARV_STREAM (uv_stream)); if (priv->thread_data != NULL) { ArvUvStreamThreadData *thread_data; thread_data = priv->thread_data; arv_info_stream ("[UvStream::finalize] n_completed_buffers = %" G_GUINT64_FORMAT, thread_data->statistics.n_completed_buffers); arv_info_stream ("[UvStream::finalize] n_failures = %" G_GUINT64_FORMAT, thread_data->statistics.n_failures); arv_info_stream ("[UvStream::finalize] n_underruns = %" G_GUINT64_FORMAT, thread_data->statistics.n_underruns); arv_info_stream ("[UvStream::finalize] n_aborted = %" G_GUINT64_FORMAT, thread_data->statistics.n_aborted); arv_info_stream ("[UvStream::finalize] n_transferred_bytes = %" G_GUINT64_FORMAT, thread_data->statistics.n_transferred_bytes); arv_info_stream ("[UvStream::finalize] n_ignored_bytes = %" G_GUINT64_FORMAT, thread_data->statistics.n_ignored_bytes); g_mutex_clear (&thread_data->stream_mtx); g_cond_clear (&thread_data->stream_event); g_clear_object (&thread_data->uv_device); g_clear_pointer (&priv->thread_data, g_free); } G_OBJECT_CLASS (arv_uv_stream_parent_class)->finalize (object); } static void arv_uv_stream_class_init (ArvUvStreamClass *uv_stream_class) { GObjectClass *object_class = G_OBJECT_CLASS (uv_stream_class); ArvStreamClass *stream_class = ARV_STREAM_CLASS (uv_stream_class); object_class->constructed = arv_uv_stream_constructed; object_class->finalize = arv_uv_stream_finalize; object_class->set_property = arv_uv_stream_set_property; stream_class->start_thread = arv_uv_stream_start_thread; stream_class->stop_thread = arv_uv_stream_stop_thread; /** * ArvUvStream:usb-mode: * * USB device I/O mode. */ g_object_class_install_property ( object_class, ARV_UV_STREAM_PROPERTY_USB_MODE, g_param_spec_enum ("usb-mode", "USB mode", "USB device I/O mode", ARV_TYPE_UV_USB_MODE, ARV_UV_USB_MODE_DEFAULT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS) ); } aravis-0.8.34/src/arvuvstream.h000066400000000000000000000024331475431451200164220ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_STREAM_H #define ARV_UV_STREAM_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_TYPE_UV_STREAM (arv_uv_stream_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvUvStream, arv_uv_stream, ARV, UV_STREAM, ArvStream) G_END_DECLS #endif aravis-0.8.34/src/arvuvstreamprivate.h000066400000000000000000000025601475431451200200160ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_UV_STREAM_PRIVATE_H #define ARV_UV_STREAM_PRIVATE_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS ArvStream * arv_uv_stream_new (ArvUvDevice *uv_device, ArvStreamCallback callback, void *user_data, GDestroyNotify destroy, ArvUvUsbMode usb_mode, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvversion.h.in000066400000000000000000000047141475431451200166520ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_VERSION_H #define ARV_VERSION_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS /** * ARAVIS_VERSION: * * The version of the Aravis library. * * Since: 0.8.0 */ #define ARAVIS_VERSION "@ARAVIS_VERSION@" /** * ARAVIS_API_VERSION: * * The version of the Aravis library API. * * Since: 0.8.20 */ #define ARAVIS_API_VERSION "@ARAVIS_API_VERSION@" /** * ARAVIS_MAJOR_VERSION: * * The major version of the Aravis library. * * Since: 0.6.0 */ #define ARAVIS_MAJOR_VERSION @ARAVIS_MAJOR_VERSION@ /** * ARAVIS_MINOR_VERSION: * * The minor version of the Aravis library. * * Since: 0.6.0 */ #define ARAVIS_MINOR_VERSION @ARAVIS_MINOR_VERSION@ /** * ARAVIS_MICRO_VERSION: * * The micor version of the Aravis library. * * Since: 0.6.0 */ #define ARAVIS_MICRO_VERSION @ARAVIS_MICRO_VERSION@ /** * ARAVIS_CHECK_VERSION: * @major: the major version to check for * @minor: the minor version to check for * @micro: the micro version to check for * * Checks the version of the Aravis library that is being compiled * against. * * Returns: %TRUE if the version of the Aravis header files * is the same as or newer than the passed-in version. * * Since: 0.6.0 */ #define ARAVIS_CHECK_VERSION(major,minor,micro) \ (ARAVIS_MAJOR_VERSION > (major) || \ (ARAVIS_MAJOR_VERSION == (major) && ARAVIS_MINOR_VERSION > (minor)) || \ (ARAVIS_MAJOR_VERSION == (major) && ARAVIS_MINOR_VERSION == (minor) && \ ARAVIS_MICRO_VERSION >= (micro))) G_END_DECLS #endif aravis-0.8.34/src/arvwakeup.c000066400000000000000000000137351475431451200160520ustar00rootroot00000000000000/* Copyright © 2011 Canonical Limited * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Ryan Lortie */ /* arvwakeup.c is special -- GIO and some test cases include it. As such, * it cannot include other glib headers without triggering the single * includes warnings. We have to manually include its dependencies here * (and at all other use sites). */ #include /*< private > * SECTION:arvwakeup * @title: ArvWakeup * @short_description: portable cross-thread event signal mechanism * * #ArvWakeup is a simple and portable way of signaling events between * different threads in a way that integrates nicely with g_poll(). * * You first create a #ArvWakeup with arv_wakeup_new() and initialise a * #GPollFD from it using arv_wakeup_get_pollfd(). Polling on the created * #GPollFD will block until arv_wakeup_signal() is called, at which point * it will immediately return. Future attempts to poll will continue to * return until arv_wakeup_acknowledge() is called. arv_wakeup_free() is * used to free a #ArvWakeup. * * On sufficiently modern Linux, this is implemented using eventfd. On * Windows it is implemented using an event handle. On other systems it * is implemented with a pair of pipes. * * Since: 0.6.2 **/ #ifdef _WIN32 #include ArvWakeup * arv_wakeup_new (void) { HANDLE wakeup; wakeup = CreateEvent (NULL, TRUE, FALSE, NULL); if (wakeup == NULL) g_error ("Cannot create event for ArvWakeup: %s", g_win32_error_message (GetLastError ())); return (ArvWakeup *) wakeup; } void arv_wakeup_get_pollfd (ArvWakeup *wakeup, GPollFD *poll_fd) { poll_fd->fd = (gintptr) wakeup; poll_fd->events = G_IO_IN; } void arv_wakeup_acknowledge (ArvWakeup *wakeup) { ResetEvent ((HANDLE) wakeup); } void arv_wakeup_signal (ArvWakeup *wakeup) { SetEvent ((HANDLE) wakeup); } void arv_wakeup_free (ArvWakeup *wakeup) { CloseHandle ((HANDLE) wakeup); } #else #include "glib-unix.h" #include #if defined (HAVE_EVENTFD) #include #endif struct _ArvWakeup { gint fds[2]; }; /** * arv_wakeup_new: * * Creates a new #ArvWakeup. * * You should use arv_wakeup_free() to free it when you are done. * * Returns: a new #ArvWakeup * * Since: 0.6.2 **/ ArvWakeup * arv_wakeup_new (void) { GError *error = NULL; ArvWakeup *wakeup; wakeup = g_slice_new (ArvWakeup); /* try eventfd first, if we think we can */ #if defined (HAVE_EVENTFD) #ifndef TEST_EVENTFD_FALLBACK wakeup->fds[0] = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK); #else wakeup->fds[0] = -1; #endif if (wakeup->fds[0] != -1) { wakeup->fds[1] = -1; return wakeup; } /* for any failure, try a pipe instead */ #endif if (!g_unix_open_pipe (wakeup->fds, FD_CLOEXEC, &error)) g_error ("Creating pipes for ArvWakeup: %s\n", error->message); if (!g_unix_set_fd_nonblocking (wakeup->fds[0], TRUE, &error) || !g_unix_set_fd_nonblocking (wakeup->fds[1], TRUE, &error)) g_error ("Set pipes non-blocking for ArvWakeup: %s\n", error->message); return wakeup; } /** * arv_wakeup_get_pollfd: * @wakeup: a #ArvWakeup * @poll_fd: a #GPollFD * * Prepares a @poll_fd such that polling on it will succeed when * arv_wakeup_signal() has been called on @wakeup. * * @poll_fd is valid until @wakeup is freed. * * Since: 0.6.2 **/ void arv_wakeup_get_pollfd (ArvWakeup *wakeup, GPollFD *poll_fd) { poll_fd->fd = wakeup->fds[0]; poll_fd->events = G_IO_IN; } /** * arv_wakeup_acknowledge: * @wakeup: a #ArvWakeup * * Acknowledges receipt of a wakeup signal on @wakeup. * * You must call this after @wakeup polls as ready. If not, it will * continue to poll as ready until you do so. * * If you call this function and @wakeup is not signaled, nothing * happens. * * Since: 0.6.2 **/ void arv_wakeup_acknowledge (ArvWakeup *wakeup) { char buffer[16]; /* read until it is empty */ while (read (wakeup->fds[0], buffer, sizeof buffer) == sizeof buffer); } /** * arv_wakeup_signal: * @wakeup: a #ArvWakeup * * Signals @wakeup. * * Any future (or present) polling on the #GPollFD returned by * arv_wakeup_get_pollfd() will immediately succeed until such a time as * arv_wakeup_acknowledge() is called. * * This function is safe to call from a UNIX signal handler. * * Since: 0.6.2 **/ void arv_wakeup_signal (ArvWakeup *wakeup) { int res; if (wakeup->fds[1] == -1) { guint64 one = 1; /* eventfd() case. It requires a 64-bit counter increment value to be * written. */ do res = write (wakeup->fds[0], &one, sizeof one); while (G_UNLIKELY (res == -1 && errno == EINTR)); } else { guint8 one = 1; /* Non-eventfd() case. Only a single byte needs to be written, and it can * have an arbitrary value. */ do res = write (wakeup->fds[1], &one, sizeof one); while (G_UNLIKELY (res == -1 && errno == EINTR)); } } /** * arv_wakeup_free: * @wakeup: a #ArvWakeup * * Frees @wakeup. * * You must not currently be polling on the #GPollFD returned by * arv_wakeup_get_pollfd(), or the result is undefined. **/ void arv_wakeup_free (ArvWakeup *wakeup) { close (wakeup->fds[0]); if (wakeup->fds[1] != -1) close (wakeup->fds[1]); g_slice_free (ArvWakeup, wakeup); } #endif /* !_WIN32 */ aravis-0.8.34/src/arvwakeupprivate.h000066400000000000000000000023551475431451200174460ustar00rootroot00000000000000/* Copyright © 2011 Canonical Limited * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Ryan Lortie */ #ifndef ARV_WAKEUP_PRIVATE_H #define ARV_WAKEUP_PRIVATE_H #include typedef struct _ArvWakeup ArvWakeup; ArvWakeup * arv_wakeup_new (void); void arv_wakeup_free (ArvWakeup *wakeup); void arv_wakeup_get_pollfd (ArvWakeup *wakeup, GPollFD *poll_fd); void arv_wakeup_signal (ArvWakeup *wakeup); void arv_wakeup_acknowledge (ArvWakeup *wakeup); #endif aravis-0.8.34/src/arvxmlschema.c000066400000000000000000000135211475431451200165300ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ /** * SECTION: arvxmlschema * @short_description: XML Schema storage */ #include #include #include #include #include #include #include #include typedef struct { char *xsd; size_t xsd_size; xmlSchemaParserCtxtPtr parser_ctxt; xmlSchemaPtr schema; xmlSchemaValidCtxtPtr valid_ctxt; } ArvXmlSchemaPrivate; struct _ArvXmlSchema { GObject parent_instance; ArvXmlSchemaPrivate *priv; }; struct _ArvXmlSchemaClass { GObjectClass parent_class; }; G_DEFINE_TYPE_WITH_CODE (ArvXmlSchema, arv_xml_schema, G_TYPE_OBJECT, G_ADD_PRIVATE (ArvXmlSchema)) GQuark arv_xml_schema_error_quark (void) { return g_quark_from_static_string ("arv-xml-schema-error-quark"); } typedef struct { int line; int column; GError **error; } XmlSchemaError; static void #if LIBXML_VERSION >= 21200 _structured_error_handler (void *ctx, const xmlError* error) #else _structured_error_handler (void *ctx, xmlErrorPtr error) #endif { XmlSchemaError *schema_error = ctx; if (schema_error->error == NULL || *(schema_error->error) != NULL) return; schema_error->line = error->line; schema_error->column = error->int2; g_set_error_literal (schema_error->error, ARV_XML_SCHEMA_ERROR, ARV_XML_SCHEMA_ERROR_INVALID_STRUCTURE, error->message); } gboolean arv_xml_schema_validate (ArvXmlSchema *schema, const void *xml, size_t size, int *line, int *column, GError **error) { xmlDocPtr xml_doc; int result; static GMutex mutex; XmlSchemaError schema_error = {0, 0, error}; g_return_val_if_fail (ARV_IS_XML_SCHEMA (schema), FALSE); g_return_val_if_fail (xml != NULL && size > 0, FALSE); g_return_val_if_fail (schema->priv->valid_ctxt != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_mutex_lock (&mutex); xmlSetStructuredErrorFunc (&schema_error, _structured_error_handler); xml_doc = xmlParseMemory (xml, size); if (xml_doc != NULL) { xmlSchemaSetValidStructuredErrors (schema->priv->valid_ctxt, _structured_error_handler, &schema_error); xmlSchemaSetParserStructuredErrors (schema->priv->parser_ctxt, _structured_error_handler, &schema_error); result = xmlSchemaValidateDoc (schema->priv->valid_ctxt, xml_doc) == 0; xmlFreeDoc (xml_doc); } else result = FALSE; if (line != NULL) *line = schema_error.line; if (column != NULL) *column = schema_error.column; g_mutex_unlock (&mutex); return result; } ArvXmlSchema * arv_xml_schema_new_from_memory (const char *buffer, size_t size) { ArvXmlSchema *schema; g_return_val_if_fail (buffer != NULL, NULL); g_return_val_if_fail (size > 0, NULL); schema = g_object_new (ARV_TYPE_XML_SCHEMA, NULL); schema->priv->xsd = arv_memdup (buffer, size); schema->priv->xsd_size = size; schema->priv->parser_ctxt = xmlSchemaNewMemParserCtxt(schema->priv->xsd, schema->priv->xsd_size); if (schema->priv->parser_ctxt != NULL) schema->priv->schema = xmlSchemaParse (schema->priv->parser_ctxt); if (schema->priv->schema != NULL) schema->priv->valid_ctxt = xmlSchemaNewValidCtxt (schema->priv->schema); else arv_warning_dom ("[XmlSchema::new_from_memory] Invalid xsd data"); return schema; } ArvXmlSchema * arv_xml_schema_new_from_file (GFile *file) { ArvXmlSchema *schema; schema = g_object_new (ARV_TYPE_XML_SCHEMA, NULL); if (G_IS_FILE (file)) g_file_load_contents (file, NULL, &schema->priv->xsd, &schema->priv->xsd_size, NULL, NULL); if (schema->priv->xsd != NULL) schema->priv->parser_ctxt = xmlSchemaNewMemParserCtxt(schema->priv->xsd, schema->priv->xsd_size); if (schema->priv->parser_ctxt != NULL) schema->priv->schema = xmlSchemaParse (schema->priv->parser_ctxt); if (schema->priv->schema != NULL) schema->priv->valid_ctxt = xmlSchemaNewValidCtxt (schema->priv->schema); else { char *uri = g_file_get_uri (file); arv_warning_dom ("[XmlSchema::new_from_file] Invalid xsd file '%s'", uri); g_free (uri); } return schema; } ArvXmlSchema * arv_xml_schema_new_from_path (const char *path) { ArvXmlSchema *schema; GFile *file; file = g_file_new_for_path (path); schema = arv_xml_schema_new_from_file (file); g_object_unref (file); return schema; } static void arv_xml_schema_init (ArvXmlSchema *self) { self->priv = arv_xml_schema_get_instance_private (self); } static void _finalize (GObject *object) { ArvXmlSchema *schema = ARV_XML_SCHEMA (object); g_clear_pointer (&schema->priv->valid_ctxt, xmlSchemaFreeValidCtxt); g_clear_pointer (&schema->priv->schema, xmlSchemaFree); g_clear_pointer (&schema->priv->parser_ctxt, xmlSchemaFreeParserCtxt); g_clear_pointer (&schema->priv->xsd, g_free); G_OBJECT_CLASS (arv_xml_schema_parent_class)->finalize (object); } static void arv_xml_schema_class_init (ArvXmlSchemaClass *this_class) { GObjectClass *gobject_class = G_OBJECT_CLASS (this_class); gobject_class->finalize = _finalize; #if LIBXML_VERSION < 21200 xmlLineNumbersDefault (1); #endif } aravis-0.8.34/src/arvxmlschema.h000066400000000000000000000036661475431451200165460ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: * Emmanuel Pacaud */ #ifndef ARV_XML_SCHEMA_H #define ARV_XML_SCHEMA_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define ARV_XML_SCHEMA_ERROR arv_xml_schema_error_quark() ARV_API GQuark arv_xml_schema_error_quark (void); /** * ArvXmlSchemaError: * @ARV_XML_SCHEMA_ERROR_INVALID_STRUCTURE: invalid structure */ typedef enum { ARV_XML_SCHEMA_ERROR_INVALID_STRUCTURE } ArvXmlSchemaError; #define ARV_TYPE_XML_SCHEMA (arv_xml_schema_get_type ()) ARV_API G_DECLARE_FINAL_TYPE (ArvXmlSchema, arv_xml_schema, ARV, XML_SCHEMA, GObject) ARV_API ArvXmlSchema * arv_xml_schema_new_from_memory (const char *buffer, size_t size); ARV_API ArvXmlSchema * arv_xml_schema_new_from_file (GFile *file); ARV_API ArvXmlSchema * arv_xml_schema_new_from_path (const char *path); ARV_API gboolean arv_xml_schema_validate (ArvXmlSchema *schema, const void *xml, size_t size, int *line, int *column, GError **error); G_END_DECLS #endif aravis-0.8.34/src/arvzip.c000066400000000000000000000166351475431451200153620ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud * * http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * Heavily inspired by the Imposter code from Gürer Özen . * */ /** * SECTION: arvzip * @short_description: A simple zip extractor */ #include #include #include #include struct _ArvZipFile { char *name; size_t compressed_size; size_t uncompressed_size; ptrdiff_t offset; }; const char * arv_zip_file_get_name (ArvZipFile *zip_file) { g_return_val_if_fail (zip_file != NULL, NULL); return zip_file->name; } size_t arv_zip_file_get_uncompressed_size (ArvZipFile *zip_file) { g_return_val_if_fail (zip_file != NULL, 0); return zip_file->uncompressed_size; } struct _ArvZip { const unsigned char *buffer; size_t buffer_size; GSList *files; size_t header_size; ptrdiff_t directory_position; size_t directory_size; ptrdiff_t directory_offset; guint n_files; }; static void arv_zip_build_file_list (ArvZip *zip) { ArvZipFile *zip_file; const void *ptr; ptrdiff_t offset; int i; offset = zip->directory_offset; for (i = 0; i < zip->n_files; i++) { ptr = zip->buffer + zip->header_size + offset; if (ARV_GUINT32_FROM_LE_PTR (ptr, 0) != 0x02014b50) { arv_info_misc ("[Zip::build_file_list] Magic number of central directory not found (0x02014b50)"); arv_info_misc ("[Zip::build_file_list] Expected at 0x%" G_GSIZE_MODIFIER "x - found 0x%08x instead", zip->header_size + offset, ARV_GUINT32_FROM_LE_PTR (ptr, 0)); return; } zip_file = g_new0 (ArvZipFile, 1); zip_file->compressed_size = ARV_GUINT32_FROM_LE_PTR (ptr, 20); zip_file->uncompressed_size = ARV_GUINT32_FROM_LE_PTR (ptr, 24); zip_file->offset = ARV_GUINT32_FROM_LE_PTR (ptr, 42); zip_file->name = g_strndup (((char *) ptr) + 46, ARV_GUINT16_FROM_LE_PTR (ptr, 28)); arv_debug_misc ("[Zip::list_files] %s", zip_file->name); zip->files = g_slist_prepend (zip->files, zip_file); offset += 0x2e + ARV_GUINT16_FROM_LE_PTR (ptr, 28) + /* filename size */ ARV_GUINT16_FROM_LE_PTR (ptr, 30) + /* extra field */ ARV_GUINT16_FROM_LE_PTR (ptr, 32); /* file comment */ } } static ArvZipFile * arv_zip_find_file (ArvZip *zip, const char *name) { ArvZipFile *zip_file; GSList *iter; for (iter = zip->files; iter != NULL; iter = iter->next) { zip_file = iter->data; if (g_strcmp0 (zip_file->name, name) == 0) return zip_file; } return NULL; } static ptrdiff_t arv_zip_get_file_data (ArvZip *zip, ArvZipFile *zip_file) { const void *ptr; ptr = zip->buffer + zip_file->offset + zip->header_size; if (ARV_GUINT32_FROM_LE_PTR (ptr, 0) != 0x04034b50) { arv_info_misc ("[Zip::get_file_data] Magic number for file header not found (0x04034b50)"); return -1; } return zip_file->offset + zip->header_size + ARV_GUINT16_FROM_LE_PTR (ptr, 26) + ARV_GUINT16_FROM_LE_PTR (ptr, 28) + 30; } /** * arv_zip_new: (skip) * @buffer: zipped data * @size: size of the zipped data * Return value: a new #ArvZip instance */ ArvZip * arv_zip_new (const void *buffer, size_t size) { ArvZip *zip; const void *ptr; int i; gboolean directory_found; g_return_val_if_fail (buffer != NULL, NULL); g_return_val_if_fail (size > 0, NULL); zip = g_new0 (ArvZip, 1); zip->buffer = buffer; zip->buffer_size = size; directory_found = FALSE; for (i = zip->buffer_size - 4; i > 0; i--) { if (zip->buffer[i] == 0x50 && zip->buffer[i+1] == 0x4b && zip->buffer[i+2] == 0x05 && zip->buffer[i+3] == 0x06) { zip->directory_position = i; directory_found = TRUE; break; } } if (!directory_found) { arv_info_misc ("[Zip::new] Magic number for end of central directory not found (0x06054b50)"); return zip; } ptr = zip->buffer + zip->directory_position; zip->n_files = ARV_GUINT16_FROM_LE_PTR (ptr, 10); if (ARV_GUINT16_FROM_LE_PTR (ptr, 8) != zip->n_files) { arv_info_misc ("[Zip::new] Mismatch in number of files"); zip->n_files = 0; return zip; } zip->directory_size = ARV_GUINT32_FROM_LE_PTR (ptr, 12); zip->directory_offset = ARV_GUINT32_FROM_LE_PTR (ptr, 16); zip->header_size = zip->directory_position - (zip->directory_offset + zip->directory_size); arv_debug_misc ("[Zip::new] number of files = %d", zip->n_files); arv_debug_misc ("[Zip::new] directory position = 0x%08" G_GINTPTR_MODIFIER "x", zip->directory_position); arv_debug_misc ("[Zip::new] directory size = %" G_GSIZE_FORMAT, zip->directory_size); arv_debug_misc ("[Zip::new] directory offset = 0x%08" G_GINTPTR_MODIFIER "x", zip->directory_offset); arv_debug_misc ("[Zip::new] header size = %" G_GSIZE_FORMAT, zip->header_size); arv_zip_build_file_list (zip); return zip; } void arv_zip_free (ArvZip *zip) { ArvZipFile *zip_file; GSList *iter; g_return_if_fail (zip != NULL); for (iter = zip->files; iter != NULL; iter = iter->next) { zip_file = iter->data; g_free (zip_file->name); g_free (zip_file); } g_slist_free (zip->files); g_free (zip); } const GSList * arv_zip_get_file_list (ArvZip *zip) { g_return_val_if_fail (zip != NULL, NULL); return zip->files; } void * arv_zip_get_file (ArvZip *zip, const char *name, size_t *size) { ArvZipFile *zip_file; void *output_buffer; ptrdiff_t offset; if (size != NULL) *size = 0; g_return_val_if_fail (zip != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); zip_file = arv_zip_find_file (zip, name); if (!zip_file) return NULL; offset = arv_zip_get_file_data (zip, zip_file); if (offset < 0) return NULL; output_buffer = g_malloc (zip_file->uncompressed_size); if (output_buffer == NULL) return NULL; if (zip_file->compressed_size < zip_file->uncompressed_size) { z_stream zs; zs.zalloc = NULL; zs.zfree = NULL; zs.opaque = NULL; zs.next_in = (void *) &zip->buffer[offset]; zs.avail_in = zip_file->compressed_size; zs.next_out = output_buffer; zs.avail_out = zip_file->uncompressed_size; inflateInit2 (&zs, -MAX_WBITS); inflate (&zs, Z_FINISH); inflateEnd (&zs); } else memcpy (output_buffer, zip->buffer + offset, zip_file->uncompressed_size); if (size != NULL) *size = zip_file->uncompressed_size; return output_buffer; } aravis-0.8.34/src/arvzip.h000066400000000000000000000074411475431451200153620ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_ZIP_H #define ARV_ZIP_H #if !defined (ARV_H_INSIDE) && !defined (ARAVIS_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS ARV_API ArvZip * arv_zip_new (const void *buffer, size_t size); ARV_API void arv_zip_free (ArvZip *zip); ARV_API void * arv_zip_get_file (ArvZip *zip, const char *name, size_t *size); ARV_API const GSList * arv_zip_get_file_list (ArvZip *zip); ARV_API const char * arv_zip_file_get_name (ArvZipFile *zip_file); ARV_API size_t arv_zip_file_get_uncompressed_size (ArvZipFile *zip_file); #define ARV_GUINT32_FROM_LE_PTR(ptr,offset) arv_guint32_from_unaligned_le_ptr (ptr, offset) #define ARV_GUINT16_FROM_LE_PTR(ptr,offset) arv_guint16_from_unaligned_le_ptr (ptr, offset) /** * arv_guint32_from_unaligned_le_ptr: * @ptr: pointer to a little endian 32 bit usigned integer * @offset: an offset to add to @ptr * * Here's an excerpt of the ARM documentation: * * "Unaligned data access in C and C++ code * * It can be necessary to access unaligned data in memory, for example, * when porting legacy code from a CISC architecture where instructions are * available to directly access unaligned data in memory. * * On ARMv4 and ARMv5 architectures, and on the ARMv6 architecture * depending on how it is configured, care is required when accessing * unaligned data in memory, to avoid unexpected results. For example, when * a conventional pointer is used to read a word in C or C++ source code, * the ARM compiler generates assembly language code that reads the word * using an LDR instruction. This works as expected when the address is a * multiple of four, for example if it lies on a word boundary. However, if * the address is not a multiple of four, the LDR instruction returns a * rotated result rather than performing a true unaligned word load. * Generally, this rotation is not what the programmer expects. * * On ARMv6 and later architectures, unaligned access is fully supported." * * Returns: a guint32 in machine endianness */ static inline guint32 arv_guint32_from_unaligned_le_ptr (const char *ptr, gint32 offset) { guint32 val; g_return_val_if_fail (ptr != NULL, 0); ptr += offset; *((char*)(&val)) = *((char*)ptr); *(((char*)(&val))+1) = *(((char*)ptr)+1); *(((char*)(&val))+2) = *(((char*)ptr)+2); *(((char*)(&val))+3) = *(((char*)ptr)+3); return GUINT32_FROM_LE (val); } /** * arv_guint16_from_unaligned_le_ptr: * @ptr: pointer to a little endian 16 bit usigned integer * @offset: an offset to add to @ptr * * See @arv_guint32_from_unaligned_le_ptr. * * Returns: a guint16 in machine endianness */ static inline guint16 arv_guint16_from_unaligned_le_ptr (const char *ptr, gint16 offset) { guint16 val; g_return_val_if_fail (ptr != NULL, 0); ptr += offset; *((char*)(&val)) = *((char*)ptr); *(((char*)(&val))+1) = *(((char*)ptr)+1); return GUINT16_FROM_LE (val); } G_END_DECLS #endif aravis-0.8.34/src/meson.build000066400000000000000000000205331475431451200160350ustar00rootroot00000000000000library_sources = [ 'arvenums.c', 'arvdebug.c', 'arvsystem.c', 'arvevaluator.c', 'arvdomnode.c', 'arvdomnodechildlist.c', 'arvdomnodelist.c', 'arvdomnamednodemap.c', 'arvdomdocument.c', 'arvdomdocumentfragment.c', 'arvdomelement.c', 'arvdomcharacterdata.c', 'arvdomtext.c', 'arvdomparser.c', 'arvdomimplementation.c', 'arvcamera.c', 'arvgcenums.c', 'arvgc.c', 'arvgcnode.c', 'arvgcpropertynode.c', 'arvgcindexnode.c', 'arvgcvalueindexednode.c', 'arvgcinvalidatornode.c', 'arvgcfeaturenode.c', 'arvgcregisterdescriptionnode.c', 'arvgcgroupnode.c', 'arvgccategory.c', 'arvgcboolean.c', 'arvgcenumeration.c', 'arvgcenumentry.c', 'arvgcintegernode.c', 'arvgcfloatnode.c', 'arvgcregisternode.c', 'arvgcintregnode.c', 'arvgcstringnode.c', 'arvgcmaskedintregnode.c', 'arvgcfloatregnode.c', 'arvgcstringregnode.c', 'arvgcstructregnode.c', 'arvgcstructentrynode.c', 'arvgccommand.c', 'arvgcswissknife.c', 'arvgcswissknifenode.c', 'arvgcintswissknifenode.c', 'arvgcconverter.c', 'arvgcconverternode.c', 'arvgcintconverternode.c', 'arvgcport.c', 'arvgcregister.c', 'arvgcinteger.c', 'arvgcstring.c', 'arvgcselector.c', 'arvgcfloat.c', 'arvinterface.c', 'arvdevice.c', 'arvstream.c', 'arvbuffer.c', 'arvchunkparser.c', 'arvgvinterface.c', 'arvgvdevice.c', 'arvgvstream.c', 'arvfakeinterface.c', 'arvfakedevice.c', 'arvfakestream.c', 'arvfakecamera.c', 'arvgvfakecamera.c', 'arvrealtime.c', 'arvxmlschema.c' ] library_no_introspection_sources = [ 'arvmisc.c', 'arvnetwork.c', 'arvzip.c', 'arvstr.c', 'arvgvcp.c', 'arvgvsp.c', 'arvwakeup.c' ] library_headers = [ 'arv.h', 'arvtypes.h', 'arvbuffer.h', 'arvcamera.h', 'arvchunkparser.h', 'arvdebug.h', 'arvdevice.h', 'arvdomcharacterdata.h', 'arvdomdocumentfragment.h', 'arvdomdocument.h', 'arvdomelement.h', 'arvdomimplementation.h', 'arvdomnamednodemap.h', 'arvdomnode.h', 'arvdomnodechildlist.h', 'arvdomnodelist.h', 'arvdomparser.h', 'arvdomtext.h', 'arvenums.h', 'arvevaluator.h', 'arvfakecamera.h', 'arvfakedevice.h', 'arvfakeinterface.h', 'arvfakestream.h', 'arvgcboolean.h', 'arvgccategory.h', 'arvgccommand.h', 'arvgcconverter.h', 'arvgcconverternode.h', 'arvgcenumentry.h', 'arvgcenumeration.h', 'arvgcenums.h', 'arvgcfeaturenode.h', 'arvgcfloat.h', 'arvgcfloatnode.h', 'arvgcgroupnode.h', 'arvgc.h', 'arvgcindexnode.h', 'arvgcintconverternode.h', 'arvgcinteger.h', 'arvgcintegernode.h', 'arvgcinvalidatornode.h', 'arvgcnode.h', 'arvgcport.h', 'arvgcpropertynode.h', 'arvgcregisterdescriptionnode.h', 'arvgcregister.h', 'arvgcregisternode.h', 'arvgcintregnode.h', 'arvgcstringnode.h', 'arvgcmaskedintregnode.h', 'arvgcfloatregnode.h', 'arvgcstringregnode.h', 'arvgcstructregnode.h', 'arvgcstring.h', 'arvgcselector.h', 'arvgcstructentrynode.h', 'arvgcswissknife.h', 'arvgcswissknifenode.h', 'arvgcintswissknifenode.h', 'arvgcvalueindexednode.h', 'arvgvdevice.h', 'arvgvfakecamera.h', 'arvgvinterface.h', 'arvgvstream.h', 'arvinterface.h', 'arvsystem.h', 'arvrealtime.h', 'arvstream.h', 'arvxmlschema.h' ] library_private_headers = [ 'arvbufferprivate.h', 'arvchunkparserprivate.h', 'arvdebugprivate.h', 'arvdeviceprivate.h', 'arvfakedeviceprivate.h', 'arvfakeinterfaceprivate.h', 'arvfakestreamprivate.h', 'arvgcprivate.h', 'arvgcconverterprivate.h', 'arvgcdefaultsprivate.h', 'arvgcfeaturenodeprivate.h', 'arvgcregisternodeprivate.h', 'arvgcswissknifeprivate.h', 'arvgvcpprivate.h', 'arvgvdeviceprivate.h', 'arvgvinterfaceprivate.h', 'arvgvspprivate.h', 'arvgvstreamprivate.h', 'arvinterfaceprivate.h', 'arvmiscprivate.h', 'arvnetworkprivate.h', 'arvrealtimeprivate.h', 'arvstreamprivate.h', 'arvwakeupprivate.h' ] library_no_introspection_headers = [ 'arvmisc.h', 'arvzip.h', 'arvstr.h' ] if usb_dep.found() library_sources += [ 'arvuvinterface.c', 'arvuvdevice.c', 'arvuvstream.c' ] library_no_introspection_sources += [ 'arvuvcp.c', 'arvuvsp.c' ] library_headers += [ 'arvuvinterface.h', 'arvuvdevice.h', 'arvuvstream.h' ] library_private_headers += [ 'arvuvcpprivate.h', 'arvuvdeviceprivate.h', 'arvuvinterfaceprivate.h', 'arvuvstreamprivate.h', 'arvuvspprivate.h' ] endif library_enums = gnome.mkenums_simple ('arvenumtypes', decorator: 'ARV_API', header_prefix: '#include ', sources: library_headers + library_no_introspection_headers) library_private_enums = gnome.mkenums_simple ('arvenumtypesprivate', sources: library_private_headers) library_resources = gnome.compile_resources('arvresources','arvresources.xml') library_include_dir = get_option ('includedir') / 'aravis-@0@'.format (aravis_api_version) api_config_data = configuration_data () api_config_data.set ('ARV_API', cc_export_define) configure_file (input: 'arvapi.h.in', output: 'arvapi.h', configuration: api_config_data, install_dir: library_include_dir) features_library_config_data = configuration_data () features_library_config_data.set10 ('ARAVIS_HAS_USB', usb_dep.found()) features_library_config_data.set10 ('ARAVIS_HAS_PACKET_SOCKET', packet_socket_enabled) features_library_config_data.set10 ('ARAVIS_HAS_FAST_HEARTBEAT', get_option ('fast-heartbeat')) configure_file (input: 'arvfeatures.h.in', output: 'arvfeatures.h', configuration: features_library_config_data, install_dir: library_include_dir) params_library_config_data = configuration_data () params_library_config_data.set ('ARV_GV_STREAM_NUM_BUFFERS', get_option ('gv-n-buffers')) configure_file (input: 'arvparamsprivate.h.in', output: 'arvparamsprivate.h', configuration: params_library_config_data) version_config_data = configuration_data () version_config_data.set ('ARAVIS_VERSION', aravis_version) version_config_data.set ('ARAVIS_API_VERSION', aravis_api_version) version_config_data.set ('ARAVIS_MAJOR_VERSION', aravis_major_version) version_config_data.set ('ARAVIS_MINOR_VERSION', aravis_minor_version) version_config_data.set ('ARAVIS_MICRO_VERSION', aravis_micro_version) configure_file (input: 'arvversion.h.in', output: 'arvversion.h', configuration: version_config_data, install_dir: library_include_dir) library_inc = include_directories ('.') install_headers (library_headers + library_no_introspection_headers, install_dir: library_include_dir) library_c_args = [ '-DARAVIS_COMPILATION' ] aravis_library = library ('aravis-@0@'.format (aravis_api_version), library_sources, library_headers, library_no_introspection_sources, library_no_introspection_headers, library_private_headers, library_enums, library_private_enums, library_resources, version: aravis_version, soversion: '0', dependencies: aravis_dependencies, c_args: library_c_args, install: true) aravis_library_dependencies = declare_dependency (dependencies: aravis_dependencies, link_with: aravis_library, include_directories: library_inc) pkg.generate (aravis_library, filebase: 'aravis-@0@'.format (aravis_api_version), version: aravis_version, subdirs: 'aravis-@0@'.format (aravis_api_version), name: 'Aravis', description: 'Camera control and image acquisition library', requires: aravis_public_dependencies) arv_test_resources = gnome.compile_resources('arvtestresources','arvtestresources.xml') progs = [ [ 'arv-tool', 'arvtool.c'], [ 'arv-test', ['arvtest.c'] + arv_test_resources], [ 'arv-camera-test', 'arvcameratest.c'], [ 'arv-fake-gv-camera', 'arvfakegvcamera.c'], ] foreach p: progs executable ((p[0] + '-@0@').format (aravis_api_version), p[1], link_with: aravis_library, c_args: library_c_args, dependencies: aravis_dependencies, install: true) endforeach introspection_option = get_option('introspection') introspection_enabled = false if introspection_option.auto() or introspection_option.enabled() gir_scanner = find_program('g-ir-scanner', required: introspection_option) gi_dep = dependency('gobject-introspection-1.0', required: introspection_option) if gir_scanner.found() and gi_dep.found() aravis_gir = gnome.generate_gir(aravis_library, sources : [library_sources, library_headers, library_enums], nsversion : aravis_api_version, namespace : 'Aravis', symbol_prefix : 'arv_', identifier_prefix : 'Arv', header: 'arv.h', export_packages : 'aravis-@0@'.format(aravis_api_version), includes : ['GObject-2.0', 'Gio-2.0'], extra_args: '-DARAVIS_COMPILATION', install : true) introspection_enabled = true endif endif aravis-0.8.34/subprojects/000077500000000000000000000000001475431451200154445ustar00rootroot00000000000000aravis-0.8.34/subprojects/.gitignore000066400000000000000000000000131475431451200174260ustar00rootroot00000000000000gi-docgen/ aravis-0.8.34/subprojects/gi-docgen.wrap000066400000000000000000000002361475431451200201740ustar00rootroot00000000000000[wrap-git] directory=gi-docgen url=https://gitlab.gnome.org/GNOME/gi-docgen.git push-url=ssh://git@gitlab.gnome.org:GNOME/gi-docgen.git revision=main depth=1 aravis-0.8.34/tests/000077500000000000000000000000001475431451200142435ustar00rootroot00000000000000aravis-0.8.34/tests/aravis.supp000066400000000000000000000033071475431451200164440ustar00rootroot00000000000000# # glib # { g_match_info_fetch_named_glib_2.74.1_issue_2881 Memcheck:Addr4 ... fun:g_match_info_fetch_named } { glib initialisation Memcheck:Leak fun:calloc fun:g_malloc0 ... fun:_dl_init } { g_type_register_fundamental_realloc Memcheck:Leak fun:malloc fun:realloc fun:g_realloc ... fun:g_type_register_fundamental ... fun:_dl_init } { g-type-register-static Memcheck:Leak match-leak-kinds:possible,reachable fun:malloc ... fun:g_type_register_static } { g-type-register-static-realloc Memcheck:Leak match-leak-kinds:possible,reachable fun:realloc ... fun:g_type_register_static } { g-type-register-static-calloc Memcheck:Leak match-leak-kinds:reachable fun:calloc ... fun:g_type_register_static } { g_type_add_interface_static_app Memcheck:Leak fun:malloc fun:g_malloc fun:g_slice_alloc ... fun:g_type_add_interface_static } { g_type_create_instance Memcheck:Leak fun:g_type_create_instance ... fun:g_type_class_ref } { g_type_class_ref Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_type_class_ref fun:g_type_class_ref } { g_type_class_ref_Wm Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_class_init_Wm fun:g_type_class_ref fun:g_type_class_ref } # libusb { libusb_init_malloc Memcheck:Leak fun:malloc ... fun:libusb_init ... fun:arv_uv_interface_get_instance } { libusb_init_calloc Memcheck:Leak fun:calloc ... fun:libusb_init ... fun:arv_uv_interface_get_instance } { libusb_init_calloc Memcheck:Leak fun:realloc ... fun:libusb_init ... fun:arv_uv_interface_get_instance } { libusb_open_malloc Memcheck:Leak fun:malloc ... fun:libusb_open ... fun:arv_open_device } aravis-0.8.34/tests/arvacquisitiontest.c000066400000000000000000000010561475431451200203520ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include int main (int argc, char **argv) { ArvCamera *camera; ArvBuffer *buffer; camera = arv_camera_new (argc > 1 ? argv[1] : NULL, NULL); buffer = arv_camera_acquisition (camera, 0, NULL); if (ARV_IS_BUFFER (buffer) && arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) printf ("Image successfully acquired\n"); else printf ("Failed to acquire a single image\n"); g_clear_object (&camera); g_clear_object (&buffer); return EXIT_SUCCESS; } aravis-0.8.34/tests/arvautopacketsizetest.c000066400000000000000000000014351475431451200210560ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include int main (int argc, char **argv) { ArvCamera *camera; camera = arv_camera_new (argc > 1 ? argv[1] : NULL, NULL); if (!ARV_IS_CAMERA (camera)) { printf ("Camera not found\n"); return EXIT_FAILURE; } if (arv_camera_is_gv_device (camera)) { unsigned packet_size; packet_size = arv_camera_gv_auto_packet_size (camera, NULL); printf ("Packet size set to %d bytes on camera %s-%s\n", packet_size, arv_camera_get_vendor_name (camera, NULL), arv_camera_get_device_id (camera, NULL)); } else { printf ("%s-%s is not a GigEVision camera\n", arv_camera_get_vendor_name (camera, NULL), arv_camera_get_device_id (camera, NULL)); } g_clear_object (&camera); return EXIT_SUCCESS; } aravis-0.8.34/tests/arvchunkparsertest.c000066400000000000000000000034251475431451200203510ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include int main (int argc, char **argv) { ArvCamera *camera; ArvStream *stream; ArvChunkParser *parser; GError *error =NULL; /* Instantiation of the first available camera */ camera = arv_camera_new (NULL, &error); if (ARV_IS_CAMERA (camera)) { gint payload; /* Instantiation of a chunk parser */ parser = arv_camera_create_chunk_parser (camera); /* Enable chunk data */ arv_camera_set_chunks (camera, "Width,Height", NULL); /* retrieve image payload (number of bytes per image) */ payload = arv_camera_get_payload (camera, NULL); /* Create a new stream object */ stream = arv_camera_create_stream (camera, NULL, NULL, &error); if (ARV_IS_STREAM (stream)) { ArvBuffer *buffer; /* Push 1 buffer in the stream input buffer queue */ arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); /* Start the video stream */ arv_camera_start_acquisition (camera, NULL); /* Retrieve the acquired buffer */ buffer = arv_stream_pop_buffer (stream); printf ("ChunkWidth = %d\n", (int) arv_chunk_parser_get_integer_value (parser, buffer, "ChunkWidth", NULL)); printf ("ChunkHeight = %d\n", (int) arv_chunk_parser_get_integer_value (parser, buffer, "ChunkHeight", NULL)); g_object_unref (buffer); /* Stop the video stream */ arv_camera_stop_acquisition (camera, NULL); g_object_unref (stream); } else { printf ("Can't create stream thread%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message: ""); g_clear_error (&error); } g_object_unref (parser); g_object_unref (camera); } else { printf ("No camera found%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message: ""); g_clear_error (&error); } return 0; } aravis-0.8.34/tests/arvdevicescantest.c000066400000000000000000000016641475431451200201330ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include gboolean cancel = FALSE; static void set_cancel(int signal) { cancel = TRUE; } static gboolean periodic_task_cb (void *user_data) { GMainLoop *main_loop = user_data; guint n_devices; int i; if (cancel) { g_main_loop_quit(main_loop); return FALSE; } printf ("Update Device List\n"); arv_update_device_list(); n_devices = arv_get_n_devices(); printf ("Number of found Devices: %d\n", n_devices); for (i = 0; i < n_devices; i++) printf ("%s\n", arv_get_device_id (i)); return TRUE; } int main(int argc, char** argv) { GMainLoop *main_loop; void *sigint_handler; sigint_handler = signal(SIGINT, set_cancel); main_loop = g_main_loop_new (NULL, FALSE); g_timeout_add_seconds (10, periodic_task_cb, main_loop); g_main_loop_run (main_loop); signal(SIGINT, sigint_handler); g_main_loop_unref (main_loop); arv_shutdown(); return 0; } aravis-0.8.34/tests/arvdevicetest.c000066400000000000000000000222131475431451200172570ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include "../src/arvgvspprivate.h" #include "../src/arvgvcpprivate.h" static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static char *arv_option_camera_name = NULL; static char *arv_option_debug_domains = NULL; static gboolean arv_option_auto_buffer = FALSE; static int arv_option_width = -1; static int arv_option_height = -1; static int arv_option_horizontal_binning = -1; static int arv_option_vertical_binning = -1; static const GOptionEntry arv_option_entries[] = { { "name", 'n', 0, G_OPTION_ARG_STRING, &arv_option_camera_name,"Camera name", NULL}, { "auto", 'a', 0, G_OPTION_ARG_NONE, &arv_option_auto_buffer, "AutoBufferSize", NULL}, { "width", 'w', 0, G_OPTION_ARG_INT, &arv_option_width, "Width", NULL }, { "height", 'h', 0, G_OPTION_ARG_INT, &arv_option_height, "Height", NULL }, { "h-binning", '\0', 0, G_OPTION_ARG_INT, &arv_option_horizontal_binning,"Horizontal binning", NULL }, { "v-binning", '\0', 0, G_OPTION_ARG_INT, &arv_option_vertical_binning, "Vertical binning", NULL }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug mode", NULL }, { NULL } }; typedef enum { ARV_CAMERA_TYPE_BASLER, ARV_CAMERA_TYPE_PROSILICA } ArvCameraType; int main (int argc, char **argv) { ArvDevice *device; ArvStream *stream; ArvBuffer *buffer; GOptionContext *context; GError *error = NULL; char memory_buffer[100000]; int i; unsigned buffer_count = 0; guint64 start_time, time; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); arv_debug_enable (arv_option_debug_domains); if (arv_option_camera_name == NULL) g_print ("Looking for the first available camera\n"); else g_print ("Looking for camera '%s'\n", arv_option_camera_name); device = arv_open_device (arv_option_camera_name, NULL); if (device != NULL) { ArvGc *genicam; ArvGcNode *node; guint32 value; guint32 maximum; guint32 minimum; guint64 n_processed_buffers; guint64 n_failures; guint64 n_underruns; double v_double; double v_double_min; double v_double_max; const char *v_string; gboolean v_boolean; genicam = arv_device_get_genicam (device); if (arv_option_width > 0) { node = arv_gc_get_node (genicam, "Width"); arv_gc_integer_set_value (ARV_GC_INTEGER (node), arv_option_width, NULL); } if (arv_option_height > 0) { node = arv_gc_get_node (genicam, "Height"); arv_gc_integer_set_value (ARV_GC_INTEGER (node), arv_option_height, NULL); } if (arv_option_horizontal_binning > 0) { node = arv_gc_get_node (genicam, "BinningHorizontal"); arv_gc_integer_set_value (ARV_GC_INTEGER (node), arv_option_horizontal_binning, NULL); } if (arv_option_vertical_binning > 0) { node = arv_gc_get_node (genicam, "BinningVertical"); arv_gc_integer_set_value (ARV_GC_INTEGER (node), arv_option_vertical_binning, NULL); } node = arv_gc_get_node (genicam, "DeviceVendorName"); v_string = arv_gc_string_get_value (ARV_GC_STRING (node), NULL); g_print ("vendor = %s\n", v_string); node = arv_gc_get_node (genicam, "DeviceModelName"); v_string = arv_gc_string_get_value (ARV_GC_STRING (node), NULL); g_print ("model = %s\n", v_string); node = arv_gc_get_node (genicam, "DeviceID"); v_string = arv_gc_string_get_value (ARV_GC_STRING (node), NULL); g_print ("device id = %s\n", v_string); node = arv_gc_get_node (genicam, "SensorWidth"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_print ("sensor width = %d\n", value); node = arv_gc_get_node (genicam, "SensorHeight"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_print ("sensor height = %d\n", value); node = arv_gc_get_node (genicam, "Width"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); g_print ("image width = %d (max:%d)\n", value, maximum); node = arv_gc_get_node (genicam, "Height"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); g_print ("image height = %d (max:%d)\n", value, maximum); node = arv_gc_get_node (genicam, "BinningHorizontal"); if (ARV_IS_GC_NODE (node)) { value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); minimum = arv_gc_integer_get_min (ARV_GC_INTEGER (node), NULL); g_print ("horizontal binning = %d (min:%d - max:%d)\n", value, minimum, maximum); } node = arv_gc_get_node (genicam, "BinningVertical"); if (ARV_IS_GC_NODE (node)) { value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); minimum = arv_gc_integer_get_min (ARV_GC_INTEGER (node), NULL); g_print ("vertical binning = %d (min:%d - max:%d)\n", value, minimum, maximum); } node = arv_gc_get_node (genicam, "ExposureTimeAbs"); if (ARV_IS_GC_NODE (node)) { v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); v_double_min = arv_gc_float_get_min (ARV_GC_FLOAT (node), NULL); v_double_max = arv_gc_float_get_max (ARV_GC_FLOAT (node), NULL); g_print ("exposure = %g (min:%g - max:%g)\n", v_double, v_double_min, v_double_max); } node = arv_gc_get_node (genicam, "ExposureAuto"); if (ARV_IS_GC_NODE (node)) { v_string = arv_gc_enumeration_get_string_value (ARV_GC_ENUMERATION (node), NULL); g_print ("exposure auto mode = %s\n", v_string); } node = arv_gc_get_node (genicam, "GainRaw"); if (ARV_IS_GC_NODE (node)) { value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); maximum = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); minimum = arv_gc_integer_get_min (ARV_GC_INTEGER (node), NULL); g_print ("gain = %d (min:%d - max:%d)\n", value, minimum, maximum); } node = arv_gc_get_node (genicam, "GainAuto"); if (ARV_IS_GC_NODE (node)) { v_string = arv_gc_enumeration_get_string_value (ARV_GC_ENUMERATION (node), NULL); g_print ("gain auto mode = %s\n", v_string); } node = arv_gc_get_node (genicam, "TriggerSelector"); if (ARV_IS_GC_NODE (node)) { v_string = arv_gc_enumeration_get_string_value (ARV_GC_ENUMERATION (node), NULL); g_print ("trigger selector = %s\n", v_string); } node = arv_gc_get_node (genicam, "ReverseX"); if (ARV_IS_GC_NODE (node)) { v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_print ("reverse x = %s\n", v_boolean ? "TRUE" : "FALSE"); } stream = arv_device_create_stream (device, NULL, NULL, NULL); if (arv_option_auto_buffer) g_object_set (stream, "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO, "socket-buffer-size", 0, NULL); node = arv_gc_get_node (genicam, "PayloadSize"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_print ("payload size = %d (0x%x)\n", value, value); for (i = 0; i < 30; i++) arv_stream_push_buffer (stream, arv_buffer_new (value, NULL)); if (ARV_IS_GV_DEVICE (device)) { arv_device_read_register (device, ARV_GVBS_STREAM_CHANNEL_0_PORT_OFFSET, &value, NULL); g_print ("stream port = %d (%d)\n", value, arv_gv_stream_get_port (ARV_GV_STREAM (stream))); } arv_device_read_memory (device, 0x00014150, 8, memory_buffer, NULL); arv_device_read_memory (device, 0x000000e8, 16, memory_buffer, NULL); arv_device_read_memory (device, ARV_GVBS_USER_DEFINED_NAME_OFFSET, ARV_GVBS_USER_DEFINED_NAME_SIZE, memory_buffer, NULL); node = arv_gc_get_node (genicam, "AcquisitionStart"); arv_gc_command_execute (ARV_GC_COMMAND (node), NULL); signal (SIGINT, set_cancel); start_time = g_get_real_time (); do { g_usleep (100000); do { buffer = arv_stream_try_pop_buffer (stream); if (buffer != NULL) { arv_stream_push_buffer (stream, buffer); buffer_count++; } } while (buffer != NULL); time = g_get_real_time (); if (time - start_time > 1000000) { printf ("Frame rate = %d Hz\n", buffer_count); buffer_count = 0; start_time = time; } } while (!cancel); if (ARV_IS_GV_DEVICE (device)) { arv_device_read_register (device, ARV_GVBS_STREAM_CHANNEL_0_PORT_OFFSET, &value, NULL); g_print ("stream port = %d (%d)\n", value, arv_gv_stream_get_port (ARV_GV_STREAM (stream))); } arv_stream_get_statistics (stream, &n_processed_buffers, &n_failures, &n_underruns); g_print ("Processed buffers = %llu\n", (unsigned long long) n_processed_buffers); g_print ("Failures = %llu\n", (unsigned long long) n_failures); g_print ("Underruns = %llu\n", (unsigned long long) n_underruns); node = arv_gc_get_node (genicam, "AcquisitionStop"); arv_gc_command_execute (ARV_GC_COMMAND (node), NULL); g_object_unref (stream); g_object_unref (device); } else g_print ("No device found\n"); return 0; } aravis-0.8.34/tests/arvevaluatortest.c000066400000000000000000000032471475431451200200300ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static char **arv_option_expressions = NULL; static char *arv_option_debug_domains = NULL; static const GOptionEntry arv_option_entries[] = { { G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_STRING_ARRAY, &arv_option_expressions, NULL, NULL}, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug mode", NULL }, { NULL } }; int main (int argc, char **argv) { ArvEvaluator *evaluator; GOptionContext *context; GError *error = NULL; int i; double value; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); arv_debug_enable (arv_option_debug_domains); evaluator = arv_evaluator_new (NULL); arv_evaluator_set_double_variable (evaluator, "TDBL", 124.2); arv_evaluator_set_int64_variable (evaluator, "TINT", 3200); if (arv_option_expressions == NULL) { g_print ("Missing expression.\n"); return EXIT_FAILURE; } for (i = 0; arv_option_expressions[i] != NULL; i++) { arv_evaluator_set_expression (evaluator, arv_option_expressions[i]); value = arv_evaluator_evaluate_as_double (evaluator, &error); if (error != NULL) { g_print ("Error in '%s': %s\n", arv_option_expressions[i], error->message); g_error_free (error); error = NULL; } else g_print ("%s = %g\n", arv_option_expressions[i], value); } g_object_unref (evaluator); return EXIT_SUCCESS; } aravis-0.8.34/tests/arvexample.c000066400000000000000000000066341475431451200165640ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include typedef struct { GMainLoop *main_loop; int buffer_count; } ApplicationData; static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static void new_buffer_cb (ArvStream *stream, ApplicationData *data) { ArvBuffer *buffer; buffer = arv_stream_try_pop_buffer (stream); if (buffer != NULL) { if (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) data->buffer_count++; /* Image processing here */ arv_stream_push_buffer (stream, buffer); } } static gboolean periodic_task_cb (void *abstract_data) { ApplicationData *data = abstract_data; printf ("Frame rate = %d Hz\n", data->buffer_count); data->buffer_count = 0; if (cancel) { g_main_loop_quit (data->main_loop); return FALSE; } return TRUE; } static void control_lost_cb (ArvGvDevice *gv_device) { /* Control of the device is lost. Display a message and force application exit */ printf ("Control lost\n"); cancel = TRUE; } int main (int argc, char **argv) { ApplicationData data; ArvCamera *camera; ArvStream *stream; GError *error = NULL; int i; data.buffer_count = 0; /* Instantiation of the first available camera */ camera = arv_camera_new (NULL, &error); if (ARV_IS_CAMERA (camera)) { void (*old_sigint_handler)(int); gint payload; /* Set region of interrest to a 200x200 pixel area */ arv_camera_set_region (camera, 0, 0, 200, 200, NULL); /* Set frame rate to 10 Hz */ arv_camera_set_frame_rate (camera, 10.0, NULL); /* retrieve image payload (number of bytes per image) */ payload = arv_camera_get_payload (camera, NULL); /* Create a new stream object */ stream = arv_camera_create_stream (camera, NULL, NULL, &error); if (ARV_IS_STREAM (stream)) { /* Push 50 buffer in the stream input buffer queue */ for (i = 0; i < 50; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); /* Start the video stream */ arv_camera_start_acquisition (camera, NULL); /* Connect the new-buffer signal */ g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &data); /* And enable emission of this signal (it's disabled by default for performance reason) */ arv_stream_set_emit_signals (stream, TRUE); /* Connect the control-lost signal */ g_signal_connect (arv_camera_get_device (camera), "control-lost", G_CALLBACK (control_lost_cb), NULL); /* Install the callback for frame rate display */ g_timeout_add_seconds (1, periodic_task_cb, &data); /* Create a new glib main loop */ data.main_loop = g_main_loop_new (NULL, FALSE); old_sigint_handler = signal (SIGINT, set_cancel); /* Run the main loop */ g_main_loop_run (data.main_loop); signal (SIGINT, old_sigint_handler); g_main_loop_unref (data.main_loop); /* Stop the video stream */ arv_camera_stop_acquisition (camera, NULL); /* Signal must be inhibited to avoid stream thread running after the last unref */ arv_stream_set_emit_signals (stream, FALSE); g_object_unref (stream); } else { printf ("Can't create stream thread%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } g_object_unref (camera); } else { printf ("No camera found%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } return 0; } aravis-0.8.34/tests/arvgenicamtest.c000066400000000000000000000042651475431451200174320ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static char **arv_option_filenames = NULL; static char *arv_option_debug_domains = NULL; static const GOptionEntry arv_option_entries[] = { { G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_FILENAME_ARRAY, &arv_option_filenames, NULL, NULL}, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug mode", NULL }, { NULL } }; int main (int argc, char **argv) { ArvGc *genicam; char *xml; size_t size; GOptionContext *context; GError *error = NULL; int i; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); arv_debug_enable (arv_option_debug_domains); if (arv_option_filenames == NULL) { g_print ("Missing input filename.\n"); return EXIT_FAILURE; } for (i = 0; arv_option_filenames[i] != NULL; i++) { g_file_get_contents (arv_option_filenames[i], &xml, &size, NULL); if (xml != NULL) { ArvGcNode *node; g_print ("Loading '%s'.\n", arv_option_filenames[i]); genicam = arv_gc_new (NULL, xml, size); node = arv_gc_get_node (genicam, "RegAcquisitionCommand"); if (node != NULL) { g_print ("RegAcquisitionCommand address = 0x%llx - length = 0x%llx\n", (unsigned long long) arv_gc_register_get_address (ARV_GC_REGISTER (node), NULL), (unsigned long long) arv_gc_register_get_length (ARV_GC_REGISTER (node), NULL)); } node = arv_gc_get_node (genicam, "IntWidthIncrement"); if (node != NULL) { g_print ("IntWidthIncrement value = %lld\n", (long long) arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL)); } node = arv_gc_get_node (genicam, "WhitebalValueMin"); if (node != NULL) { g_print ("WhitebalValueMin value = %g\n", arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL)); } g_free (xml); g_object_unref (genicam); } else g_print ("File '%s' not found.\n", arv_option_filenames[i]); } return EXIT_SUCCESS; } aravis-0.8.34/tests/arvheartbeattest.c000066400000000000000000000104711475431451200177620ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static char *arv_option_camera_name = NULL; static char *arv_option_feature_name = "Gain"; static int arv_option_min = 0; static int arv_option_max = 10; static char *arv_option_debug_domains = "device"; static const GOptionEntry arv_option_entries[] = { { "name", 'n', 0, G_OPTION_ARG_STRING, &arv_option_camera_name, "Camera name", NULL }, { "feature", 'f', 0, G_OPTION_ARG_STRING, &arv_option_feature_name, "Feature name", NULL }, { "max", 'a', 0, G_OPTION_ARG_INT, &arv_option_max, "Max", NULL }, { "min", 'i', 0, G_OPTION_ARG_INT, &arv_option_min, "Min", NULL }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug domains", NULL }, { NULL } }; int main(int argc, char *argv[]) { ArvDevice *device; ArvStream *stream; ArvCamera *camera; ArvGcFeatureNode *feature; guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; GOptionContext *context; GError *error = NULL; void (*old_sigint_handler)(int); int i, payload; context = g_option_context_new (NULL); g_option_context_set_summary (context, "Test of heartbeat robustness while continuously changing a feature."); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); arv_debug_enable (arv_option_debug_domains); camera = arv_camera_new (arv_option_camera_name, &error); if (!ARV_IS_CAMERA (camera)) { printf ("Device not found%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); return EXIT_FAILURE; } device = arv_camera_get_device (camera); stream = arv_camera_create_stream (camera, NULL, NULL, &error); if (!ARV_IS_STREAM (stream)) { printf ("Invalid device%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); g_clear_object (&camera); return EXIT_FAILURE; } else { payload = arv_camera_get_payload (camera, NULL); if (ARV_IS_GV_STREAM (stream)) { g_object_set (stream, //"socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO, "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_FIXED, "socket-buffer-size", payload*6, "packet-timeout", 1000 * 1000, "frame-retention", 100 * 1000, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_ALWAYS, NULL); } for (i = 0; i < 100; i++) arv_stream_push_buffer(stream, arv_buffer_new(payload, NULL)); arv_camera_set_acquisition_mode(camera, ARV_ACQUISITION_MODE_CONTINUOUS, NULL); feature = ARV_GC_FEATURE_NODE (arv_device_get_feature (device, arv_option_feature_name)); arv_camera_start_acquisition (camera, NULL); old_sigint_handler = signal (SIGINT, set_cancel); while (!cancel) { ArvBuffer *buffer = arv_stream_timeout_pop_buffer(stream, 2000000); if (buffer) { g_usleep(10); arv_stream_push_buffer (stream, buffer); } if (!(++i%5)) { char *value; if ((i/100) % 2 == 0) value = g_strdup_printf ("%d", arv_option_min); else value = g_strdup_printf ("%d", arv_option_max); fprintf (stderr, "Setting %s from %s to %s\n", arv_option_feature_name, arv_gc_feature_node_get_value_as_string (feature, NULL), value); arv_gc_feature_node_set_value_from_string (feature, value, NULL); g_free (value); } } signal (SIGINT, old_sigint_handler); arv_stream_get_statistics (stream, &n_completed_buffers, &n_failures, &n_underruns); g_print ("\nCompleted buffers = %" G_GUINT64_FORMAT "\n", n_completed_buffers); g_print ("Failures = %" G_GUINT64_FORMAT "\n", n_failures); g_print ("Underruns = %" G_GUINT64_FORMAT "\n", n_underruns); arv_camera_stop_acquisition (camera, NULL); } g_object_unref (camera); return 0; } aravis-0.8.34/tests/arvnetworktest.c000066400000000000000000000032531475431451200175140ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include "../src/arvnetworkprivate.h" #define _ALEN 16 #define _ALENS "16" /* Put interface name at the end, it can be quite long under Windows */ #define _LINEFMT "%5s %" _ALENS "s %" _ALENS "s %" _ALENS "s %s\r\n" int main (int argc, char **argv){ GList *ifaces; GList *iface_iter; ifaces = arv_enumerate_network_interfaces (); if (!ifaces) { fprintf (stderr,"No network interfaces found (or enumeration failed)."); return 1; } printf(_LINEFMT,"proto","address","mask","broadcast","interface"); for (iface_iter=ifaces; iface_iter!=NULL; iface_iter=iface_iter->next){ ArvNetworkInterface* ani = (ArvNetworkInterface*)iface_iter->data; char addr[_ALEN]; char netmask[_ALEN]; char broadaddr[_ALEN]; int fam = arv_network_interface_get_addr(ani)->sa_family; if (fam==AF_INET){ inet_ntop (fam, &((struct sockaddr_in *) arv_network_interface_get_addr(ani))->sin_addr, &addr[0], _ALEN); inet_ntop (fam, &((struct sockaddr_in *) arv_network_interface_get_netmask(ani))->sin_addr, &netmask[0], _ALEN); inet_ntop (fam, &((struct sockaddr_in*)arv_network_interface_get_broadaddr(ani))->sin_addr, &broadaddr[0], _ALEN); printf (_LINEFMT, "IPv4", addr, netmask, broadaddr, arv_network_interface_get_name(ani)); } else if (fam==AF_INET6){ fprintf (stderr,"%s: IPv6 not yet reported correctly", arv_network_interface_get_name(ani)); } else { fprintf (stderr,"%s: unrecognized family %d", arv_network_interface_get_name(ani), fam); } } g_list_free_full (ifaces, (GDestroyNotify) arv_network_interface_free); return 0; } aravis-0.8.34/tests/arvroitest.c000066400000000000000000000127511475431451200166170ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include #include #define N_BUFFERS 20 #define WIDTH_MIN 100 #define HEIGHT_MIN 100 #define WIDTH_MAX 600 #define HEIGHT_MAX 600 #define SIZE_INC 20 typedef struct { ArvCamera *camera; ArvStream *stream; GMainLoop *main_loop; int buffer_count; int width, height; } ApplicationData; static gboolean cancel = FALSE; static void set_cancel (int signal) { cancel = TRUE; } static void new_buffer_cb (ArvStream *stream, ApplicationData *data) { ArvBuffer *buffer; buffer = arv_stream_try_pop_buffer (stream); if (buffer != NULL) { if (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_SUCCESS) data->buffer_count++; if (arv_buffer_get_image_width (buffer) != data->width || arv_buffer_get_image_height (buffer) != data->height) printf ("Size error! %dx%d (expected: %dx%d)\n", arv_buffer_get_image_width (buffer), arv_buffer_get_image_height (buffer), data->width, data->height); /* Image processing here */ arv_stream_push_buffer (stream, buffer); } } static gboolean framerate_task_cb (void *abstract_data) { ApplicationData *data = abstract_data; printf ("Frame rate = %g Hz\n", data->buffer_count / 10.0); data->buffer_count = 0; return TRUE; } static gboolean check_cancel_task_cb (void *abstract_data) { ApplicationData *data = abstract_data; if (cancel) { g_main_loop_quit (data->main_loop); return FALSE; } return TRUE; } static gboolean switch_roi (gpointer user_data) { ApplicationData *data = user_data; gint width; gint height; guint n_deleted; arv_camera_stop_acquisition (data->camera, NULL); n_deleted = arv_stream_stop_thread (data->stream, FALSE); g_assert (n_deleted == 0); data->width += SIZE_INC; if (data->width > WIDTH_MAX) data->width = WIDTH_MIN; data->height += SIZE_INC; if (data->height > HEIGHT_MAX) data->height = HEIGHT_MIN; arv_camera_set_region (data->camera, 0, 0, data->width, data->height, NULL); arv_camera_get_region (data->camera, NULL, NULL, &width, &height, NULL); g_assert (width == data->width); g_assert (height == data->height); printf ("image size set to %dx%d\n", width, height); arv_stream_start_thread (data->stream); arv_camera_start_acquisition (data->camera, NULL); return TRUE; } int main (int argc, char **argv) { ApplicationData data; GError *error = NULL; const char *camera_name = NULL; int i; data.buffer_count = 0; data.width = WIDTH_MIN; data.height = HEIGHT_MIN; if (argc > 2) { printf ("Usage: arv-roi-test \n"); return EXIT_FAILURE; } if (argc == 1) { g_print ("Looking for the first available camera\n"); } else { camera_name = argv[1]; g_print ("Looking for camera '%s'\n", camera_name); } data.camera = arv_camera_new (camera_name, &error); if (ARV_IS_CAMERA (data.camera)) { void (*old_sigint_handler)(int); gint max_payload; gint x, y, width, height; guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; arv_camera_set_region (data.camera, 0, 0, WIDTH_MAX, HEIGHT_MAX, NULL); max_payload = arv_camera_get_payload (data.camera, NULL); arv_camera_set_frame_rate (data.camera, 20.0, NULL); arv_camera_get_region (data.camera, &x, &y, &width, &height, NULL); arv_camera_set_region (data.camera, 0, 0, data.width, data.height, NULL); printf ("vendor name = %s\n", arv_camera_get_vendor_name (data.camera, NULL)); printf ("model name = %s\n", arv_camera_get_model_name (data.camera, NULL)); printf ("device id = %s\n", arv_camera_get_device_id (data.camera, NULL)); printf ("image x = %d\n", x); printf ("image y = %d\n", y); printf ("image width = %d\n", width); printf ("image height = %d\n", height); data.stream = arv_camera_create_stream (data.camera, NULL, NULL, &error); if (ARV_IS_STREAM (data.stream)) { g_signal_connect (data.stream, "new-buffer", G_CALLBACK (new_buffer_cb), &data); arv_stream_set_emit_signals (data.stream, TRUE); for (i = 0; i < N_BUFFERS; i++) arv_stream_push_buffer (data.stream, arv_buffer_new (max_payload, NULL)); arv_camera_set_acquisition_mode (data.camera, ARV_ACQUISITION_MODE_CONTINUOUS, NULL); arv_camera_start_acquisition (data.camera, NULL); data.main_loop = g_main_loop_new (NULL, FALSE); old_sigint_handler = signal (SIGINT, set_cancel); g_timeout_add_seconds (10, framerate_task_cb, &data); g_timeout_add_seconds (1, check_cancel_task_cb, &data); g_timeout_add (697, switch_roi, &data); g_main_loop_run (data.main_loop); signal (SIGINT, old_sigint_handler); g_main_loop_unref (data.main_loop); arv_stream_get_statistics (data.stream, &n_completed_buffers, &n_failures, &n_underruns); g_print ("Completed buffers = %" G_GUINT64_FORMAT "\n", n_completed_buffers); g_print ("Failures = %" G_GUINT64_FORMAT "\n", n_failures); g_print ("Underruns = %" G_GUINT64_FORMAT "\n", n_underruns); arv_camera_stop_acquisition (data.camera, NULL); arv_stream_set_emit_signals (data.stream, FALSE); g_object_unref (data.stream); } else { printf ("Can't create stream thread%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } g_object_unref (data.camera); } else { printf ("No camera found%s%s\n", error != NULL ? ": " : "", error != NULL ? error->message : ""); g_clear_error (&error); } return 0; } aravis-0.8.34/tests/arvziptest.c000066400000000000000000000024341475431451200166250ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static char **arv_option_filenames = NULL; static char *arv_option_debug_domains; static const GOptionEntry arv_option_entries[] = { { G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_FILENAME_ARRAY, &arv_option_filenames, NULL, NULL}, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_option_debug_domains, "Debug mode", NULL }, { NULL } }; int main (int argc, char **argv) { ArvZip *zip; char *buffer; size_t size; GOptionContext *context; GError *error = NULL; int i; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, arv_option_entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); arv_debug_enable (arv_option_debug_domains); if (arv_option_filenames == NULL) { g_print ("Missing input filename.\n"); return EXIT_FAILURE; } for (i = 0; arv_option_filenames[i] != NULL; i++) { g_file_get_contents (arv_option_filenames[i], &buffer, &size, NULL); if (buffer != NULL) { zip = arv_zip_new (buffer, size); arv_zip_free (zip); } } return EXIT_SUCCESS; } aravis-0.8.34/tests/buffer.c000066400000000000000000000057201475431451200156640ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static void simple_buffer_test (void) { ArvBuffer *buffer; size_t size; const void *data; buffer = arv_buffer_new (1024, NULL); g_assert (ARV_IS_BUFFER (buffer)); data = arv_buffer_get_data (buffer, &size); g_assert (data != NULL); g_assert (size == 0); g_assert (arv_buffer_get_payload_type (buffer) == ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN); g_assert (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_CLEARED); g_assert (arv_buffer_get_user_data (buffer) == NULL); g_assert (arv_buffer_get_frame_id (buffer) == 0); g_object_unref (buffer); } static void preallocated_buffer_test (void) { ArvBuffer *buffer; void *data = g_malloc (1024); buffer = arv_buffer_new (1024, data); g_assert (ARV_IS_BUFFER (buffer)); g_assert (arv_buffer_get_data (buffer, NULL) == data); g_assert (arv_buffer_get_payload_type (buffer) == ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN); g_assert (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_CLEARED); g_assert (arv_buffer_get_user_data (buffer) == NULL); g_object_unref (buffer); g_free (data); } static void full_buffer_destroy_func (void *data) { *((int *) data) = 4321; } static void full_buffer_test (void) { ArvBuffer *buffer; int value = 1234; const void *data; size_t size; buffer = arv_buffer_new_full (1024, NULL, &value, full_buffer_destroy_func); g_assert (ARV_IS_BUFFER (buffer)); data = arv_buffer_get_data (buffer, &size); g_assert (data != NULL); g_assert (size == 0); g_assert (arv_buffer_get_payload_type (buffer) == ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN); g_assert (arv_buffer_get_status (buffer) == ARV_BUFFER_STATUS_CLEARED); g_assert (arv_buffer_get_user_data (buffer) == &value); g_object_unref (buffer); g_assert (value == 4321); } static void timestamp (void) { ArvBuffer *buffer; buffer = arv_buffer_new (1024, NULL); g_assert (ARV_IS_BUFFER (buffer)); g_assert_cmpint (arv_buffer_get_timestamp (buffer), == , 0); arv_buffer_set_timestamp (buffer, 1234); g_assert_cmpint (arv_buffer_get_timestamp (buffer), == , 1234); g_assert_cmpint (arv_buffer_get_system_timestamp (buffer), == , 0); arv_buffer_set_system_timestamp (buffer, 1234); g_assert_cmpint (arv_buffer_get_system_timestamp (buffer), == , 1234); g_object_unref (buffer); } static void allocate (void) { ArvBuffer *buffer; const void *data; size_t size; buffer = arv_buffer_new_allocate (512); data = arv_buffer_get_data (buffer, &size); g_assert (data != NULL); g_assert (size == 0); g_object_unref (buffer); } int main (int argc, char *argv[]) { int result; g_test_init (&argc, &argv, NULL); g_test_add_func ("/buffer/simple-buffer", simple_buffer_test); g_test_add_func ("/buffer/preallocated-buffer", preallocated_buffer_test); g_test_add_func ("/buffer/full-buffer", full_buffer_test); g_test_add_func ("/buffer/timestamp", timestamp); g_test_add_func ("/buffer/allocate", allocate); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/cpp.cc000066400000000000000000000002711475431451200153340ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include /* Dumb test program, just to make sure aravis headers are c++ compatible */ int main (int argc, char **argv) { return 0; } aravis-0.8.34/tests/data/000077500000000000000000000000001475431451200151545ustar00rootroot00000000000000aravis-0.8.34/tests/data/genicam.xml000066400000000000000000000412241475431451200173040ustar00rootroot00000000000000 description tooltip display_name Available Available RWFloat RWBoolean RWInteger Enumeration ChunkInt ChunkString ChunkFloat ChunkBoolean Guru 0.1 -10.0 10.0 2.0 -20.0 20.0 3.0 RWFloat P_RWFloat_Min P_RWFloat_Max P_RWFloat_Inc Expert 1 23 RWBooleanValue 23 42 Invisible 1 -10 10 2 -20 20 3 RWInteger P_RWInteger_Min P_RWInteger_Max P_RWInteger_Inc 0 1 NotAvailable 2 NotAvailable 3 EnumerationValue 0 0 1 0 0x50
0x1000
IntRegisterAddress 8 RO NoCache 5 Unsigned Pa BigEndian
0x1000
IntRegisterAddress 8 RW NoCache 5 Unsigned Pa BigEndian
0x2000
0xff 8 RW NoCache 5 Unsigned BigEndian
0x1000
8 RW NoCache 5 BigEndian Device
0x1004
4 RW NoCache 5 BigEndian Signed Device
0x1004
4 RW NoCache 5 BigEndian Unsigned Device
0x1000
2 RW NoCache 5 BigEndian Signed Device
0x1000
2 RW NoCache 5 BigEndian Unsigned Device
0x1002
2 RW NoCache 5 BigEndian 7 4 Signed V Device
0x1002
2 RW NoCache 5 BigEndian 7 4 Unsigned A Device
0x1000
4 RW NoCache LittleEndian mA Device
0x1000
8 RW NoCache LittleEndian mV Device
StringReg Hello World!
0x1002
4 RW NoCache Device
(0x12345678 & 0x10305070) (0 && 1)?2:3 0x1234 X 10 TEN + X TEN * XPLUS2 4 10 20 5 Bug699228_T Bug699228_L Bug699228_FILTER ((((L%2) = 0) &&((T%2) = 0) && (FILTER = 0))?0:( (((L%2) = 0) &&((T%2) = 1) && (FILTER = 1))?0:( (((L%2) = 1) &&((T%2) = 0) && (FILTER = 2))?0:( (((L%2) = 1) &&((T%2) = 1) && (FILTER = 3))?0:( (((L%2) = 0) &&((T%2) = 0) && (FILTER = 1))?1:( (((L%2) = 0) &&((T%2) = 1) && (FILTER = 0))?1:( (((L%2) = 1) &&((T%2) = 0) && (FILTER = 3))?1:( (((L%2) = 1) &&((T%2) = 1) && (FILTER = 2))?1:( (((L%2) = 0) &&((T%2) = 0) && (FILTER = 2))?2:( (((L%2) = 0) &&((T%2) = 1) && (FILTER = 3))?2:( (((L%2) = 1) &&((T%2) = 0) && (FILTER = 0))?2:( (((L%2) = 1) &&((T%2) = 1) && (FILTER = 1))?2:( (((L%2) = 0) &&((T%2) = 0) && (FILTER = 3))?3:( (((L%2) = 0) &&((T%2) = 1) && (FILTER = 2))?3:( (((L%2) = 1) &&((T%2) = 0) && (FILTER = 1))?3:( (((L%2) = 1) &&((T%2) = 1) && (FILTER = 0))?3:( 4 ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )) Indicates whether a live grab is under way Invisible 0 0 1
0xe8
16 RW Device
0x00
4 R IntPort NoCache Unsigned BigEndian
12345678
0x00
8 R FloatPort NoCache Unsigned BigEndian
12345679
0x00
64 R StringPort
87654321 ChunkBooleanReg 1 0
0x00
1 R BooleanPort NoCache Unsigned BigEndian
12345680 0.5 * FROM 2.0 * TO ROIntRegisterA 0.5 * FROM 2.0 * TO ConverterValue 10 * FROM 0.1 * TO ConverterValue 100 4 + FROM 4 + TO Enumeration 10 TEN TO * TEN * XPLUS2 FROM / XPLUS2 IntConverterTestSubAndConstantValue 100 TableSelector 100 200 600 20 FloatTableSelector 100.1 200.2 600.6 20 MultiplexerSelector MultiplexerEntry0 MultiplexerEntry1 MultiplexerDefaultValue 20 100 200 600 FloatMultiplexerSelector FloatMultiplexerEntry0 FloatMultiplexerEntry1 FloatMultiplexerDefaultValue 20 100.1 200.2 600.6
100
20
IndexedRegisterIndex1 IndexedRegisterIndex2 IndexedRegisterIndex3 4
5 3 2 IntegerLock 0 FloatLock 0 BooleanLock 0 0 0 0 0 RWRegister RW RWRegister RO RORegister RW RORegister RO RWStructEntry RW RWStructEntry RO ROStructEntry RW ROStructEntry RO
0x1100
4 RW Device
0x1100
4 RO Device
0x1100
4 RW Device 0 1 RO
aravis-0.8.34/tests/data/pipelines.txt000066400000000000000000000036631475431451200177150ustar00rootroot00000000000000Theora - udp ~~~~~~~~~~~~ aravissrc width=256 height=256 v-binning=4 h-binning=4 frame-rate=25.0 ! ffmpegcolorspace ! theoraenc bitrate=150 ! udpsink host=127.0.0.1 port=1234 udpsrc port=1234 ! theoradec ! xvimagesink Mjpeg - tcp ~~~~~~~~~~~ aravissrc width=256 height=256 v-binning=4 h-binning=4 ! ffmpegcolorspace ! jpegenc quality=50 ! multipartmux ! tcpserversink port=1234 tcpclientsrc host=127.0.0.1 port=1234 ! multipartdemux ! jpegdec ! xvimagesink Smoke - tcp ~~~~~~~~~~~ aravissrc width=256 height=256 v-binning=4 h-binning=4 ! ffmpegcolorspace ! smokeenc ! multipartmux ! tcpserversink port=1234 tcpclientsrc host=127.0.0.1 port=1234 ! multipartdemux ! smokedec ! xvimagesink Direct output ~~~~~~~~~~~~~ aravissrc width=256 height=256 v-binning=4 h-binning=4 ! ffmpegcolorspace ! xvimagesink RTP ~~~ gstrtpbin name=rtpbin aravissrc width=256 height=256 v-binning=4 h-binning=4 ! ffmpegcolorspace ! queue ! x264enc byte-stream=true bitrate=300 ! rtph264pay ! rtpbin.send_rtp_sink_0 rtpbin.send_rtp_src_0 ! udpsink port=5000 host=127.0.0.1 ts-offset=0 name=vrtpsink rtpbin.send_rtcp_src_0 ! udpsink port=5001 host=127.0.0.1 sync=false async=false name=vrtcpsink udpsrc port=5005 name=vrtpsrc ! rtpbin.recv_rtcp_sink_0 gstrtpbin name=rtpbin latency=200 udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H264" port=5000 ! rtpbin.recv_rtp_sink_0 rtpbin. ! rtph264depay ! ffdec_h264 ! xvimagesink udpsrc port=5001 ! rtpbin.recv_rtcp_sink_0 rtpbin.send_rtcp_src_0 ! udpsink port=5005 host=127.0.0.1 sync=false async=false udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1 rtpbin.send_rtcp_src_1 ! udpsink port=5007 host=127.0.0.1 sync=false async=false sdp file: v=0 o=- 1188340656180883 1 IN IP4 127.0.0.1 s=Session streamed by GStreamer i=server.sh t=0 0 a=tool:GStreamer a=type:broadcast m=video 5000 RTP/AVP 96 c=IN IP4 127.0.0.1 a=rtpmap:96 H264/90000 aravis-0.8.34/tests/dom.c000066400000000000000000000021671475431451200151740ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include static void child_list_test (void) { ArvDevice *device; ArvGc *genicam; GError *error = NULL; ArvGcNode *node; ArvDomNodeList *children; ArvDomNode *item; unsigned int length, i; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "Root"); children = arv_dom_node_get_child_nodes(ARV_DOM_NODE(node)); length = arv_dom_node_list_get_length(children); g_assert (length > 0); for (i = 0; i < length; ++i) { item = arv_dom_node_list_get_item(children, i); g_assert (ARV_IS_DOM_NODE (item)); } g_object_unref (device); } int main (int argc, char *argv[]) { int result; g_test_init (&argc, &argv, NULL); arv_set_fake_camera_genicam_filename (GENICAM_FILENAME); g_test_add_func ("/dom/child-list", child_list_test); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/evaluator.c000066400000000000000000000310411475431451200164100ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include typedef struct { const char *test_name; const char *expression; gint64 result_int64; gboolean expected_error_int64; double result_double; } ExpressionTestData; static const ExpressionTestData expression_test_data[] = { {"/evaluator/addition", "10+2", 12, FALSE, 12.0}, {"/evaluator/parentheses", "(24+2)*2", 52, FALSE, 52.0}, {"/evaluator/precedence", "24+2*8", 40, FALSE, 40.0}, {"/evaluator/ternary-true", "1=1?1:0", 1, FALSE, 1.0}, {"/evaluator/ternary-false", "1=0?1:0", 0, FALSE, 0.0}, {"/evaluator/greater-true", "2>1", 1, FALSE, 1.0}, {"/evaluator/greater-false", "2>2", 0, FALSE, 0.0}, {"/evaluator/lower-true", "1<2", 1, FALSE, 1.0}, {"/evaluator/lower-false", "2<2", 0, FALSE, 0.0}, {"/evaluator/substraction", "10-8", 2, FALSE, 2.0}, {"/evaluator/substraction-float", "10.1-8.1", 2, FALSE, 2.0}, {"/evaluator/multiplication", "2.5*4", 8, FALSE, 10.0}, {"/evaluator/division", "10/4", 2, FALSE, 2.5}, {"/evaluator/division-float", "10.0/4", 2, FALSE, 2.5}, {"/evaluator/simple-minus", "-1", -1, FALSE, -1.0}, {"/evaluator/minus", "4*-3", -12, FALSE, -12.0}, {"/evaluator/plus", "4*+3", 12, FALSE, 12.0}, {"/evaluator/remainder", "10%3", 1, FALSE, 1.0}, {"/evaluator/power", "2**10", 1024, FALSE, 1024.0}, {"/evaluator/power-precedence", "2**10*2", 2048, FALSE, 2048.0}, {"/evaluator/ln", "LN(E)", 1, TRUE, 1.0}, {"/evaluator/lg", "LG(10)", 1, TRUE, 1.0}, {"/evaluator/sqrt", "SQRT(16)", 4, TRUE, 4.0}, {"/evaluator/tan", "TAN(0)", 0, TRUE, 0.0}, {"/evaluator/atan", "ATAN(0)", 0, TRUE, 0.0}, {"/evaluator/exp", "EXP(1)", 2, TRUE, G_E}, {"/evaluator/trunc-plus", "TRUNC(10.7)", 10, TRUE, 10.0}, {"/evaluator/trunc-minus", "TRUNC(-11.9)", -11, TRUE, -11.0}, {"/evaluator/round-plus", "ROUND(10.1)", 10, TRUE, 10.0}, {"/evaluator/round-plus-plus", "ROUND(10.9)", 11, TRUE, 11.0}, {"/evaluator/round-minus", "ROUND(-20.1)", -20, TRUE, -20.0}, {"/evaluator/round-minus-minus", "ROUND(-20.9)", -21, TRUE, -21.0}, {"/evaluator/round-plus-with-precision", "ROUND(10.11, 1)", 10, TRUE, 10.1}, {"/evaluator/round-plus-plus-with-precision", "ROUND(10.99, 1)", 11, TRUE, 11.0}, {"/evaluator/round-minus-with-precision", "ROUND(-20.11, 1)", -20, TRUE, -20.1}, {"/evaluator/round-minus-minus-with-precision", "ROUND(-20.99, 1)", -21, TRUE, -21.0}, {"/evaluator/floor-plus", "FLOOR(10.7)", 10, TRUE, 10.0}, {"/evaluator/floor-minus", "FLOOR(-11.9)", -12, TRUE, -12.0}, {"/evaluator/ceil-plus", "CEIL(10.7)", 11, TRUE, 11.0}, {"/evaluator/ceil-minus", "CEIL(-11.9)", -11, TRUE, -11.0}, {"/evaluator/sign-plus", "SGN(2)", 1, FALSE, 1.0}, {"/evaluator/sign-minus", "SGN(-2)", -1, FALSE, -1.0}, {"/evaluator/sign-zero", "SGN(0)", 0, FALSE, 0.0}, {"/evaluator/sign-plus-float", "SGN(2.0)", 1, FALSE, 1.0}, {"/evaluator/sign-minus-floa", "SGN(-2.0)", -1, FALSE, -1.0}, {"/evaluator/sign-zero-float", "SGN(0.0)", 0, FALSE, 0.0}, {"/evaluator/neg", "NEG(-1)", 1, FALSE, 1.0}, {"/evaluator/neg-float", "NEG(-2.5)", 2, FALSE, 2.5}, {"/evaluator/and", "255 & 8", 8, FALSE, 8.0}, {"/evaluator/or", "128 | 8", 136, FALSE, 136.0}, {"/evaluator/xor", "3 ^ 1", 2, FALSE, 2.0}, {"/evaluator/not", "~255", -256, FALSE, -256.0}, {"/evaluator/not-equal-true", "1<>2", 1, FALSE, 1.0}, {"/evaluator/not-equal-false", "1<>1", 0, FALSE, 0.0}, {"/evaluator/equal-true", "1=1", 1, FALSE, 1.0}, {"/evaluator/equal-false", "1=2", 0, FALSE, 0.0}, {"/evaluator/less-true", "1<2", 1, FALSE, 1.0}, {"/evaluator/less-false", "1<1", 0, FALSE, 0.0}, {"/evaluator/greater-or-equal-true", "2>=2", 1, FALSE, 1.0}, {"/evaluator/greater-or-equal-false", "1>=2", 0, FALSE, 0.0}, {"/evaluator/less-or-equal-true", "2<=2", 1, FALSE, 1.0}, {"/evaluator/less-or-equal-false", "2<=1", 0, FALSE, 0.0}, {"/evaluator/not-equal-true-double", "2.1<>2", 0, FALSE, 1.0}, {"/evaluator/not-equal-false-double", "1.0<>1", 0, FALSE, 0.0}, {"/evaluator/equal-true-double", "1.0=1", 1, FALSE, 1.0}, {"/evaluator/equal-false-double", "2.1=2", 1, FALSE, 0.0}, {"/evaluator/greater-true-double", "2.1>2", 0, FALSE, 1.0}, {"/evaluator/greater-false-double", "2>2.1", 0, FALSE, 0.0}, {"/evaluator/less-true-double", "2<2.1", 0, FALSE, 1.0}, {"/evaluator/less-false-double", "2.1<2", 0, FALSE, 0.0}, {"/evaluator/greater-or-equal-true-double", "2.1>=2", 1, FALSE, 1.0}, {"/evaluator/greater-or-equal-false-double", "2>=2.1", 1, FALSE, 0.0}, {"/evaluator/less-or-equal-true-double", "2<=2.1", 1, FALSE, 1.0}, {"/evaluator/less-or-equal-false-double", "2.1<=2", 1, FALSE, 0.0}, {"/evaluator/logical-and-true", "(2=2)&&(1=1)", 1, FALSE, 1.0}, {"/evaluator/logical-and-false", "(2=2)&&(1=2)", 0, FALSE, 0.0}, {"/evaluator/logical-or-true", "(2=2)||(1=2)", 1, FALSE, 1.0}, {"/evaluator/logical-or-false", "(1=2)||(0=2)", 0, FALSE, 0.0}, {"/evaluator/left-shift", "1<<4", 16, FALSE, 16.0}, {"/evaluator/right-shift", "16>>4", 1, FALSE, 1.0}, {"/evaluator/cos", "COS(PI)", -1, TRUE, -1.0}, {"/evaluator/sin", "SIN(-PI/2)", -1, TRUE, -1.0}, {"/evaluator/acos", "ACOS(1)", 0, TRUE, 0.0}, {"/evaluator/asin", "ASIN(0)", 0, TRUE, 0.0}, {"/evaluator/abs", "ABS(-1)", 1, TRUE, 1.0}, {"/evaluator/abs-float", "ABS(-10.3)", 10, TRUE, 10.3}, {"/evaluator/abs64bits", "ABS(-10000000000)", 10000000000, TRUE, 1e10}, {"/evaluator/bugs/681048-remaining-op", "(0 & 1)=0?((0 & 1)+2):1", 2, FALSE, 2.0}, {"/evaluator/bugs/743025-division-by-zero", "(4/(20/10000))", 2000, TRUE, 2000.0}, {"/evaluator/bugs/gh98-not-operatorprecedence", "(~(~0xC2000221|0xFEFFFFFF)) ? 2:0", 0, FALSE, 0.0}, }; static void expression_test (ExpressionTestData *data) { ArvEvaluator *evaluator; const char *expression; gint64 v_int64; double v_double; GError *error = NULL; evaluator = arv_evaluator_new (data->expression); v_int64 = arv_evaluator_evaluate_as_int64 (evaluator, &error); if (data->expected_error_int64) { g_assert (error != NULL); g_clear_error (&error); } else { g_assert (error == NULL); g_assert_cmpint (v_int64, ==, data->result_int64); } v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error == NULL); g_assert_cmpfloat (v_double, ==, data->result_double); expression = arv_evaluator_get_expression (evaluator); g_assert_cmpstr (expression, ==, data->expression); g_object_unref (evaluator); } static void set_get_expression_test (void) { ArvEvaluator *evaluator; const char *expression; evaluator = arv_evaluator_new (NULL); expression = arv_evaluator_get_expression (evaluator); g_assert_cmpstr (expression, == , NULL); arv_evaluator_set_expression (evaluator, "1+1"); expression = arv_evaluator_get_expression (evaluator); g_assert_cmpstr (expression, == , "1+1"); arv_evaluator_set_expression (evaluator, NULL); expression = arv_evaluator_get_expression (evaluator); g_assert_cmpstr (expression, == , NULL); g_object_unref (evaluator); } static void set_double_variable_test (void) { ArvEvaluator *evaluator; GError *error = NULL; double v_double; evaluator = arv_evaluator_new ("V_DBLA+V_DBLB"); arv_evaluator_set_double_variable (evaluator, "V_DBLA", 123.0); arv_evaluator_set_double_variable (evaluator, "V_DBLB", 0.4); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert_cmpfloat (v_double, ==, 123.4); g_assert (error == NULL); g_object_unref (evaluator); } static void set_int64_variable_test (void) { ArvEvaluator *evaluator; GError *error = NULL; gint64 v_int64; evaluator = arv_evaluator_new ("A_1"); arv_evaluator_set_int64_variable (evaluator, "A_1", 123); v_int64 = arv_evaluator_evaluate_as_int64 (evaluator, &error); g_assert_cmpint (v_int64, ==, 123); g_assert (error == NULL); arv_evaluator_set_int64_variable (evaluator, "A_1", 0x7fffffffffffffff); v_int64 = arv_evaluator_evaluate_as_int64 (evaluator, &error); g_assert_cmpint (v_int64, ==, 0x7fffffffffffffff); g_assert (error == NULL); g_object_unref (evaluator); } static void sub_expression_test (void) { ArvEvaluator *evaluator; GError *error = NULL; double v_double; const char *sub_expression; evaluator = arv_evaluator_new ("ZERO5 + SUB_EXP"); arv_evaluator_set_sub_expression (evaluator, "ZERO5", "0.5"); arv_evaluator_set_sub_expression (evaluator, "SUB_EXP", "2*X"); arv_evaluator_set_int64_variable (evaluator, "X", 6); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert_cmpfloat (v_double, ==, 12.5); g_assert (error == NULL); sub_expression = arv_evaluator_get_sub_expression (evaluator, "SUB_EXP"); g_assert_cmpstr (sub_expression, ==, "2*X"); arv_evaluator_set_sub_expression (evaluator, "SUB_EXP", "2 + X"); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert_cmpfloat (v_double, ==, 8.5); g_assert (error == NULL); arv_evaluator_set_sub_expression (evaluator, "SUB_EXP", NULL); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); sub_expression = arv_evaluator_get_sub_expression (evaluator, "SUB_EXP"); g_assert (sub_expression == NULL); arv_evaluator_set_sub_expression (evaluator, "SUB_EXP", "SUB_EXP"); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (evaluator); } static void constant_test (void) { ArvEvaluator *evaluator; GError *error = NULL; double v_double; const char *constant; evaluator = arv_evaluator_new ("ZERO5 + TEN * X"); arv_evaluator_set_constant (evaluator, "ZERO5", "0.5"); arv_evaluator_set_constant (evaluator, "TEN", "10"); arv_evaluator_set_int64_variable (evaluator, "X", 6); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert_cmpfloat (v_double, ==, 60.5); g_assert (error == NULL); constant = arv_evaluator_get_constant (evaluator, "TEN"); g_assert_cmpstr (constant, ==, "10"); arv_evaluator_set_constant (evaluator, "TEN", "20"); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert_cmpfloat (v_double, ==, 120.5); g_assert (error == NULL); arv_evaluator_set_constant (evaluator, "TEN", NULL); v_double = arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); constant = arv_evaluator_get_constant (evaluator, "TEN"); g_assert (constant == NULL); g_object_unref (evaluator); } static void empty_test (void) { ArvEvaluator *evaluator; GError *error = NULL; gint64 v_int64; evaluator = arv_evaluator_new (NULL); arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); arv_evaluator_set_expression (evaluator, "10 * 3"); v_int64 = arv_evaluator_evaluate_as_int64 (evaluator, &error); g_assert_cmpint (v_int64, ==, 30); g_assert (error == NULL); arv_evaluator_set_expression (evaluator, NULL); arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (evaluator); } static void error_test (void) { ArvEvaluator *evaluator; GError *error = NULL; evaluator = arv_evaluator_new ("("); arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); arv_evaluator_evaluate_as_int64 (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (evaluator); evaluator = arv_evaluator_new ("UNKNOWN("); arv_evaluator_evaluate_as_double (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); arv_evaluator_evaluate_as_int64 (evaluator, &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (evaluator); } int main (int argc, char *argv[]) { int result; int i; g_test_init (&argc, &argv, NULL); for (i = 0; i < G_N_ELEMENTS (expression_test_data); i++) g_test_add_data_func (expression_test_data[i].test_name, &expression_test_data[i], (void *) expression_test); g_test_add_func ("/evaluator/set-get-expression", set_get_expression_test); g_test_add_func ("/evaluator/double-variable", set_double_variable_test); g_test_add_func ("/evaluator/int64-variable", set_int64_variable_test); g_test_add_func ("/evaluator/sub-expression", sub_expression_test); g_test_add_func ("/evaluator/constant", constant_test); g_test_add_func ("/evaluator/empty", empty_test); g_test_add_func ("/evaluator/error", error_test); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/exception.py000066400000000000000000000026551475431451200166230ustar00rootroot00000000000000# SPDX-License-Identifier:Unlicense import gi import os gi.require_version ('Aravis', '0.8') from gi.repository import Aravis,GLib Aravis.set_fake_camera_genicam_filename (os.getenv ('FAKE_GENICAM_PATH')) Aravis.enable_interface ("Fake") try: camera = Aravis.Camera.new ("NoCamera") except GLib.Error as err: assert err.matches (Aravis.device_error_quark(), Aravis.DeviceError.NOT_FOUND) camera = Aravis.Camera.new ("Fake_1") try: camera.get_integer ("Width") camera.get_integer ("NoFeature") assert False # Not reached camera.get_integer ("Height") except GLib.Error as err: assert err.matches (Aravis.device_error_quark(), Aravis.DeviceError.FEATURE_NOT_FOUND) try: camera.get_float ("NoFeature") assert False # Not reached except GLib.Error as err: assert err.matches (Aravis.device_error_quark(), Aravis.DeviceError.FEATURE_NOT_FOUND) try: camera.get_string ("NoFeature") assert False # Not reached except GLib.Error as err: assert err.matches (Aravis.device_error_quark(), Aravis.DeviceError.FEATURE_NOT_FOUND) try: camera.get_boolean ("NoFeature") assert False # Not reached except GLib.Error as err: assert err.matches (Aravis.device_error_quark(), Aravis.DeviceError.FEATURE_NOT_FOUND) value = camera.get_boolean ("TestBoolean") assert value is False camera.set_boolean ("TestBoolean", True) value = camera.get_boolean ("TestBoolean") assert value is True aravis-0.8.34/tests/fake.c000066400000000000000000000610751475431451200153260ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static void discovery_test (void) { int n_devices; int i; n_devices = arv_get_n_devices (); for (i = 0; i < n_devices; i++) { if (g_strcmp0 (arv_get_device_vendor (i), "Aravis") == 0 && g_strcmp0 (arv_get_device_model (i), "Fake") == 0 && g_strcmp0 (arv_get_device_serial_nbr (i), "1") == 0 && g_strcmp0 (arv_get_device_manufacturer_info (i), "none") == 0 && g_strcmp0 (arv_get_device_id (i), "Fake_1") == 0 && g_strcmp0 (arv_get_device_physical_id (i), "Fake_1") == 0) return; } g_assert_not_reached (); } static void trigger_registers_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; gint64 address; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "TriggerModeRegister"); g_assert (ARV_IS_GC_NODE (node)); address = arv_gc_register_get_address (ARV_GC_REGISTER (node), NULL); g_assert_cmpint (address, ==, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE); address = arv_gc_register_get_address (ARV_GC_REGISTER (arv_gc_get_node (genicam, "TriggerSourceRegister")), NULL); g_assert_cmpint (address, ==, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOURCE); address = arv_gc_register_get_address (ARV_GC_REGISTER (arv_gc_get_node (genicam, "TriggerActivationRegister")), NULL); g_assert_cmpint (address, ==, ARV_FAKE_CAMERA_REGISTER_TRIGGER_ACTIVATION); address = arv_gc_register_get_address (ARV_GC_REGISTER (arv_gc_get_node (genicam, "TriggerSoftwareCommandRegister")), NULL); g_assert_cmpint (address, ==, ARV_FAKE_CAMERA_REGISTER_TRIGGER_SOFTWARE); arv_device_set_string_feature_value (device, "TriggerSelector", "AcquisitionStart", NULL); address = arv_gc_register_get_address (ARV_GC_REGISTER (node), NULL); g_assert_cmpint (address, ==, ARV_FAKE_CAMERA_REGISTER_TRIGGER_MODE + ARV_FAKE_CAMERA_REGISTER_ACQUISITION_START_OFFSET); g_object_unref (device); } static void registers_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; ArvGcNode *node_0_15; ArvGcNode *node_16_31; ArvGcNode *node_15; ArvGcNode *node_0_31; GError *error = NULL; gint64 value; gint64 address; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); arv_gc_set_range_check_policy (genicam, ARV_RANGE_CHECK_POLICY_ENABLE); node = arv_gc_get_node (genicam, "TestRegister"); g_assert (ARV_IS_GC_NODE (node)); address = arv_gc_register_get_address (ARV_GC_REGISTER (node), &error); g_assert_cmpint (address, ==, 0x1f0); g_assert (error == NULL); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 0x12345678, &error); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 0x12345678); g_assert (error == NULL); node_0_15 = arv_gc_get_node (genicam, "StructEntry_0_15"); g_assert (ARV_IS_GC_NODE (node_0_15)); node_16_31 = arv_gc_get_node (genicam, "StructEntry_16_31"); g_assert (ARV_IS_GC_NODE (node_16_31)); node_15 = arv_gc_get_node (genicam, "StructEntry_15"); g_assert (ARV_IS_GC_NODE (node_15)); node_0_31 = arv_gc_get_node (genicam, "StructEntry_0_31"); g_assert (ARV_IS_GC_NODE (node_0_31)); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_0_15), NULL); g_assert_cmpint (value, ==, address); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_16_31), NULL); g_assert_cmpint (value, ==, address); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_15), NULL); g_assert_cmpint (value, ==, address); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_0_31), NULL); g_assert_cmpint (value, ==, address); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_0_15), NULL); g_assert_cmpint (value, ==, 0x1234); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_16_31), NULL); g_assert_cmpint (value, ==, 0x5678); arv_gc_integer_set_value (ARV_GC_INTEGER (node_16_31), 0x10101010, &error); g_assert (error != NULL); g_assert (error->code == ARV_GC_ERROR_OUT_OF_RANGE); g_clear_error (&error); arv_gc_integer_set_value (ARV_GC_INTEGER (node_0_31), 0x10101010, &error); g_assert (error == NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_0_15), NULL); g_assert_cmpint (value, ==, 0x1010); arv_gc_integer_set_value (ARV_GC_INTEGER (node_0_15), 0xabcdefaa, &error); g_assert (error != NULL); g_assert (error->code == ARV_GC_ERROR_OUT_OF_RANGE); g_clear_error (&error); arv_gc_integer_set_value (ARV_GC_INTEGER (node_0_31), 0xabcdefaa, &error); g_assert (error == NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_0_15), NULL); g_assert_cmpint (value, ==, 0xabcd); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_16_31), NULL); g_assert_cmpint (value, ==, -4182); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_15), NULL); g_assert_cmpint (value, ==, 0x1); arv_gc_integer_set_value (ARV_GC_INTEGER (node_15), 0xff, &error); g_assert (error != NULL); g_assert (error->code == ARV_GC_ERROR_OUT_OF_RANGE); g_clear_error (&error); arv_gc_integer_set_value (ARV_GC_INTEGER (node_0_31), 0xffffff, &error); g_assert (error == NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_15), NULL); g_assert_cmpint (value, ==, 1); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_16_31), NULL); g_assert_cmpint (value, ==, -1); arv_gc_integer_set_value (ARV_GC_INTEGER (node_16_31), 0xff, NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_16_31), NULL); g_assert_cmpint (value, ==, 0xff); arv_gc_integer_set_value (ARV_GC_INTEGER (node_15), 0x0, NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_15), NULL); g_assert_cmpint (value, ==, 0); g_object_unref (device); } static void fake_device_test (void) { ArvDevice *device; GError *error = NULL; int int_value; double dbl_value; double boolean_value; gint64 minimum, maximum; gint64 *values; const char **string_values; const char *string_value; guint n_values; double float_minimum, float_maximum; const char *genicam; gsize size; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam_xml (device, &size); g_assert (genicam != NULL); /* Check default */ int_value = arv_device_get_integer_feature_value (device, "Width", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_WIDTH_DEFAULT); arv_device_set_integer_feature_value (device, "Width", 1024, NULL); int_value = arv_device_get_integer_feature_value (device, "Width", NULL); g_assert_cmpint (int_value, ==, 1024); /* Check default */ int_value = arv_device_get_integer_feature_value (device, "Height", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_HEIGHT_DEFAULT); arv_device_set_integer_feature_value (device, "Height", 1024, NULL); int_value = arv_device_get_integer_feature_value (device, "Height", NULL); g_assert_cmpint (int_value, ==, 1024); int_value = arv_device_get_integer_feature_value (device, "BinningHorizontal", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "BinningVertical", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_BINNING_VERTICAL_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "PixelFormat", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_PIXEL_FORMAT_DEFAULT); dbl_value = arv_device_get_float_feature_value (device, "AcquisitionFrameRate", NULL); g_assert_cmpfloat (dbl_value, ==, ARV_FAKE_CAMERA_ACQUISITION_FRAME_RATE_DEFAULT); dbl_value = arv_device_get_float_feature_value (device, "ExposureTimeAbs", NULL); g_assert_cmpfloat (dbl_value, ==, ARV_FAKE_CAMERA_EXPOSURE_TIME_US_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "GainRaw", NULL); g_assert_cmpint (int_value, ==, 0); int_value = arv_device_get_integer_feature_value (device, "GainAuto", NULL); g_assert_cmpint (int_value, ==, 1); int_value = arv_device_get_integer_feature_value (device, "PayloadSize", NULL); g_assert_cmpint (int_value, ==, 1024 * 1024); arv_device_set_boolean_feature_value (device, "TestBoolean", FALSE, NULL); boolean_value = arv_device_get_boolean_feature_value (device, "TestBoolean", NULL); g_assert_cmpint (boolean_value, ==, FALSE); int_value = arv_device_get_integer_feature_value (device, "TestRegister", NULL); g_assert_cmpint (int_value, ==, 123); arv_device_set_boolean_feature_value (device, "TestBoolean", TRUE, NULL); boolean_value = arv_device_get_boolean_feature_value (device, "TestBoolean", NULL); g_assert_cmpint (boolean_value, ==, TRUE); int_value = arv_device_get_integer_feature_value (device, "TestRegister", NULL); g_assert_cmpint (int_value, ==, 321); arv_device_get_integer_feature_bounds (device, "Width", &minimum, &maximum, NULL); g_assert_cmpint (minimum, ==, 1); g_assert_cmpint (maximum, ==, 2048); arv_device_get_float_feature_bounds (device, "ExposureTimeAbs", &float_minimum, &float_maximum, NULL); g_assert_cmpfloat (float_minimum, ==, 10.0); g_assert_cmpfloat (float_maximum, ==, 10000000.0); arv_device_set_float_feature_value (device, "ExposureTimeAbs", 20.0, NULL); dbl_value = arv_device_get_float_feature_value (device, "ExposureTimeAbs", NULL); g_assert_cmpfloat (dbl_value, ==, 20.0); values = arv_device_dup_available_enumeration_feature_values (device, "GainAuto", &n_values, NULL); g_assert (values != NULL); g_assert_cmpint (n_values, ==, 3); g_free (values); string_values = arv_device_dup_available_enumeration_feature_values_as_strings (device, "GainAuto", &n_values, NULL); g_assert (string_values != NULL); g_assert_cmpint (n_values, ==, 3); g_free (string_values); arv_device_set_string_feature_value (device, "TestStringReg", "String", NULL); string_value = arv_device_get_string_feature_value (device, "TestStringReg", NULL); g_assert_cmpstr (string_value, ==, "String"); g_object_unref (device); } static void fake_device_error_test (void) { ArvDevice *device; GError *error = NULL; int int_value; double boolean_value; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); int_value = arv_device_get_integer_feature_value (device, "Unknown", &error); g_assert_cmpint (int_value, ==, 0); g_assert (error != NULL); g_clear_error (&error); boolean_value = arv_device_get_boolean_feature_value (device, "Unknown", &error); g_assert_cmpint (boolean_value, ==, 0); g_assert (error != NULL); g_clear_error (&error); g_object_unref (device); } static void fill_pattern_cb (ArvBuffer *buffer, void *fill_pattern_data, guint32 exposure_time_us, guint32 gain, ArvPixelFormat pixel_format) { gint *counter = fill_pattern_data; (*counter)++; } static void fake_stream_test (void) { ArvCamera *camera; ArvDevice *device; ArvFakeCamera *fake_camera; ArvStream *stream; ArvBuffer *buffer; GError *error = NULL; guint64 n_completed_buffers; guint64 n_failures; guint64 n_underruns; gint n_input_buffers; gint n_output_buffers; gint payload; gint counter = 0; guint n_infos; const char *info_name; GType info_type; camera = arv_camera_new ("Fake_1", &error); g_assert (ARV_IS_CAMERA (camera)); g_assert (error == NULL); device = arv_camera_get_device (camera); g_assert (ARV_IS_DEVICE (device)); fake_camera = arv_fake_device_get_fake_camera (ARV_FAKE_DEVICE (device)); g_assert (ARV_IS_FAKE_CAMERA (fake_camera)); stream = arv_camera_create_stream (camera, NULL, NULL, &error); g_assert (ARV_IS_STREAM (stream)); g_assert (error == NULL); arv_fake_camera_set_fill_pattern (fake_camera, fill_pattern_cb, &counter); payload = arv_camera_get_payload (camera, NULL); arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); arv_camera_set_acquisition_mode (camera, ARV_ACQUISITION_MODE_SINGLE_FRAME, NULL); arv_camera_start_acquisition (camera, NULL); buffer = arv_stream_pop_buffer (stream); arv_camera_stop_acquisition (camera, NULL); arv_fake_camera_set_fill_pattern (fake_camera, NULL, NULL); g_assert_cmpint (counter, ==, 1); g_assert (ARV_IS_BUFFER (buffer)); arv_stream_get_statistics (stream, &n_completed_buffers, &n_failures, &n_underruns); g_assert_cmpint (n_completed_buffers, == ,1); g_assert_cmpint (n_failures, ==, 0); g_assert_cmpint (n_underruns, ==, 0); arv_stream_get_n_buffers (stream, &n_input_buffers, &n_output_buffers); g_assert_cmpint (n_input_buffers, ==, 0); g_assert_cmpint (n_output_buffers, ==, 0); n_infos = arv_stream_get_n_infos (stream); g_assert_cmpint (n_infos, ==, 5); info_name = arv_stream_get_info_name (stream, 0); g_assert_cmpstr (info_name, ==, "n_completed_buffers"); info_name = arv_stream_get_info_name (stream, 1); g_assert_cmpstr (info_name, ==, "n_failures"); info_name = arv_stream_get_info_name (stream, 2); g_assert_cmpstr (info_name, ==, "n_underruns"); info_type = arv_stream_get_info_type (stream, 0); g_assert_cmpint (info_type, ==, G_TYPE_UINT64); info_type = arv_stream_get_info_type (stream, 1); g_assert_cmpint (info_type, ==, G_TYPE_UINT64); info_type = arv_stream_get_info_type (stream, 2); g_assert_cmpint (info_type, ==, G_TYPE_UINT64); g_assert_cmpint (n_completed_buffers, ==, arv_stream_get_info_uint64_by_name (stream, "n_completed_buffers")); g_assert_cmpint (n_completed_buffers, ==, arv_stream_get_info_uint64 (stream, 0)); g_assert_cmpint (n_failures, ==, arv_stream_get_info_uint64_by_name (stream, "n_failures")); g_assert_cmpint (n_failures, ==, arv_stream_get_info_uint64 (stream, 1)); g_assert_cmpint (n_underruns, ==, arv_stream_get_info_uint64_by_name (stream, "n_underruns")); g_assert_cmpint (n_underruns, ==, arv_stream_get_info_uint64 (stream, 2)); g_clear_object (&buffer); g_clear_object (&stream); g_clear_object (&camera); } static void camera_api_test (void) { ArvCamera *camera; ArvPixelFormat pixel_format; GError *error = NULL; int x, y, w, h; const char *string; void *ptr; unsigned int n; gboolean b; double d; camera = arv_camera_new ("Fake_1", &error); g_assert (ARV_IS_CAMERA (camera)); g_assert (error == NULL); string = arv_camera_get_vendor_name (camera, &error); g_assert (error == NULL); g_assert (string != NULL); string = arv_camera_get_model_name (camera, &error); g_assert (error == NULL); g_assert (string != NULL); string = arv_camera_get_device_id (camera, &error); g_assert (error == NULL); g_assert (string != NULL); arv_camera_get_region (camera, &x, &y, &w, &h, &error); g_assert (error == NULL); g_assert_cmpint (x, ==, 0); g_assert_cmpint (y, ==, 0); g_assert_cmpint (w, ==, ARV_FAKE_CAMERA_WIDTH_DEFAULT); g_assert_cmpint (h, ==, ARV_FAKE_CAMERA_HEIGHT_DEFAULT); arv_camera_get_sensor_size (camera, &w, &h, &error); g_assert (error == NULL); g_assert_cmpint (w, ==, ARV_FAKE_CAMERA_SENSOR_WIDTH); g_assert_cmpint (h, ==, ARV_FAKE_CAMERA_SENSOR_HEIGHT); arv_camera_set_region (camera, 10, 20, 30, 40, &error); g_assert (error == NULL); arv_camera_get_region (camera, &x, &y, &w, &h, &error); g_assert (error == NULL); g_assert_cmpint (x, ==, 10); g_assert_cmpint (y, ==, 20); g_assert_cmpint (w, ==, 30); g_assert_cmpint (h, ==, 40); arv_camera_get_binning (camera, &x, &y, &error); g_assert (error == NULL); g_assert_cmpint (x, ==, ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT); g_assert_cmpint (y, ==, ARV_FAKE_CAMERA_BINNING_VERTICAL_DEFAULT); arv_camera_set_binning (camera, 2, 4, &error); g_assert (error == NULL); arv_camera_get_binning (camera, &x, &y, &error); g_assert (error == NULL); g_assert_cmpint (x, ==, 2); g_assert_cmpint (y, ==, 4); b = arv_camera_is_binning_available (camera, &error); g_assert (error == NULL); g_assert (b); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_MONO_8); arv_camera_set_pixel_format (camera, ARV_PIXEL_FORMAT_RGB_8_PACKED, &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_RGB_8_PACKED); string = arv_camera_get_pixel_format_as_string (camera, &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "RGB8"); arv_camera_set_pixel_format (camera, ARV_PIXEL_FORMAT_BAYER_BG_8, &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_BAYER_BG_8); string = arv_camera_get_pixel_format_as_string (camera, &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "BayerBG8"); arv_camera_set_pixel_format (camera, ARV_PIXEL_FORMAT_BAYER_GB_8, &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_BAYER_GB_8); string = arv_camera_get_pixel_format_as_string (camera, &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "BayerGB8"); arv_camera_set_pixel_format (camera, ARV_PIXEL_FORMAT_BAYER_GR_8, &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_BAYER_GR_8); string = arv_camera_get_pixel_format_as_string (camera, &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "BayerGR8"); arv_camera_set_pixel_format (camera, ARV_PIXEL_FORMAT_BAYER_RG_8, &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_BAYER_RG_8); string = arv_camera_get_pixel_format_as_string (camera, &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "BayerRG8"); arv_camera_set_pixel_format_from_string (camera, "Mono8", &error); g_assert (error == NULL); pixel_format = arv_camera_get_pixel_format (camera, &error); g_assert (error == NULL); g_assert_cmpint (pixel_format, ==, ARV_PIXEL_FORMAT_MONO_8); ptr = arv_camera_dup_available_pixel_formats (camera, &n, &error); g_assert (error == NULL); g_assert (ptr != NULL); g_assert_cmpint (n, ==, 7); g_clear_pointer (&ptr, g_free); ptr = arv_camera_dup_available_pixel_formats_as_strings (camera, &n, &error); g_assert (error == NULL); g_assert (ptr != NULL); g_assert_cmpint (n, ==, 7); g_clear_pointer (&ptr, g_free); ptr = arv_camera_dup_available_pixel_formats_as_display_names (camera, &n, &error); g_assert (error == NULL); g_assert (ptr != NULL); g_assert_cmpint (n, ==, 7); g_clear_pointer (&ptr, g_free); b = arv_camera_is_frame_rate_available (camera, &error); g_assert (error == NULL); g_assert (b); d = arv_camera_get_frame_rate (camera, &error); g_assert (error == NULL); g_assert_cmpfloat (d, ==, ARV_FAKE_CAMERA_ACQUISITION_FRAME_RATE_DEFAULT); arv_camera_set_frame_rate (camera, 10.0, &error); g_assert (error == NULL); d = arv_camera_get_frame_rate (camera, &error); g_assert (error == NULL); g_assert_cmpfloat (d, ==, 10.0); b = arv_camera_is_exposure_auto_available (camera, &error); g_assert (error == NULL); g_assert (!b); b = arv_camera_is_exposure_time_available (camera, &error); g_assert (error == NULL); g_assert (b); arv_camera_set_gain (camera, 1.0, &error); g_assert (error == NULL); d = arv_camera_get_gain (camera, &error); g_assert (error == NULL); g_assert_cmpfloat (d, ==, 1.0); arv_camera_set_integer (camera, "Unknown", 0, &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (camera); } static void set_flag (gpointer user_data, GObject *object) { gboolean *flag = user_data; *flag = TRUE; } static void camera_device_test (void) { ArvDevice *device; ArvDevice *camera_device; ArvCamera *camera; GError *error = NULL; gboolean device_released = FALSE; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); g_object_weak_ref (G_OBJECT (device), set_flag, &device_released); camera = arv_camera_new_with_device (device, &error); g_assert (ARV_IS_CAMERA (camera)); g_assert (error == NULL); camera_device = arv_camera_get_device (camera); g_assert (ARV_IS_FAKE_DEVICE (camera_device)); g_assert (device == camera_device); g_assert (!device_released); g_object_unref (device); g_assert (!device_released); g_object_unref (camera); g_assert (device_released); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*"); arv_camera_new_with_device (NULL, NULL); g_test_assert_expected_messages (); } static void camera_trigger_selector_test (void) { ArvCamera *camera; GError *error = NULL; const char *string; camera = arv_camera_new ("Fake_1", &error); g_assert (ARV_IS_CAMERA (camera)); g_assert (error == NULL); /* Enable TriggerMode on FrameStart */ arv_camera_set_string (camera, "TriggerSelector", "FrameStart", &error); g_assert (error == NULL); arv_camera_set_string (camera, "TriggerMode", "On", &error); g_assert (error == NULL); string = arv_camera_get_string(camera, "TriggerMode", &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "On"); /* Disable TriggerMode on AcquisitionStart */ arv_camera_set_string (camera, "TriggerSelector", "AcquisitionStart", &error); g_assert (error == NULL); arv_camera_set_string (camera, "TriggerMode", "Off", &error); g_assert (error == NULL); string = arv_camera_get_string(camera, "TriggerMode", &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "Off"); /* Re-check TriggerMode value on FrameStart */ arv_camera_set_string (camera, "TriggerSelector", "FrameStart", &error); g_assert (error == NULL); string = arv_camera_get_string(camera, "TriggerMode", &error); g_assert (error == NULL); g_assert_cmpstr (string, ==, "On"); g_object_unref (camera); } static void set_features_from_string_test (void) { ArvDevice *device; GError *error = NULL; gboolean success; gint64 int_value; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); success = arv_device_set_features_from_string (device, "PixelFormat=RGB8 Height=1048 AcquisitionStart", &error); g_assert (success); g_assert (error == NULL); int_value = arv_device_get_integer_feature_value (device, "Height", NULL); g_assert_cmpint (int_value, ==, 1048); success = arv_device_set_features_from_string (device, "NotAFeature=NotAValue", &error); g_assert (!success); g_assert (error != NULL); g_clear_error (&error); success = arv_device_set_features_from_string (device, "NotAFeature", &error); g_assert (!success); g_assert (error != NULL); g_clear_error (&error); success = arv_device_set_features_from_string (device, "PixelFormat", &error); g_assert (!success); g_assert (error != NULL); g_clear_error (&error); success = arv_device_set_features_from_string (device, "PixelFormat=NotAValidEntry", &error); g_assert (!success); g_assert (error != NULL); g_clear_error (&error); success = arv_device_set_features_from_string (device, "StartAcquitision=Value", &error); g_assert (!success); g_assert (error != NULL); g_clear_error (&error); success = arv_device_set_features_from_string (device, "PixelFormat=RGB8 AcquisitionStart", &error); g_assert (success); g_assert (error == NULL); success = arv_device_set_features_from_string (device, NULL, NULL); g_assert (success); g_object_unref (device); } int main (int argc, char *argv[]) { int result; g_test_init (&argc, &argv, NULL); arv_set_fake_camera_genicam_filename (GENICAM_FILENAME); arv_enable_interface ("Fake"); arv_update_device_list (); g_test_add_func ("/fake/discovery-test", discovery_test); g_test_add_func ("/fake/trigger-registers", trigger_registers_test); g_test_add_func ("/fake/registers", registers_test); g_test_add_func ("/fake/fake-device", fake_device_test); g_test_add_func ("/fake/fake-device-error", fake_device_error_test); g_test_add_func ("/fake/fake-stream", fake_stream_test); g_test_add_func ("/fake/camera-api", camera_api_test); g_test_add_func ("/fake/camera-device", camera_device_test); g_test_add_func ("/fake/camera-trigger-selector", camera_trigger_selector_test); g_test_add_func ("/fake/set-features-from-string", set_features_from_string_test); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/fake.py000066400000000000000000000037531475431451200155330ustar00rootroot00000000000000# SPDX-License-Identifier:Unlicense import gi import os gi.require_version ('Aravis', '0.8') from gi.repository import Aravis Aravis.set_fake_camera_genicam_filename (os.getenv ('FAKE_GENICAM_PATH')) Aravis.enable_interface ("Fake") camera = Aravis.Camera.new ("Fake_1") assert camera is not None camera.set_region (10,20,100,200) camera.set_frame_rate (50.0) camera.set_pixel_format (Aravis.PIXEL_FORMAT_MONO_8) (x,y,w,h) = camera.get_region () assert x == 10 assert y == 20 assert w == 100 assert h == 200 assert camera.get_pixel_format_as_string () == 'Mono8' payload = camera.get_payload () [x,y,width,height] = camera.get_region () callback_map = { Aravis.StreamCallbackType.INIT: 0, Aravis.StreamCallbackType.START_BUFFER: 0, Aravis.StreamCallbackType.BUFFER_DONE: 0, Aravis.StreamCallbackType.EXIT: 0, } test_user_data = [1, 2, 3] def callback(user_data, cb_type, buffer): assert user_data == test_user_data callback_map[cb_type] += 1 if cb_type == Aravis.StreamCallbackType.BUFFER_DONE: assert buffer is not None stream = camera.create_stream (callback, test_user_data) for i in range(0,10): stream.push_buffer (Aravis.Buffer.new_allocate (payload)) camera.start_acquisition () for i in range(0,20): buffer = stream.pop_buffer () assert buffer.get_status () == Aravis.BufferStatus.SUCCESS if buffer: stream.push_buffer (buffer) camera.stop_acquisition () # Explicitly delete the stream here. Otherwise it will likely be garbage collected only # when the test application exits. This is to test that we receive a StreamCallbackType.EXIT # event. del stream assert callback_map[Aravis.StreamCallbackType.INIT] == 1 assert callback_map[Aravis.StreamCallbackType.START_BUFFER] >= 20 assert callback_map[Aravis.StreamCallbackType.BUFFER_DONE] == callback_map[Aravis.StreamCallbackType.START_BUFFER] assert callback_map[Aravis.StreamCallbackType.EXIT] == 1 buffer = camera.acquisition (0) data = buffer.get_data () assert len (data) == 20000 aravis-0.8.34/tests/fakegv.c000066400000000000000000000225651475431451200156640ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include static ArvCamera *camera = NULL; static void discovery_test (void) { ArvCamera *test_camera; char *discovery_iface; int n_devices; int i; arv_gv_interface_set_discovery_interface_name("not-an-iface"); arv_update_device_list(); discovery_iface = arv_gv_interface_dup_discovery_interface_name(); g_assert (discovery_iface != NULL); g_assert_cmpstr("not-an-iface", ==, discovery_iface); g_free (discovery_iface); test_camera = arv_camera_new ("Aravis-GVTest", NULL); g_assert (test_camera == NULL); arv_gv_interface_set_discovery_interface_name(NULL); arv_update_device_list(); discovery_iface = arv_gv_interface_dup_discovery_interface_name(); g_assert (discovery_iface == NULL); test_camera = arv_camera_new ("Aravis-GVTest", NULL); g_assert (ARV_IS_CAMERA(test_camera)); g_clear_object (&test_camera); n_devices = arv_get_n_devices (); for (i = 0; i < n_devices; i++) { if (g_strcmp0 (arv_get_device_vendor (i), "Aravis") == 0 && g_strcmp0 (arv_get_device_model (i), "Fake") == 0 && g_strcmp0 (arv_get_device_serial_nbr (i), "GVTest") == 0 && g_strcmp0 (arv_get_device_manufacturer_info (i), "none") == 0 && g_strcmp0 (arv_get_device_id (i), "Aravis-Fake-GVTest") == 0 && g_strcmp0 (arv_get_device_physical_id (i), "00:00:00:00:00:00") == 0) return; } g_assert_not_reached (); } static void register_test (void) { ArvDevice *device; int int_value; double dbl_value; double boolean_value; device = arv_camera_get_device (camera); g_assert (ARV_IS_GV_DEVICE (device)); /* Check default */ int_value = arv_device_get_integer_feature_value (device, "Width", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_WIDTH_DEFAULT); arv_device_set_integer_feature_value (device, "Width", 1024, NULL); int_value = arv_device_get_integer_feature_value (device, "Width", NULL); g_assert_cmpint (int_value, ==, 1024); /* Check default */ int_value = arv_device_get_integer_feature_value (device, "Height", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_HEIGHT_DEFAULT); arv_device_set_integer_feature_value (device, "Height", 1024, NULL); int_value = arv_device_get_integer_feature_value (device, "Height", NULL); g_assert_cmpint (int_value, ==, 1024); int_value = arv_device_get_integer_feature_value (device, "BinningHorizontal", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_BINNING_HORIZONTAL_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "BinningVertical", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_BINNING_VERTICAL_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "PixelFormat", NULL); g_assert_cmpint (int_value, ==, ARV_FAKE_CAMERA_PIXEL_FORMAT_DEFAULT); dbl_value = arv_device_get_float_feature_value (device, "AcquisitionFrameRate", NULL); g_assert_cmpfloat (dbl_value, ==, ARV_FAKE_CAMERA_ACQUISITION_FRAME_RATE_DEFAULT); dbl_value = arv_device_get_float_feature_value (device, "ExposureTimeAbs", NULL); g_assert_cmpfloat (dbl_value, ==, ARV_FAKE_CAMERA_EXPOSURE_TIME_US_DEFAULT); int_value = arv_device_get_integer_feature_value (device, "GainRaw", NULL); g_assert_cmpint (int_value, ==, 0); int_value = arv_device_get_integer_feature_value (device, "GainAuto", NULL); g_assert_cmpint (int_value, ==, 1); int_value = arv_device_get_integer_feature_value (device, "PayloadSize", NULL); g_assert_cmpint (int_value, ==, 1024 * 1024); arv_device_set_boolean_feature_value (device, "TestBoolean", FALSE, NULL); boolean_value = arv_device_get_boolean_feature_value (device, "TestBoolean", NULL); g_assert_cmpint (boolean_value, ==, FALSE); int_value = arv_device_get_integer_feature_value (device, "TestRegister", NULL); g_assert_cmpint (int_value, ==, 123); arv_device_set_boolean_feature_value (device, "TestBoolean", TRUE, NULL); boolean_value = arv_device_get_boolean_feature_value (device, "TestBoolean", NULL); g_assert_cmpint (boolean_value, ==, TRUE); int_value = arv_device_get_integer_feature_value (device, "TestRegister", NULL); g_assert_cmpint (int_value, ==, 321); } static void acquisition_test (void) { GError *error = NULL; ArvBuffer *buffer; gint x, y, width, height; const char *ignore_buffer; ignore_buffer = g_getenv("ARV_TEST_IGNORE_BUFFER"); buffer = arv_camera_acquisition (camera, 0, &error); g_assert (error == NULL); g_assert (ARV_IS_BUFFER (buffer)); if (ignore_buffer == NULL && arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS) { arv_buffer_get_image_region (buffer, &x, &y, &width, &height); g_assert_cmpint (x, ==, 0); g_assert_cmpint (y, ==, 0); g_assert_cmpint (width, ==, 1024); g_assert_cmpint (height, ==, 1024); g_assert_cmpint (arv_buffer_get_image_x (buffer), ==, 0); g_assert_cmpint (arv_buffer_get_image_y (buffer), ==, 0); g_assert_cmpint (arv_buffer_get_image_width (buffer), ==, 1024); g_assert_cmpint (arv_buffer_get_image_height (buffer), ==, 1024); g_assert (arv_buffer_get_image_pixel_format (buffer) == ARV_PIXEL_FORMAT_MONO_8); } g_clear_object (&buffer); } static void new_buffer_cb (ArvStream *stream, unsigned *buffer_count) { ArvBuffer *buffer; buffer = arv_stream_try_pop_buffer (stream); if (buffer != NULL) { (*buffer_count)++; if (*buffer_count == 10) { /* Sleep after the last buffer was received, in order * to keep a reference to stream while the main loop * ends. If the main is able to unref stream while * this signal callback is still waiting, stream will * be finalized in its stream thread contex (because * g_signal_emit holds a reference to stream), leading * to a deadlock. */ g_usleep (1000000); } arv_stream_push_buffer (stream, buffer); } } static void stream_test (void) { ArvStream *stream; GError *error = NULL; size_t payload; unsigned buffer_count = 0; unsigned i; stream = arv_camera_create_stream (camera, NULL, NULL, &error); g_assert (ARV_IS_STREAM (stream)); g_assert (error == NULL); payload = arv_camera_get_payload (camera, NULL); for (i = 0; i < 5; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &buffer_count); arv_stream_set_emit_signals (stream, TRUE); arv_camera_start_acquisition (camera, NULL); while (buffer_count < 10) g_usleep (1000); arv_camera_stop_acquisition (camera, NULL); /* The following will block until the signal callback returns * which avoids a race and possible deadlock. */ arv_stream_set_emit_signals (stream, FALSE); g_clear_object (&stream); /* For actually testing the deadlock condition (see comment in * new_buffer_cb), one must wait a bit before leaving this test, * because otherwise the stream thread will be killed while sleeping. */ g_usleep (2000000); } #define N_BUFFERS 5 static struct { int width; int height; } rois[] = { {100, 100}, {200, 200}, {300,300} }; static void dynamic_roi_test (void) { ArvStream *stream; GError *error = NULL; size_t payload; unsigned buffer_count = 0; unsigned i, j; stream = arv_camera_create_stream (camera, NULL, NULL, &error); g_assert (ARV_IS_STREAM (stream)); g_assert (error == NULL); payload = arv_camera_get_payload (camera, NULL); g_signal_connect (stream, "new-buffer", G_CALLBACK (new_buffer_cb), &buffer_count); arv_stream_set_emit_signals (stream, TRUE); for (j = 0; j < G_N_ELEMENTS (rois); j++) { unsigned int n_deleted; int height, width; n_deleted = arv_stream_stop_thread (stream, TRUE); if (j == 0) g_assert (n_deleted == 0); else g_assert (n_deleted == N_BUFFERS); buffer_count = 0; arv_camera_set_region (camera, 0, 0, rois[j].width , rois[j].height, NULL); arv_camera_get_region (camera, NULL, NULL, &width, &height, NULL); g_assert (width == rois[j].width); g_assert (height == rois[j].height); payload = arv_camera_get_payload (camera, NULL); for (i = 0; i < N_BUFFERS; i++) arv_stream_push_buffer (stream, arv_buffer_new (payload, NULL)); arv_stream_start_thread (stream); arv_camera_start_acquisition (camera, NULL); while (buffer_count < 10) { g_usleep (10000); } arv_camera_stop_acquisition (camera, NULL); } arv_stream_set_emit_signals (stream, FALSE); g_clear_object (&stream); } int main (int argc, char *argv[]) { ArvGvFakeCamera *simulator; int result; g_test_init (&argc, &argv, NULL); arv_set_fake_camera_genicam_filename (GENICAM_FILENAME); simulator = arv_gv_fake_camera_new ("127.0.0.1", "GVTest"); g_assert (ARV_IS_GV_FAKE_CAMERA (simulator)); camera = arv_camera_new ("Aravis-GVTest", NULL); g_assert (ARV_IS_CAMERA (camera)); arv_update_device_list (); g_test_add_func ("/fakegv/discovery", discovery_test); g_test_add_func ("/fakegv/device_registers", register_test); g_test_add_func ("/fakegv/acquisition", acquisition_test); g_test_add_func ("/fakegv/stream", stream_test); g_test_add_func ("/fakegv/dynamic_roi", dynamic_roi_test); result = g_test_run(); g_object_unref (camera); g_object_unref (simulator); arv_shutdown (); return result; } aravis-0.8.34/tests/genicam.c000066400000000000000000001417541475431451200160260ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include #include typedef struct { const char *name; GType type; } NodeTypes; NodeTypes node_types[] = { {"RWFloat", G_TYPE_DOUBLE}, {"P_RWFloat_Min", G_TYPE_DOUBLE}, {"P_RWFloat_Max", G_TYPE_DOUBLE}, {"P_RWFloat_Inc", G_TYPE_DOUBLE}, {"P_RWFloat", G_TYPE_DOUBLE}, {"RWBoolean", G_TYPE_BOOLEAN}, {"P_RWBoolean", G_TYPE_BOOLEAN}, {"RWInteger", G_TYPE_INT64}, {"P_RWInteger", G_TYPE_INT64}, {"P_RWInteger_Min", G_TYPE_INT64}, {"P_RWInteger_Max", G_TYPE_INT64}, {"P_RWInteger_Inc", G_TYPE_INT64}, {"Enumeration", G_TYPE_INT64}, {"EnumerationValue", G_TYPE_INT64}, {"IntRegisterA", G_TYPE_INT64}, {"IntRegisterB", G_TYPE_INT64}, {"IntSwissKnifeTest", G_TYPE_INT64}, {"DeviceUserID", G_TYPE_STRING} }; static void node_value_type_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; int i; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); for (i = 0; i < G_N_ELEMENTS (node_types); i++) { node = arv_gc_get_node (genicam, node_types[i].name); switch (node_types[i].type) { case G_TYPE_STRING: g_assert (ARV_IS_GC_STRING (node)); break; case G_TYPE_DOUBLE: g_assert (ARV_IS_GC_FLOAT (node)); break; case G_TYPE_INT64: g_assert (ARV_IS_GC_INTEGER (node)); break; case G_TYPE_BOOLEAN: g_assert (ARV_IS_GC_BOOLEAN (node)); break; } } g_object_unref (device); } static void integer_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; gint64 v_int64; const char *v_string; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "RWInteger"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 1); v_int64 = arv_gc_integer_get_min (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, -10); v_int64 = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 10); v_int64 = arv_gc_integer_get_inc (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 2); v_string = arv_gc_feature_node_get_value_as_string (ARV_GC_FEATURE_NODE (node), NULL); g_assert_cmpstr (v_string, ==, "1"); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "4", &error); g_assert (error == NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &error); g_assert (error == NULL); g_assert_cmpint (v_int64, ==, 4); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0xf", &error); g_assert (error == NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), &error); g_assert (error == NULL); g_assert_cmpint (v_int64, ==, 15); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0.4", &error); g_assert (error != NULL); g_assert (error->domain == ARV_GC_ERROR); g_assert (error->code == ARV_GC_ERROR_INVALID_SYNTAX); g_clear_error (&error); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 2, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 2); node = arv_gc_get_node (genicam, "P_RWInteger"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 2); v_int64 = arv_gc_integer_get_min (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, -20); v_int64 = arv_gc_integer_get_max (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 20); v_int64 = arv_gc_integer_get_inc (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 3); g_object_unref (device); } static void indexed_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; ArvGcNode *selector; GError *error = NULL; gint64 v_int64; double v_double; device = arv_fake_device_new ("TEST0", NULL); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "Table"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); selector = arv_gc_get_node (genicam, "TableSelector"); g_assert (ARV_IS_GC_INTEGER_NODE (selector)); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 200); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 150, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 150); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), -1, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 600); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 10, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 100); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 20, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 150); node = arv_gc_get_node (genicam, "Multiplexer"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); selector = arv_gc_get_node (genicam, "MultiplexerSelector"); g_assert (ARV_IS_GC_INTEGER_NODE (selector)); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (selector), NULL); g_assert_cmpint (v_int64, ==, 20); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 200); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 150, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 150); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), -1, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 600); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 10, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 100); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 20, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 150); node = arv_gc_get_node (genicam, "FloatTable"); g_assert (ARV_IS_GC_FLOAT_NODE (node)); selector = arv_gc_get_node (genicam, "FloatTableSelector"); g_assert (ARV_IS_GC_INTEGER_NODE (selector)); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 200.2); arv_gc_float_set_value (ARV_GC_FLOAT (node), 150.15, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 150.15); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), -1, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 600.6); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 10, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 100.1); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 20, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 150.15); node = arv_gc_get_node (genicam, "FloatMultiplexer"); g_assert (ARV_IS_GC_FLOAT_NODE (node)); selector = arv_gc_get_node (genicam, "FloatMultiplexerSelector"); g_assert (ARV_IS_GC_INTEGER_NODE (selector)); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (selector), NULL); g_assert_cmpint (v_int64, ==, 20); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 200.2); arv_gc_float_set_value (ARV_GC_FLOAT (node), 150.15, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 150.15); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), -1, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 600.6); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 10, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 100.1); arv_gc_integer_set_value (ARV_GC_INTEGER (selector), 20, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 150.15); node = arv_gc_get_node ((genicam), "IndexedRegister"); g_assert (ARV_IS_GC_REGISTER (node)); v_int64 = arv_gc_register_get_address (ARV_GC_REGISTER (node), &error); g_assert (error == NULL); g_assert_cmpint (v_int64, ==, 35128); g_object_unref (device); } static void boolean_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; ArvGcNode *node_b; GError *error = NULL; gboolean v_boolean; const char *v_string; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "RWBoolean"); g_assert (ARV_IS_GC_BOOLEAN (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, TRUE); v_string = arv_gc_feature_node_get_value_as_string (ARV_GC_FEATURE_NODE (node), NULL); g_assert_cmpstr (v_string, ==, "true"); arv_gc_boolean_set_value (ARV_GC_BOOLEAN (node), 0, NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); v_string = arv_gc_feature_node_get_value_as_string (ARV_GC_FEATURE_NODE (node), NULL); g_assert_cmpstr (v_string, ==, "false"); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "True", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, TRUE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "False", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "true", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, TRUE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "false", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "1", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, TRUE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "what", &error); g_assert (error != NULL); g_assert (error->domain == ARV_GC_ERROR); g_assert (error->code == ARV_GC_ERROR_INVALID_SYNTAX); g_clear_error (&error); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "2", &error); g_assert (error != NULL); g_assert (error->domain == ARV_GC_ERROR); g_assert (error->code == ARV_GC_ERROR_INVALID_SYNTAX); g_clear_error (&error); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0", &error); g_assert (error == NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); node = arv_gc_get_node (genicam, "P_RWBoolean"); g_assert (ARV_IS_GC_BOOLEAN (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, TRUE); node_b = arv_gc_get_node (genicam, "RWBooleanValue"); g_assert (ARV_IS_GC_INTEGER (node_b)); arv_gc_integer_set_value (ARV_GC_INTEGER (node_b), 42, NULL); v_boolean = arv_gc_boolean_get_value (ARV_GC_BOOLEAN (node), NULL); g_assert_cmpint (v_boolean, ==, FALSE); g_object_unref (device); } static void float_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; double v_double; const char *v_string; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "RWFloat"); g_assert (ARV_IS_GC_FLOAT_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 0.1); v_double = arv_gc_float_get_min (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, -10.0); v_double = arv_gc_float_get_max (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 10.0); v_double = arv_gc_float_get_inc (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 2.0); v_string = arv_gc_feature_node_get_value_as_string (ARV_GC_FEATURE_NODE (node), NULL); g_assert_cmpstr (v_string, ==, "0.1"); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0.4", &error); g_assert (error == NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), &error); g_assert (error == NULL); g_assert_cmpfloat (v_double, ==, 0.4); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "0.4.", &error); g_assert (error != NULL); g_assert (error->domain == ARV_GC_ERROR); g_assert (error->code == ARV_GC_ERROR_INVALID_SYNTAX); g_clear_error (&error); arv_gc_float_set_value (ARV_GC_FLOAT (node), 0.2, NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 0.2); node = arv_gc_get_node (genicam, "P_RWFloat"); g_assert (ARV_IS_GC_FLOAT_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 0.2); v_double = arv_gc_float_get_min (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, -20.0); v_double = arv_gc_float_get_max (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 20.0); v_double = arv_gc_float_get_inc (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 3.0); g_object_unref (device); } static void enumeration_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; gint64 v_int64; gint64 *values; guint n_values; const char *v_string; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "Enumeration"); g_assert (ARV_IS_GC_ENUMERATION (node)); g_assert (ARV_IS_GC_INTEGER (node)); g_assert (ARV_IS_GC_STRING (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_int64 = arv_gc_enumeration_get_int_value (ARV_GC_ENUMERATION (node), NULL); g_assert_cmpint (v_int64, ==, 0); values = arv_gc_enumeration_dup_available_int_values (ARV_GC_ENUMERATION (node), &n_values, NULL); g_assert_cmpint (n_values, ==, 2); g_assert (values != NULL); g_free (values); arv_gc_enumeration_set_string_value (ARV_GC_ENUMERATION (node), "Entry1", NULL); v_int64 = arv_gc_enumeration_get_int_value (ARV_GC_ENUMERATION (node), NULL); g_assert_cmpint (v_int64, ==, 1); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 1); v_string = arv_gc_string_get_value (ARV_GC_STRING (node), NULL); g_assert_cmpstr (v_string, ==, "Entry1"); arv_gc_string_set_value (ARV_GC_STRING (node), "Entry0", NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 0); v_string = arv_gc_string_get_value (ARV_GC_STRING (node), NULL); g_assert_cmpstr (v_string, ==, "Entry0"); v_int64 = arv_gc_string_get_max_length (ARV_GC_STRING (node), NULL); g_assert_cmpint (v_int64, ==, strlen ("EntryNotImplemented")); g_object_unref (device); } static void swiss_knife_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; gint64 value; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "IntSwissKnifeTest"); g_assert (ARV_IS_GC_SWISS_KNIFE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 0x1234); node = arv_gc_get_node (genicam, "IntSwissKnifeTestEntity"); g_assert (ARV_IS_GC_SWISS_KNIFE (node)); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 0x10305070); node = arv_gc_get_node (genicam, "IntSwissKnifeTestEntity2"); g_assert (ARV_IS_GC_SWISS_KNIFE (node)); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 3); node = arv_gc_get_node (genicam, "IntSwissKnifeBug699228"); g_assert (ARV_IS_GC_SWISS_KNIFE (node)); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 4); node = arv_gc_get_node (genicam, "IntSwissKnifeTestSubAndConstant"); g_assert (ARV_IS_GC_SWISS_KNIFE (node)); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (value, ==, 140); g_object_unref (device); } static void converter_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; double v_double; gint64 v_int64; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "ROConverter"); g_assert (ARV_IS_GC_CONVERTER (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "Converter"); g_assert (ARV_IS_GC_CONVERTER (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 200.0); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "100.0", NULL); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpfloat (v_double, ==, 100.0); node = arv_gc_get_node (genicam, "IntConverter"); g_assert (ARV_IS_GC_CONVERTER (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 5); arv_gc_feature_node_set_value_from_string (ARV_GC_FEATURE_NODE (node), "100", NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 100); node = arv_gc_get_node (genicam, "Enumeration"); g_assert (ARV_IS_GC_ENUMERATION (node)); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 1, NULL); node = arv_gc_get_node (genicam, "ConverterEnumeration"); g_assert (ARV_IS_GC_CONVERTER (node)); v_double = arv_gc_float_get_value (ARV_GC_FLOAT (node), NULL); g_assert_cmpint (v_double, ==, 5); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node = arv_gc_get_node (genicam, "IntConverterTestSubAndConstant"); g_assert (ARV_IS_GC_CONVERTER (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 10000); arv_gc_integer_set_value (ARV_GC_INTEGER (node), 100, NULL); v_int64 = arv_gc_integer_get_value (ARV_GC_INTEGER (node), NULL); g_assert_cmpint (v_int64, ==, 1000); g_object_unref (device); } static void register_test (void) { GError *error = NULL; ArvDevice *device; ArvGc *genicam; ArvGcNode *node_a; ArvGcNode *node_b; ArvGcNode *node_c; ArvGcNode *node_sc; ArvGcNode *node_uc; ArvGcNode *node_f; ArvGcNode *node_str; ArvGcAccessMode access_mode; const char *string; gint64 value; double value_f; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); /* 64 bit IntReg */ node_a = arv_gc_get_node (genicam, "ROIntRegisterA"); g_assert (ARV_IS_GC_REGISTER (node_a)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_a)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node_a = arv_gc_get_node (genicam, "IntRegisterA"); g_assert (ARV_IS_GC_REGISTER (node_a)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_a), NULL); g_assert_cmpint (value, ==, 0); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_a), NULL); g_assert_cmpint (value, ==, G_MAXINT64); string = arv_gc_integer_get_unit (ARV_GC_INTEGER (node_a)); g_assert_cmpstr (string, ==, "Pa"); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_a)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_a), NULL); g_assert_cmpint (value, ==, 0x1050); node_b = arv_gc_get_node (genicam, "IntRegisterB"); g_assert (ARV_IS_GC_REGISTER (node_b)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_b)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); value = arv_gc_register_get_address (ARV_GC_REGISTER (node_b), NULL); g_assert_cmpint (value, ==, 0x20ff); node_c = arv_gc_get_node (genicam, "IntRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_c)); /* 32 bit IntReg */ arv_gc_integer_set_value (ARV_GC_INTEGER (node_c), 0x1234567887654321, NULL); node_sc = arv_gc_get_node (genicam, "IntSigned32BitRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_sc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, G_MININT32); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, G_MAXINT32); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, 1); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_sc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node_uc = arv_gc_get_node (genicam, "IntUnsigned32BitRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_uc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 0); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, G_MAXUINT32); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 1); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_uc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); arv_gc_integer_set_value (ARV_GC_INTEGER (node_sc), -1, NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, -1); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 0xffffffff); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_c), NULL); g_assert_cmpint (value, ==, 0x12345678ffffffff); arv_gc_integer_set_value (ARV_GC_INTEGER (node_sc), 0x7fffffff, NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, 0x7fffffff); /* 16 bit IntReg */ node_sc = arv_gc_get_node (genicam, "IntSigned16BitRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_sc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, G_MININT16); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, G_MAXINT16); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, 1); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_sc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node_uc = arv_gc_get_node (genicam, "IntUnsigned16BitRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_uc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 0); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, G_MAXUINT16); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 1); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_uc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); arv_gc_integer_set_value (ARV_GC_INTEGER (node_sc), -1, NULL); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, -1); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 0xffff); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_c), NULL); g_assert_cmpint (value, ==, 0xffff56787fffffff); /* MaskedIntReg */ node_sc = arv_gc_get_node (genicam, "MaskedIntSignedRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_sc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, -8); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, 7); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_sc), NULL); g_assert_cmpint (value, ==, 1); string = arv_gc_integer_get_unit (ARV_GC_INTEGER (node_sc)); g_assert_cmpstr (string, ==, "V"); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_sc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node_uc = arv_gc_get_node (genicam, "MaskedIntUnsignedRegisterC"); g_assert (ARV_IS_GC_REGISTER (node_uc)); value = arv_gc_integer_get_min (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 0); value = arv_gc_integer_get_max (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 15); value = arv_gc_integer_get_inc (ARV_GC_INTEGER (node_uc), NULL); g_assert_cmpint (value, ==, 1); string = arv_gc_integer_get_unit (ARV_GC_INTEGER (node_uc)); g_assert_cmpstr (string, ==, "A"); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_uc)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); /* 4 byte FLoatReg */ arv_gc_integer_set_value (ARV_GC_INTEGER (node_c), 0x1234567887654321, NULL); node_f = arv_gc_get_node (genicam, "FloatReg4C"); g_assert (ARV_IS_GC_REGISTER (node_f)); value_f = arv_gc_float_get_min (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, -G_MAXFLOAT); value_f = arv_gc_float_get_max (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, G_MAXFLOAT); string = arv_gc_float_get_unit (ARV_GC_FLOAT (node_f)); g_assert_cmpstr (string, ==, "mA"); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_f)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); arv_gc_float_set_value (ARV_GC_FLOAT (node_f), 2.0, NULL); value_f = arv_gc_float_get_value (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, 2.0); arv_gc_float_set_value (ARV_GC_FLOAT (node_f), -1.0, NULL); value_f = arv_gc_float_get_value (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, -1.0); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_c), NULL); g_assert_cmpint (value, ==, 0x000080bf87654321); /* 8 byte FLoatReg */ node_f = arv_gc_get_node (genicam, "FloatReg8C"); g_assert (ARV_IS_GC_REGISTER (node_f)); value_f = arv_gc_float_get_min (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, -G_MAXDOUBLE); value_f = arv_gc_float_get_max (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, G_MAXDOUBLE); string = arv_gc_float_get_unit (ARV_GC_FLOAT (node_f)); g_assert_cmpstr (string, ==, "mV"); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_f)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); arv_gc_float_set_value (ARV_GC_FLOAT (node_f), 1.2, NULL); value_f = arv_gc_float_get_value (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, 1.2); arv_gc_float_set_value (ARV_GC_FLOAT (node_f), -1.0, NULL); value_f = arv_gc_float_get_value (ARV_GC_FLOAT (node_f), NULL); g_assert_cmpfloat (value_f, ==, -1.0); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_c), NULL); g_assert_cmpint (value, ==, 0x000000000000f0bf); /* StringReg */ arv_gc_integer_set_value (ARV_GC_INTEGER (node_c), 0x1234567887654321, NULL); node_str = arv_gc_get_node (genicam, "StringReg"); g_assert (ARV_IS_GC_REGISTER (node_str)); value = arv_gc_string_get_max_length (ARV_GC_STRING (node_str), NULL); g_assert_cmpint (value, ==, 4); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_str)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); arv_gc_string_set_value (ARV_GC_STRING (node_str), "Toto", NULL); string = arv_gc_string_get_value (ARV_GC_STRING (node_str), NULL); g_assert_cmpstr (string, ==, "Toto"); value = arv_gc_integer_get_value (ARV_GC_INTEGER (node_c), NULL); g_assert_cmpint (value, ==, 0x1234546f746f4321); arv_gc_string_set_value (ARV_GC_STRING (node_str), "TotoTata", &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (device); } static void string_test (void) { GError *error = NULL; ArvDevice *device; ArvGc *genicam; ArvGcNode *node_str; gint64 value; const char *str; ArvGcAccessMode access_mode; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node_str = arv_gc_get_node (genicam, "StringNodeA"); g_assert (ARV_IS_GC_STRING (node_str)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_str)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); value = arv_gc_string_get_max_length (ARV_GC_STRING (node_str), &error); g_assert_cmpint (value, ==, 4); g_assert (error == NULL); arv_gc_string_set_value (ARV_GC_STRING (node_str), "Too long", &error); g_assert (error != NULL); g_clear_error (&error); arv_gc_string_set_value (ARV_GC_STRING (node_str), "Test", &error); g_assert (error == NULL); str = arv_gc_string_get_value (ARV_GC_STRING (node_str), &error); g_assert_cmpstr (str, ==, "Test"); g_assert (error == NULL); node_str = arv_gc_get_node (genicam, "StringNodeB"); g_assert (ARV_IS_GC_STRING (node_str)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node_str)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); value = arv_gc_string_get_max_length (ARV_GC_STRING (node_str), &error); g_assert_cmpint (value, ==, G_MAXINT64); g_assert (error == NULL); str = arv_gc_string_get_value (ARV_GC_STRING (node_str), &error); g_assert_cmpstr (str, ==, "Hello World!"); g_assert (error == NULL); arv_gc_string_set_value (ARV_GC_STRING (node_str), "It fits", &error); g_assert (error == NULL); str = arv_gc_string_get_value (ARV_GC_STRING (node_str), &error); g_assert_cmpstr (str, ==, "It fits"); g_assert (error == NULL); g_object_unref (device); } GRegex *arv_gv_device_get_url_regex (void); const struct { const char *url; const char *scheme; const char *authority; const char *path; const char *query; const char *fragment; guint64 address; guint64 size; } genicam_urls[] = { { "Local:Basler_Ace_GigE_e7c9b87e_Version_3_3.zip;c0000000;10cca", "Local", NULL, "Basler_Ace_GigE_e7c9b87e_Version_3_3.zip", NULL, NULL, 0xc0000000, 0x10cca }, { "local:C4_2040_GigE_1.0.0.zip;0x8C400904;0x4D30", "local", NULL, "C4_2040_GigE_1.0.0.zip", NULL, NULL, 0x8C400904, 0x4D30 }, { "Local:Mikrotron_GmbH_MC206xS11_Rev0_00_007.zip;8001000;395C?SchemaVersion=1.1.0", "Local", NULL, "Mikrotron_GmbH_MC206xS11_Rev0_00_007.zip", "SchemaVersion=1.1.0", NULL, 0x8001000, 0x395C }, { "http://github.com/AravisProject/aravis/tree/master/tests/data/genicam.xml", "http", "github.com", "/AravisProject/aravis/tree/master/tests/data/genicam.xml", NULL, NULL, 0, 0 }, { "file:///C|program%20files/aravis/genicam.xml?SchemaVersion=1.0.0", "file", NULL, "/C|program%20files/aravis/genicam.xml", "SchemaVersion=1.0.0", NULL, 0, 0}, { "Local: guide_gige_test.zip; 00010000; 06c5", "Local", NULL, "guide_gige_test.zip", NULL, NULL, 0x10000, 0x6c5}, { "Local: guide_gige_test.zip; 00010000; 06c5", "Local", NULL, "guide_gige_test.zip", NULL, NULL, 0x10000, 0x6c5} }; static void url_test (void) { unsigned int i; for (i = 0; i < G_N_ELEMENTS (genicam_urls); i++) { gboolean success; char *scheme = NULL; char *authority = NULL; char *path = NULL; char *query = NULL; char *fragment = NULL; guint64 address; guint64 size; success = arv_parse_genicam_url (genicam_urls[i].url, -1, &scheme, &authority, &path, &query, &fragment, &address, &size); g_assert (success); g_assert_cmpstr (scheme, ==, genicam_urls[i].scheme); g_assert_cmpstr (authority, ==, genicam_urls[i].authority); g_assert_cmpstr (path, ==, genicam_urls[i].path); g_assert_cmpstr (query, ==, genicam_urls[i].query); g_assert_cmpstr (fragment, ==, genicam_urls[i].fragment); g_assert_cmpint (address, ==, genicam_urls[i].address); g_assert_cmpint (size, ==, genicam_urls[i].size); success = arv_parse_genicam_url (genicam_urls[i].url, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL); g_assert (success); g_free (scheme); g_free (authority); g_free (path); g_free (query); g_free (fragment); } } static void mandatory_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "TLParamsLocked"); g_assert (ARV_IS_GC_INTEGER (node)); g_object_unref (device); } #if defined (__GNUC__) #define ARV_PACK(_structure) _structure __attribute__((__packed__)) #elif defined (_MSC_VER) && (_MSC_VER >= 1500) #define ARV_PACK(_structure) __pragma(pack(push, 1)) _structure __pragma(pack(pop)) #else #error "Structure packing is not defined for this compiler!" #endif ARV_PACK(typedef struct { guint32 id; guint32 size; }) ArvChunkInfos; static ArvBuffer * create_buffer_with_chunk_data (void) { ArvBuffer *buffer; ArvChunkInfos *chunk_infos; char *data; size_t size; guint32 int_value; guint8 *boolean_value; guint offset; guint64 float_value; size = 64 + 8 + 64 + 8 + 1 + 5 * sizeof (ArvChunkInfos); buffer = arv_buffer_new (size, NULL); buffer->priv->payload_type = ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA; buffer->priv->has_chunks = TRUE; buffer->priv->received_size = size; buffer->priv->status = ARV_BUFFER_STATUS_SUCCESS; data = (char *) arv_buffer_get_data (buffer, NULL); memset ((char *) data, '\0', size); offset = size - sizeof (ArvChunkInfos); chunk_infos = (ArvChunkInfos *) &data[offset]; chunk_infos->id = GUINT32_TO_BE (0x12345678); chunk_infos->size = GUINT32_TO_BE (8); int_value = GUINT32_TO_BE (0x11223344); memcpy ((char *) &data[offset - 8], (char *)&int_value, sizeof(int_value)); offset -= 8 + sizeof (ArvChunkInfos); chunk_infos = (ArvChunkInfos *) &data[offset]; chunk_infos->id = GUINT32_TO_BE (0x87654321); chunk_infos->size = GUINT32_TO_BE (64); memcpy ((char *) &data[offset - 64], "Hello" ,sizeof ("Hello")); offset -= 64 + sizeof (ArvChunkInfos); chunk_infos = (ArvChunkInfos *) &data[offset]; chunk_infos->id = GUINT32_TO_BE (0x12345679); chunk_infos->size = GUINT32_TO_BE (8); float_value = GUINT64_TO_BE (0x3FF199999999999A); /* Hexadecimal representation of 1.1 as double */ memcpy ((char *) &data[offset - 8], (char *)&float_value, sizeof(float_value)); offset -= 8 + sizeof (ArvChunkInfos); chunk_infos = (ArvChunkInfos *) &data[offset]; chunk_infos->id = GUINT32_TO_BE (0x12345680); chunk_infos->size = GUINT32_TO_BE (1); boolean_value = (guint8 *) &data[offset - 1]; *boolean_value = 0x1; offset -= 1 + sizeof (ArvChunkInfos); chunk_infos = (ArvChunkInfos *) &data[offset]; chunk_infos->id = GUINT32_TO_BE (0x44444444); chunk_infos->size = GUINT32_TO_BE (64); g_assert_cmpint (offset, ==, 64); #if 0 { GString *string; string= g_string_new (""); arv_g_string_append_hex_dump (string, data, size); printf ("%s\n", string->str); g_string_free (string, TRUE); } #endif return buffer; } static void chunk_data_test (void) { ArvDevice *device; ArvChunkParser *parser; ArvBuffer *buffer; ArvGc *genicam; GError *error = NULL; guint32 int_value; double float_value; gboolean boolean_value; const char *chunk_data; const char *data; const char *string_value; size_t size; size_t chunk_data_size; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); parser = arv_device_create_chunk_parser (device); g_assert (ARV_IS_CHUNK_PARSER (parser)); g_object_get (parser, "genicam", &genicam, NULL); g_object_unref (genicam); buffer = create_buffer_with_chunk_data (); g_assert (ARV_IS_BUFFER (buffer)); data = arv_buffer_get_data (buffer, &size); chunk_data = arv_buffer_get_chunk_data (buffer, 0x12345678, &chunk_data_size); g_assert (chunk_data != NULL); g_assert_cmpint (chunk_data_size, ==, 8); g_assert_cmpint (chunk_data - data, ==, 1 + 64 + 64 + 8 + 4 * sizeof (ArvChunkInfos)); chunk_data = arv_buffer_get_chunk_data (buffer, 0x12345679, &chunk_data_size); g_assert (chunk_data != NULL); g_assert_cmpint (chunk_data_size, ==, 8); g_assert_cmpint (chunk_data - data, ==, 1 + 64 + 2 * sizeof (ArvChunkInfos)); chunk_data = arv_buffer_get_chunk_data (buffer, 0x87654321, &chunk_data_size); g_assert (chunk_data != NULL); g_assert_cmpint (chunk_data_size, ==, 64); g_assert_cmpint (chunk_data - data, ==, 1 + 64 + 8 + 3 * sizeof (ArvChunkInfos)); chunk_data = arv_buffer_get_chunk_data (buffer, 0x01020304, &chunk_data_size); g_assert (chunk_data == NULL); g_assert_cmpint (chunk_data_size, ==, 0); int_value = arv_chunk_parser_get_integer_value (parser, buffer, "ChunkInt", &error); g_assert_cmpint (int_value, ==, 0x11223344); g_assert (error == NULL); float_value = arv_chunk_parser_get_float_value (parser, buffer, "ChunkFloat", &error); g_assert_cmpfloat (float_value, ==, 1.1); g_assert (error == NULL); string_value = arv_chunk_parser_get_string_value (parser, buffer, "ChunkString", &error); g_assert_cmpstr (string_value, ==, "Hello"); g_assert (error == NULL); boolean_value = arv_chunk_parser_get_boolean_value (parser, buffer, "ChunkBoolean", &error); g_assert (boolean_value); g_assert (error == NULL); arv_chunk_parser_get_integer_value (parser, buffer, "Dummy", &error); g_assert (error != NULL); g_clear_error (&error); arv_chunk_parser_get_float_value (parser, buffer, "Dummy", &error); g_assert (error != NULL); g_clear_error (&error); arv_chunk_parser_get_string_value (parser, buffer, "Dummy", &error); g_assert (error != NULL); g_clear_error (&error); g_object_unref (buffer); g_object_unref (parser); g_object_unref (device); } static void visibility_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "RWInteger"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); g_assert_cmpint (arv_gc_feature_node_get_visibility (ARV_GC_FEATURE_NODE (node)), ==, ARV_GC_VISIBILITY_INVISIBLE); node = arv_gc_get_node (genicam, "RWFloat"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); g_assert_cmpint (arv_gc_feature_node_get_visibility (ARV_GC_FEATURE_NODE (node)), ==, ARV_GC_VISIBILITY_GURU); node = arv_gc_get_node (genicam, "RWBoolean"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); g_assert_cmpint (arv_gc_feature_node_get_visibility (ARV_GC_FEATURE_NODE (node)), ==, ARV_GC_VISIBILITY_EXPERT); node = arv_gc_get_node (genicam, "Enumeration"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); g_assert_cmpint (arv_gc_feature_node_get_visibility (ARV_GC_FEATURE_NODE (node)), ==, ARV_GC_VISIBILITY_BEGINNER); g_object_unref (device); } static void category_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; GError *error = NULL; const GSList *features; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "Root"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); g_assert (ARV_IS_GC_CATEGORY (node)); g_assert_cmpstr (arv_gc_feature_node_get_description (ARV_GC_FEATURE_NODE (node)), ==, "description"); g_assert_cmpstr (arv_gc_feature_node_get_tooltip (ARV_GC_FEATURE_NODE (node)), ==, "tooltip"); g_assert_cmpstr (arv_gc_feature_node_get_display_name (ARV_GC_FEATURE_NODE (node)), ==, "display_name"); features = arv_gc_category_get_features (ARV_GC_CATEGORY (node)); g_assert_cmpint (g_slist_length ((GSList *) features), ==, 8); g_object_unref (device); } static void lock_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; ArvGcNode *lock; GError *error = NULL; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "LockedByInteger"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); lock = arv_gc_get_node (genicam, "IntegerLock"); g_assert (ARV_IS_GC_INTEGER (lock)); g_assert (!arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); arv_gc_integer_set_value (ARV_GC_INTEGER (lock), 10, &error); g_assert (arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); node = arv_gc_get_node (genicam, "LockedByFloat"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); lock = arv_gc_get_node (genicam, "FloatLock"); g_assert (ARV_IS_GC_FLOAT (lock)); g_assert (!arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); arv_gc_float_set_value (ARV_GC_FLOAT (lock), 10.0, &error); g_assert (arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); node = arv_gc_get_node (genicam, "LockedByBoolean"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); lock = arv_gc_get_node (genicam, "BooleanLock"); g_assert (ARV_IS_GC_BOOLEAN (lock)); g_assert (!arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); arv_gc_boolean_set_value (ARV_GC_BOOLEAN (lock), TRUE, &error); g_assert (arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_assert (error == NULL); node = arv_gc_get_node (genicam, "NoLock"); g_assert (ARV_IS_GC_INTEGER_NODE (node)); g_assert (!arv_gc_feature_node_is_locked (ARV_GC_FEATURE_NODE (node), &error)); g_object_unref (device); } static void access_mode_test (void) { ArvDevice *device; ArvGc *genicam; ArvGcNode *node; ArvGcAccessMode access_mode; GError *error = NULL; device = arv_fake_device_new ("TEST0", &error); g_assert (ARV_IS_FAKE_DEVICE (device)); g_assert (error == NULL); genicam = arv_device_get_genicam (device); g_assert (ARV_IS_GC (genicam)); node = arv_gc_get_node (genicam, "RWRegister_ImposedAccessModeRW"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node = arv_gc_get_node (genicam, "RWRegister_ImposedAccessModeRO"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "RORegister_ImposedAccessModeRW"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "RORegister_ImposedAccessModeRO"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "RWStructEntry_ImposedAccessModeRW"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RW); node = arv_gc_get_node (genicam, "RWStructEntry_ImposedAccessModeRO"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "ROStructEntry_ImposedAccessModeRW"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); node = arv_gc_get_node (genicam, "ROStructEntry_ImposedAccessModeRO"); g_assert (ARV_IS_GC_FEATURE_NODE (node)); access_mode = arv_gc_feature_node_get_actual_access_mode (ARV_GC_FEATURE_NODE (node)); g_assert_cmpint (access_mode, ==, ARV_GC_ACCESS_MODE_RO); g_object_unref (device); } int main (int argc, char *argv[]) { int result; g_test_init (&argc, &argv, NULL); arv_set_fake_camera_genicam_filename (GENICAM_FILENAME); g_test_add_func ("/genicam/value-type", node_value_type_test); g_test_add_func ("/genicam/integer", integer_test); g_test_add_func ("/genicam/boolean", boolean_test); g_test_add_func ("/genicam/float", float_test); g_test_add_func ("/genicam/enumeration", enumeration_test); g_test_add_func ("/genicam/swissknife", swiss_knife_test); g_test_add_func ("/genicam/converter", converter_test); g_test_add_func ("/genicam/register", register_test); g_test_add_func ("/genicam/string", string_test); g_test_add_func ("/genicam/url", url_test); g_test_add_func ("/genicam/mandatory", mandatory_test); g_test_add_func ("/genicam/chunk-data", chunk_data_test); g_test_add_func ("/genicam/indexed", indexed_test); g_test_add_func ("/genicam/visibility", visibility_test); g_test_add_func ("/genicam/category", category_test); g_test_add_func ("/genicam/lock", lock_test); g_test_add_func ("/genicam/access-mode", access_mode_test); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/js/000077500000000000000000000000001475431451200146575ustar00rootroot00000000000000aravis-0.8.34/tests/js/arv-camera-test.js000066400000000000000000000025671475431451200202220ustar00rootroot00000000000000#!/usr/bin/env gjs /* SPDX-License-Identifier:Unlicense */ /* If you have installed aravis in a non standard location, you may need to make GI_TYPELIB_PATH point to the correct location. For example: export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ You may also have to give the path to libaravis.so, using LD_PRELOAD or LD_LIBRARY_PATH. */ const GLib = imports.gi.GLib; const Aravis = imports.gi.Aravis; let camera = Aravis.Camera.new (null); camera.set_region (0,0,128,128); camera.set_pixel_format (Aravis.PIXEL_FORMAT_MONO_8); camera.set_frame_rate (10.0); let [x,y,width,height] = camera.get_region (); let payload = camera.get_payload (); print ("Camera vendor : ", camera.get_vendor_name ()); print ("Camera model : ", camera.get_model_name ()); print ("ROI : ", width, "x", height, " at ", x, ",", y); print ("Payload : ", payload); print ("Pixel format : ", camera.get_pixel_format_as_string ()); let stream = camera.create_stream (null); for (let i = 0; i < 10; i++) { stream.push_buffer (Aravis.Buffer.new_allocate (payload)); } print ("Start Acquisition"); camera.start_acquisition (); print ("Acquisition"); for (let i = 0; i < 20; i++) { let buffer = stream.pop_buffer (); print (buffer); if (buffer) { stream.push_buffer (buffer); } } print ("Stop acquisition"); camera.stop_acquisition (); aravis-0.8.34/tests/js/arv-evaluator-test.js000066400000000000000000000014171475431451200207650ustar00rootroot00000000000000#!/usr/bin/env gjs /* SPDX-License-Identifier:Unlicense */ /* If you have installed aravis in a non standard location, you may need to make GI_TYPELIB_PATH point to the correct location. For example: export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ You may also have to give the path to libaravis.so, using LD_PRELOAD or LD_LIBRARY_PATH. */ const GLib = imports.gi.GLib; const Aravis = imports.gi.Aravis; let evaluator = Aravis.Evaluator.new ("1+2*4.4"); let intResult = evaluator.evaluate_as_int64 (); let dblResult = evaluator.evaluate_as_double (); print (intResult); print (dblResult); evaluator.set_expression ("VAR+10"); evaluator.set_double_variable ("VAR", 1.2); dblResult = evaluator.evaluate_as_double (); print (dblResult); aravis-0.8.34/tests/js/arv-fake-test.js000066400000000000000000000014441475431451200176710ustar00rootroot00000000000000#!/usr/bin/env gjs /* SPDX-License-Identifier:Unlicense */ /* If you have installed aravis in a non standard location, you may need to make GI_TYPELIB_PATH point to the correct location. For example: export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ You may also have to give the path to libaravis.so, using LD_PRELOAD or LD_LIBRARY_PATH. */ const GLib = imports.gi.GLib; const Aravis = imports.gi.Aravis; let device = Aravis.FakeDevice.new ("TEST0"); let genicam = device.get_genicam (); let payload = genicam.get_node ("PayloadSize").get_value (); let width = genicam.get_node ("SensorWidth").get_value (); let height = genicam.get_node ("SensorHeight").get_value (); print ("Payload : ", payload); print ("Width : ", width); print ("Height : ", height); aravis-0.8.34/tests/js/launch000077500000000000000000000001541475431451200160570ustar00rootroot00000000000000#!/bin/sh export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:../../src export LD_LIBRARY_PATH=../../src/.libs gjs $* aravis-0.8.34/tests/jslaunch-dbg.in000077500000000000000000000002451475431451200171400ustar00rootroot00000000000000#!/bin/sh export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ gdb -ex run --args gjs $* aravis-0.8.34/tests/jslaunch.in000077500000000000000000000002221475431451200164010ustar00rootroot00000000000000#!/bin/sh export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ gjs $* aravis-0.8.34/tests/loadhttptest.c000066400000000000000000000022271475431451200171310ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include #include #include int main (int argc, char **argv) { GFile *file; GFileInputStream *stream; char *scheme = NULL; char *path = NULL; char *genicam = NULL; const char *filename; gsize len = 0; if (argc != 2) { printf ("Usage: load-http-test \n"); return EXIT_FAILURE; } filename = argv[1]; if (arv_parse_genicam_url (filename, -1, &scheme, NULL, &path, NULL, NULL, NULL, NULL)) { if (g_ascii_strcasecmp (scheme, "http:") == 0) { file = g_file_new_for_uri (filename); stream = g_file_read (file, NULL, NULL); if(stream) { GDataInputStream *data_stream; data_stream = g_data_input_stream_new (G_INPUT_STREAM (stream)); genicam = g_data_input_stream_read_upto (data_stream, "", 0, &len, NULL, NULL); g_object_unref (data_stream); g_object_unref (stream); } g_object_unref (file); } } g_print ("size = %" G_GSIZE_FORMAT "\n", len); g_print ("%s\n", genicam != NULL ? genicam : "NULL"); g_free (scheme); g_free (path); g_free (genicam); return EXIT_SUCCESS; } aravis-0.8.34/tests/meson.build000066400000000000000000000073231475431451200164120ustar00rootroot00000000000000doc_examples = files (['arvexample.c', 'arvchunkparsertest.c']) if get_option('tests') add_test_setup ( 'valgrind', exe_wrapper:[ 'valgrind', '--tool=memcheck', '--leak-check=yes', '--suppressions=' + meson.project_source_root() / 'tests' / 'aravis.supp', '--error-exitcode=1' ], env:['DEBUGINFOD_URLS=', 'ARV_TEST_IGNORE_BUFFER='], exclude_suites:['python'], timeout_multiplier:5 ) tests = [ ['evaluator', ['main'], []], ['buffer', ['main'], []], ['misc', ['main'], []], ['dom', ['main'], ['-DGENICAM_FILENAME="@0@/tests/data/genicam.xml"'.format (meson.project_source_root ())]], ['genicam', ['main'], ['-DGENICAM_FILENAME="@0@/tests/data/genicam.xml"'.format (meson.project_source_root ())]], ['fake', ['main'], ['-DGENICAM_FILENAME="@0@/src/arv-fake-camera.xml"'.format (meson.project_source_root ())]], ['fakegv', ['network'], ['-DGENICAM_FILENAME="@0@/src/arv-fake-camera.xml"'.format (meson.project_source_root ())]] ] foreach t: tests exe = executable (t[0], '@0@.c'.format (t[0]), c_args: [t[2]], link_with: aravis_library, dependencies: aravis_dependencies, include_directories: [library_inc]) test (t[0], exe, suite: t[1], timeout: 60) endforeach if introspection_enabled pymod = import ('python') py = pymod.find_installation ('python3', required: false) if py.found() python_tests = [ ['fake.py', []], ['exception.py', []] ] environment = [ 'GI_TYPELIB_PATH=' + meson.project_build_root() / 'src', 'LD_LIBRARY_PATH=' + meson.project_build_root() / 'src', 'FAKE_GENICAM_PATH=' + meson.project_source_root() / 'src' / 'arv-fake-camera.xml' ] foreach t: python_tests test (t[0], py, suite: 'python', args: files (t[0]), env: environment) endforeach endif py_script_config_data = configuration_data () py_script_config_data.set ('GI_TYPELIB_PATH', meson.project_build_root() / 'src') py_script_config_data.set ('LD_LIBRARY_PATH', meson.project_build_root() / 'src') py_script_config_data.set ('GST_PLUGIN_PATH', meson.project_build_root() / 'gst') py_script_config_data.set ('FAKE_GENICAM_PATH', meson.project_source_root() / 'src' / 'arv-fake-camera.xml') configure_file (input: 'pylaunch.in', output: 'pylaunch', configuration: py_script_config_data) configure_file (input: 'pylaunch-dbg.in', output: 'pylaunch-dbg', configuration: py_script_config_data) configure_file (input: 'jslaunch.in', output: 'jslaunch', configuration: py_script_config_data) configure_file (input: 'jslaunch-dbg.in', output: 'jslaunch-dbg', configuration: py_script_config_data) endif examples = [ ['arv-network-test', 'arvnetworktest.c'], ['arv-device-test', 'arvdevicetest.c'], ['arv-genicam-test', 'arvgenicamtest.c'], ['arv-evaluator-test', 'arvevaluatortest.c'], ['arv-zip-test', 'arvziptest.c'], ['arv-chunk-parser-test', 'arvchunkparsertest.c'], ['arv-heartbeat-test', 'arvheartbeattest.c'], ['arv-acquisition-test', 'arvacquisitiontest.c'], ['arv-example', 'arvexample.c'], ['arv-auto-packet-size-test', 'arvautopacketsizetest.c'], ['arv-device-scan-test', 'arvdevicescantest.c'], ['arv-roi-test', 'arvroitest.c'], ['time-test', 'timetest.c'], ['load-http-test', 'loadhttptest.c'], ['cpp-test', 'cpp.cc'] ] if host_machine.system()=='linux' examples+=[['realtime-test','realtimetest.c']] # uses Linux RT API unavailable on other platforms endif foreach example: examples exe = executable (example[0], example[1], link_with: aravis_library, dependencies: aravis_dependencies, include_directories: [library_inc]) endforeach endif aravis-0.8.34/tests/misc.c000066400000000000000000000176661475431451200153620ustar00rootroot00000000000000/* SPDX-License-Identifier:Unlicense */ #include #include #include #include #include "../src/arvmiscprivate.h" #if !ARAVIS_CHECK_VERSION (ARAVIS_MAJOR_VERSION, ARAVIS_MINOR_VERSION, ARAVIS_MICRO_VERSION) #error #endif static void unaligned_from_le_ptr_test (void) { char test[]= {'\x11', '\x22', '\x33', '\x44','\x55', '\x66', '\x77', '\x88', '\x99', '\xaa', '\xbb', '\xcc'}; guint32 v_uint32; guint16 v_uint16; v_uint32 = ARV_GUINT32_FROM_LE_PTR (&test[0], 0); g_assert_cmpuint (v_uint32, ==, 0x44332211); v_uint32 = ARV_GUINT32_FROM_LE_PTR (&test[1], 0); g_assert_cmpuint (v_uint32, ==, 0x55443322); v_uint32 = ARV_GUINT32_FROM_LE_PTR (&test[2], 0); g_assert_cmpuint (v_uint32, ==, 0x66554433); v_uint32 = ARV_GUINT32_FROM_LE_PTR (&test[3], 0); g_assert_cmpuint (v_uint32, ==, 0x77665544); v_uint16 = ARV_GUINT16_FROM_LE_PTR (&test[0], 0); g_assert_cmpuint (v_uint16, ==, 0x2211); v_uint16 = ARV_GUINT16_FROM_LE_PTR (&test[1], 0); g_assert_cmpuint (v_uint16, ==, 0x3322); v_uint16 = ARV_GUINT16_FROM_LE_PTR (&test[2], 0); g_assert_cmpuint (v_uint16, ==, 0x4433); v_uint16 = ARV_GUINT16_FROM_LE_PTR (&test[3], 0); g_assert_cmpuint (v_uint16, ==, 0x5544); } #define ILLEGAL_CHARACTERS "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \ " _-" #define REPLACEMENT_CHARACTER '-' struct { char *before; char *after; } strip_strings[] = { {"\n\tHello\r\nworld!\n\t", "Hello-world!"}, {"\n\tHello", "Hello"}, {"Hello\r\t", "Hello"}, {"Hello\rworld!", "Hello-world!"}, {"Hello\r- -_\rworld!", "Hello-world!"}, {"", ""}, {"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", ""}, {"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", ""}, {"\r", ""}, {"\n\n", ""}, {"Hétéroclite", "Hétéroclite"} }; static void arv_str_strip_test (void) { unsigned i; char *string; for (i = 0; i < G_N_ELEMENTS (strip_strings); i++) { string = g_strdup (strip_strings[i].before); arv_str_strip (string, ILLEGAL_CHARACTERS, REPLACEMENT_CHARACTER); g_assert_cmpstr (string, ==, strip_strings[i].after); g_free (string); } string = g_strdup ("Hello\r\n world"); arv_str_strip (string, ILLEGAL_CHARACTERS, '\0'); g_assert_cmpstr (string, ==, "Helloworld"); g_free (string); g_assert (arv_str_strip (NULL, ILLEGAL_CHARACTERS, REPLACEMENT_CHARACTER) == NULL); g_assert (arv_str_strip (NULL, NULL, REPLACEMENT_CHARACTER) == NULL); } struct { char *uri; gboolean is_valid; } uris[] = { {"http://www.gnome/org", TRUE}, {"file:///file.txtx", TRUE}, {"", FALSE} }; static void arv_str_uri_test (void) { char *uri; unsigned i; gboolean success; for (i = 0; i < G_N_ELEMENTS (uris); i++) { success = arv_str_is_uri (uris[i].uri); g_assert (success == uris[i].is_valid); } uri = arv_str_to_uri ("http://www.gnome.org/test"); g_assert_cmpstr (uri, ==, "http://www.gnome.org/test"); g_free (uri); uri = arv_str_to_uri ("/test.txt"); g_assert_cmpstr (uri, ==, "file:///test.txt"); g_free (uri); uri = arv_str_to_uri ("test.txt"); g_assert (g_str_has_suffix (uri, "test.txt")); g_assert (g_str_has_prefix (uri, "file:///")); g_free (uri); } static void arv_str_parse_double_test (void) { gboolean success; char *test1 = "-10.0"; char *test2 = "+10.0"; char *test3 = "11.0a"; double value; success = arv_str_parse_double (&test1, &value); g_assert (success); g_assert_cmpfloat (value, ==, -10.0); g_assert_cmpint (test1[0], ==, '\0'); success = arv_str_parse_double (&test2, &value); g_assert (success); g_assert_cmpfloat (value, ==, 10.0); g_assert_cmpint (test2[0], ==, '\0'); success = arv_str_parse_double (&test3, &value); g_assert (success); g_assert_cmpfloat (value, ==, 11.0); g_assert_cmpint (test3[0], ==, 'a'); } static void arv_str_parse_double_list_test (void) { char *test1 = " -10.0 +20.0 "; double value[2]; unsigned n_values; n_values = arv_str_parse_double_list (&test1, 2, value); g_assert_cmpint (n_values, ==, 2); g_assert_cmpfloat (value[0], ==, -10.0); g_assert_cmpfloat (value[1], ==, 20.0); g_assert_cmpint (test1[0], ==, '\0'); } static void arv_vendor_alias_lookup_test (void) { const char *vendor_a = "The Imaging Source Europe GmbH"; const char *vendor_b = "Unknown Vendor"; const char *alias; alias = arv_vendor_alias_lookup (NULL); g_assert (alias == NULL); alias = arv_vendor_alias_lookup (vendor_a); g_assert_cmpstr (alias, == ,"TIS"); alias = arv_vendor_alias_lookup (vendor_b); g_assert (alias == vendor_b); } struct { guint64 pixel_format; } caps_data[] = { { ARV_PIXEL_FORMAT_MONO_8 }, { ARV_PIXEL_FORMAT_MONO_10 }, { ARV_PIXEL_FORMAT_MONO_12 }, { ARV_PIXEL_FORMAT_MONO_12_PACKED }, { ARV_PIXEL_FORMAT_MONO_14 }, { ARV_PIXEL_FORMAT_MONO_16 }, { ARV_PIXEL_FORMAT_BAYER_GB_8 }, { ARV_PIXEL_FORMAT_BAYER_RG_8 }, { ARV_PIXEL_FORMAT_BAYER_GR_8 }, { ARV_PIXEL_FORMAT_BAYER_BG_8 }, { ARV_PIXEL_FORMAT_YUV_422_PACKED }, { ARV_PIXEL_FORMAT_YUV_422_YUYV_PACKED }, { ARV_PIXEL_FORMAT_CUSTOM_YUV_422_YUYV_PACKED }, }; static void caps_string_test (void) { unsigned int i; for (i = 0; i < G_N_ELEMENTS (caps_data); i++) { const char *caps_string; caps_string = arv_pixel_format_to_gst_caps_string (caps_data[i].pixel_format); g_assert (caps_string != NULL); } for (i = 0; i < G_N_ELEMENTS (caps_data); i++) { const char *caps_string; caps_string = arv_pixel_format_to_gst_caps_string_full (caps_data[i].pixel_format, "Vendor", "Model"); g_assert (caps_string != NULL); } } struct { const char *glob_pattern; const char *pattern; } glob_patterns[] = { { "*Foo*", "^.*Foo.*$"}, { "Bar", "^Bar$"}, { "http://www.gnome.org/index.html", "^http://www\\.gnome\\.org/index\\.html$"}, { " Foo* | Bar* ", "^Foo.*$|^Bar.*$"} }; static void glob_test (void) { unsigned i; for (i = 0; i < G_N_ELEMENTS (glob_patterns); i++) { GRegex *regex; const char *pattern; regex = arv_regex_new_from_glob_pattern (glob_patterns[i].glob_pattern, TRUE); g_assert (regex != NULL); pattern = g_regex_get_pattern (regex); g_assert_cmpstr (pattern, ==, glob_patterns[i].pattern); g_regex_unref (regex); } } struct { const char *glob; const char *check; const char *check_caseless; } match_data[] = { { "Foo*", "Foobar", "foobar"}, { "Foo*", "Foo", "foo"}, { "*Bar", "fooBar", "foobar"}, { "*Bar", "Bar", "bar"}, { "FooBar", "FooBar", "foobar"}, { "???{Foo}*r", "bar{Foo}bar", "bar{foo}bar"} }; static void match_test (void) { unsigned i; for (i = 0; i < G_N_ELEMENTS (match_data); i++) { GRegex *regex; regex = arv_regex_new_from_glob_pattern (match_data[i].glob,FALSE); g_assert (regex != NULL); g_assert (g_regex_match (regex, match_data[i].check, 0, NULL)); g_regex_unref (regex); regex = arv_regex_new_from_glob_pattern (match_data[i].glob, TRUE); g_assert (regex != NULL); g_assert (g_regex_match (regex, match_data[i].check_caseless, 0, NULL)); g_regex_unref (regex); } } int main (int argc, char *argv[]) { int result; g_test_init (&argc, &argv, NULL); g_test_add_func ("/buffer/unaligned-from-le", unaligned_from_le_ptr_test); g_test_add_func ("/str/arv-str-strip", arv_str_strip_test); g_test_add_func ("/str/arv-str-uri", arv_str_uri_test); g_test_add_func ("/str/arv-str-parse-double", arv_str_parse_double_test); g_test_add_func ("/str/arv-str-parse-double-list", arv_str_parse_double_list_test); g_test_add_func ("/misc/arv-vendor-alias-lookup", arv_vendor_alias_lookup_test); g_test_add_func ("/gstreamer/caps-string", caps_string_test); g_test_add_func ("/misc/globs", glob_test); g_test_add_func ("/misc/matches", match_test); result = g_test_run(); arv_shutdown (); return result; } aravis-0.8.34/tests/pylaunch-dbg.in000077500000000000000000000003221475431451200171500ustar00rootroot00000000000000#!/bin/sh export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ export GST_PLUGIN_PATH=@GST_PLUGIN_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ gdb -ex run --args python3 $* aravis-0.8.34/tests/pylaunch.in000077500000000000000000000002771475431451200164270ustar00rootroot00000000000000#!/bin/sh export GI_TYPELIB_PATH=@GI_TYPELIB_PATH@ export LD_LIBRARY_PATH=@LD_LIBRARY_PATH@ export GST_PLUGIN_PATH=@GST_PLUGIN_PATH@ export FAKE_GENICAM_PATH=@FAKE_GENICAM_PATH@ python3 $* aravis-0.8.34/tests/python/000077500000000000000000000000001475431451200155645ustar00rootroot00000000000000aravis-0.8.34/tests/python/arv-buffer-test.py000066400000000000000000000012561475431451200211560ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis buffer_a = Aravis.Buffer.new_allocate (1024) buffer_b = Aravis.Buffer.new_allocate (1024) print ("Buffer a refcount : %d" %(buffer_a.__grefcount__)) print ("Buffer b refcount : %d" %(buffer_b.__grefcount__)) aravis-0.8.34/tests/python/arv-camera-test.py000066400000000000000000000030121475431451200211250ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import sys import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis Aravis.enable_interface ("Fake") try: if len(sys.argv) > 1: camera = Aravis.Camera.new (sys.argv[1]) else: camera = Aravis.Camera.new (None) except TypeError: print ("No camera found") exit () camera.set_region (0,0,128,128) camera.set_frame_rate (10.0) camera.set_pixel_format (Aravis.PIXEL_FORMAT_MONO_8) payload = camera.get_payload () [x,y,width,height] = camera.get_region () print ("Camera vendor : %s" %(camera.get_vendor_name ())) print ("Camera model : %s" %(camera.get_model_name ())) print ("ROI : %dx%d at %d,%d" %(width, height, x, y)) print ("Payload : %d" %(payload)) print ("Pixel format : %s" %(camera.get_pixel_format_as_string ())) stream = camera.create_stream (None, None) for i in range(0,10): stream.push_buffer (Aravis.Buffer.new_allocate (payload)) print ("Start acquisition") camera.start_acquisition () print ("Acquisition") for i in range(0,20): image = stream.pop_buffer () print (image) if image: stream.push_buffer (image) print ("Stop acquisition") camera.stop_acquisition () aravis-0.8.34/tests/python/arv-enum-test.py000066400000000000000000000012241475431451200206440ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis print (Aravis.Auto) print (Aravis.Auto.OFF) print (Aravis.BufferStatus) print (Aravis.DebugLevel) print (Aravis.DomNodeType) print (Aravis.GvStreamPacketResend) print (Aravis.PIXEL_FORMAT_MONO_8) aravis-0.8.34/tests/python/arv-evaluator-test.py000066400000000000000000000014161475431451200217050ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis evaluator = Aravis.Evaluator.new ("1+2*4.4") int_result = evaluator.evaluate_as_int64 () dbl_result = evaluator.evaluate_as_double () print (int_result) print (dbl_result) evaluator.set_expression ("VAR+10") evaluator.set_double_variable ("VAR", 1.2) dbl_result = evaluator.evaluate_as_double() print (dbl_result) aravis-0.8.34/tests/python/arv-feature-test.py000066400000000000000000000030501475431451200213320ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis Aravis.enable_interface("Fake") try: camera = Aravis.Camera(name="Fake_1") except TypeError: print ("No camera found") exit () device = camera.get_device () device.set_integer_feature_value ("Width", 1024) device.set_integer_feature_value ("Height", 1024) print ("Width = %d" %(device.get_integer_feature_value ("Width"))) print ("Height = %d" %(device.get_integer_feature_value ("Height"))) def pt(x): print(type(x), x) pt(device.get_feature_value("DeviceVersion")) device.set_feature_value("Width", 640.0) pt(device.get_feature_value("Width")) device.set_feature_value("AcquisitionFrameRate", 2) pt(device.get_feature_value("AcquisitionFrameRate")) device.set_feature_value("TestBoolean", True) pt(device.get_feature_value("TestBoolean")) try: device.set_feature_value("TestBoolean", None) except TypeError: pass else: print("Expected TypeError") device.set_feature_value("TestBoolean", 0) pt(device.get_feature_value("TestBoolean")) device.set_feature_value("TestStringReg", "TestValue") pt(device.get_feature_value("TestStringReg")) pt(device.get_feature_value("GainAuto")) aravis-0.8.34/tests/python/arv-genicam-test.py000066400000000000000000000014221475431451200213030ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import sys import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis Aravis.enable_interface ("Fake") try: if len(sys.argv) > 1: camera = Aravis.Camera.new (sys.argv[1]) else: camera = Aravis.Camera.new (None) except TypeError: print ("No camera found") exit () device = camera.get_device () genicam = device.get_genicam_xml () print (genicam) aravis-0.8.34/tests/python/arv-gst-software-trigger.py000077500000000000000000000045571475431451200230300ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. # # As this example alos uses the aravis gstreamer plugin, you may need to tell the path to the plugin using # GST_PLUGIN_PATH. # Example of a gstreamer pipeline using a software trigger # # Author: WhaSukGO import gi import threading import time gi.require_version('Gst', '1.0') from gi.repository import Gst, GLib # Initialize GStreamer Gst.init(None) def main(): # Create GStreamer elements aravissrc = Gst.ElementFactory.make("aravissrc", "source") aravissrc.set_property("exposure", 1700) aravissrc.set_property("do-timestamp", True) aravissrc.set_property("trigger", "Software") bayer_caps = Gst.Caps.from_string("video/x-bayer,format=rggb,width=800,height=600") bayer2rgb = Gst.ElementFactory.make("bayer2rgb", "bayer2rgb") videoconvert = Gst.ElementFactory.make("videoconvert", "videoconvert") videosink = Gst.ElementFactory.make("autovideosink", "videosink") # Create a new pipeline pipeline = Gst.Pipeline.new("mypipeline") # Add elements to the pipeline pipeline.add(aravissrc) pipeline.add(bayer2rgb) pipeline.add(videoconvert) pipeline.add(videosink) # Link the elements with the capsfilter in between aravissrc.link_filtered(bayer2rgb, bayer_caps) bayer2rgb.link(videoconvert) videoconvert.link(videosink) # Function to trigger the camera def trigger_camera(): while True: time.sleep(0.5) # Sleep for 2 milliseconds (500fps) aravissrc.emit("software-trigger") # Create and start the trigger thread trigger_thread = threading.Thread(target=trigger_camera) trigger_thread.daemon = True # Daemonize thread so it exits when the main program does trigger_thread.start() # Start the pipeline pipeline.set_state(Gst.State.PLAYING) # Run the pipeline loop = GLib.MainLoop() try: loop.run() except KeyboardInterrupt: pass # Clean up pipeline.set_state(Gst.State.NULL) if __name__ == "__main__": main() aravis-0.8.34/tests/python/arv-roi-test.py000066400000000000000000000042241475431451200204740ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import sys import gi import signal gi.require_version('Aravis', '0.8') from gi.repository import Aravis,GLib class SIGINT_handler(): def __init__(self): self.SIGINT = False def signal_handler(self, signal, frame): print('You pressed Ctrl+C!') self.SIGINT = True handler = SIGINT_handler() signal.signal(signal.SIGINT, handler.signal_handler) try: if len(sys.argv) > 1: print ("Looking for camera '" + sys.argv[1] + "'"); camera = Aravis.Camera.new (sys.argv[1]) else: print ("Looking for the first available camera"); camera = Aravis.Camera.new (None) except GLib.Error as err: print ("No camera found: " + err.message) exit () camera.set_region(0, 0, 100, 100) camera.set_frame_rate(20.0) region = camera.get_region() print("vendor name = " + camera.get_vendor_name()) print("model name = " + camera.get_model_name()) print("region = {0}".format(region)) stream = camera.create_stream() payload = camera.get_payload() for i in range(10): stream.push_buffer(Aravis.Buffer.new(payload)) camera.start_acquisition() counter=0 while not handler.SIGINT: buffer = stream.timeout_pop_buffer(1000000) if buffer != None: print("Buffer {0}x{0} {1}".format(buffer.get_image_width(), buffer)) stream.push_buffer(buffer) if counter % 10 == 0: width = ((counter / 10) % 2 + 1) * 100 print (width) camera.stop_acquisition() stream.stop_thread(True) camera.set_region(0, 0, width, width) payload = camera.get_payload() for i in range(10): stream.push_buffer(Aravis.Buffer.new(payload)) stream.start_thread() camera.start_acquisition() counter = counter + 1 camera.stop_acquisition() aravis-0.8.34/tests/python/arv-single-buffer-test.py000066400000000000000000000041321475431451200224310ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import sys import gi gi.require_version ('Aravis', '0.8') from gi.repository import Aravis # Hexadecimal dump code from http://www.alexonlinux.com/hex-dump-functions def DumpBuffer (buf, length, caption="", dest=sys.stdout): def ArvGetPrintableChar(c): if c.isalpha(): return c else: return '.' dest.write('---------> %s <--------- (%d bytes)\n' % (caption, length)) dest.write(' +0 +4 +8 +c 0 4 8 c\n') i = 0 while i < length: if length - i > 16: l = 16 else: l = length - i dest.write('+%04x ' % i) s = ' '.join(["%02x" % c for c in buf[i:i + l]]) dest.write(s) sp = 49 - len(s) dest.write(' ' * sp) s = ''.join(["%c" % ArvGetPrintableChar(str(chr(c))) for c in buf[i:i + l]]) dest.write(s) dest.write('\n') i = i + 16 Aravis.enable_interface ("Fake") try: if len(sys.argv) > 1: camera = Aravis.Camera.new (sys.argv[1]) else: camera = Aravis.Camera.new (None) except TypeError: print ("No camera found") exit () payload = camera.get_payload () [x,y,width,height] = camera.get_region () print ("Camera vendor : %s" %(camera.get_vendor_name ())) print ("Camera model : %s" %(camera.get_model_name ())) print ("ROI : %dx%d at %d,%d" %(width, height, x, y)) print ("Payload : %d" %(payload)) print ("Pixel format : %s" %(camera.get_pixel_format_as_string ())) stream = camera.create_stream (None, None) stream.push_buffer (Aravis.Buffer.new_allocate (payload)) print ("Start acquisition") camera.start_acquisition () print ("Acquisition") image = stream.pop_buffer () data = image.get_data () DumpBuffer (data, len(data), "Image buffer") print ("Stop acquisition") camera.stop_acquisition () aravis-0.8.34/tests/python/arv-stream-callback.py000066400000000000000000000025641475431451200217600ustar00rootroot00000000000000#!/usr/bin/env python3 # SPDX-License-Identifier:Unlicense # If you have installed aravis in a non standard location, you may need # to make GI_TYPELIB_PATH point to the correct location. For example: # # export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/opt/bin/lib/girepositry-1.0/ # # You may also have to give the path to libaravis.so, using LD_PRELOAD or # LD_LIBRARY_PATH. import threading import time import gi # autopep8: off gi.require_version('Aravis', '0.8') from gi.repository import Aravis # noqa: E402 # autopep8: on Aravis.enable_interface('Fake') class UserData: def __init__(self) -> None: self.stream = None def callback(user_data, cb_type, buffer): print(f'Callback[{threading.get_native_id()}] {cb_type.value_name} {buffer=}') if buffer is not None: # Fake some light computation here (like copying the buffer). Do not do heavy image processing here # since it would block the acquisition thread. time.sleep(0.01) # Re-enqueue the buffer user_data.stream.push_buffer(buffer) print(f'Main thread[{threading.get_native_id()}]') cam = Aravis.Camera.new('Fake_1') user_data = UserData() stream = cam.create_stream(callback, user_data) user_data.stream = stream payload = cam.get_payload() stream.push_buffer(Aravis.Buffer.new_allocate(payload)) cam.start_acquisition() time.sleep(1) cam.stop_acquisition() aravis-0.8.34/tests/realtimetest.c000066400000000000000000000103501475431451200171100ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009 Lennart Poettering * Copyright © 2010 David Henningsson * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * . */ #include #include #include #include #include #include #include #include #include #ifndef SCHED_RESET_ON_FORK #define SCHED_RESET_ON_FORK 0x40000000 #endif #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif static void print_status(const char *t) { #if !defined(__APPLE__) && !defined(G_OS_WIN32) int ret; if ((ret = sched_getscheduler(0)) < 0) { fprintf(stderr, "sched_getscheduler() failed: %s\n", strerror(errno)); return; } printf("%s:\n" "\tSCHED_RESET_ON_FORK: %s\n", t, (ret & SCHED_RESET_ON_FORK) ? "yes" : "no"); if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_RR) { struct sched_param param; if (sched_getparam(0, ¶m) < 0) { fprintf(stderr, "sched_getschedparam() failed: %s\n", strerror(errno)); return; } printf("\tSCHED_RR with priority %i\n", param.sched_priority); } else if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_OTHER) { errno = 0; ret = getpriority(PRIO_PROCESS, 0); if (errno != 0) { fprintf(stderr, "getpriority() failed: %s\n", strerror(errno)); return; } printf("\tSCHED_OTHER with nice level: %i\n", ret); } else fprintf(stderr, "Neither SCHED_RR nor SCHED_OTHER.\n"); #else printf ("SCHED API not supported on OSX/Windows\n"); #endif } int main(int argc, char *argv[]) { GDBusConnection *bus; GError *error = NULL; int max_realtime_priority, min_nice_level; long long rttime_usec_max; struct rlimit rlim; bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (!G_IS_DBUS_CONNECTION (bus)) { fprintf (stderr, "Failed to connect to system bus: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } max_realtime_priority = arv_rtkit_get_max_realtime_priority (bus, &error); if (error != NULL) { fprintf (stderr, "Failed to get MaxRealtimePriority: %s\n", error->message); g_error_free (error); error = NULL; } else printf ("MaxRealtimePriority = %d\n", max_realtime_priority); min_nice_level = arv_rtkit_get_min_nice_level (bus, &error); if (error != NULL) { fprintf (stderr, "Failed to get MinNiceLevel: %s\n", error->message); g_error_free (error); error = NULL; } else printf ("MinNiceLevel = %d\n", min_nice_level); rttime_usec_max = arv_rtkit_get_rttime_usec_max (bus, &error); if (error != NULL) { fprintf (stderr, "Failed to get RTTimeUSecMax: %s\n", error->message); g_error_free (error); error = NULL; } else printf ("RTTimeUSecMax = %lld\n", rttime_usec_max); memset(&rlim, 0, sizeof(rlim)); rlim.rlim_cur = rlim.rlim_max = 100000000ULL; /* 100ms */ if ((setrlimit(RLIMIT_RTTIME, &rlim) < 0)) fprintf(stderr, "Failed to set RLIMIT_RTTIME: %s\n", strerror(errno)); print_status("before"); arv_rtkit_make_high_priority (bus, 0, -10, &error); if (error != NULL) { fprintf (stderr, "Failed to become high priority: %s\n", error->message); g_error_free (error); error = NULL; } else printf ("Successfully became high priority\n"); print_status("after high priority"); arv_rtkit_make_realtime (bus, 0, 10, &error); if (error != NULL) { fprintf (stderr, "Failed to get become realtime: %s\n", error->message); g_error_free (error); error = NULL; } else printf ("Successfully became realtime\n"); print_status("after realtime"); g_object_unref (bus); return EXIT_SUCCESS; } aravis-0.8.34/tests/timetest.c000066400000000000000000000025101475431451200162430ustar00rootroot00000000000000#include #include #include #include static gint64 time_wait (gint64 usec ) { gint64 wt, st, tt; gint64 i; st = g_get_real_time (); wt = st + usec; for (i = 0, tt = g_get_real_time(); tt < wt; i++ ) tt = g_get_real_time (); return tt-st; } static gint64 sleep_meas (gint64 usec) { gint64 tt; tt = g_get_real_time (); g_usleep (usec); tt = g_get_real_time () - tt; return tt; } #define MAX_TIME_US 200000 #define N_ITERS 100 int main (int argc, char **argv) { gint64 i, j; double val, wt, min, max, swt; for( i = 1; i < MAX_TIME_US; i = i*2 ) { max = wt = swt = 0.; min = MAX_TIME_US; for (j = 0; j < N_ITERS; j++ ) { val = sleep_meas (i); wt += val; swt += val*val; if (val < min) min = val; if (val > max) max = val; } wt /= j; g_print ("SleepMeas: %6" G_GINT64_FORMAT " - Mean %7g Max %5g Min %5g rms %g\n", i, wt, max, min, sqrt(swt/j - wt*wt)); } for( i = 1; i < MAX_TIME_US; i = i*2 ) { max = wt = swt = 0.; min = MAX_TIME_US; for (j = 0; j < N_ITERS; j++ ) { val = time_wait (i); wt += val; swt += val*val; if( val < min ) min = val; if( val > max ) max = val; } wt /= j; g_print ("TimeWait: %6" G_GINT64_FORMAT " - Mean %7g Max %5g Min %5g rms %g\n", i, wt, max, min, sqrt(swt/j - wt*wt)); } return EXIT_SUCCESS; } aravis-0.8.34/viewer/000077500000000000000000000000001475431451200144025ustar00rootroot00000000000000aravis-0.8.34/viewer/arv-viewer.ui000066400000000000000000001346371475431451200170460ustar00rootroot00000000000000 False acquisition_button True False start 6 6 6 6 6 6 True False Frame rate: 1 1 0 True True 10 False False 2 0 True False Hz 0 3 0 2 Auto False True True True 0 1 True False Exposure: 1 1 1 Auto False True True True 0 2 True False Gain: 1 1 2 True True 11 False False True 2 1 True True 11 False False digits 2 True 2 2 True False µs 0 3 1 200 True True False 4 1 True True False 4 2 True False dB 3 2 False 0 0 Auto False True True True 0 3 True False Black level: 1 1 3 True True 11 False False digits 2 True 2 3 True True False 4 3 400 False False True False True False crossfade True False 6 6 vertical 200 True True 6 True always out True True True camera_liststore False 0 Vendor 2 Model 4 1 3 Serial 4 True True 0 True False 6 True False Refresh camera list True view-refresh-symbolic False True True False False Start video acquisition True media-playback-start-symbolic False True False True 1 True False slide-up True False end 6 6 6 12 6 6 True False Pixel format: right 1 0 1 True False Region size: right 1 0 4 True False x 2 4 True False pixels 4 4 True False Region position: right 1 0 2 True False pixels 4 2 True True 10 10 1 2 True True 10 10 3 2 True True 10 10 3 4 True True 10 10 False 1 4 True False 1 1 True False Binning: right 1 0 3 True True 10 10 False 1 3 True True 10 10 False 3 3 True False pixels 4 3 True False Component: right 1 0 0 True False 1 0 Enable True True False True 3 0 False True 2 page0 True False vertical True False 0 0 none True True 0 True False 6 True True False 4 4 fps (MB/s) False True 0 True False 4 4 Images / Errors False True 1 False True 1 page1 1 -1 True False center start True False 0 0 none True False 6 True False center vertical 6 True False False True 0 True False True True False True 1 False True 0 True True True True False window-close-symbolic False True 1 True False Aravis Viewer True True True Return to camera list True True False go-previous-symbolic 1 False True True True Save a snapshot into image folder True False document-save-symbolic 2 False True True True Rotate image to the right True False object-rotate-right-symbolic 3 False True True True Flip image horizontally True False object-flip-horizontal-symbolic 4 True True True Acquisition settings acquisition_popover True False open-menu-symbolic end 5 False True True True Flip image vertically True False object-flip-vertical-symbolic 5 aravis-0.8.34/viewer/arvviewer.c000066400000000000000000002340311475431451200165630ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include "libxml/parser.h" #include #include #include #include #include #include #include #include #include #include #ifdef GDK_WINDOWING_X11 #include // for GDK_WINDOW_XID #endif #ifdef GDK_WINDOWING_WIN32 #include // for GDK_WINDOW_HWND #endif #define ARV_VIEWER_NOTIFICATION_TIMEOUT 10 #define ARV_VIEWER_N_BUFFERS 10 static gboolean has_autovideo_sink = FALSE; static gboolean has_gtksink = FALSE; static gboolean has_gtkglsink = FALSE; static gboolean has_bayer2rgb = FALSE; static gboolean gstreamer_plugin_check (void) { static gsize check_done = 0; static gboolean check_success = FALSE; if (g_once_init_enter (&check_done)) { GstRegistry *registry; GstPluginFeature *feature; unsigned int i; gboolean success = TRUE; static char *plugins[] = { "appsrc", "videoconvert", "videoflip" }; registry = gst_registry_get (); for (i = 0; i < G_N_ELEMENTS (plugins); i++) { feature = gst_registry_lookup_feature (registry, plugins[i]); if (!GST_IS_PLUGIN_FEATURE (feature)) { g_print ("Gstreamer plugin '%s' is missing.\n", plugins[i]); success = FALSE; } else g_object_unref (feature); } feature = gst_registry_lookup_feature (registry, "autovideosink"); if (GST_IS_PLUGIN_FEATURE (feature)) { has_autovideo_sink = TRUE; g_object_unref (feature); } feature = gst_registry_lookup_feature (registry, "gtksink"); if (GST_IS_PLUGIN_FEATURE (feature)) { has_gtksink = TRUE; g_object_unref (feature); } feature = gst_registry_lookup_feature (registry, "gtkglsink"); if (GST_IS_PLUGIN_FEATURE (feature)) { has_gtkglsink = TRUE; g_object_unref (feature); } feature = gst_registry_lookup_feature (registry, "bayer2rgb"); if (GST_IS_PLUGIN_FEATURE (feature)) { has_bayer2rgb = TRUE; g_object_unref (feature); } if (!has_autovideo_sink && !has_gtkglsink && !has_gtksink) { g_print ("Missing GStreamer video output plugin (autovideosink, gtksink or gtkglsink)\n"); success = FALSE; } if (!success) g_print ("Check your gstreamer installation.\n"); /* Kludge, prevent autoloading of coglsink, which doesn't seem to work for us */ feature = gst_registry_lookup_feature (registry, "coglsink"); if (GST_IS_PLUGIN_FEATURE (feature)) { gst_plugin_feature_set_rank (feature, GST_RANK_NONE); g_object_unref (feature); } check_success = success; g_once_init_leave (&check_done, 1); } return check_success; } struct _ArvViewer { GtkApplication parent_instance; ArvCamera *camera; char *camera_name; ArvStream *stream; ArvBuffer *last_buffer; guint component_id; GstElement *pipeline; GstElement *appsrc; GstElement *transform; GstElement *videosink; guint rotation; gboolean flip_vertical; gboolean flip_horizontal; double gain_min, gain_max; double exposure_min, exposure_max; ArvGcRepresentation gain_representation; ArvGcRepresentation exposure_time_representation; GtkWidget *main_window; GtkWidget *main_stack; GtkWidget *main_headerbar; GtkWidget *camera_box; GtkWidget *refresh_button; GtkWidget *video_mode_button; GtkWidget *camera_tree; GtkWidget *back_button; GtkWidget *snapshot_button; GtkWidget *rotate_cw_button; GtkWidget *flip_vertical_toggle; GtkWidget *flip_horizontal_toggle; GtkWidget *camera_parameters; GtkWidget *component_label; GtkWidget *component_combo; GtkWidget *component_check; GtkWidget *pixel_format_combo; GtkWidget *camera_x; GtkWidget *camera_y; GtkWidget *camera_position_label; GtkWidget *camera_position_units; GtkWidget *camera_binning_x; GtkWidget *camera_binning_y; GtkWidget *camera_binning_label; GtkWidget *camera_binning_units; GtkWidget *camera_width; GtkWidget *camera_height; GtkWidget *video_box; GtkWidget *video_frame; GtkWidget *fps_label; GtkWidget *image_label; GtkWidget *trigger_combo_box; GtkWidget *frame_rate_entry; GtkWidget *exposure_spin_button; GtkWidget *exposure_hscale; GtkWidget *auto_exposure_toggle; GtkWidget *gain_spin_button; GtkWidget *gain_hscale; GtkWidget *auto_gain_toggle; GtkWidget *black_level_spin_button; GtkWidget *black_level_hscale; GtkWidget *auto_black_level_toggle; GtkWidget *acquisition_button; GtkWidget *notification_revealer; GtkWidget *notification_label; GtkWidget *notification_details; GtkWidget *notification_dismiss; guint notification_timeout; gulong camera_selected; gulong exposure_spin_changed; gulong exposure_hscale_changed; gulong auto_exposure_clicked; gulong gain_spin_changed; gulong gain_hscale_changed; gulong auto_gain_clicked; gulong black_level_spin_changed; gulong black_level_hscale_changed; gulong auto_black_level_clicked; gulong camera_x_changed; gulong camera_y_changed; gulong camera_binning_x_changed; gulong camera_binning_y_changed; gulong camera_width_changed; gulong camera_height_changed; gulong component_changed; gulong component_toggled; gulong pixel_format_changed; gulong rotate_cw_clicked; gulong flip_vertical_clicked; gulong flip_horizontal_clicked; guint gain_update_event; guint black_level_update_event; guint exposure_update_event; guint status_bar_update_event; gint64 last_status_bar_update_time_ms; guint64 last_n_images; guint64 last_n_bytes; gboolean auto_socket_buffer; gboolean packet_resend; guint initial_packet_timeout; guint packet_timeout; guint frame_retention; ArvRegisterCachePolicy register_cache_policy; ArvRangeCheckPolicy range_check_policy; ArvUvUsbMode usb_mode; gulong video_window_xid; }; typedef GtkApplicationClass ArvViewerClass; G_DEFINE_TYPE (ArvViewer, arv_viewer, GTK_TYPE_APPLICATION) typedef enum { ARV_VIEWER_MODE_CAMERA_LIST, ARV_VIEWER_MODE_VIDEO } ArvViewerMode; static void select_mode (ArvViewer *viewer, ArvViewerMode mode); void arv_viewer_set_options (ArvViewer *viewer, gboolean auto_socket_buffer, gboolean packet_resend, guint initial_packet_timeout, guint packet_timeout, guint frame_retention, ArvRegisterCachePolicy register_cache_policy, ArvRangeCheckPolicy range_check_policy, ArvUvUsbMode usb_mode) { g_return_if_fail (viewer != NULL); viewer->auto_socket_buffer = auto_socket_buffer; viewer->packet_resend = packet_resend; viewer->initial_packet_timeout = initial_packet_timeout; viewer->packet_timeout = packet_timeout; viewer->frame_retention = frame_retention; viewer->register_cache_policy = register_cache_policy; viewer->range_check_policy = range_check_policy; viewer->usb_mode = usb_mode; } static double arv_viewer_value_to_log (double value, double min, double max) { if (min >= max) return 1.0; if (value < min) return 0.0; return (log10 (value) - log10 (min)) / (log10 (max) - log10 (min)); } static double arv_viewer_value_from_log (double value, double min, double max) { if (min <= 0.0 || max <= 0) return 0.0; if (value > 1.0) return max; if (value < 0.0) return min; return pow (10.0, (value * (log10 (max) - log10 (min)) + log10 (min))); } static void notification_dismiss_clicked_cb (GtkButton *dismiss, ArvViewer *viewer) { if (viewer->notification_timeout > 0) g_source_remove (viewer->notification_timeout); gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->notification_revealer), FALSE); viewer->notification_timeout = 0; } static gboolean hide_notification (gpointer user_data) { ArvViewer *viewer = user_data; gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->notification_revealer), FALSE); viewer->notification_timeout = 0; return G_SOURCE_REMOVE; } static void arv_viewer_show_notification (ArvViewer *viewer, const char *message, const char *details) { g_return_if_fail (ARV_IS_VIEWER (viewer)); g_return_if_fail (message != NULL); if (viewer->notification_timeout > 0) g_source_remove (viewer->notification_timeout); gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->notification_revealer), FALSE); gtk_label_set_text (GTK_LABEL (viewer->notification_label), message); if (details != NULL) { g_autofree char *text = g_strdup_printf ("%s", details); gtk_widget_show (viewer->notification_details); gtk_label_set_markup (GTK_LABEL (viewer->notification_details), text); } else { gtk_widget_hide (viewer->notification_details); } gtk_revealer_set_reveal_child (GTK_REVEALER (viewer->notification_revealer), TRUE); viewer->notification_timeout = g_timeout_add_seconds (ARV_VIEWER_NOTIFICATION_TIMEOUT, hide_notification, viewer); } typedef struct { GWeakRef stream; ArvBuffer* arv_buffer; void *data; } ArvGstBufferReleaseData; static void gst_buffer_release_cb (void *user_data) { ArvGstBufferReleaseData* release_data = user_data; ArvStream* stream = g_weak_ref_get (&release_data->stream); g_free (release_data->data); if (stream) { gint n_input_buffers, n_output_buffers; arv_stream_get_n_buffers (stream, &n_input_buffers, &n_output_buffers); arv_debug_viewer ("push buffer (%d,%d)", n_input_buffers, n_output_buffers); arv_stream_push_buffer (stream, release_data->arv_buffer); g_object_unref (stream); } else { arv_info_viewer ("invalid stream object"); g_object_unref (release_data->arv_buffer); } g_weak_ref_clear (&release_data->stream); g_free (release_data); } static GstBuffer * arv_to_gst_buffer (ArvBuffer *arv_buffer, guint part_id, ArvStream *stream) { ArvGstBufferReleaseData* release_data; int arv_row_stride; int width, height; char *buffer_data; size_t buffer_size; size_t size; void *data; buffer_data = (char *) arv_buffer_get_part_data (arv_buffer, part_id, &buffer_size); arv_buffer_get_part_region (arv_buffer, part_id, NULL, NULL, &width, &height); arv_row_stride = width * ARV_PIXEL_FORMAT_BIT_PER_PIXEL (arv_buffer_get_part_pixel_format (arv_buffer, part_id)) / 8; release_data = g_new0 (ArvGstBufferReleaseData, 1); g_weak_ref_init (&release_data->stream, stream); release_data->arv_buffer = arv_buffer; /* Gstreamer requires row stride to be a multiple of 4 */ if ((arv_row_stride & 0x3) != 0) { int gst_row_stride; int i; gst_row_stride = (arv_row_stride & ~(0x3)) + 4; size = height * gst_row_stride; data = g_malloc (size); for (i = 0; i < height; i++) memcpy (((char *) data) + i * gst_row_stride, buffer_data + i * arv_row_stride, arv_row_stride); release_data->data = data; } else { data = buffer_data; size = buffer_size; } return gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, data, size, 0, size, release_data, gst_buffer_release_cb); } static void new_buffer_cb (ArvStream *stream, ArvViewer *viewer) { ArvBuffer *arv_buffer; gint n_input_buffers, n_output_buffers; arv_buffer = arv_stream_pop_buffer (stream); if (arv_buffer == NULL) return; arv_stream_get_n_buffers (stream, &n_input_buffers, &n_output_buffers); arv_debug_viewer ("pop buffer (%d,%d)", n_input_buffers, n_output_buffers); if (arv_buffer_get_status (arv_buffer) == ARV_BUFFER_STATUS_SUCCESS && /* Ensure there is still available buffers for the stream thread */ n_input_buffers + n_output_buffers > 0) { size_t size; gint part_id; part_id = arv_buffer_find_component(arv_buffer, viewer->component_id); if (part_id < 0) part_id = 0; arv_buffer_get_part_data (arv_buffer, part_id, &size); g_clear_object( &viewer->last_buffer ); viewer->last_buffer = g_object_ref( arv_buffer ); gst_app_src_push_buffer (GST_APP_SRC (viewer->appsrc), arv_to_gst_buffer (arv_buffer, part_id, stream)); } else { arv_debug_viewer ("push discarded buffer"); arv_stream_push_buffer (stream, arv_buffer); } } static void _apply_frame_rate (GtkEntry *entry, ArvViewer *viewer, gboolean grab_focus) { char *text; double frame_rate; text = (char *) gtk_entry_get_text (entry); arv_camera_set_frame_rate (viewer->camera, g_strtod (text, NULL), NULL); frame_rate = arv_camera_get_frame_rate (viewer->camera, NULL); text = g_strdup_printf ("%g", frame_rate); gtk_entry_set_text (entry, text); if (grab_focus) gtk_widget_grab_focus (GTK_WIDGET(entry)); g_free (text); } static void frame_rate_entry_cb (GtkEntry *entry, ArvViewer *viewer) { _apply_frame_rate (entry, viewer, TRUE); } static gboolean frame_rate_entry_focus_cb (GtkEntry *entry, GdkEventFocus *event, ArvViewer *viewer) { _apply_frame_rate (entry, viewer, FALSE); return FALSE; } static void exposure_spin_cb (GtkSpinButton *spin_button, ArvViewer *viewer) { double exposure = gtk_spin_button_get_value (spin_button); double scaled_exposure = viewer->exposure_time_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_to_log (exposure, viewer->exposure_min, viewer->exposure_max) : exposure; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_exposure_toggle), FALSE); arv_camera_set_exposure_time (viewer->camera, exposure, NULL); g_signal_handler_block (viewer->exposure_hscale, viewer->exposure_hscale_changed); gtk_range_set_value (GTK_RANGE (viewer->exposure_hscale), scaled_exposure); gtk_widget_grab_focus (GTK_WIDGET (spin_button)); g_signal_handler_unblock (viewer->exposure_hscale, viewer->exposure_hscale_changed); } static void gain_spin_cb (GtkSpinButton *spin_button, ArvViewer *viewer) { double gain = gtk_spin_button_get_value (spin_button); double scaled_gain = viewer->gain_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_to_log (gain, viewer->gain_min, viewer->gain_max) : gain; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_gain_toggle), FALSE); arv_camera_set_gain (viewer->camera, gtk_spin_button_get_value (spin_button), NULL); g_signal_handler_block (viewer->gain_hscale, viewer->gain_hscale_changed); gtk_range_set_value (GTK_RANGE (viewer->gain_hscale), scaled_gain); gtk_widget_grab_focus (GTK_WIDGET (spin_button)); g_signal_handler_unblock (viewer->gain_hscale, viewer->gain_hscale_changed); } static void black_level_spin_cb (GtkSpinButton *spin_button, ArvViewer *viewer) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_black_level_toggle), FALSE); arv_camera_set_black_level (viewer->camera, gtk_spin_button_get_value (spin_button), NULL); g_signal_handler_block (viewer->black_level_hscale, viewer->black_level_hscale_changed); gtk_range_set_value (GTK_RANGE (viewer->black_level_hscale), gtk_spin_button_get_value (spin_button)); gtk_widget_grab_focus (GTK_WIDGET (spin_button)); g_signal_handler_unblock (viewer->black_level_hscale, viewer->black_level_hscale_changed); } static void exposure_scale_cb (GtkRange *range, ArvViewer *viewer) { double scaled_exposure = gtk_range_get_value (range); double exposure = viewer->exposure_time_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_from_log (scaled_exposure, viewer->exposure_min, viewer->exposure_max) : scaled_exposure; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_exposure_toggle), FALSE); arv_camera_set_exposure_time (viewer->camera, exposure, NULL); g_signal_handler_block (viewer->exposure_spin_button, viewer->exposure_spin_changed); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->exposure_spin_button), exposure); g_signal_handler_unblock (viewer->exposure_spin_button, viewer->exposure_spin_changed); } static void gain_scale_cb (GtkRange *range, ArvViewer *viewer) { double scaled_gain = gtk_range_get_value (range); double gain = viewer->gain_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_from_log (scaled_gain, viewer->gain_min, viewer->gain_max) : scaled_gain; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_gain_toggle), FALSE); arv_camera_set_gain (viewer->camera, gain, NULL); g_signal_handler_block (viewer->gain_spin_button, viewer->gain_spin_changed); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->gain_spin_button), gain); g_signal_handler_unblock (viewer->gain_spin_button, viewer->gain_spin_changed); } static void black_level_scale_cb (GtkRange *range, ArvViewer *viewer) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_black_level_toggle), FALSE); arv_camera_set_black_level (viewer->camera, gtk_range_get_value (range), NULL); g_signal_handler_block (viewer->black_level_spin_button, viewer->black_level_spin_changed); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->black_level_spin_button), gtk_range_get_value (range)); g_signal_handler_unblock (viewer->black_level_spin_button, viewer->black_level_spin_changed); } static gboolean update_exposure_cb (void *data) { ArvViewer *viewer = data; double exposure; double scaled_exposure; exposure = arv_camera_get_exposure_time (viewer->camera, NULL); scaled_exposure = viewer->exposure_time_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_to_log (exposure, viewer->exposure_min, viewer->exposure_max) : exposure; g_signal_handler_block (viewer->exposure_hscale, viewer->exposure_hscale_changed); g_signal_handler_block (viewer->exposure_spin_button, viewer->exposure_spin_changed); gtk_range_set_value (GTK_RANGE (viewer->exposure_hscale), scaled_exposure); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->exposure_spin_button), exposure); g_signal_handler_unblock (viewer->exposure_spin_button, viewer->exposure_spin_changed); g_signal_handler_unblock (viewer->exposure_hscale, viewer->exposure_hscale_changed); return TRUE; } static void update_exposure_ui (ArvViewer *viewer, gboolean is_auto) { update_exposure_cb (viewer); if (viewer->exposure_update_event > 0) { g_source_remove (viewer->exposure_update_event); viewer->exposure_update_event = 0; } if (is_auto) viewer->exposure_update_event = g_timeout_add_seconds (1, update_exposure_cb, viewer); } static void auto_exposure_cb (GtkToggleButton *toggle, ArvViewer *viewer) { gboolean is_auto; is_auto = gtk_toggle_button_get_active (toggle); arv_camera_set_exposure_time_auto (viewer->camera, is_auto ? ARV_AUTO_CONTINUOUS : ARV_AUTO_OFF, NULL); update_exposure_ui (viewer, is_auto); } static gboolean update_gain_cb (void *data) { ArvViewer *viewer = data; double gain; double scaled_gain; gain = arv_camera_get_gain (viewer->camera, NULL); scaled_gain = viewer->gain_representation == ARV_GC_REPRESENTATION_LOGARITHMIC ? arv_viewer_value_to_log (gain, viewer->gain_min, viewer->gain_max) : gain; g_signal_handler_block (viewer->gain_hscale, viewer->gain_hscale_changed); g_signal_handler_block (viewer->gain_spin_button, viewer->gain_spin_changed); gtk_range_set_value (GTK_RANGE (viewer->gain_hscale), scaled_gain); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->gain_spin_button), gain); g_signal_handler_unblock (viewer->gain_spin_button, viewer->gain_spin_changed); g_signal_handler_unblock (viewer->gain_hscale, viewer->gain_hscale_changed); return TRUE; } static void update_gain_ui (ArvViewer *viewer, gboolean is_auto) { update_gain_cb (viewer); if (viewer->gain_update_event > 0) { g_source_remove (viewer->gain_update_event); viewer->gain_update_event = 0; } if (is_auto) viewer->gain_update_event = g_timeout_add_seconds (1, update_gain_cb, viewer); } static void auto_gain_cb (GtkToggleButton *toggle, ArvViewer *viewer) { gboolean is_auto; is_auto = gtk_toggle_button_get_active (toggle); arv_camera_set_gain_auto (viewer->camera, is_auto ? ARV_AUTO_CONTINUOUS : ARV_AUTO_OFF, NULL); update_gain_ui (viewer, is_auto); } static gboolean update_black_level_cb (void *data) { ArvViewer *viewer = data; double black_level; black_level = arv_camera_get_black_level (viewer->camera, NULL); g_signal_handler_block (viewer->black_level_hscale, viewer->black_level_hscale_changed); g_signal_handler_block (viewer->black_level_spin_button, viewer->black_level_spin_changed); gtk_range_set_value (GTK_RANGE (viewer->black_level_hscale), black_level); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->black_level_spin_button), black_level); g_signal_handler_unblock (viewer->black_level_spin_button, viewer->black_level_spin_changed); g_signal_handler_unblock (viewer->black_level_hscale, viewer->black_level_hscale_changed); return TRUE; } static void update_black_level_ui (ArvViewer *viewer, gboolean is_auto) { update_black_level_cb (viewer); if (viewer->black_level_update_event > 0) { g_source_remove (viewer->black_level_update_event); viewer->black_level_update_event = 0; } if (is_auto) viewer->black_level_update_event = g_timeout_add_seconds (1, update_black_level_cb, viewer); } static void auto_black_level_cb (GtkToggleButton *toggle, ArvViewer *viewer) { gboolean is_auto; is_auto = gtk_toggle_button_get_active (toggle); arv_camera_set_black_level_auto (viewer->camera, is_auto ? ARV_AUTO_CONTINUOUS : ARV_AUTO_OFF, NULL); update_black_level_ui (viewer, is_auto); } static void set_camera_widgets(ArvViewer *viewer) { g_autofree char *string = NULL; gboolean is_frame_rate_available; gboolean is_gain_available; gboolean auto_gain; double black_level_min, black_level_max; gboolean is_black_level_available; gboolean auto_black_level; gboolean is_exposure_available; gboolean auto_exposure; g_signal_handler_block (viewer->gain_hscale, viewer->gain_hscale_changed); g_signal_handler_block (viewer->gain_spin_button, viewer->gain_spin_changed); g_signal_handler_block (viewer->black_level_hscale, viewer->black_level_hscale_changed); g_signal_handler_block (viewer->black_level_spin_button, viewer->black_level_spin_changed); g_signal_handler_block (viewer->exposure_hscale, viewer->exposure_hscale_changed); g_signal_handler_block (viewer->exposure_spin_button, viewer->exposure_spin_changed); arv_camera_get_exposure_time_bounds (viewer->camera, &viewer->exposure_min, &viewer->exposure_max, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->exposure_spin_button), viewer->exposure_min, viewer->exposure_max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->exposure_spin_button), 200.0, 1000.0); arv_camera_get_gain_bounds (viewer->camera, &viewer->gain_min, &viewer->gain_max, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->gain_spin_button), viewer->gain_min, viewer->gain_max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->gain_spin_button), 1, 10); arv_camera_get_black_level_bounds (viewer->camera, &black_level_min, &black_level_max, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->black_level_spin_button), black_level_min, black_level_max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->black_level_spin_button), 1, 10); gtk_range_set_range (GTK_RANGE (viewer->black_level_hscale), black_level_min, black_level_max); is_frame_rate_available = arv_camera_is_frame_rate_available (viewer->camera, NULL); gtk_widget_set_sensitive (viewer->frame_rate_entry, is_frame_rate_available); string = g_strdup_printf ("%g", arv_camera_get_frame_rate (viewer->camera, NULL)); gtk_entry_set_text (GTK_ENTRY (viewer->frame_rate_entry), string); is_gain_available = arv_camera_is_gain_available (viewer->camera, NULL); if (is_gain_available) { viewer->gain_representation = arv_camera_get_gain_representation(viewer->camera); if (viewer->gain_representation == ARV_GC_REPRESENTATION_UNDEFINED) { if (viewer->gain_min < viewer->gain_max) viewer->gain_representation = ARV_GC_REPRESENTATION_LINEAR; else viewer->gain_representation = ARV_GC_REPRESENTATION_PURE_NUMBER; } switch(viewer->gain_representation){ case ARV_GC_REPRESENTATION_UNDEFINED: case ARV_GC_REPRESENTATION_LINEAR: gtk_range_set_range (GTK_RANGE (viewer->gain_hscale), viewer->gain_min, viewer->gain_max); gtk_widget_set_sensitive (viewer->gain_hscale, is_gain_available); gtk_widget_set_sensitive (viewer->gain_spin_button, is_gain_available); break; case ARV_GC_REPRESENTATION_LOGARITHMIC: gtk_range_set_range (GTK_RANGE (viewer->gain_hscale), 0.0, 1.0); gtk_widget_set_sensitive (viewer->gain_hscale, is_gain_available); gtk_widget_set_sensitive (viewer->gain_spin_button, is_gain_available); break; case ARV_GC_REPRESENTATION_PURE_NUMBER: gtk_widget_set_sensitive (viewer->gain_hscale, FALSE); gtk_widget_set_sensitive (viewer->gain_spin_button, is_gain_available); break; default: gtk_widget_set_sensitive (viewer->gain_hscale, FALSE); gtk_widget_set_sensitive (viewer->gain_spin_button, FALSE); } } else { gtk_widget_set_sensitive (viewer->gain_hscale, FALSE); gtk_widget_set_sensitive (viewer->gain_spin_button, FALSE); } is_black_level_available = arv_camera_is_black_level_available (viewer->camera, NULL); gtk_widget_set_sensitive (viewer->black_level_hscale, is_black_level_available); gtk_widget_set_sensitive (viewer->black_level_spin_button, is_black_level_available); is_exposure_available = arv_camera_is_exposure_time_available (viewer->camera, NULL); if (is_exposure_available){ if (viewer->exposure_min < viewer->exposure_max) if (viewer->exposure_min > 0) viewer->exposure_time_representation = ARV_GC_REPRESENTATION_LOGARITHMIC; else viewer->exposure_time_representation = ARV_GC_REPRESENTATION_LINEAR; else viewer->exposure_time_representation = ARV_GC_REPRESENTATION_PURE_NUMBER; switch (viewer->exposure_time_representation) { case ARV_GC_REPRESENTATION_UNDEFINED: case ARV_GC_REPRESENTATION_LINEAR: gtk_range_set_range (GTK_RANGE (viewer->exposure_hscale), viewer->exposure_min, viewer->exposure_max); break; case ARV_GC_REPRESENTATION_LOGARITHMIC: gtk_range_set_range (GTK_RANGE (viewer->exposure_hscale), 0.0, 1.0); gtk_widget_set_sensitive (viewer->exposure_hscale, TRUE); gtk_widget_set_sensitive (viewer->exposure_spin_button, TRUE); break; case ARV_GC_REPRESENTATION_PURE_NUMBER: gtk_widget_set_sensitive (viewer->exposure_hscale, FALSE); gtk_widget_set_sensitive (viewer->exposure_spin_button, TRUE); break; default: gtk_widget_set_sensitive (viewer->exposure_hscale, FALSE); gtk_widget_set_sensitive (viewer->exposure_spin_button, FALSE); } }else{ gtk_widget_set_sensitive (viewer->exposure_hscale, FALSE); gtk_widget_set_sensitive (viewer->exposure_spin_button, FALSE); } g_signal_handler_unblock (viewer->gain_hscale, viewer->gain_hscale_changed); g_signal_handler_unblock (viewer->gain_spin_button, viewer->gain_spin_changed); g_signal_handler_unblock (viewer->black_level_hscale, viewer->black_level_hscale_changed); g_signal_handler_unblock (viewer->black_level_spin_button, viewer->black_level_spin_changed); g_signal_handler_unblock (viewer->exposure_hscale, viewer->exposure_hscale_changed); g_signal_handler_unblock (viewer->exposure_spin_button, viewer->exposure_spin_changed); auto_gain = arv_camera_get_gain_auto (viewer->camera, NULL) != ARV_AUTO_OFF; auto_black_level = arv_camera_get_black_level_auto (viewer->camera, NULL) != ARV_AUTO_OFF; auto_exposure = arv_camera_get_exposure_time_auto (viewer->camera, NULL) != ARV_AUTO_OFF; update_gain_ui (viewer, auto_gain); update_black_level_ui (viewer, auto_black_level); update_exposure_ui (viewer, auto_exposure); g_signal_handler_block (viewer->auto_gain_toggle, viewer->auto_gain_clicked); g_signal_handler_block (viewer->auto_black_level_toggle, viewer->auto_black_level_clicked); g_signal_handler_block (viewer->auto_exposure_toggle, viewer->auto_exposure_clicked); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_gain_toggle), auto_gain); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_black_level_toggle), auto_black_level); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (viewer->auto_exposure_toggle), auto_exposure); gtk_widget_set_sensitive (viewer->auto_gain_toggle, arv_camera_is_gain_auto_available (viewer->camera, NULL)); gtk_widget_set_sensitive (viewer->auto_black_level_toggle, arv_camera_is_black_level_auto_available (viewer->camera, NULL)); gtk_widget_set_sensitive (viewer->auto_exposure_toggle, arv_camera_is_exposure_auto_available (viewer->camera, NULL)); g_signal_handler_unblock (viewer->auto_gain_toggle, viewer->auto_gain_clicked); g_signal_handler_unblock (viewer->auto_black_level_toggle, viewer->auto_black_level_clicked); g_signal_handler_unblock (viewer->auto_exposure_toggle, viewer->auto_exposure_clicked); } static gboolean _save_gst_sample_to_file (GstSample *sample, const char *path, const char *mime_type, GError **error) { GstSample *converted; GstCaps *caps; GstBuffer *gst_buffer; gboolean success = FALSE; g_return_val_if_fail (GST_IS_SAMPLE (sample), FALSE); caps = gst_caps_from_string (mime_type); converted = gst_video_convert_sample (sample, caps, GST_CLOCK_TIME_NONE, NULL); gst_caps_unref (caps); gst_buffer = gst_sample_get_buffer (converted); if (gst_buffer) { GstMapInfo map; gst_buffer_map (gst_buffer, &map, GST_MAP_READ); success = g_file_set_contents (path, (void *) map.data, map.size, error); gst_buffer_unmap (gst_buffer, &map); } else gst_sample_unref (converted); return success; } static void snapshot_cb (GtkButton *button, ArvViewer *viewer) { GtkFileFilter *filter; GtkFileFilter *filter_all; GtkWidget *dialog; GstSample *sample = NULL; ArvBuffer *buffer = NULL; char *path; char *filename; GDateTime *date; char *date_string; int width, height; const char *data; const char *pixel_format; size_t size; gint result; if (GST_IS_ELEMENT (viewer->videosink)) sample = gst_base_sink_get_last_sample (GST_BASE_SINK (viewer->videosink)); if (ARV_IS_BUFFER (viewer->last_buffer)) buffer = g_object_ref (viewer->last_buffer); if (!ARV_IS_BUFFER (buffer) && !GST_IS_SAMPLE (sample)) { arv_viewer_show_notification (viewer, "No buffer available", NULL); return; } g_return_if_fail (ARV_IS_CAMERA (viewer->camera)); pixel_format = arv_camera_get_pixel_format_as_string (viewer->camera, NULL); arv_buffer_get_image_region (buffer, NULL, NULL, &width, &height); data = arv_buffer_get_image_data (buffer, &size); date = g_date_time_new_now_local (); date_string = g_date_time_format (date, "%Y-%m-%d-%H:%M:%S"); filename = g_strdup_printf ("%s-%s-%d-%d-%s-%s.raw", arv_camera_get_vendor_name (viewer->camera, NULL), arv_camera_get_device_serial_number (viewer->camera, NULL), width, height, pixel_format != NULL ? pixel_format : "Unknown", date_string); g_free (date_string); g_date_time_unref (date); path = g_build_filename (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES), filename, NULL); dialog = gtk_file_chooser_dialog_new ("Save Snapshot", GTK_WINDOW (viewer->main_window), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), path); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename); filter_all = gtk_file_filter_new (); gtk_file_filter_set_name (filter_all, "Supported image formats"); if (GST_IS_SAMPLE (sample)) { filter = gtk_file_filter_new (); gtk_file_filter_add_mime_type (filter, "image/png"); gtk_file_filter_add_mime_type (filter_all, "image/png"); gtk_file_filter_set_name (filter, "PNG (*.png)"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); filter = gtk_file_filter_new (); gtk_file_filter_add_mime_type (filter, "image/jpeg"); gtk_file_filter_add_mime_type (filter_all, "image/jpeg"); gtk_file_filter_set_name (filter, "JPEG (*.jpeg)"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); } if (ARV_IS_BUFFER (buffer)) { filter = gtk_file_filter_new (); gtk_file_filter_add_pattern (filter, "*.raw"); gtk_file_filter_add_pattern (filter_all, "*.raw"); gtk_file_filter_set_name (filter, "Raw images (*.raw)"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); } gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter_all); gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter_all); g_free (path); g_free (filename); result = gtk_dialog_run (GTK_DIALOG (dialog)); if (result == GTK_RESPONSE_ACCEPT) { g_autoptr (GError) error = NULL; g_autofree char * content_type = NULL; gboolean success = FALSE; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); content_type = g_content_type_guess (filename, NULL, 0, NULL); if (GST_IS_SAMPLE (sample) && g_content_type_is_mime_type (content_type, "image/png")) { success = _save_gst_sample_to_file (sample, filename, "image/png", NULL); } else if (GST_IS_SAMPLE (sample) && g_content_type_is_mime_type (content_type, "image/jpeg")) { success = _save_gst_sample_to_file (sample, filename, "image/jpeg", NULL); } else if (ARV_IS_BUFFER (buffer)) { success = g_file_set_contents (filename, data, size, &error); g_free (filename); } if (!success) arv_viewer_show_notification (viewer, "Failed to save image to file", error != NULL ? error->message : NULL); } gtk_widget_destroy (dialog); g_clear_pointer (&sample, gst_sample_unref); g_clear_object (&buffer); } static void update_transform (ArvViewer *viewer) { static const gint methods[4][4] = { {0, 1, 2, 3}, {4, 6, 5, 7}, {5, 7, 4, 6}, {2, 3, 0, 1} }; int index = (viewer->flip_horizontal ? 1 : 0) + (viewer->flip_vertical ? 2 : 0); g_object_set (viewer->transform, "method", methods[index][viewer->rotation % 4], NULL); } static void rotate_cw_cb (GtkButton *button, ArvViewer *viewer) { viewer->rotation = (viewer->rotation + 1) % 4; update_transform (viewer); } static void flip_horizontal_cb (GtkToggleButton *toggle, ArvViewer *viewer) { viewer->flip_horizontal = gtk_toggle_button_get_active (toggle); update_transform (viewer); } static void flip_vertical_cb (GtkToggleButton *toggle, ArvViewer *viewer) { viewer->flip_vertical = gtk_toggle_button_get_active (toggle); update_transform (viewer); } static void stream_cb (void *user_data, ArvStreamCallbackType type, ArvBuffer *buffer) { if (type == ARV_STREAM_CALLBACK_TYPE_INIT) { if (!arv_make_thread_realtime (10) && !arv_make_thread_high_priority (-10)) arv_warning_viewer ("Failed to make stream thread high priority"); } } static gboolean update_status_bar_cb (void *data) { ArvViewer *viewer = data; char *text; gint64 time_ms = g_get_real_time () / 1000; gint64 elapsed_time_ms = time_ms - viewer->last_status_bar_update_time_ms; guint64 n_images = arv_stream_get_info_uint64_by_name (viewer->stream, "n_completed_buffers"); guint64 n_bytes = arv_stream_get_info_uint64_by_name (viewer->stream, "n_transferred_bytes"); guint64 n_errors = arv_stream_get_info_uint64_by_name (viewer->stream, "n_failures"); if (elapsed_time_ms == 0) return TRUE; text = g_strdup_printf ("%.1f fps (%.1f MB/s)", 1000.0 * (n_images - viewer->last_n_images) / elapsed_time_ms, ((n_bytes - viewer->last_n_bytes) / 1000.0) / elapsed_time_ms); gtk_label_set_label (GTK_LABEL (viewer->fps_label), text); g_free (text); text = g_strdup_printf ("%" G_GUINT64_FORMAT " image%s / %" G_GUINT64_FORMAT " error%s", n_images, n_images > 0 ? "s" : "", n_errors, n_errors > 0 ? "s" : ""); gtk_label_set_label (GTK_LABEL (viewer->image_label), text); g_free (text); viewer->last_status_bar_update_time_ms = time_ms; viewer->last_n_images = n_images; viewer->last_n_bytes = n_bytes; return TRUE; } static void update_camera_region (ArvViewer *viewer) { gint x, y, width, height; gint dx, dy; gint min, max; gint inc; g_signal_handler_block (viewer->camera_x, viewer->camera_x_changed); g_signal_handler_block (viewer->camera_y, viewer->camera_y_changed); g_signal_handler_block (viewer->camera_width, viewer->camera_width_changed); g_signal_handler_block (viewer->camera_height, viewer->camera_height_changed); g_signal_handler_block (viewer->camera_binning_x, viewer->camera_binning_x_changed); g_signal_handler_block (viewer->camera_binning_y, viewer->camera_binning_y_changed); arv_camera_get_region (viewer->camera, &x, &y, &width, &height, NULL); arv_camera_get_binning (viewer->camera, &dx, &dy, NULL); if (gtk_widget_get_visible(viewer->camera_binning_x)) { arv_camera_get_x_binning_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_x_binning_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_binning_x), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_binning_x), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_binning_x), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_binning_x), dx); } if (gtk_widget_get_visible(viewer->camera_binning_y)) { arv_camera_get_y_binning_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_y_binning_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_binning_y), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_binning_y), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_binning_y), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_binning_y), dy); } if (gtk_widget_get_visible(viewer->camera_x)) { arv_camera_get_x_offset_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_x_offset_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_x), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_x), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_x), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_x), x); } if (gtk_widget_get_visible(viewer->camera_y)) { arv_camera_get_y_offset_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_y_offset_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_y), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_y), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_y), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_y), y); } if (gtk_widget_get_visible(viewer->camera_width)) { arv_camera_get_width_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_width_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_width), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_width), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_width), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_width), width); } if (gtk_widget_get_visible(viewer->camera_height)) { arv_camera_get_height_bounds (viewer->camera, &min, &max, NULL); inc = arv_camera_get_height_increment (viewer->camera, NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (viewer->camera_height), min, max); gtk_spin_button_set_increments (GTK_SPIN_BUTTON (viewer->camera_height), inc, inc * 10); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (viewer->camera_height), TRUE); gtk_spin_button_set_value (GTK_SPIN_BUTTON (viewer->camera_height), height); } g_signal_handler_unblock (viewer->camera_x, viewer->camera_x_changed); g_signal_handler_unblock (viewer->camera_y, viewer->camera_y_changed); g_signal_handler_unblock (viewer->camera_width, viewer->camera_width_changed); g_signal_handler_unblock (viewer->camera_height, viewer->camera_height_changed); g_signal_handler_unblock (viewer->camera_binning_x, viewer->camera_binning_x_changed); g_signal_handler_unblock (viewer->camera_binning_y, viewer->camera_binning_y_changed); } static void update_pixel_format (ArvViewer *viewer) { GtkListStore *list_store; GtkTreeIter iter; gint64 *pixel_formats; const char *pixel_format_string; const char **pixel_format_strings; guint i, n_pixel_formats, n_pixel_format_strings, n_valid_formats; gboolean bayer_tooltip = FALSE; gint current_format = -1; g_signal_handler_block (viewer->pixel_format_combo, viewer->pixel_format_changed); list_store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (viewer->pixel_format_combo))); gtk_list_store_clear (list_store); n_valid_formats = 0; pixel_format_strings = arv_camera_dup_available_pixel_formats_as_strings (viewer->camera, &n_pixel_format_strings, NULL); pixel_formats = arv_camera_dup_available_pixel_formats (viewer->camera, &n_pixel_formats, NULL); g_assert (n_pixel_formats == n_pixel_format_strings); pixel_format_string = arv_camera_get_pixel_format_as_string (viewer->camera, NULL); for (i = 0; i < n_pixel_formats; i++) { const char *caps_string = arv_pixel_format_to_gst_caps_string (pixel_formats[i]); gboolean valid = FALSE; gtk_list_store_append (list_store, &iter); if (caps_string != NULL && g_str_has_prefix (caps_string, "video/x-bayer") && !has_bayer2rgb) { bayer_tooltip = TRUE; } else if (caps_string != NULL) { if (current_format < 0 || g_strcmp0 (pixel_format_strings[i], pixel_format_string) == 0) current_format = i; n_valid_formats++; valid = TRUE; } gtk_list_store_set (list_store, &iter, 0, pixel_format_strings[i], 1, valid, -1); } g_free (pixel_formats); g_free (pixel_format_strings); gtk_widget_set_sensitive (viewer->pixel_format_combo, n_valid_formats > 0); gtk_widget_set_sensitive (viewer->video_mode_button, n_valid_formats > 0 && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(viewer->component_check))); gtk_combo_box_set_active (GTK_COMBO_BOX (viewer->pixel_format_combo), current_format >= 0 ? current_format : 0); gtk_widget_set_tooltip_text (GTK_WIDGET (viewer->pixel_format_combo), bayer_tooltip ? "Found bayer pixel formats, but the GStreamer bayer plugin " "is not installed." : NULL); g_signal_handler_unblock (viewer->pixel_format_combo, viewer->pixel_format_changed); } static void camera_region_cb (GtkSpinButton *spin_button, ArvViewer *viewer) { int x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_x)); int y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_y)); int width = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_width)); int height = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_height)); arv_camera_set_region (viewer->camera, x, y, width, height, NULL); update_camera_region (viewer); } static void camera_binning_cb (GtkSpinButton *spin_button, ArvViewer *viewer) { int dx = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_binning_x)); int dy = gtk_spin_button_get_value (GTK_SPIN_BUTTON (viewer->camera_binning_y)); arv_camera_set_binning (viewer->camera, dx, dy, NULL); update_camera_region (viewer); } static void component_combo_cb (GtkComboBoxText *combo, ArvViewer *viewer) { char *component; gboolean enabled; component = gtk_combo_box_text_get_active_text (combo); enabled = arv_camera_select_component (viewer->camera, component, ARV_COMPONENT_SELECTION_FLAGS_NONE, NULL, NULL); g_free (component); g_signal_handler_block (viewer->component_check, viewer->component_toggled); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->component_check), enabled); g_signal_handler_unblock (viewer->component_check, viewer->component_toggled); update_camera_region (viewer); update_pixel_format(viewer); } static void component_toggled_cb (GtkToggleButton *button, ArvViewer *viewer) { char *component; gboolean enable; component = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(viewer->component_combo)); enable = gtk_toggle_button_get_active(button); gtk_widget_set_sensitive (viewer->video_mode_button, enable); arv_camera_select_component(viewer->camera, component, enable ? ARV_COMPONENT_SELECTION_FLAGS_ENABLE : ARV_COMPONENT_SELECTION_FLAGS_DISABLE, NULL, NULL); g_free (component); } static void pixel_format_combo_cb (GtkComboBoxText *combo, ArvViewer *viewer) { char *pixel_format; pixel_format = gtk_combo_box_text_get_active_text (combo); arv_camera_set_pixel_format_from_string (viewer->camera, pixel_format, NULL); g_free (pixel_format); } static void update_device_list_cb (GtkToolButton *button, ArvViewer *viewer) { GtkListStore *list_store; GtkTreeIter iter; unsigned int n_devices; unsigned int i; gtk_widget_set_sensitive (viewer->video_mode_button, FALSE); gtk_revealer_set_reveal_child (GTK_REVEALER(viewer->camera_parameters), FALSE); gtk_widget_set_sensitive (viewer->camera_parameters, FALSE); g_signal_handler_block (gtk_tree_view_get_selection (GTK_TREE_VIEW (viewer->camera_tree)), viewer->camera_selected); list_store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (viewer->camera_tree))); gtk_list_store_clear (list_store); arv_update_device_list (); n_devices = arv_get_n_devices (); for (i = 0; i < n_devices; i++) { GString *protocol; protocol = g_string_new (NULL); g_string_append_printf (protocol, "aravis-%s-symbolic", arv_get_device_protocol (i)); g_string_ascii_down (protocol); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, arv_get_device_id (i), 1, protocol->str, 2, arv_get_device_vendor (i), 3, arv_get_device_model (i), 4, arv_get_device_serial_nbr (i), -1); g_string_free (protocol, TRUE); } g_signal_handler_unblock (gtk_tree_view_get_selection (GTK_TREE_VIEW (viewer->camera_tree)), viewer->camera_selected); } static void remove_widget (GtkWidget *widget, gpointer data) { gtk_container_remove (data, widget); g_object_unref (widget); } static void stop_video (ArvViewer *viewer) { if (GST_IS_PIPELINE (viewer->pipeline)) gst_element_set_state (viewer->pipeline, GST_STATE_NULL); if (ARV_IS_STREAM (viewer->stream)) arv_stream_set_emit_signals (viewer->stream, FALSE); g_clear_object (&viewer->stream); g_clear_object (&viewer->pipeline); viewer->appsrc = NULL; g_clear_object (&viewer->last_buffer); if (ARV_IS_CAMERA (viewer->camera)) arv_camera_stop_acquisition (viewer->camera, NULL); gtk_container_foreach (GTK_CONTAINER (viewer->video_frame), remove_widget, viewer->video_frame); if (viewer->status_bar_update_event > 0) { g_source_remove (viewer->status_bar_update_event); viewer->status_bar_update_event = 0; } if (viewer->exposure_update_event > 0) { g_source_remove (viewer->exposure_update_event); viewer->exposure_update_event = 0; } if (viewer->gain_update_event > 0) { g_source_remove (viewer->gain_update_event); viewer->gain_update_event = 0; } if (viewer->black_level_update_event > 0) { g_source_remove (viewer->black_level_update_event); viewer->black_level_update_event = 0; } } static GstBusSyncReply bus_sync_handler (GstBus *bus, GstMessage *message, gpointer user_data) { ArvViewer *viewer = user_data; if (!gst_is_video_overlay_prepare_window_handle_message(message)) return GST_BUS_PASS; if (viewer->video_window_xid != 0) { GstVideoOverlay *videooverlay; videooverlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)); gst_video_overlay_set_window_handle (videooverlay, viewer->video_window_xid); } else { g_warning ("Should have obtained video_window_xid by now!"); } gst_message_unref (message); return GST_BUS_DROP; } static gboolean start_video (ArvViewer *viewer) { GstElement *videoconvert; GstCaps *caps; ArvPixelFormat pixel_format; unsigned payload; unsigned i; gint width, height; const char *caps_string; char *component; if (!ARV_IS_CAMERA (viewer->camera)) return FALSE; stop_video (viewer); component = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(viewer->component_combo)); if (component != NULL) { arv_camera_select_component (viewer->camera, component, ARV_COMPONENT_SELECTION_FLAGS_NONE, &viewer->component_id, NULL); g_free (component); } viewer->rotation = 0; viewer->stream = arv_camera_create_stream (viewer->camera, stream_cb, NULL, NULL); if (!ARV_IS_STREAM (viewer->stream)) { g_object_unref (viewer->camera); viewer->camera = NULL; return FALSE; } if (ARV_IS_GV_STREAM (viewer->stream)) { if (viewer->auto_socket_buffer) g_object_set (viewer->stream, "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO, "socket-buffer-size", 0, NULL); if (!viewer->packet_resend) g_object_set (viewer->stream, "packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL); g_object_set (viewer->stream, "initial-packet-timeout", (unsigned) viewer->initial_packet_timeout * 1000, "packet-timeout", (unsigned) viewer->packet_timeout * 1000, "frame-retention", (unsigned) viewer->frame_retention * 1000, NULL); } arv_stream_set_emit_signals (viewer->stream, TRUE); payload = arv_camera_get_payload (viewer->camera, NULL); for (i = 0; i < ARV_VIEWER_N_BUFFERS; i++) arv_stream_push_buffer (viewer->stream, arv_buffer_new (payload, NULL)); set_camera_widgets(viewer); pixel_format = arv_camera_get_pixel_format (viewer->camera, NULL); caps_string = arv_pixel_format_to_gst_caps_string_full (pixel_format, arv_camera_get_vendor_name (viewer->camera, NULL), arv_camera_get_model_name (viewer->camera, NULL)); if (caps_string == NULL) { g_message ("GStreamer cannot understand this camera pixel format: 0x%x!", (int) pixel_format); stop_video (viewer); return FALSE; } else if (g_str_has_prefix (caps_string, "video/x-bayer") && !has_bayer2rgb) { g_message ("GStreamer bayer plugin is required for pixel format: 0x%x!", (int) pixel_format); stop_video (viewer); return FALSE; } arv_camera_set_acquisition_mode (viewer->camera, ARV_ACQUISITION_MODE_CONTINUOUS, NULL); arv_camera_start_acquisition (viewer->camera, NULL); viewer->pipeline = gst_pipeline_new ("pipeline"); viewer->appsrc = gst_element_factory_make ("appsrc", NULL); videoconvert = gst_element_factory_make ("videoconvert", NULL); viewer->transform = gst_element_factory_make ("videoflip", NULL); gst_bin_add_many (GST_BIN (viewer->pipeline), viewer->appsrc, videoconvert, viewer->transform, NULL); if (g_str_has_prefix (caps_string, "video/x-bayer")) { GstElement *bayer2rgb; bayer2rgb = gst_element_factory_make ("bayer2rgb", NULL); gst_bin_add (GST_BIN (viewer->pipeline), bayer2rgb); gst_element_link_many (viewer->appsrc, bayer2rgb, videoconvert, viewer->transform, NULL); } else { gst_element_link_many (viewer->appsrc, videoconvert, viewer->transform, NULL); } if (has_gtksink || has_gtkglsink) { GtkWidget *video_widget; #if 0 /* Disable glsink for now, it crashes when we come back to camera list with: (lt-arv-viewer:29151): Gdk-WARNING **: eglMakeCurrent failed (lt-arv-viewer:29151): Gdk-WARNING **: eglMakeCurrent failed (lt-arv-viewer:29151): Gdk-WARNING **: eglMakeCurrent failed Erreur de segmentation (core dumped) */ videosink = gst_element_factory_make ("gtkglsink", NULL); if (GST_IS_ELEMENT (videosink)) { GstElement *glupload; glupload = gst_element_factory_make ("glupload", NULL); gst_bin_add_many (GST_BIN (viewer->pipeline), glupload, videosink, NULL); gst_element_link_many (viewer->transform, glupload, videosink, NULL); } else { #else { #endif viewer->videosink = gst_element_factory_make ("gtksink", NULL); gst_bin_add_many (GST_BIN (viewer->pipeline), viewer->videosink, NULL); gst_element_link_many (viewer->transform, viewer->videosink, NULL); } g_object_get (viewer->videosink, "widget", &video_widget, NULL); gtk_container_add (GTK_CONTAINER (viewer->video_frame), video_widget); gtk_widget_show (video_widget); g_object_set(G_OBJECT (video_widget), "force-aspect-ratio", TRUE, NULL); gtk_widget_set_size_request (video_widget, 640, 480); } else { viewer->videosink = gst_element_factory_make ("autovideosink", NULL); gst_bin_add (GST_BIN (viewer->pipeline), viewer->videosink); gst_element_link_many (viewer->transform, viewer->videosink, NULL); } g_object_set(G_OBJECT (viewer->videosink), "sync", FALSE, NULL); caps = gst_caps_from_string (caps_string); arv_camera_get_region (viewer->camera, NULL, NULL, &width, &height, NULL); gst_caps_set_simple (caps, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "framerate", GST_TYPE_FRACTION, 0, 1, NULL); gst_app_src_set_caps (GST_APP_SRC (viewer->appsrc), caps); gst_caps_unref (caps); g_object_set(G_OBJECT (viewer->appsrc), "format", GST_FORMAT_TIME, "is-live", TRUE, "do-timestamp", TRUE, NULL); if (!has_gtkglsink && !has_gtksink) { GstBus *bus; bus = gst_pipeline_get_bus (GST_PIPELINE (viewer->pipeline)); gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, viewer, NULL); gst_object_unref (bus); } gst_element_set_state (viewer->pipeline, GST_STATE_PLAYING); viewer->last_status_bar_update_time_ms = g_get_real_time () / 1000; viewer->last_n_images = 0; viewer->last_n_bytes = 0; viewer->status_bar_update_event = g_timeout_add_seconds (1, update_status_bar_cb, viewer); g_signal_connect (viewer->stream, "new-buffer", G_CALLBACK (new_buffer_cb), viewer); return TRUE; } static gboolean select_camera_list_mode (gpointer user_data) { ArvViewer *viewer = user_data; select_mode (viewer, ARV_VIEWER_MODE_CAMERA_LIST); update_device_list_cb (GTK_TOOL_BUTTON (viewer->refresh_button), viewer); return FALSE; } static void control_lost_cb (ArvCamera *camera, ArvViewer *viewer) { g_main_context_invoke (NULL, select_camera_list_mode, viewer); } static void stop_camera (ArvViewer *viewer) { gtk_revealer_set_reveal_child (GTK_REVEALER(viewer->camera_parameters), FALSE); gtk_widget_set_sensitive (viewer->camera_parameters, FALSE); gtk_widget_set_sensitive (viewer->video_mode_button, FALSE); stop_video (viewer); g_clear_object (&viewer->camera); g_clear_pointer (&viewer->camera_name, g_free); } static void set_sensitive (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { gboolean valid; gtk_tree_model_get (tree_model, iter, 1, &valid, -1); g_object_set(cell, "sensitive", valid, NULL); } static gboolean start_camera (ArvViewer *viewer, const char *camera_id) { GtkTreeIter iter; GtkListStore *list_store; const char **components; guint n_components; gboolean binning_available; gboolean region_offset_available; unsigned int i; stop_camera (viewer); viewer->camera = arv_camera_new (camera_id, NULL); if (!ARV_IS_CAMERA (viewer->camera)) { gtk_revealer_set_reveal_child (GTK_REVEALER(viewer->camera_parameters), FALSE); return FALSE; } arv_device_set_register_cache_policy (arv_camera_get_device (viewer->camera), viewer->register_cache_policy); arv_device_set_range_check_policy (arv_camera_get_device (viewer->camera), viewer->range_check_policy); if (arv_camera_is_uv_device (viewer->camera)) arv_camera_uv_set_usb_mode (viewer->camera, viewer->usb_mode); if (arv_camera_is_gv_device(viewer->camera)) arv_camera_gv_set_multipart(viewer->camera, TRUE, NULL); viewer->camera_name = g_strdup (camera_id); gtk_widget_set_sensitive (viewer->camera_parameters, TRUE); gtk_revealer_set_reveal_child (GTK_REVEALER(viewer->camera_parameters), TRUE); arv_camera_set_chunk_mode (viewer->camera, FALSE, NULL); if (arv_camera_is_component_available(viewer->camera, NULL)) { gint first_enabled_component = -1; gtk_widget_set_visible(viewer->component_label, TRUE); gtk_widget_set_visible(viewer->component_combo, TRUE); gtk_widget_set_visible(viewer->component_check, TRUE); g_signal_handler_block (viewer->component_check, viewer->component_toggled); g_signal_handler_block (viewer->component_combo, viewer->component_changed); list_store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (viewer->component_combo))); gtk_list_store_clear (list_store); components = arv_camera_dup_available_components (viewer->camera, &n_components, NULL); for (i = 0; i < n_components; i++) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, components[i], -1); if (first_enabled_component < 0 && arv_camera_select_component(viewer->camera, components[i], ARV_COMPONENT_SELECTION_FLAGS_NONE, NULL, NULL)) first_enabled_component = i; } g_free (components); gtk_widget_set_sensitive (viewer->component_combo, n_components > 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->component_check), n_components > 0); if (n_components > 0) gtk_combo_box_set_active (GTK_COMBO_BOX (viewer->component_combo), MAX(first_enabled_component, 0)); g_signal_handler_unblock (viewer->component_check, viewer->component_toggled); g_signal_handler_unblock (viewer->component_combo, viewer->component_changed); } else { g_signal_handler_block (viewer->component_check, viewer->component_toggled); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->component_check), TRUE); g_signal_handler_unblock (viewer->component_check, viewer->component_toggled); gtk_widget_set_visible(viewer->component_label, FALSE); gtk_widget_set_visible(viewer->component_combo, FALSE); gtk_widget_set_visible(viewer->component_check, FALSE); } region_offset_available = arv_camera_is_region_offset_available (viewer->camera, NULL); gtk_widget_set_visible (viewer->camera_x, region_offset_available); gtk_widget_set_visible (viewer->camera_y, region_offset_available); gtk_widget_set_visible (viewer->camera_position_label, region_offset_available); gtk_widget_set_visible (viewer->camera_position_units, region_offset_available); binning_available = arv_camera_is_binning_available (viewer->camera, NULL); gtk_widget_set_visible (viewer->camera_binning_x, binning_available); gtk_widget_set_visible (viewer->camera_binning_y, binning_available); gtk_widget_set_visible (viewer->camera_binning_label, binning_available); gtk_widget_set_visible (viewer->camera_binning_units, binning_available); update_pixel_format(viewer); update_camera_region (viewer); g_signal_connect (arv_camera_get_device (viewer->camera), "control-lost", G_CALLBACK (control_lost_cb), viewer); return TRUE; } static void camera_selection_changed_cb (GtkTreeSelection *selection, ArvViewer *viewer) { GtkTreeIter iter; GtkTreeModel *tree_model; char *camera_id = NULL; if (gtk_tree_selection_get_selected (selection, &tree_model, &iter)) { gtk_tree_model_get (tree_model, &iter, 0, &camera_id, -1); start_camera (viewer, camera_id); g_free (camera_id); } else stop_camera (viewer); } static void select_mode (ArvViewer *viewer, ArvViewerMode mode) { gboolean video_visibility; char *subtitle; gint width, height, x, y; if (!ARV_IS_CAMERA (viewer->camera)) mode = ARV_VIEWER_MODE_CAMERA_LIST; switch (mode) { case ARV_VIEWER_MODE_CAMERA_LIST: video_visibility = FALSE; gtk_stack_set_visible_child (GTK_STACK (viewer->main_stack), viewer->camera_box); gtk_header_bar_set_title (GTK_HEADER_BAR (viewer->main_headerbar), "Aravis Viewer"); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (viewer->main_headerbar), NULL); stop_video (viewer); break; case ARV_VIEWER_MODE_VIDEO: g_signal_handler_block (viewer->flip_vertical_toggle, viewer->flip_vertical_clicked); g_signal_handler_block (viewer->flip_horizontal_toggle, viewer->flip_horizontal_clicked); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(viewer->flip_vertical_toggle), FALSE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(viewer->flip_horizontal_toggle), FALSE); g_signal_handler_unblock (viewer->flip_vertical_toggle, viewer->flip_vertical_clicked); g_signal_handler_unblock (viewer->flip_horizontal_toggle, viewer->flip_horizontal_clicked); video_visibility = TRUE; arv_camera_get_region (viewer->camera, &x, &y, &width, &height, NULL); subtitle = g_strdup_printf ("%s %dx%d@%d,%d %s", arv_camera_get_model_name (viewer->camera, NULL), width, height, x, y, arv_camera_get_pixel_format_as_string (viewer->camera, NULL)); gtk_stack_set_visible_child (GTK_STACK (viewer->main_stack), viewer->video_box); gtk_header_bar_set_title (GTK_HEADER_BAR (viewer->main_headerbar), viewer->camera_name); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (viewer->main_headerbar), subtitle); g_free (subtitle); start_video (viewer); break; default: g_assert_not_reached (); break; } gtk_widget_set_visible (viewer->back_button, video_visibility); gtk_widget_set_visible (viewer->rotate_cw_button, video_visibility); gtk_widget_set_visible (viewer->flip_vertical_toggle, video_visibility); gtk_widget_set_visible (viewer->flip_horizontal_toggle, video_visibility); gtk_widget_set_visible (viewer->snapshot_button, video_visibility); gtk_widget_set_visible (viewer->acquisition_button, video_visibility); } static void switch_to_camera_list_cb (GtkToolButton *button, ArvViewer *viewer) { select_mode (viewer, ARV_VIEWER_MODE_CAMERA_LIST); } static void switch_to_video_mode_cb (GtkToolButton *button, ArvViewer *viewer) { select_mode (viewer, ARV_VIEWER_MODE_VIDEO); } static void arv_viewer_quit_cb (GtkApplicationWindow *window, ArvViewer *viewer) { stop_camera (viewer); g_application_quit (G_APPLICATION (viewer)); } static void video_frame_realize_cb (GtkWidget * widget, ArvViewer *viewer) { #ifdef GDK_WINDOWING_X11 viewer->video_window_xid = GDK_WINDOW_XID (gtk_widget_get_window (widget)); #endif #ifdef GDK_WINDOWING_WIN32 viewer->video_window_xid = (guintptr) GDK_WINDOW_HWND (gtk_widget_get_window (widget)); #endif } static void activate (GApplication *application) { ArvViewer *viewer = (ArvViewer *) application; g_autoptr (GtkBuilder) builder = NULL; g_autoptr (GtkListStore) list_store = NULL; GtkCellRenderer *renderer; builder = gtk_builder_new_from_resource ("/org/aravis/viewer/arv-viewer.ui"); viewer->main_window = GTK_WIDGET (gtk_builder_get_object (builder, "main_window")); viewer->main_stack = GTK_WIDGET (gtk_builder_get_object (builder, "main_stack")); viewer->main_headerbar = GTK_WIDGET (gtk_builder_get_object (builder, "main_headerbar")); viewer->camera_box = GTK_WIDGET (gtk_builder_get_object (builder, "camera_box")); viewer->refresh_button = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_button")); viewer->video_mode_button = GTK_WIDGET (gtk_builder_get_object (builder, "video_mode_button")); viewer->back_button = GTK_WIDGET (gtk_builder_get_object (builder, "back_button")); viewer->snapshot_button = GTK_WIDGET (gtk_builder_get_object (builder, "snapshot_button")); viewer->camera_tree = GTK_WIDGET (gtk_builder_get_object (builder, "camera_tree")); viewer->camera_parameters = GTK_WIDGET (gtk_builder_get_object (builder, "camera_parameters")); viewer->component_label = GTK_WIDGET (gtk_builder_get_object (builder, "component_label")); viewer->component_combo = GTK_WIDGET (gtk_builder_get_object (builder, "component_combo")); viewer->component_check = GTK_WIDGET (gtk_builder_get_object (builder, "component_check")); viewer->pixel_format_combo = GTK_WIDGET (gtk_builder_get_object (builder, "pixel_format_combo")); viewer->camera_x = GTK_WIDGET (gtk_builder_get_object (builder, "camera_x")); viewer->camera_y = GTK_WIDGET (gtk_builder_get_object (builder, "camera_y")); viewer->camera_position_label = GTK_WIDGET(gtk_builder_get_object(builder, "camera_position_label")); viewer->camera_position_units = GTK_WIDGET(gtk_builder_get_object(builder, "camera_position_units")); viewer->camera_binning_x = GTK_WIDGET (gtk_builder_get_object (builder, "camera_binning_x")); viewer->camera_binning_y = GTK_WIDGET (gtk_builder_get_object (builder, "camera_binning_y")); viewer->camera_binning_label = GTK_WIDGET(gtk_builder_get_object(builder, "camera_binning_label")); viewer->camera_binning_units = GTK_WIDGET(gtk_builder_get_object(builder, "camera_binning_units")); viewer->camera_width = GTK_WIDGET (gtk_builder_get_object (builder, "camera_width")); viewer->camera_height = GTK_WIDGET (gtk_builder_get_object (builder, "camera_height")); viewer->video_box = GTK_WIDGET (gtk_builder_get_object (builder, "video_box")); viewer->video_frame = GTK_WIDGET (gtk_builder_get_object (builder, "video_frame")); viewer->fps_label = GTK_WIDGET (gtk_builder_get_object (builder, "fps_label")); viewer->image_label = GTK_WIDGET (gtk_builder_get_object (builder, "image_label")); viewer->trigger_combo_box = GTK_WIDGET (gtk_builder_get_object (builder, "trigger_combobox")); viewer->frame_rate_entry = GTK_WIDGET (gtk_builder_get_object (builder, "frame_rate_entry")); viewer->exposure_spin_button = GTK_WIDGET (gtk_builder_get_object (builder, "exposure_spinbutton")); viewer->exposure_hscale = GTK_WIDGET (gtk_builder_get_object (builder, "exposure_hscale")); viewer->auto_exposure_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "auto_exposure_togglebutton")); viewer->gain_spin_button = GTK_WIDGET (gtk_builder_get_object (builder, "gain_spinbutton")); viewer->gain_hscale = GTK_WIDGET (gtk_builder_get_object (builder, "gain_hscale")); viewer->auto_gain_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "auto_gain_togglebutton")); viewer->black_level_spin_button = GTK_WIDGET (gtk_builder_get_object (builder, "black_level_spinbutton")); viewer->black_level_hscale = GTK_WIDGET (gtk_builder_get_object (builder, "black_level_hscale")); viewer->auto_black_level_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "auto_black_level_togglebutton")); viewer->rotate_cw_button = GTK_WIDGET (gtk_builder_get_object (builder, "rotate_cw_button")); viewer->flip_vertical_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "flip_vertical_togglebutton")); viewer->flip_horizontal_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "flip_horizontal_togglebutton")); viewer->acquisition_button = GTK_WIDGET (gtk_builder_get_object (builder, "acquisition_button")); viewer->notification_revealer = GTK_WIDGET (gtk_builder_get_object (builder, "notification_revealer")); viewer->notification_label = GTK_WIDGET (gtk_builder_get_object (builder, "notification_label")); viewer->notification_details = GTK_WIDGET (gtk_builder_get_object (builder, "notification_details")); viewer->notification_dismiss = GTK_WIDGET (gtk_builder_get_object (builder, "notification_dismiss")); g_signal_connect (viewer->notification_dismiss, "clicked", G_CALLBACK (notification_dismiss_clicked_cb), viewer); list_store = gtk_list_store_new(1, G_TYPE_STRING); gtk_combo_box_set_model (GTK_COMBO_BOX (viewer->component_combo), GTK_TREE_MODEL (list_store)); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_clear (GTK_CELL_LAYOUT (viewer->component_combo)); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (viewer->component_combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (viewer->component_combo), renderer, "text", 0, NULL); list_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_combo_box_set_model (GTK_COMBO_BOX (viewer->pixel_format_combo), GTK_TREE_MODEL (list_store)); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_clear (GTK_CELL_LAYOUT (viewer->pixel_format_combo)); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (viewer->pixel_format_combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (viewer->pixel_format_combo), renderer, "text", 0, NULL); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (viewer->pixel_format_combo), renderer, set_sensitive, NULL, NULL); gtk_widget_set_no_show_all (viewer->trigger_combo_box, TRUE); gtk_widget_show_all (viewer->main_window); gtk_application_add_window (GTK_APPLICATION (application), GTK_WINDOW (viewer->main_window)); g_signal_connect (viewer->refresh_button, "clicked", G_CALLBACK (update_device_list_cb), viewer); g_signal_connect (viewer->video_mode_button, "clicked", G_CALLBACK (switch_to_video_mode_cb), viewer); g_signal_connect (viewer->back_button, "clicked", G_CALLBACK (switch_to_camera_list_cb), viewer); g_signal_connect (viewer->main_window, "destroy", G_CALLBACK (arv_viewer_quit_cb), viewer); g_signal_connect (viewer->snapshot_button, "clicked", G_CALLBACK (snapshot_cb), viewer); viewer->rotate_cw_clicked = g_signal_connect (viewer->rotate_cw_button, "clicked", G_CALLBACK (rotate_cw_cb), viewer); viewer->flip_horizontal_clicked = g_signal_connect (viewer->flip_horizontal_toggle, "clicked", G_CALLBACK (flip_horizontal_cb), viewer); viewer->flip_vertical_clicked = g_signal_connect (viewer->flip_vertical_toggle, "clicked", G_CALLBACK (flip_vertical_cb), viewer); g_signal_connect (viewer->frame_rate_entry, "activate", G_CALLBACK (frame_rate_entry_cb), viewer); g_signal_connect (viewer->frame_rate_entry, "focus-out-event", G_CALLBACK (frame_rate_entry_focus_cb), viewer); if (!has_gtksink && !has_gtkglsink) { g_signal_connect (viewer->video_frame, "realize", G_CALLBACK (video_frame_realize_cb), viewer); } viewer->camera_selected = g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (viewer->camera_tree)), "changed", G_CALLBACK (camera_selection_changed_cb), viewer); viewer->exposure_spin_changed = g_signal_connect (viewer->exposure_spin_button, "value-changed", G_CALLBACK (exposure_spin_cb), viewer); viewer->exposure_hscale_changed = g_signal_connect (viewer->exposure_hscale, "value-changed", G_CALLBACK (exposure_scale_cb), viewer); viewer->auto_exposure_clicked = g_signal_connect (viewer->auto_exposure_toggle, "clicked", G_CALLBACK (auto_exposure_cb), viewer); viewer->gain_spin_changed = g_signal_connect (viewer->gain_spin_button, "value-changed", G_CALLBACK (gain_spin_cb), viewer); viewer->gain_hscale_changed = g_signal_connect (viewer->gain_hscale, "value-changed", G_CALLBACK (gain_scale_cb), viewer); viewer->auto_gain_clicked = g_signal_connect (viewer->auto_gain_toggle, "clicked", G_CALLBACK (auto_gain_cb), viewer); viewer->black_level_spin_changed = g_signal_connect (viewer->black_level_spin_button, "value-changed", G_CALLBACK (black_level_spin_cb), viewer); viewer->black_level_hscale_changed = g_signal_connect (viewer->black_level_hscale, "value-changed", G_CALLBACK (black_level_scale_cb), viewer); viewer->auto_black_level_clicked = g_signal_connect (viewer->auto_black_level_toggle, "clicked", G_CALLBACK (auto_black_level_cb), viewer); viewer->component_changed = g_signal_connect (viewer->component_combo, "changed", G_CALLBACK (component_combo_cb), viewer); viewer->component_toggled = g_signal_connect (viewer->component_check, "toggled", G_CALLBACK (component_toggled_cb), viewer); viewer->pixel_format_changed = g_signal_connect (viewer->pixel_format_combo, "changed", G_CALLBACK (pixel_format_combo_cb), viewer); viewer->camera_x_changed = g_signal_connect (viewer->camera_x, "value-changed", G_CALLBACK (camera_region_cb), viewer); viewer->camera_y_changed = g_signal_connect (viewer->camera_y, "value-changed", G_CALLBACK (camera_region_cb), viewer); viewer->camera_width_changed = g_signal_connect (viewer->camera_width, "value-changed", G_CALLBACK (camera_region_cb), viewer); viewer->camera_height_changed = g_signal_connect (viewer->camera_height, "value-changed", G_CALLBACK (camera_region_cb), viewer); viewer->camera_binning_x_changed = g_signal_connect (viewer->camera_binning_x, "value-changed", G_CALLBACK (camera_binning_cb), viewer); viewer->camera_binning_y_changed = g_signal_connect (viewer->camera_binning_y, "value-changed", G_CALLBACK (camera_binning_cb), viewer); gtk_widget_set_sensitive (viewer->camera_parameters, FALSE); gtk_revealer_set_reveal_child (GTK_REVEALER(viewer->camera_parameters), FALSE); select_mode (viewer, ARV_VIEWER_MODE_CAMERA_LIST); update_device_list_cb (GTK_TOOL_BUTTON (viewer->refresh_button), viewer); } static void startup (GApplication *application) { arv_enable_interface ("Fake"); G_APPLICATION_CLASS (arv_viewer_parent_class)->startup (application); } static void viewer_shutdown (GApplication *application) { G_APPLICATION_CLASS (arv_viewer_parent_class)->shutdown (application); arv_shutdown (); } static void finalize (GObject *object) { G_OBJECT_CLASS (arv_viewer_parent_class)->finalize (object); } ArvViewer * arv_viewer_new (void) { ArvViewer *arv_viewer; if (!gstreamer_plugin_check ()) return NULL; g_set_application_name ("Aravis Viewer"); arv_viewer = g_object_new (arv_viewer_get_type (), "application-id", "org.aravis.viewer", "flags", G_APPLICATION_NON_UNIQUE, "inactivity-timeout", 30000, NULL); return arv_viewer; } static void arv_viewer_init (ArvViewer *viewer) { viewer->auto_socket_buffer = FALSE; viewer->packet_resend = TRUE; viewer->packet_timeout = 20; viewer->frame_retention = 100; viewer->register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEFAULT; viewer->range_check_policy = ARV_RANGE_CHECK_POLICY_DEFAULT; } static void arv_viewer_class_init (ArvViewerClass *class) { GApplicationClass *application_class = G_APPLICATION_CLASS (class); GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->finalize = finalize; application_class->startup = startup; application_class->shutdown = viewer_shutdown; application_class->activate = activate; } aravis-0.8.34/viewer/arvviewer.h000066400000000000000000000030731475431451200165700ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include G_BEGIN_DECLS #define ARV_TYPE_VIEWER (arv_viewer_get_type ()) #define ARV_IS_VIEWER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ARV_TYPE_VIEWER)) GType arv_viewer_get_type (void); ArvViewer * arv_viewer_new (void); void arv_viewer_set_options (ArvViewer *viewer, gboolean auto_socket_buffer, gboolean packet_resend, guint initial_packet_timeout, guint packet_timeout, guint frame_retention, ArvRegisterCachePolicy register_cache_policy, ArvRangeCheckPolicy range_check_policy, ArvUvUsbMode usb_mode); G_END_DECLS aravis-0.8.34/viewer/arvviewerresources.xml000066400000000000000000000006151475431451200210730ustar00rootroot00000000000000 arv-viewer.ui icons/gnome/scalable/devices/aravis-gigevision-symbolic.svg icons/gnome/scalable/devices/aravis-usb3vision-symbolic.svg icons/gnome/scalable/devices/aravis-fake-symbolic.svg aravis-0.8.34/viewer/arvviewertypes.h000066400000000000000000000020211475431451200176450ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #ifndef ARV_VIEWER_TYPES_H #define ARV_VIEWER_TYPES_H #include G_BEGIN_DECLS typedef struct _ArvViewer ArvViewer; G_END_DECLS #endif aravis-0.8.34/viewer/data/000077500000000000000000000000001475431451200153135ustar00rootroot00000000000000aravis-0.8.34/viewer/data/aravis-video.png000066400000000000000000016743721475431451200204360ustar00rootroot00000000000000PNG  IHDR^E>sBIT|dtEXtSoftwaregnome-screenshot>)tEXtCreation Timeven. 09 juil. 2021 11:07:15#l IDATxyE?U=3{&C6CoEGQA}GEA.oD} G $!' 7w$=f?g;;3{&fgz~]]U_ p8p8p8p8p8 p8>N:pTqY҉Xp8&.n;Jh%\'~p8Nl"R]ටxlIXĭp8ljAB%]m{ lIwLsu88d~FYԶU,T:>}p8O*;֊tXl&ےpp8Cs3X5Y֒8ZTB6mNfp8}VtfabQqmNlw8pوls9iJZ*3t6mp8GhM7}֎&R[M=չup8c4+`҄76-չp8pt[M_st@dɛL/pr8pt,وtV8aX"s֢m VJ&KuL*'np8ڗLo5MKЦ f&鄬p8GǒwH >kfkzjiEL9ʃJ L9p8'w68ن5.jE_h*lzkS ۆ4+r[M1M5 MeMp8:Z*huqt𒽳  yi2),L ݁@Nyhp8*8 ak(nzs!+Z"h՜|`_}/^B8mp8q2aA)_=o/>qg?嘨mt d4F6l= ElsѨQƾkw9J}]JӦp8 K^~5Gq97Yfg[{ќ)>7<,o#G7S_WSsp8'O6ڵk.j8&hj7s漳lǢ뚉p8qK(۫w/̢6[/mJA5cO&A ήi6:{Ak:;Sl_r<\>( N5࿞zjvd^6If3+ *o/IY<5HfLٙgg;r\qDrr_%`Ǽ鼵CRyj4A٬rМvu."c bzkkΛ &  O8IKy"p8Ƙ@iI,yKu2vuh-ەjjW<#)#lEBmY7{s ;oAJ JPNglp8Z;ucڳ (+[ܪfAtcg1H&mݴ|c zX=F :k xCWAPNgl٣qp8A>,5HC2Mm36{ Nۀцh]- c1G)رu p!d$gD xwټu+RJzΘѣ{¨7݊?du^}}=J)rssR6{ey9l^ʃt+*ba\ue/U|maZt),l@)\y)67WKSTԕÆ򩫮=iKٗ^g!&O2}o\/qg(l19o4ߏ)] VO/$=\wuE$dcse8rܵzu-Zu~#O=p{$ 3O^એ}=1g|ܳ*pkQ~?]7qU8$ᖖrUWp1r06JIփihVCiY36Uˉxrv ggv Svq݋ï(Ͽ eL{o-^Ə~swq8W_K^PR5k?Ű!4~,3Na mϡGqqkڳo/5 2޽zdusS(P ~v,[ӧOB1Mn^ɏ9m 7oaU9 ߡk׮14M6n`7=rxƸOqE]sbTUkaAs# ,aCjsD"a>u^ҷS&g rrV]]+(ПUk?d蠁L0.cz< Da*vbNܧ23du썇ǟ`m\ry̘: }b{9ٽ}bӖ-[kyoh )9mj#S߭[W\!wɈC6yBS W#>&FƇ.8/ u(\}As"זY'F[d3Q`Ѧe;;ۈ}IJyv^zePFbk _ݷcԈM‰SPPwP(?zG;VeҸ̘2 YA{9 0I<=wKO=8f`YGRJw6d0nc+4~u`bviSx'xgjcڽ{rg#Ly?}䕗3q،q_֚KsuWؼs7{ϛɲ+Ǐ ?p_~~徟Nں]?!?>ɪ54>;A?l~C0dl:Tf`U< 0/|`iXn=gHFZuϳEKn>}9mF|C˯s5WIS[SZw)=>t~zAΦlIAGd#jSM ˚T66oj8^"-'پ|P,y{ZkƏ "fL; SJI(Xq>|$kv`߁}Cs^{c^s k㒷O~ѱG2rP ȻmWVgboѷwo#Y5|/a|oPZR/3gNx.x٬߰CGcf˶;8l@(Jt:z#G_jtOQQ~0_/}5w[qσu~! ۻwymW_Q7FlC2\}ԦmK~`1r2r'jt@_nn.W|R2kݐҮ#{#Syp9Ͼ2C=4-{ޒk̔ޫgOesҕᶔTfcK{<94l̶%M|^FUWzr7ncs07S{ݗ#7&>.nɲ|,]^j+[#GSɋ֛+U!d t%5Μ6G8.g嚵y&|⊏OӧOW|I:i%%xcm5kf[X ph@,}{OZ,9أYazhԁ#{?~9s3cL6,%]cG}HWۣԖTwEKq o{Xb%g>.l^u8k%:YtRNf8%/V4[3>6楃ᛷs.JocxN(=~vfLMvv,kJƭlݶ-o{E7r+uggyƏ=WYd)p.,97 Pාs>Ҥs\ XF6 ,'I6l@M'5ޣ[1{6~o9k ﯤ[n]{`A(9urʴHNf!/ ^tAqm޺y7S&NްS_}6Ae1|qcNe۶| IFeɧ#:|%le_KW_# i:Gn|xwow$Q}!_{^=3aLט_4huį5ni9uIa7|ڴCAfݟ[Rs'_;_`A>}ˇزu;h>tQ j)m t5ε4m J) 9mȬ+> O>Mm]]qm(-)a%< D?X&sGUxm\z$󭛯3gq(/~CƲLyɧ"z-7\ht+.⅗_#?7/udk9XuOi1m2'b]ߒ{eYpqb۱iZSZ 6z[ʘSGӭSGz= kVRص)e =s0N{ۙ_и*>=̨#9??!gl޺2נa#oK¾=,פ4{mێm[l~]8_5M W|z6n8{ *n[l`2MνΟ7{7wLԶ Y|&u=ғ)q|xsbLx-qt;̓~z7|l [n甾}'i,i*͑PUeNC1[6Ɇt :" qbJnwyoFB{TD &\pl` g:WTp-r]wң{'oV7]JMmmIPh pzqt;ii?vs} j}w'P~ZGv+ >7wha:fD PHc[ƻs-^x-uv7;t(jg{y-$896.Gk[nK<Ԥ=ʎp|ic=zguoyToڲaC;~wם|[a{u3z;Crif&/'̃ASniuxkg'x`> C0xбu WGfњ<{􏶻'eoGh4m; =NuD0[mg`YugN^~v3q}0ptT]| ߘ~GJQYƜ0'u*Iƚ&i_0Ɛ_OlkμCY/mQagP&?'C1~ IDATYϷyV"~vM_C Agz_W11~| oߟwXV &۵ŎX!2b Z~FlpƏe`y\|y'~{}3qľ 7e6?̴6hc6dcFJ^YyWa0f(q̩>tH9x-g7c Ͼ2奥9l޺wVIawX,ƠL4!v;8XuX4JQq1OD׮]pϢoS[SKn̜6%Sc k]{6S[[G~~>fL[Q}Bk͢ٱk1qXJKNWWd;TV$Z_O޽1eyyyY+Fc<>{1~̩&4gC2-96r14ZJK1eFo˔?osH$¤c/mGCۺR;onO+}۪2aʊ6c 9a|q nē-Hi3핞{9ۓ瘚OÇᅲ^#J)^s;$-;;R]͋_[q7VrFU*d.ZK!//Rc'Vdꔉc UUUM^+V;۫5M~µ>񽲲Y9gNqQW 1t@Θ>Csi CdǮ],^'һWO1T 鵳InYi s.s΢k.^.s.`5!sȑ&e-G؇/^å_@<)5b{}V\âsͦ@Ŏ,YǞƔLضڹy-p H{ٙ6|cȍaÎ;i}f99mJTvv$ *,,jꚔ嶄2dВx2\~g,Yg̜wӿE]0|FF] */%83^M!݂qs;wfΩb 80޾ʃt^LQ?!4we`*M9o-yϘ !//ig*{҅C"ٴ5r8koaF>Kl]%Ht*n6o኏~{n X{63S'/ߊP8̠,\Y c\r9=@8aDr"d)9+ iɱ83ӎay `m9S^Z}NחP8u\Gh3'HKH,yC~N$ܱ/Hسo?fNk[mm-\Hաh2h ʃYmjjkY3KFy;z9sy]rax̝OISy_zjk8nLn G _>O]߰K/EB!!v.н8~5 -elzl,w^=Yl9׾_ûݺsBl?=HAAAA)Ea.޳ 7>%Xꚣ̜:5Ͼ0'лW&q:rF^^={u۶FcUk28w3n7wV3f(rssٽ{/^N^\)}0bVYR*kZbo*{\Yڵg.'Z_Oqq?4q&>nK &K))/+71~֯C:;xy %q9;[}}B1J7Iܜ\MDBQy%JKӽW3N{1yW2g Ғό)ޭ[R>534e6fL4-f7zG.4+d<&GN:I; mسwk֮gqT4:M[Xj1;.4^|Abli8]j-k[".>lVYߡFj"D=JE.Ϟl-u7"\_)vӣ{w۸jBR]]͞={6i"Gʜ׹3z~]ڳݳW8  5_y99E{T:D׻Ovc`}L{(sC]@a~>/ƀ@нEa:fT[[Ǽ c嚵,yfMJw+bˎq7=ʘѣ6x0ƞ3/K_K=mVo'qdM'zhm''0U-l=%J蠸.88M[75NϊU8ZS15(?F~~ݻw 99F$m_r 'X?G3oA cп/y] hEeUI?dPCaNÇ5TVVc..>ս{7<}:uu=^ `VJL8p8D~^^DK]L7q3ygӳgwrɉ `¸BKSkn0kT^|e]2鬱)lqFrEc>|OŎL!1eVdiSճ'6mϽ%vppiG6!04vm`Yo.ZJMM-dž TVV1L2nE,hŒ~aFFF8*B0|*vƒ-5! CvdhDTbǮ$!א7Q__7#`æ\~54rswکT* ۻe(*B>ݻ׷oR зOo8f嚵-"Q__1&1A0 !=p)k~a.TWWSZ!@%݊vB!jzwϞ)B D)Enn.>*h=E]~6T4uu)f B{֬Ow(^z L8*ڽ.] >d0Wba,pu?PEM|)/Nglg'w~0yyVJ顔@A"99)5G^=zw^ɚ7~1rP͜II,ލ XVQ`ٟ}9N9e+\uuܵ~}$DcB{)+-eيc8R]ͦ[*cnjjҭ(݊ٳo݋=j8~%fe,[9d!2bm=]لeg(gcЭ[7zΚwoE] b}ֲ{^vU킫7Uܼ< XClzu-3f MQ#h2.Bƍ9qcNK{ꨑ-u?v,&ñHJ,RP8Ĺg¥Xn=!/aC(?m~nѣgݺ1ix,ZB}4J8ba;C빓I8'Us@$}n` o$ kW05Zk|?V~emf֚۷"=P(2ay"AoQ>?e)8KW`ϞL2nں:"0^H&jjjyn+\K;Ԕ|˒;#Ox~TM7ؒqi4)̎BI6]I~v-jQERT>Į(L0FN<سwO惚cǰsxN!M[|,_yҙy^\㸮Ck_ i{6^H$B^A!FC0BtcbhmGiڠ 5"dﻔCH ۀBbF410 r"o`478 %?'PXSGR%.]).JQ^W )S擛WR{H!Cz #-( !!39Qg&|hUGTx1xĔ'h H !cr A(Fo(b<$ej1G&PhFEP< (02"QaQ" }`ILX @IV!BF"L=J|  #` xB'5Gh% AO(x>[gM#BU2atU!EBF(ڄQ H!ўD(ccdp̀'JH"2-A!:|mKĴUO*fEP:&6(FB!CaECz4h塍1!D^F CQP11`E ;Exs؊H{cS!AѾ:ƀF"}G#0">Q l  3„"$lA؊XAB9hP i<gaAh#WYW<+n (CSx2F VIm*>O؊_Ri~1 md 4 LAkU""1HP#I1h|)!1 >F WZ}JaIEc4qjH߄ =v2qQh4Fʄx00hq 0eSsAez6ͯz{ U*?>Y`n<\aRD >@86eʐ >שϮ4x1 VXيA%l6$] <=aH6 M` ly 5RZHaMQ1~p1{Hx6((mw"0 gdmz(RT YW @bX Fhоm0 c>64!M 蠎9ŬBB z H4“e06LFmR)6^8Jou-֑0HP-0BsbH'c:JHx&m$Qx (G{1@bֈh<FJZCyx*7C ! i"hCHD #|Q1!mǗ>"hhS>PЀ YGD) $"F e,P~=B!@xYD\QжKa02dkF3] (߮V1T-rcQ<)1#ADP6LlXcF(|QTHۈ"jhe˫<"AYoYў 3)AI4ց(؊Q0ϷIim1k"RڲdUFZ$_}H ;4 zlwK+'&4 ¶,$a+3! &b@A8pEFȘ}0!51x̴TA @!#XO#RA"mb+$@!cm bxX1R)0Z`"Wh) PiG/H2dEVx,؃ #buh}Lt [&HmMmKe5D!){%2hb="h+0? ,cAcE# JŬLj[A.uVZ}l^ lm5A  V RJJ+M<( FZmK6 0ҳI^HPJjPaJ& bRki>h!'^-$"Ky!&>H=a?Dgˇ 2Č-?³za„L=Ɠ"h (˦ReDzXN)Bx!"fa/ :Cu8j{")O*u̘@ +$x0A6&dRAeЋeBikI>| (a|4%1Y IDATx_ PuVX ed@=EX/QHC"PRU>&Mϗ ~!$2:5Q"Ɗ~c`B猰2FxI|ms\ *&o @3![ '=$1i[a@  <&ŠٸF0#b`PZ7Ϧ !*xLB aFͿ #Z!V+9?BxCءF A#ϲAZ/ N0sJ]A@؊E1F @j2^Ra p @4ˬ xd3رD5)k%Y6p6 j'1qAo@ >D0z΅%6H؊6  ۄhF;8aLown{TS`SPm/loBma! Aw\*_y)ĽԶaAضچAq)HD&V$z,'Ȏ&q5Ll&Bct1ʌvJ+!=z o ^aTpRi[FDvuYdGDV!e6nrUpD.@f4SmMr]2#p8wjDO0[(h9k; *mU'|nM$&PL X0ϥkݘ}&%[1J$ϰ p9p2geַbnd(Z?Zu0ky[p˒" Z6bGpN]_t~, i,LҼh~Al~pSN3*u9ZEE0ŦE׍|Se\4zF,=,DFl Nt}Ƹ5v wQ"“o@B\ $o sFZL0~auCj]Di^r#ĮY],7&iO(_] vnjcB7Q |/򆾡v Q8g׵,Zۚ) [{6O*ҵ@bg9DGu* yًmbKkقfK0eL*WWY AmtRЂߘI3F1bf&wE!h[ɪaܥ).os N4-فPMz,> O?ÿKإԓ&敾'5n*`֧Gc8M 6HCY 3486U3=LsnNKc٘)|N+V[D%Fߞ=>?xsƙ+6rbRk'={h5/mK+FdZJ?@=AYx݇tIU5ebT]ƾ&jLThSbӫhs]dY͟YnF ;ͱҳ*="~y&G8s"JAtdìr|@+cS9:3:F۬CfϜ6>_?1X, \7?D&h .ͽ+6n۔"J&2h˚-غNe/1xl@ǩ(NVExȺ|E8Ddin:.H Qx]sm֬m Uxӡ=pVpbhZ 8Ҽҧ4's#9kP s0i8$&?zlcԇ;Z]e5 >y6f~Wh L:#8{[98w*9-3ۀE~G&!t)8*OVae:\5t̒y¼aJܟpیe Pa.&s;{ Tnz>iܶt':02'\- @y_irUPCIW1c0GhwP\z])h=~{*SܔeD:Hg?D]ZcC8! ÌIC4/l %^*tSުhW-m;t|'ɇZ{VV,^] [?t_RM!rzk,%'Px'QA=ɘMPÚ}vo퐁b;$3sKܙ~s?E:Dl,a-wפ& pZN˶nZ9<@&K=wd5L]#..PMWo>v_ĬvE BLfwKND :˂PEsԦt'Xҭ`S~ # p:>VK-ُOqeJ)Ķ [h0:)bAG}gJ3_VRIU( b~ X}H h}5N0n,lMNQ-٣rV z/47.+y9^Ezw P) R4*ql* fcI2h4g)/WއNJIW*B= iJetg7~a9a=l끖h >r0" zRk[DB9RWM\Ih6%}*G&SsxX ?Mmtmm.EZْD55̝ BXv4q@gf3'ZC")HLJ9ƣם7:fR݇'e^:[[ru{6cՀsl=c`5̤ݤ+My.0RT:Tb(ޤ|K'fT=*kFT/N %.B.ɻK%6[f rs s.l_r+鏊{e[Ȣ)H0&Ljc|.tEn ؙ2ƒֲ;lMa[9Gߚk< +vm7:J,GoQE7E\'/u ?KK;/>S40Kk86Nwڂ7߰iEgBoV9a_:0Vuo:~Ͱ63qq>5bqc&"lc^|a|°]XX}47{SFKYz&`/a7؋MelIwq!B5%+.lG?k;sGn+bSeonO[s*4f}Ho*/nu'Cuv79yVEl˪2W[lX}AyQ"/X>L$Vt֧q%mL+V C7?GZ]EEOAGZEѱ5FF)3HK|Ju~Liv2(w}ŤbIB* cKn]/B0;횱%+h$iQet1C1e0Ǎ.Ywқض՚cۺyj3UN[SU=VHbC4s} 1Sb~ mS.{210fՃ:FXajd# V&.#vS68O1߄! ajn&ZrҴ"oXÌ7߀*(*Ce ,/;f ,,NIalh;͇}fQz@< ?W_ǵ}_Q5@جfγIu|tJ}3:lv7 )9K*c'@`׭Fy]S< )Ǒ8ظ5, l6|}Zꚇx< EG{ՌA͏^yV ¸`]JbAi q,[;=,=Yǧ#_xNN5LUL& 6\x׎nB$2M13p9o-m\<>[.Tf̿g||S]?VO|]\,^W|W@x_trzBb=Rc*˥/Irdt? Q#ܱz沋nEW{kH2Y(_I2Y-wn褖ޚݛ f[]-$vt7dO#9UXKdXr+ch=r66j)2GĒ66?w p|,L?_j!ja^\.Ouq`M+h`HڨU/ιX9Iz5.6Ă[-e{j }hMm3m'4wf`:t_oJc(٘~mmB>brD+ٓ֟rDEp.}[e&\s%@q/5QEoǴ v+KY)GfIL$zNV)r;eB 4I\݆)  ؀NY*jcQvhߚdhHF/6@S@:d<,6ow bJZ-= 2ThC8ܟKG9HC??߃3: d?z3&ahi=?W>TZLYx,+yo"G})̛dr4i'2 x8J5*錫#! =/]EKuRns}X 3@ \Z[RG;?6Vb]/~~^/%N!o10kn G tNV`d*Jz%PTe*0f0cO4"9oS5`́tzPX0ݘbk~VsQ`tBx;K$V 67uo 61撶UEý zS=--kÞ;\D!ph/gR42HXlO45r2_\.y"]-ڛWKS=h+uy4/:ͣ&3]M[)ӷHmZH~"=qc +m) -Ya.ZwrI_"e.Ց;r٪y8Y}#"6~xO7O12Ke0AƋ`{K%83o,ؽCMQthX\ &HWQ_ͫ]̉(@{a˭06,PcL"sN-6AOBB.f7ذ7v9Mѭbrx fVtu˱b˰K1dDT/T!,^:Gk#!xͺL);2ĚS?&Ui@ +5e烙xSRN怭ֲ@EI J9ر?FE05zVxOEm\>{&&gK;zFDWw6ifY(лG0}>=+[U?;s6 ?4 ~N8?8!ΐFf)e'>=q`­ Lȍ/mWm1\=T33IG&u\;?y(Z(x=΃u謅GcVInNO0E#f ~I|,3,jzcyKBf\j7MZE=X6}ّ mLI͚~ɮ"{I8ؿ3XVE nj$3ε:'(5 *#.wܛ!Խ.j_ w1.:Jkauau>P1n IDATaOۓ+X vq[]'#4TU\ԫ[Ii:`n5ȯ7ؽ$i,3nRZj2ֺ'xuK-ց-Ɇn2Yu&aiȃ|Qe`P`lP* 㢦IvtKhsM 59Qu%.TS,0mE[M/Thffu1v g2i^o#g;hGe6d p`ƺmuQy{kNJv0iYbOIF~ꇉ.UM{h&6v*|-VSx> n/<÷"aJite)KuQEa#ݴ5F (W>ivJ S`v4m8`H8qGZ PL)-G8xk( C\u&0]Ǐ IYyAct fJrAeú!4ט1 kME3f}sHlc12Ag\$qhʛ֟?$E׵YؑFG7 o 3\]N@aWZo(.8^ǍAA26 l^et8ٸe:'63Iμhͮ\`wA616"Nd34-}S{$~uZ 'hvP1NWfa1q?˪&gmL'`I e{WѿZ/^bkwX~ȸ/6H2aJUV"kmiSzoU I G/VoRŢ* 6O!آ٦Leyx3E_xmkƣȖ6UUj\ګ0L#lquM䍅 f'GG"ȽszsiKz/=ŬNYW.2%ؖġmt>X@ Rq8fs:6zMę)&-t@` `q"YX&DEmwa(#}8&=2T8A=aԦ F{&] ]Oݣ>Zy.}RN?~ΗV_x.$)x<7Xb8d30Mжb]GTqFpԴb G_;FMkIB \ȟxʍjd4R`(f4Ϟƶ+4(ڜ˜aZkO<_k/_T(%>U27vM֦JVA̫j'-;7*\z9ԌEd_=bK:{^-نe(> m,&VPB!GL6"Ou>Z|M^I&Tk]9mOeԳuXXLjZM{dS_bg[/z'QTc= Y eX}X/KwZ拗5ml /׏TP&!W֦oj}Ⱦ,]vaܾ(;xffm*I{ZiRIگV1SFO%o4ܷȠ쵈2I8z;DM#7m6e/z汇?\ ,߾Z$P$y>Gٻ؞_ʾ[$pHېn-I\]se Ŷ7/贤|Jg/YqLSNWPʥ,o_x6TccVCW~bd풍'%ј` QimDZz8§OtJa5zjqt!tOjmmb R$7WxH YNzb9^&>c*nNZ4ú\$_yp3ԖX!=F٣bk4]aO.VKl=li4pfZ*i7v&ݬ=,Xau;{|8YP!14G$•a}|MVTRه9:FzxlwXob`SPͩ5[ 7h:b6wU 3@9^q|W;:?.pj XS8Aܹ'35͘J"Qu]@|gqM3c1xaB=I BNz6apEWbC~5sjw]c败{$UV)6Ik(^Npnq<뫳'sPp`2ۺNDL'!3\㭦$Jۜ f,pծ\Y]v$TxҴ;(t]t;SI9Jnu渫H/eE`aUZjzSZr2hָ>O` L(UPWƸt^F"o:Œ .M6b_7QJC[zaMmxz/_K`(VoR`ض&ƥAEP[}l{[%yUYmĐWfG lik+`_~S޸AX߬hUN1͗ n[Pm_PM\"dzf8wZoJjK6 J%4j[;^Bʄu_yX¹G)ܾ־ns`afL#Vqq S!-<{at8>E+ZJ, &%D Q,:&4ݴ1ka1>q0 _i4T bJ1;bKr7}Qb&=j#f&1~g{6S!Y?a0l5ePNG0f;t՞4uV9h{5: tˌX5f#~>`tmm(?({gE0>k?[ts=Sp!q ׌m,οulF.`8ڞ4axqt+w|GHZKR5Lڣ)sXݣQYQCF3J3#f4 1D?;Y!&a+ F̲94LYC,=ogIڴg!=@[ 9铏_>IZڷhhgKAdώak[d}i:̕:Om E\K!A5sd2bo'|8cu4 IIA < ?/u6R76a}KIO/m*Lq_)Iit3C8ź1~a:b;1H%ɸQU"o{͞u͊EK]\to.kw_,RdU;Z Z, _HðfNb㾱(0Gz{)-jqً$a <%#ɋ#joښ/r؋{"?'LR?9m}/&zvy4jxq7OE/XFJ~q`7σT 煡5 B[MnJ5Qdұ5?yaJĝlLIΊ?eLaSmlk.*i6 Zі Fcө4zjs;\r~_XS˗RU=iJM":Zб*Oj.a.@ͳ*ucP>}ay܉:K`$_Y~FW :k`zY0uu]G:6: Lrޫ$>cv3"< wjR t߅ٰl6g{NNc-sA4jc< CvJ nYVsf5l= km@EەS8,1,29 RR6PeuvXk&?Z(;aC!׹a{-T Fv).e-6q*9sb2N3쾟,~YӖa+=gP0'1A_o5Om,thw@eLam E~]Ůb>KN(D m7Np9ٽ{*fc?7mHڜLj"T͹4wbspkVprkܶv"_T4,qgū[0{ᾩt\u/j1؄MoHhUaF`9z<`24@p_}i=dDY_%fT?SHb-ʰv A"]gĭ,ۿrK{kǜ/5RP&vs_zN}QܓEX#i% Skץ0jR i{y,OTNEs¯S^1`ٞg3Ѧg6i)"r vau{ GjTSyKZ"rgT\0bOt>ڟYXRUv`=,pAyVXp 27 #T@PC Xv^ޣhX~R>]Ȯ:@6FJ >+3:I_00sT:kc{x@Xj=l6rD[.5pP`6'EKuy)*I"{SHX~7x sE^0mh|"A`8j1:יVh"紩Jfhi=W Pd}_??ty0ކ']S\[q=HeӚOڼl֘4wCn$ʔQҸFJkl;u$6>cК{*+b,찘sȞ{aղXiV_-1,eǮ[SJ]i;i۵(G`G0kZl:{TXr:)VTP޲sd3̳To(pφ|$KԯP{yY낸~ū?_u>Q7`N/||n'[Qt I)OX=v$=5ܸÿ? _G.xz__XunM(\;vPل`j^䒭Wvc\&VS&|cO~k}avsQ.h׋^=Β OfPz;*N7,2buŖ#;\v~'}57ŗ H3NcE4Lqi۫dq}Ц+.cQrDyl$ % 91ea%D"gRE|:>o/5ȱ~7?k7b©IJ^$uS٥դKK7WQ/ҵUt?@ 32zhedJ%\Tr?]ә,凳Ҩuiw*&T˿eT%w9Q@.53I; eRovm;{8ET')J& }5XT7_A2UN^ɚ9RV s:z2tL}Շ@f&aAYp}⼆Ioa{G $י .0evGNk=-$.3=%) {Ia<*Q*?t2?:eZND 34 3j-x:h9rjUO+6 -a@]ҁfsƦYQ _b .'Ȥu/Ҋѵicg hINZ]NuyTc25>FDaդ)X^ ܪi&=N_A\oև|_?o[jv".FmJdoק >ԧ6|ܾXs5*orypnߴEzk|ԩNhj[̈́ pMoǷ۫XVӁc8+o/ezv Ho.pɐSquC0m_g=V1 ^mwA5 v(%1Ҩj쥆97oSVXnioyBM:\ؽdo/U]}ý;mR+5mt#|7 :1 ·!K咲 La06EwXɌ>au+9;i3zΤ5zlFƇ56G6]dmv9Ǿ,`+t Ҋg'0at'aBzѐǤձ6]F3DZg.y)Nۣ<~#8nbKjÌj1+*Ax*MC̞bF &:χ>}!-0ꢢ) D҈eoN>EVS[fp^e&Ugʟ?bx/^r _{v IDAT< 7.SCyR𭵤lUVWO|p)>%/`4Xޒ'tKUN4I]SD@|iu zbA65@=,l/Gz+v _J( Y܄;6/֕:s[* "Z+.,bQ.h}*[Xah4_ֺ;Dg:&Z/fVATOqw̩^uI[hO;/$9RŊj,=x OzuSCՁ,tDya.{E0ښMUriFuo$9#~Ꙟ^ٛ/wDvuefP=2Fdf*IBP"E [6;}6\ z;_7UJr [R;Fv=2RFҮːi] ?IY]VT 8 " ñztںi66+k??i6{ХDi@^ +B6BhMA|/?__uq߿~AyE5;2nmvxd]/; " QRy$c T͇)ŭGӝ ,?rLX5abe`͢bPJ gL sb3Pu ܅!`vݸV''>p&K8'(Aj.7!PL`Vb{W >.:YO,e!vKZƔ >q>LR5SҾ%ɓRȔfRчRɜ|}PF?Cˉ/@YQ~PpqOIZ:CCf"&B:~F.M^+^O}<(ӽl;,JIV2ji5^AcƚN:ۓT=]TT3h?]GuI_hT,+N`P:JtµW{ᡸt@i*ײ]`McOC6 d;چPvcF6־ԊnJCN[GEV4T$hxAk[Lmh0V}v=fҿZ-c}C]_K`l8!fawxڿnk/o_SY3`?،7O{jlOյRk@R%tȫP}c򖵾/yHև9¡b=/ys6p=Z5ֺmKIc&o 6wEH|&Nݢ5%N1Kz:p;[QP v# 5@=s $fGk[Z][4@=U0*c+Sf<˅7H*te1 }w wd$ОŚ/<O>Nϓ??7O_?PUҍ20=LFd ^" L^"1|<~rZq/~7~19p?$X**j# \T2+g1wDE`qaCM+`eW` 1vfXq ,;8; ǴZFK'-#umY.q\aEcd`Pu8mO$*4"TEh$U!OXy DҎn M#9H{dT=$YYn+Լ5 vMw!?C5ULdRur1Nz1W̕DN8d F ZP̚I[}f~rHPZ٠ˇCjGBY>Tf3Q0SSĸދlhwuMZow6Xq9'X3&G}갆2ҒU:p\|?&]e#/>;kPO W-Wﺁ mޛjuZtTm_f:hͩY|gPz=W亂"׭Wkjꦶ#"ٿJ*8ZL7v!ٖu<ǹ3gAm:0Drty`BoZ{:ouHs.H1, =4菱t-@M7ӫ\*)jp$;Z{1˼X>u1] _vvWGC*]5%Ogf)⯐ӂ#"?6mMB`|h uIEjҥCCM9f(V~71{~(98ɟKHV &/2NΖ%IyRfqK!{L[ pyvY1a|ه("ԟÃXPcJÚ71tMrf> ȩ2lhq?ҵX+HtuHV|a-OL{^)bbM縛)-cׯ\elx뜉7YN?`^JIפN˸1Qka#t¾d}S 8L}Vj)`X^3sNپ9yrAP}l[R"5x+ogIC Q=TY4gXr&p!7kz[j ̮Dj|&&& 9롯}}+[Z՚br%GPSsd;zJf7V{Za_{(ժn?P|oa]}n5^#["Kj=joeC*X|5VqYabipHֽ{"L9 .ؗ$Q WV Ӊ){/*TR$=̖ PH՗iZxnU+9Lʥaf[2:k-M|WI<6t"0!Qf}-LԒ.p\բד=Ύ5lsF]PMyo.<*Wzr\CŞ`̓8pi0`Rucs'sdOG#׿x}Ttz^V9r'_nKy,Y"C8vN,VWEIedu~ufh ͩ-J-ﭷYɚFь:|!Z -BˀC Y6lf f6{.rKJ2OFlj>HeQ=`l29Lg5$f Ӱ9./%Oޙ-/rgz߼#NIMYCƲ^5a+ڹa(w+#$eEdvF23cL[Afo"WHpBnDpeM1(ޛ~3-nN7kM`Q!gў4fWIȿcs Q\jq>P; ARKGX{ީkrun;t/Yڶ| cGzzs{H2HWk_c3i޽e!aS5 x ^[WM %+~1b-gO%֛^uYr bЇAֺVQ9%|0ߝ]\XXyp/u|~0|kZ8S~^]AH p70oqI@V#۷&$OdqfylG n:ԩ<\ N/n,X{@\]W|/>ØVW;ƇEv 0MnѸeD @D@K;O, (N|IV1X%b(*.WȐs쎫l=Ūj1IALS [Dip<t\Gb%87P2ѰH*<~3jDm' bյc. EU%ɪj&|q<- U^ޭ[&A -:> 69^ a\L?mcmfRKl3FҌoSL'T^hv8[썡بN! =uF"B}f,-İӼƚܔRŁK6Oѭp0E`mYb РMuY2lc-u:lw>g ͤ͜ +[T8>_l?cKv*wTFHf-}0v%P/BD*6ZvUM (+ےz_hFqV2='ՅvIur<k] %wd]˕-1ַ+8#Xlz:PRLĪ}?_\bbx<]m4򇶒fQijvZEo_G{Mre"% |iM"HlRkڤƾko%}u^lz ZXM . =tV#M=7Մ5h+'^fYҦz1b͔L8y;dR*"1VWML?u`ݿxA|~pz )xf˽ʨNA뺰V36"z??9zhVnn7uM~`qi?n&ƈUz5KCJ΃3o%Y Q*1 @U "c1o#b?ZpuC$2V4diu0g`v{{ؒ*MۧUԁam E}"H띬5CiwWr2R7ɒ>_Dp5nu]όf:ḱَ-E'.]'dwdÉZLz=!.yG2 ?ga6Ybm r5:ϵLiXa/8>y&vj[F֧)kÛ%Qy%xHdU!^v鋐 jRWeOݮ+e7ʄ!&i-8L>Z#j9 rxaئHmlF,CHV";K׵` ̬L?_szl`ͶZ(ST#!Pmma08&y>&rVX؉~7p-ooתA l}72 "WơHKlbo5^B1%^OAUD2N< rz!|~.rXm%>,Y2*=W3KTº˟4lu+hb&-ZҿFPiŚot 5՝h2~տ^feCxw3ڛU.3bk3FKwjV ˥xԞ$k&sQ牯/ץ_N.r*t'1Jp^ph熺AEE>ñE]E,[x&U4cEJXc9Ü9~F (QU6ͩ~$=7ڥXS=׼ 6x@tҲ3 4٢ib2~?xc<a@'5'椥Z_6atkRe?{H$Վ^xN{W`z,!Gj26ŕ 8wև5XȚM pcGNίTՖTy84%YtJۘTvՠw&]5KuZBA6|XD"L Ov*Q/PfA-.lךQ~ʬft݆͞Fi5ĠL A7Bۇ)36;TRݵ ]oMQ\^6l'8Ժa8;*h y0ȯ}vCdJ8*aec09L~2"S0ǑGZD3.T1ȼ5A]Y:C@w;%C!buA4dK$?tIĦ"[2a1%l'Q*dA*I1uLݭefJ޲nɲctvyw$B.ȇ=OS;xK4s#]Z^ ]lYB> .F],.Jȶ+'pVlNP^k= 1@;ԴP;=6na4UC*ZS@LCT| @Rj_:jWOKP]+h`˟VY[ԺZ{5f˓=)iI]+}O7Lz`4[Chϵ~\Bjb/hF/tøP.x+dn 'H\W}clkf{Ù6V lcvfԻ}\z-RkuH[]HwPղ $(f;A>?r,WFJ7E}/<*fL.HZ_j=U8YvSC\ Ӂ݅],N4!}ou 035MJ?q!zޙ}9VX~qSLf-"SOkz]5ՙZ!B& 0&_Hx$:yI?c|`v˪&.#`}ɰgg'Tԩ EwdfO* N~ro=OhrV OT(a3.V:NիXTxN m10z@;Fw"uFe;훞SiR!;n cxAD9m۲,j$/N[h 5eIe ?Gfy'ynl0S7֊Xu%.I)󥁺(cwLth?_N(*$˴2b)Pal/=n~ZC֜cK:g4=6hS67%SjDښ՞ZÃApe/lPFRW;uNM mhA ϡۖ{=tIt &k r3Phx,7kYA<>לfJ2/[8#6h6K!Lw 檶rM \%S};>Mܦ 6XLCiAo* )HuoN v*x67&`mfؿ~Q_op_;:0Xa:ܚ,-mf]:m6XE TN8ϵ%7t#1hs rzfj־tQiLx߻k'lAmXbz4?6}EߋI]^^kiV߳¯xO"z^zs3~X__5c|In_ml~z}q7ƘS}l_(X }rgز(~%P.$A,쾖Z5n5~a? sUD,?Xd➒PGРMƝ?ȸϻ&+̋$ s;e?' B N:j݄}Pb;rܦ/iI ɾȪk_z My ;479%R/mlPT\.u^X Ç0WwqqB#}~ISSv ^,?Vǭ)wΑtKW/#1snϮ2su'Pé[EuLIt߶C-;7ç~jPP,if<eeQ ^@5zȐFbPڱM=BlZnZVuvpd,Jxpݶ?h]@[2k=Y v-Y$NvZCf}lfLYt'Yk-flP|S} jizUW'q\&z̒5k(hfW M)ECAТ7渻;Wq}7} ZH_:<0_ ޛg͆>?߷&amr~Fm C][(|q|P󱰥fymCڿ1[lOL]W#ucl |N5I!b%::t%.\LL?CVх{Z}d:FK{&Vy֎]@WC7s֝yOTE$hhϦd2Z AzR={ =j{q ;c ?(ŵ.bU{O'#" ;>YO'1$FA)+4[Ѡխk]Ƚ"sR+h֮4a8! 9u7i8''8Q'13eĜ$j\n#Ou ?$f27[VSHH\:QUOi|Q1%Üh '08 PMV]XOׂb1%,Z"CCd),E4ɘ*~s9 }i_R+ |&/ v"* ѹ3ע"Ik2(T7afmW߬1[`[>|6+r_+,k3C#(fi֓>..r=] zߩflK߽rB*P7}7=@.Ck!qw$g)nfJhy`!FKS^ϭR[h'QF`u^C3pNaS}dȂ>RwɋH2 *^70COWJkݢmgrh!g?/-lk:ʊ,ԔI$Sԩ514]:S;z&|:b'#0 Q5On 8>ɎxpJIpྰ[Hh׆ M^_. 9Ф;<LyI]Zg|;lPS(W NjCETZ7Z`Y5+^'(Jbbq$Xr/#gƸg? 덵8ғjb֑QT9+^˼8o hv X>]d6-tpèYiD=;.[Q`Nfg XiԢo"=Q}n GaH43Y)~2!.`z5$YLM~{7tw"%c6H7+: akDH)NEζdDyQƎ`Ak64["qg5Û$D=D L>g #byXFn|up[`+ WmD~Dsozri^WT"f|ognkm?;Ͱ@Pfe[:ڈ,(Z5َc'Ds3Fgys'la%!(]MhuLٝefOܜW9PQ"_ #7`{ك0GC;חњZ{r3eTAfձI&&8Z }|"/cs>>kšuqćq2g/^Ͽ᧘=)CӒhs((BkS+uL,7-;x4 ;3ӝChk:)nf9a ֢Уnø-:z85.iOО8/9DXNܵZ'+ͲS E>`9 94;eV֘ _Win{tȡLjU]N͌ ruDG6~H:ΪzxNʄ| f_F2(^ bs& z.C:\7=xlaLWb1YIx T7H_2b2C1zm4TMG xQ~+O2i2,lE ͉,t^ຩ0w۴S&oݱ(aXө-~%XH(f;VlqZFxm@ bZjgywmCJuӵs}~S.ItB4ad+rd&%Ѩd<8bakjޕ}},lJ ZDR/dzfT{뵲&ۖ1T cl/1yG Gk* TDS@]<:Zœd2me}4]VtAt1&k lvY@ /U_vc#r*딣t=oBv`[j%`t^1cr{_FHb-priҎėa~Y_txZfs6p`sK&n[Pp11SA-JkkG{Ӵ-ɩ73Db+K3 ,o hN9&Z~A$3X(#]jfEN ڀ4 VHUSJ";YʷL!ܫ[wmerMi-s6ۊ} Fv#C:C{iv= lakQfl{ 6Ii*[#%)i4S1Bj2xInzovvEKz_[z*[Z a xĞӒhHse]\fkCQ`n\!Qify߲^'M- pk7c}>|R+x 8m_jӡZ=hI7\{D7]+e}+3Ӽ%>"9+[f:Lm RfZf-wوhpf6GDº ;Jջ0R|u&=k IlŇ4 Q٠9Ji_C^4ZсW],k=Ξ O#ٲ춥7@z~.Tm-.WMP]"^w.I?\ӀJ# 'CLY[4Dl`_xQZ+èQ-/ZԚwB1;94IE!':~:|]Wj}26*+&+&yn 㕓Ct^{Rk,gy뙿m1j5阎 nK6\l 6$WHJXLմŴ~D%eSY Vauhf3mbJ< y͔C\zE3H ̶LXc ێ tbYrA"-WwxYO]ϊv)Zޥt/\t9b>V⾸LE[RQYb;1caSco h5y]c)<5JR5SeoTrA,f2OS`@{2Ȳn'p[!g]Gw3x3-2 k\7f o`-۞&@ZwEXHbn\o[U 5k%qyOC60?hJlhj#kMV~cv+T-ɖXWހPzKkO' yBgCZetUؼZ'cL#EϼYZ]L4tInWl@K3-ڼb[FߠM@&Pik*7C޻e-Dr?Iqh4KMڢ[~z&^v}E.pDA\):F`>dN\ }%+Z~ԣӡ-ɖ(^`V<7WE. z?ҵSVȎthVJo?_eU{n6WKnm.lͮuq;%1ڑ[`]}[m5Y({}T>.i(:F,q|kjг8ή5Hfݐ5Hcܴʱ$d)3 l?'v\p'uct~v2?}i0m}2s,|JN~%J}Y-nZYbok 2Y4%"'=nNyQ<Э.j`KX>uH|9>e'tZ&U7b%8[Ln%gSSdi"rI.eOH& dH%lJc5V c5qɖ̽$X^*" f&RrYK㹠T4^nԦO~")5P`$jvu]Ttvx#ShЀRGY3&bTt;>nAqdeyjp :u<$i'YHQif}u!0#Z04SFV4 IDAT?&+[/]16=&:hOS"˺fLbV2o9Uh^0q0"`CEb%J_wwGGD`sFV3#zVd 8uU(wܚfVGI"۞*K!Fd*c"YVjiޠcW*Tdn7]Ԣj}uރ!P!Z?%wWč\be\>09e1\*FT/5݃d%fЛXۤ:` Pl=m/ N0n.#83>;N>灇\iu"F[9Mdrka>bbY'eJg <Ծ^\%3¤|S˃(ʸ1$Ƨ3ohtb\ fX;:+oxy3ۏ}y{U!ۮkY[7AE~AfsLvWj7ރe56틱Nd%y'VA< l ʝea8:t~)|]Д(d~l\,1yk,vo[ڪssMv FR5f P52jfqnK\ .=2rփBCmEMОxg,I뾟{D?;@0iIH"3=I@U2&Ȍ&I.w=_fmʛqs1nbgg͵43p?dƑҭ:QR#K# `Mi@j;vA 9U#@v0#P-DQSՊ-vFeEk\Ԩ͢~O L Te\ƖX3];xҪڸYڔ0C@FWJ4U4W5: CK,({H,u]]pwh+l4L H9mټ~a!h8>yK׻_qޜmIYAZO^oUH̪*Pd_58MWATPH [- JkݮI dD!%{@VIIPO:VSZzwK,df %0 8TG>ϮHnPm2)lbiYU_s#[sZ"|EebVg\NF3MrVLD2gt#1ct̞oд<7زUrUb$[ZIDfFc+jbh^͈l:#vCAg #04%JA&&'ha9Se ō E[+ݣavef#<ލ9:ؐwb^CzkYR\8 jAי'kn(-`2 s6E2YjhYu茏6ԌIR2FG$ޮL:|S"d ; hGQi `:JوPT}³uS/tiE)WrM%2Q&ZtY s YZ82P,S8ӑY^Ey.xZs)FR c$quLi 7}g d'ƩltCNyj'Y0SwƗ_e|ԯq-q,}k, o=I[ *ĠU[hеS RVeّ= 6S)ƫII\lJ$,BIqFIm60wI 3 8UbZW%ܝY( ̆1edopldϷ%щaE\HAqO(aP~WVBJlѦd؀ݒ/1l{c ܌dn;9;o9 iȶWd1LuvTc:G[f7 l`^}3!fS2 Zrɡ:^wiNfzU]8ns*قNKuzMbgDxLe'13SÒ%a&Kiy&ƅ"Cm7io!jBf0Nb:7{yg>LlmU&ѐ\C>-u^ʂ#&[Ix=.Rׁ6=k_$5,bMAAt)1Q^BlI~.Fz hSA +k*ϙ֔<ҮFFRVŠ~D!8Ŗ`AZ^@giE P\؈X:IfeL/2J>luDG CDZ]dК*KvKx&tdiMuO*& hSXdD "=bܗI5\bԚg``f1RQōbYl 8!,<~\(pB% X\~{ޯ,$l1Jt~[Ew 3V3o'] kdޣM21:8jǛ\~pa>wZYH}bgV` ךF~rM>=L%) Uˉ>UzyF'A‚b/ O:T0}{-Fs`bc3ڭktAE,I~M9)8%˪l|eB\]5V.ଛdEK ߟיfhM/$KKtwpb,mk ߚn**L-,Qo)6X +W=E(u!h$),/L-|yFd̺Ax= C?kʊ5'FMN@RI\ ZXViJt;f,~jú?Y bϭ5hɣu (={a _bb{SH! XHζ5Mf jO/X?Oy*^toP܇J6%V}5$R EϽ yu=ݸۃ=L~V+}+@*HzrLSQe~/izQ{Rُۊ|VX ()T+_,ʧȷ6awILU{k7%GZ# fhH%;Y!!J!r֥g,=ـ)$ (aCpPA҉SKu^l%}{`3Hr8c̩eE55eS]d"b*t6s%[ѭLSknye#U|,mԽ>Cvk1dJdb d'FbqɃ1Fy$7F-CS%ȹC\ ^J WI4J6Kxh-RtN'X(k %hK! dbԿE@3fve+FMI֠ա}tKM`K L mMj\g6Vϛ5;bڒJ!I:#9Ϯ)Y@0ƶ]hMʼ+lR;в8oZ;mIB`#Mp"U{>[yuFbT1,6 v XhZv+_2{`afY? V]WltyV-ߊC]%(҉[1ݣVF= Y0Qޙi-ORꎵraB+뽱_^.D6pMqv{q}uOǕ8J_kU)(ݵF˺[}S;aۜ2y`rܜ}K菌9>y >VAgk@N!͵r()uflǢypgc`69*PC4aOۃfy:1 wMч:]:)FS8W ,OZEǑNQ3.NC!JJ؁'|sϒDXި R79aظN7 N[|J %%3hAIw\{\aƴ4Hnr6*,"nz?٣ٍR Kn7ζv`2j0Ӧ 3wO'A ; hL 9b\bjĔ$j ~Wl&|g!0/F7L:MZָBl/CRŚVW8HH; ĩ{4=!Tm-&¥/sKfV?bJd!aeU~#iRcp #)|bhKS gN,mӒ&A!,Mi\ iD&0qOP@ W^.;X#) u}]KLחGП=aԟM[ Xi}wDϟX3EuK?%YEE.}[LkM#NX7XCX1 I{BFt͍}+IHW1亾sufX(-}`bX{$Z5 Pf 3Q-LY94oL*}As׵$^[74_JUHMS6f(d)j /OQEznw*aUxWdP-H9k)6J $U&).)7c+LVA5ZچG{!/_{\^<Й50iBW|w_ŗ_7^y;c2(LS-id{EZaal޸M,py,l@Vw6_Q`e3tdmUp"fWO%&V:-S0>CL5e5Lb lJ ttңoyXLi1ju JPETaA6{[Hnȡǁ5aSS:s:kLH&%DvڴkMKSKpr,U2NƱicZnJ<]w)pY8yZf"MV{:Y2[6rs4pE$!h,Jp>aM-,XK.NjYEEO޿e >e)PFs,YOȒw{-J3\ZVkEVOUXٷm?-_+e^TP~5^kjgbi]gEM䟮KWulT_*Ue*c-(D@ ߨُ`G;PN7ϮU,2Vv`'_׆*@/]l{XCS a}l/<|x.xx{1/?>hK_~ʷ;~|||o`ΒyAt#NӾi]lx_0!&s蝥5X{dPnNe4`&D`1i5 kˏ2x~TCxek-.`Tlii*,5<ϵK+JY:J5\dnJkCK $c 6\}*T]jDRbM,_XXFU7Ǐ_{\~|q#>G,wyէ|Oo⋯}o칚IN֌oC z]Pq#K&S=q~0oMf9t#l8vy9鎴Ž#\6`Dw0z4wL?ah`:39nGY~2|5UvrÑֵM3~z]#7I$[1UGQVؼ%w!pB=rԽ=jʊ׳0xip+zc IDATHae- k1U[:$*l[G^5\fiq`(2svt7Za>d߂jLl/8dژ7WcDiTҚ#( mBiǣZKEES0^:~fpwZeS&AiK Ъs?~_)3O(pVQf*vZ?&A\e{)lK-g8Ug SB'8i7[:['kbo{3ZScx.e&VIZ,X t-jF6 異NcV"W=U!52Pע?h^'bÒ+K !68[nB~KKKXp?{2NYdBd%­8.uaYZ؈qOV>Nabϟ/tЁ_}jXPuƌs=OYím>?W?~̇|‹ˇ|'/߾~~~o +$rk%0s1%/`١YIkKb"]f$'b[҃}ۜ~cn:6[$mivȶl['δJ Gu7bKܟƨ 哜ȗ?t= \L͐ sM$@gH0i fp3uvtƘ={LY6HkK0=݀76<"Qdkv r"mx2iKzc~Бez d0coi%3Iy"[s;r*q!sbv7gY HЩNS !MI3_$m|&KXm4]\*;BR 4f0CDEF;7C+xO1y5(q嵩AzcJC:MbRn.Vd"9OM gaZlXH:fi%]L U `RztpҔPfwзoQ vCҳ:̽6/0x:Rf,4{(%K-`+macf ٩*blt]Ѳ!:N2fi9C;c{l-5ch}VX>YSxUV;ɢ;aϫ)Tv^b㨿[zj/=Z8gD]ea 0@e+Q*36Z^V@Gtްb%Dk`+ ?qγp;,sV,hyr^Ϻ{:zY2ß~?|?ǿ-_'77XcNW5"]R;EdȎҁ'փomb!~֌шk .GFsd0\-s1en=+V$A7 y{P:h f[n`&KZ]QIM$H;XQlY\a4b%ɱz YP#>Z !.,d#R]L^!Fr=~"&8@VcY.Y:ƲPX /}S. 3 f,_g lS=?Kk9 >nwgỔX3_LI1Xc"pUw|Yaˣ ֟Ԟls{ _c^.?S ???%OxOxO?k_{~~ #oɀ<Ð? D*jL77Zla;_90C\`bdVatڛӛ:vF'qc$r 3I[0{3hiC9LK;-Ir4l;y]ldH׀Y[f!=dcٚF$%sSŕ>l#Kޖx [H;&+Cs# MA Zvnr%9I$wI{0wh1gPDNKؔ| |,Ss eXwiVF YW&UhJ"AR1"a#P$dW*OBT(ϮKR:qiQgK: Q,&Z'*%O, GI.ip2YZSYr-A{M'`4rɵ:FLT۸&W4q$X_;G$1IM7oz%2x _iΣ_hć/3j>1hSdJQ>5a҃D4Î6.lXd=s F 2&#Ib#UWun.wVhƴFF0ZvSO+"GZجq+1=]G-%C9o46ӆM2hpC-CY"i g3}rJ(|=EEqv0p7|!O|͟|{s^_-a>SگO>rri}|SŶa *4ZÚ+Y>i O^߶lyN Fbso2HF RGTGq*Ȳ/xG`iV5 # 4i]8:(IwbF/*cHq3𫈒 Bߌ :Cn!7 u\'$䂵CMu"42$#- 3& 8`#{ hbb@&sF04ۧ}fxd=t7b=ld`5hXh',Cf%yy>A\y׾I@V|ԩS!lЗUhB64Iր`LSd  iO%ΦӐXjL(@6- %{04} rӆ^H&6ufi:[99$QS]fj[Цå54k |NP)hM6KUi=;>|&ca-zjE (5եS^jCR:PV` y6ݗF3FVU,#巘9eXYuUYB2ec:uml}jST1>AF=gV!oVҊ; h5a/gV?~r6 BĀ^)})T_lr#U,Eﬦquh) %_V GJ?"B{xsFaܛU4zY66bKWA$캘;\ Sg]wrIMB [ #sk1,aXe~6[On"J#o%(XWwdp_yU VK"DN[Tዞ.l [Uvy%{0C.*mѲ[ޫUHt}D8- x?dyx}@yx0O oFVCVȫ5o0F:k~|;E?H.3ջDl*"ܪV^̼&'uq&ݷl> i=^<}vi&b>q;[*Gwft+m )Y!=P(ջBRl 06K >4_h$CԹ&7ڭ ̮n،r+փOua b,X6"}-ݱqj5ZI ,Frɔd{V9\?"KwgkCil7ΞB%EQ(G9*ܵќ_YEa*yjV"ru)࿊,hYαL~جE~.7!D-NXyVej I )eR&*HJwÛF.P2UgaAkP1Yp3G}*|^lUJ׺XAyĜ_s\ۍ#|C.mAeO"*p'4\^j<鲯CkpAn;=늍@<$!Un\HlMv .A3aAb$sv |6y6#ݠ 0bnXk46u)V[,8K&]&DZa7kq) kZG<ɲ~l`2e[;ۍ>VRt<`ݰ 陊BEIG1z7ldL;AګHb3N:F"SlmJ=BV* i@/n05DggoHs;HW_X6_:6t -`iٷ6-ZH†|tϐ 8if΍ {'ȋ4dփ4M ^gr3;>e\G:,&#mÆæk͓cmU(I:Lf!5b]4D0WnEdځmWA@!C>\lB[oW.mwdv51>)C++3;}1ʓW~<BXlh02&>QzPq$J€쾘Fr@0ǷE?YAObSY:|SՖ/fW<v iϚEY '}cyVZ4I-|*J^3-F1:a&@ap+%꾭>zU*VlWYe ܟm[ѯrzZcVßբ_V*j&Bdf:ۯ&Hr.xU/ +GaxW<>AAC [{ )"xΪ- D Z1Jp A4A0 f65>^29^mQ(?nGNAkN^Z_86zj7n>Lڄa;7| 햴ۍ ,/>.&΁Oh16׻ɺ+]0[rL}>L͘! ˫ e:Wȋi3P%<0w'eO搱qt7|vD;;8AtOts&MC{:=r^AV!hW$Β(rشJ!lMk>ʖ6d5@+۳YZY$; h}%Z}i 61.Lm95{S5`0#9l0z%2 ⠯d0JchJZlXbKY"?{iT fJh?{$I+g~{d\9%EǾ[.)Yry]fP#(R"̖ʌp7BPl"d&1(9EiJw`^\u4.Tn/LKh\ՙR:ϡb/F@Ru$6ɤ*<XQ>LĆ QZU77w9蕬 و{gA"6b6oF,,1goGn!x[uYWN ׇ5;N#G;1z&W_AOݥ[zWWTyߪ:=jd\Utfʢ޶g ,DT%HT@'P'E X-^rV:.yk70y}!c}Fyg(Z[=`/yv1 _+;cr#c<ĴGT}/&^$<OE΍!Mbwȹj=%nZvMcsT\< ֵ1Va`}83%s-Vn9Sv=AtZģ ?X[r~I? <}In7Rd ?7NR``^{IF^Z8+êu#ϳl&1~1 vgrUը.u`?rR.O;cj-FCcIB~J@kVX<[PrVEcpro26+x<CGۖ:,RC,mj}u]/ŐP$&l(%vEm/|$낕fjSuU Naۮp$f /oĸk)m OUG(eMa+UQ=, kiQa]$Fm\QVFumf6W3wL޼t󣭹rt>Z'ۼ]ɭ}oTpWYg } SB)EU >o ljhgD~5 BPwϖ:TEff:,KМHLt"?Aj֐޴a`CYjwBDM j~+ZѺnraYnҟ^`ERƱzRjtu@EtV:jx54@$3[L<Q47ц)T`^4n =5^+A]=]I\J׸ l$bĐVf'uf:EK^D u+n&K2"|Tx5/l$x\|T%2jiD$MM_S}VZŅmgYRu|7 H#ǂ\NH[KBf?Ak&r3ʏ/3]5;\/v\&NI<5fە{㥃7;e,NY}Ynƴ\JһZB;Tx6 H)> @‰;xy*  kENa>XZ7)Rf52Nr+([ V[@fbs H7^)B8jSh%Ei0֨ dnJ+ X}l{V A웡K?%G͏Erv(Zt aj[yF}P h5Kץ-EwHmANL6UKos;Zl-=lj>* OYmR-*+pae\ܒR$Ҧze`v Ĵ+ NlzRH>1,3t6N*uY>_L, aY\-%0xu7+sƨ]Nw.cρ_d'֋u몎WyjZGLۍ6zɥln5ux5cAs86.McW1Z|ħio ?>8{TukO՝%/d#;*T1HfCޛ1iB#TFx͘QTe1bv؀/C =KO5~0O^5,+{ ;p[\?&M 6]>s.%:`K}Ϧ+u>4ێ[v&0K{RYf_lER*}(/P4U-~J@EWk җf̉!w(,cgߨ[OU:q} Jp|]R5*F )+t'liI^ d)0:K3Kl m)-\«!-fl!ma\llomrgGuH>"`dQ>mj' /e{P@wѾjaJV\U1S"K{EDVuR:(]tfolH~B]#Reղ2dÖTgdxL_b:I+jh6]KÁYJmyo7efe @%l%Ő5׍Izau_jHRP`U׺J@Gwb8"(fsKfQrx=R58ImM[PgxVPk_;(j)MOv Qn! JG9F]c*_zFX̺HYegVϤR+`nqk#h͔WWYo hgk64߹NVĨuw,2T/`[L#-Af|v]¬ӝ~u}Q:ؼg{/־$Wͪ 31vلk ~}x9>c*r`]7Ee4'7ηSL3%l;aSd[f+;0.,'9ד*q;H;j|5^"TaX /?a'gIb7^#|0$Wd!ܕou69TPv\1qfe].2y ewI 8^ql14vb˳dQIP1KWs9;$^A~Oc.?(d=جqHƀG'y]@1,3E'ӛ5y~J@6t BHYj䡢 sIH3*` U:`UYqddbC^QO;=m&IҠX ||HT!'gVKOk2JTƪVZ8$/.I]-CNFll& .^ʫq6BٽQnZ mJ4fցCw=R1鵺H+\ϼ_!x3IQR׊on^?=]@Dz6Y1 ĬXgbj^4@qz_=+l}edVU~Iv> =Vτ(ق!d7sB+ 9p떻חb[=PP@&d*,{k`<"fe3Bm eEV(XlvnRY8IQ`5ȥoIJ/ Q{3XDfu!k"$C-zRv+jYI^\W.~jEmt$6mwhߪڗ5sɸo ZXU{cְA|xs! q԰,_ŵ/11ɣ~с)Q)q$O,^d<>Әw<\"ċӰ)_8/":3.Øp,'~;0 [Te^)Jx;<>!7 p>Hr;x/Ô`ȪҤ `_&=e䘘MO!3++%`fA ٟ%)bb3y0~?+D^\(w0e#x^X;ƴ ~r_j4qi,q FزROF~`6w{b:ڰbDZ%δg)Q짴:j *m-KaJІ}9p .3"cplQ8:7'" .. qɔU)"{9É6/f.ŕQGe/v ;V^bj ,؎jMh 9v&gֹf7ږ5IUF@YZtߠFbG 5M] `[m],:nذ$sbc3w7F7`n}]pSV,sFdo/?G5y Xs^l-&j $3%;]DW2ܟ޿7v64g ;h}Gr#xvSvk"aN[ K]OߺL~ 5YaFjK1!~-tϒD(PePpqX@M{]Xg Й =J2(+) @euz(}$c#{՝x= ݋`ڛQw@2Kyr3Fܞ#;4T"4+L i+vU{g 1?߾ db||['q/Yk[Xjbt`ɜ&ko>?x8Sp>19oj!B"Y[;aOqJn?1|.}Y`y1FՔNn$O!mΜ3d2yRо`y]`WAl.YuNfr}\!F>s0ݴTE*к3!мa/&ܶj5 d" aeCKMIB?}SsIŚedˀTbCQ nb͜. kl_?%M;1H ~mbUL1 V*:]H?顨L ({8ݺso`cka]7Y%_,"uT@bo7<@NZDJ*~+^F<&[,XNv/nu3f _`)cv GeuUN]SU;kFkcOqotNZ:B&%bohbW-*aq|nPAz3/o,VڽP_|FM%6Bw𖄘 V`{Z"k=R+tָ*8Nd¾>k^dO,֠ 𾰱)-許%؋*Ťƺ_ CA%fׁTb 3f%)h~ϋ` :೒"uĦX{\O9_RN"Jvqˆʺ_۷_ǃyKq1x Y^!9v~1m{'b]S6R:K."YZBִoq HQU p^YjoysqHԜt߼V]0Yv SgJ\{ŶvTl.`JeP$w`+ydKIbe^UI>HYԸ"MJ.ଡl'R=,ٓ+.X,W,%9e!|gڼ|tKhlVadL3vxiD#a8'aW.U]M]QܾFU&~kǴ!V( ;˭, قb6 Vk:qKÚҌXKڨBV(&~ 9CiZk[`8LkN#QHf؞1:?Ǒ_9>9d^/|$, ,Ы #1{2Isu!|$HՑX|)%ҙ>2XcݟqdIUj~Pp1epȃ}`/u.`8+0\aOc;R]`d E0Yl?q`5\CvɒTC`܁xLVc @vN=r̻٤_ N<4WY64C-,U brR WX{kdYi¹ ڪlDᴅq4+ f(ݓVCDl ɖQ|4MuAf lhVNaD$ʪw۳vݍf %T*Z\X[HCҐm ²ChpNVZJ@gYUWҢ5t FXw51KDu1헙X{[~9򖔈Ɵu q|BJu[]ԍ(JN6Kg5JZ+ x1o{4hO?[ ًa7Kl֢lR‡*( xU/u@t-; WivyF^*]UNj\b I6%# o޻֤ue%Yke4[C?S~t"39r#4Z5+jVk["^:uo}Ж]g !c };Z:pFe9K|\dM%!E:f""=f4c'~~c^/ɾ~cma2h z]W1iR5Ɍ:׋6緓/׿s׿q~ǟsLms+.jvTmbr$2[WwƥLU9qJz0y5 Xf],c.Jco7/t sў8yI/Q}:sFy Ǚ&qT zgaqHSvk@ Tà EEOzqg6xőεUuXYL端0b+S&O+lvd(蝃U/m *hZ…RSL) lMcWC23F@fRGa$ҔBȍUUUkA"%`02#-Fq8yyme㨹AG|Fz1lV qt1nLAes5iMSmOM4 ٵTvF+}3ocm)-O.r)ľ;*yl ig=Ru!Cf,z*zƴpJQ@WWLqS㉨]ٞ9M{DTx5X.@K̛͢}kľN 쒼 ^)~܌s1Y75h! 7?fRff.JPzSd[p|2NkʔlZ犩-v  E^*ko6Uq_cwTžju`ypue"54Uzл$"n}{3"z$.T&;V fr{[NSr-OY}PQZ [;fCޡ# IkߦYݳy k ̪:jnU L3Ǝ txXe3J;tRm[|m1M}`dσy~z6$ fQZI蠕LY7Ou<}xoJG8N` g;a9Yi3XMSH5@ܪ P[ PWz?$y,S,al|5kLLnQ12.? Ζx5W0&{$KӝFW3LѤLº{0V?Ol}axl_v'kQŽYcTw94yj ҹtS5YOh)kO/a[L,$``nꀙ |1UJ &a `5Epuz(]t$^l^jh~$n [a'CEyVǑŝSg[^DD?OSZIQp]e<_ ϮW+Lw]!L!ݣm| Ig2qި6(a.-!7&F]mv9$ȶ^OO,MXJMm(M|b4<1` P(Mk`p.c]e-vSg T dNr#AƋו>w:uɡb~d3}d!.qSYP)i%SpSz;ZVbJJ~ fEUOEi;1ޗ=.fz\zB`4W֒h0{{d۹ALXZ2+ b \D e'(t$5of̑ĺX ^oLigS,;[`D#CF10o +@,qX~Fc [+i&{mרH\sln"FIώjJX}~ .+&2+lYޠ~3C\oc. kN^c;Ppźv eeNC]^\a,]ypSĢ]i%w]E!P(fH@@S8 pue[U cۅiH| H2]SRݍ,4h֚K iywPޠͦC58bXa+vTaPۦhj%:tY [kuhO!+y@,0Ui\bM싵ޥDUEY^e=s.nղp2b'`ll_0Qw&9p ·"i){62 V3 "O’#gm1j0Tڒ񝉀Cg$r*4Y{$sL /IݭTIT۔} t-ؑE"y#V9rぅZNԞ2liBFIagvqԩ{imlv ?-9m|0U!ܦv{>vlyUI^zXg &]0mUy qx1X?z(VXLq{zClWJšLHO0mlAC#ŗKh:d2V?2huIfU~I;WU]m%98/HYƹ_c`;xk[Wٮfr)^nԠ}>)+ܹ Cf{+%jr 2!NwB:k 4ܺjCĹ}D1U'Yw1Lgx'#/]'I/BZ[d@+w/:6ʒsx kN(k\ޓ#)mFPlfsqb*O k p?*νUஂdYqWpڛ11r8 g[O"ҳcx'}`EVԚ2bn8`= 㰓1N}&]*J=Z# ] زqIiLyJC#al/Yqq ;p&S6"<`oVr^W56s͖r'fOF,u>-L{ejIUɱ$`c3ذ"}&+To^ 2H^1|pd:lks gz}`b==k`[N Sڰa6Y2A#DE2zoTmm[ PXAnEzUQDTK)Mn bdUGȮ t@skھ10dVB[eM%V>c bUCReMmhQf4t<|T*J]V@1¥b%3@>~U zu@d f6U`tudM#Lkα޹@zu:7h³B@q:An5||%~Р]"qbh}?>7׭`b,a|V 2]{%*%|hYf `z{r\ LFv.z]fۡ{4O}P_sO[2𚛵IS۔4.f@WeY5m=5w!)CU853^;1/ wK[jOn;pnh5-esm(96 䗊dEFQ~.KS,, W~OƑ9?~}[5njMtf߾}x<1'c>&?~8e̟FL!3fɶQG&;SEF1e'+`Š*f=׾ O">>P-YHl/A pui9f٨rpr]D0rCI͂/ce+"FTmVpH^Z-Vb#,c/X-CXS"XpX 'Lv<.'8 ^ l™1烇?ڒ}̩Hʮ;k3^}߆O~ IDATo㓿L1?1Y׋&JwYD,4ؘ=;0 EO&]`ApvO|:a}4n+y/XdLCs#PMadYbSNOht< *PoZpr\e|}2\]v&AAl_ak91dBӃ)m38lJWuUX"#! Etfnƨ+8zٍm =xYnr{/?6q$̝UըNZZߥ5lU.PRA*@T5jİy&Ga( T/m Sۥ5sl%}țE54螰jStkQu?%LUjVݙI ,V'+%Ѭ-[xk3ne|_5 facYF_ޜ @ A_F+QqMky3z=}7)(WӃcʨ,ݽt7HeQ:, AIbLGNxYH[0=?D8L7nIC3r#ab`+Gih eY` I tmo]@uBO!= eZ䰞vO*-7J{ϝV#Λ8Ĩ_VQl3 ݚ+/u/0Mݔlzn':g/>H_}JVr ¨vEt] ^6Oypo|w7?.as%=ٶW)Z_6|_t>Mg$A.UAו/Cs'3+j9% p_kbdΚ1^'">fļ99){6plIY |{,G9/bu\w3.M,򏵅ba6yR'E2a ׳j:?|b{ݙM LdeI 8yer<*ս=0>%6dU-y~J@.C%f4v~J.`؊FC. Yp\3X.NUKAW<}H,ו(Qrȡ@L61Ķtd1|/.t̋mgUEbhJL"v Om[ɢ_Eb*HvzZ)^B`Pl7FS,1VlX9଱U>M)k` gc:<; mR7b͸b hF\Ӏ[E%Hh(Yo4? o1!(5Jg+w7l@~+n:r/71ٿk/7Jck %)SC֩Z o w S7R:4%cU-+u{|,Y@Q2wKK +6r}Gr0e)-{y?Vc<+F߅Nc6"͖I am%VzuQ엇d%eVzAejw$AQ@p2CC[c*\5?קּ׺QdFl2x B;C-I^ '~Oɹ< Uxe #I^Ʒ? =?Ƒ|;O7jBΩ3}pWI a. ]^Ⱦ˅:g9,wu%' #H#!Li\sbF?0a)1ĊAILĪw۟M&aw,K9׿wfVuhQ ٰ%_oGliA&e!HdwuUלk&DwgU><1bwZ+:z+CYs#jYЭa3X=)l0`)89ۥ T\RWAL VN7s?|ak|J@k&{A<Np@Y,pJیi;H@EUWw{3=*E#mgU4nF@$FG@Ջ3:dTxUҥU:`3|@.uj#b̩JΰdȨ›jj`/ #MҐIg툭U/)-\*z Zur@\Q&߳"v3JQhN@ jY+ˍAkW*_ 9 *M\X7A=]{s3bϸ6`? (;J,}]pswT4PyE}SҀ73uo xyRf=uud}+-n~bFQ=2/j*QSy $*뉍擁(j^H`&B@IxWit=hyXC.`*PŚ%-yo5H%3Su}] ǨּE:.p:Z׭T(_kJsf3^?CKY[0w(Ugnp\,MZKT5ypryߔe><?۩5(X .kuyW~,O֗@7x'^ngDfCM~ q}4i^jaZ6+g3mҚ(ԒBGazf='h_toܴ`+kƪQreP0zGc| T: P@y2$.O26QM86"f8b<-ld]@\ÝN3rUNl-LLԸzJF䛴CH+d#98H8T [p1f071t"sh%K:]RaEdZמD}3$bp짳PSv!I?¯S_,6+mgǃS/V+_[JF"6vxՀ6q81H[>nƽY'sQ2T-@iHbBbb#Y6YE@&fliˁaJ .v q ݓ-q҅{eU%mV۴d{N_/)੆.Pk\| :6SSN{t 5k]Ltl%9ꋪǻWz@fg\gYT)9H]q &)V\}`g4 z>/?;ooi,85ϳs+'7m$'NNOVkh 66O*25xӌmγjSn6ĆAOOZΡ|3̾σ^ʴtlnx 򨱼Q{=p1N,HfV!m#Ɠ嬡ğXy._7k2V0*5Lnf0I^ "AҬe=7N<5 ?` xls^O2i_<{-,qnp f$11|Lr4p?zr[ZY9Q) &TPi"@`S %waЛؒiJm a+ VbN3AD\y34nY2H}FlH`eV,vǺ@wft+H/ 1#$-[7+=wթ[C?Uq6Q|A.Hڦ)dtwb1 (t.iZd mvhj $Ӎzþ[]+صA2ywNmE;rt5g%:d ^F炘f"5լRd8Sii˲Ʋ?ACSW3~Fy~9dUfC=`fbblұHSi6Uĉ`SML.71]=RL-6^`۳]ǘ4fUKIqNf+aU`v;w݋2Sy5)Vͳ7 [zS' IV[֭NWgOاc[LMԶ@3-qԺtǻypQżXeM4~,7;Y ǔ%bfy#CG<]ĕ 묮s'1>2^Ϝ_r#Xn-'{_M৿ۏW>į䌅* i:|s9'QLsLfnd>s3"Yj[lu`cw- 'sHlJ  aC`Y&灰`ǃd2̓C}"cL]l\{-zA3B}KVr 11yqxɛU\t SEvղh8<)f˖:tͽy}EЪjWm5]M 9 ^ef&s0P2bcz!esb>e,T,)7]:Ԑɼy: ϊSahR)LIȹ!g DtACMLmW趛+1ItdxM}]nYR2Ķ,ٮ-#IdgC'.]vݛ|]a~]4*56QSn۪ްN/ b6iYAFhN2ĎLc‹Y@tqӊ+͡ljg]T $]tujlC`>d X^d[g{#+F>HٲO#!BENc>u%)p+;V(Nom@e_%߆{x7fbW7Z֚XAi: BlX@]tA;X(]򶺐U] 2i^T}pW?JRDµ.n#^1qw1*HK&0r4Q{TFcl0H #a SW>%a:VVkӳkX*5"655wơ!l~+35EQdV>e?V [?>rZ5zܨrk|kO8,aEȭ'a siK,2U!Vw,9[LX5}cFc7g_oQ?(źve b\ofj ̉TEznmF1 &s1 Pom>6YeBk+|{Go 1J×NE!}`rV P36 oad*\M] I[0-h5K3Sm]mlF=7h0 EhHVr1f!_KaƙbldK\h2+z1Tߘ+-11,bL^ui ;crmt[CZr>q E쾕Lb;DKb/L&†;}Q1TSr&QKi d?(zfT2 V%VZ 1WsZbѣ4@卬:$YirӰ}*Rҫnr&i逵fZY*cA_#Th|]r\{?qt9% @rЈl?jБ]I?]Ph>i:_M9hPsL%%0eW0, |R  < Lne>k{[dV!UA_cWD破@d]LWLjÚe|7vR%%*/)LMd7U0.z2 |4YL~.޺ZܴD'))0֊&YTu[ٚdF1*mI:eRR!JO xb IDAT@#J+=.2hMӍ9NUߧ9gqH#AX09ӰaDJyl6"EL;# J;[{['s`x°d11LZ3ؕ1ȍ"sv%5iLx.i:1hiݩ`4ɒg~]К?y(u_خhW~\4F'$WMY8b[ybp|`c˫jmdnҔ=SvTė*ȭg#\\lkYf{uN Ca-V4R+^!?7ctWTG*XN"KmB\lm/ Gf:J@#EQ@@~HXjA%R\)ģIK4zH+]F-h7tn}m"f >Ҥк ׭O\ŢfW@/k'TJ:kѕsdkj}% gԺzG=c11dQc\WTiukv&{)5s6+,)@ª1Lڐ]6YxQZd;q-d;|ѯVwk_?,W 4V0!S%:Q0XٶKtwTߧ#IF &xMDs@]4+ҍk[r-YZ Yу|uk\ײ eZwkon+F1Yۺi8ٻRlSPUR(p:/?e" MY.3jEKcĜoS~_M>~/N˞%C&} 2xD)IJ×agu/VԶ)ӐarlM͓,6E r99D^Yd1yEl御 )\"\ֻT`2 rb[x9vHo EϟVqXr5}=Rr?@Gx 8K[KmSQ269LAK)3MB>fx50 ,H[|VՀ PzbVtyĐU. yP&?gieKlB=bڊVvbp@IC<77Vz6$GZ)>͊kq k׺gi /Wi-ROŽwkmmV*_,Zd4ZfIle,c18ʭǣA܀#xbͰgūi!@.ecTvY +ZXaQeVhO Hpty{x n%̄Qw*,6Wm0t_ee3Cm.&j@{#|U.B`w$'|FhezESZƕk.o⳴']1\D sfYƚeEg,Q |2NgZv%kavxZ6N*T4o. {77'&v`Z+Jo`T!2*ӊ<1VXJIL_D0.@S6 .7zVZ+yͪh_Z@SҮzb gd{U!&CHRԠC=kTj7AlXZ Tz10dw6JMcӭzu kgwfc>ON6Y RlX8xdBu@GmhwpI"ܳb*C* =}fҟ7b+@lJFv]zyzY`Qf{+f9.WaevIBzbH e> ?*ʮ)Xž**&b2=@ҁڤ5uCtLVKX]yyCwl5sSs $$omB94{w97UK/ $Avh] mjgg N#+k0|5^G֟e{5"Ts'_?/?9ox/|o'?{]6jn9!' ?!dƩ'bZ N6c -U@-5J I7ln'dӔpU/\n(91dS:g)w?ԚXbۖ\&66:~7a"a/?*Dsˑ `$&iHSnal0O|oژMl@ExwS'5X5< .2+|ɞ'ɰY4PJyV&=P5ĝ鸫b˕KKF#9YlƦ4V%N-J+*zTrsc;Z- HeV,)UAn"OREU 4!-ҮE1OK/aSTiK$&v#OGg1"/X\U ]DuqHhlFk:D~)fsCzۥZ p NY5&8bwf:غNF//]k-n<*J qUIh-uTU.'bӳ,k<=TDGr\LCϰMs3%ͨt;kohT;ר$Hө]-Xwp؁p},Ba b.+n} 7j;+I8݄ddVfeOT?XO +dGKo N8z/|_~?f2Wb`/Yp8j.bibdN]KO<_d$#++#dk'x; ya8,73/W> ?񡚝\flFYLi6xI,e0#6rc;vM<,_A1L/ޜcqAύ{LAwؑ~W*튓IU$d,v9~&es\Y]Kp49bf;  Zn W% Or $-4jQwU$`>zisvxUx`CP T=E3̰T @{15dYu3ڷUذve,~9m؂j6j[-!\i@*N3 `U]6$w)(~*2m]rGb-ŦP5@x\iXmNŖfkQ:Š%%T{FmȺgڃS aͶW*?b; ̚)|3vGWH_l9ʪ fIEXvAҍJzA+%u*C lo PpgV=UET·@TJp(` JhjDJ_V Ԣ}V ۩~(U4ڵI{n8gs]lJAd]}%IjV^0Bm[K!| ^ P)`143 ElJwQ(]RFxez(JUVB-^2)Y@zKoJ gL:Uv"=h}+lG60RJ>C(۟!'ۃ\O_% ~0BZ)c#>/ib4-2'\/$ 7^V\s[񘩱B=j(9FdH=poZ@ r3ʐe%-$oAFj6a)V k5]ğ.@tQ"W]6Fk,0PeSA:m46(&%n& `Pb̠:9N7`kXLm $ZAq=V3`eVT,f7˻ծjB&sX_]~AlFUHym lPVM=wf8G}W@VTq/ٚ*a mC^h-]σaQȠ6Kph0غ*z>V$l ~t:Qi˼UI}Pڶjlp{\CLI 05U0ٞ}LedXkWd\VϺޫ,ldmdwMEkZ v7' "n@;pϛ0SbV]ƨvAbt5k/\$pq8+P$XRi8@Hsz(euw &o6$}kw`OÕܑA ?o}GePp$gٍtr/r12$S]0a J6̄3NerLco?UPJ8|b8ܥ;24ɲ"j!\>zy"k+WDrN$\EWupbJ{X2CIj^EpeH{yݯS d4ymH\`V=U42 }Wܲe\Z@ݗre(PWDUܡX@5TN)$W5_0C,񳸙8e0UӶS!zOUxf-ڀmEu T[ZuƱuŦu[F{NF~w>~OKvyI2qy_ZIᘓ9MӝqԭXȄ<ŀJiAx-ָǃֺr$ʼ4cg}iK>{>_mER+$֭OjU٥9./uw_#r@ 0zҪw]jG~]%o?(%wɧ<\#/mo~6;/+Gv`Rr 䞌 ۂf% Yd,&m3$)݃8,ߋ/:>DPmI6L}e`Rێ_!Ilİ*XQ>*t(:MKsyGMbju7x$W S O3eԩ7+MQcc{vjXL'1{1HAqn'w1 mtMf˜͈j⅊UL&V1RV̲ID5P;Ykb^˺|ST;XXFwlsm`Dk ^ς~^ufLplu\:X慹:IzP)b4[R!V6[3ZihYW,:hӾԸSL1Z-͗zz1U4#氒eGoĔɨ 5IKPV%fہL<sɔ!8+,gZRf_ﬤ!U50ZPr\ʒ 4(kxZ>4O{wjb+Ъ] sWmS9|{Z "W _QNJ?pT껋 H;ZUKФ;̥0N÷Vj kM9F=]LYdo?^{IfiZ$/_~+'sksZӝqwVs`Y,0'Q$L썭{zT`[ ,G*՝P8Rms-ݸ%iC_RӦ{6`WϤ=ܰ$k0?&k$N|2LYÝ؊ǜE%c,X!:j٫70AY"T34C_%"HS׫`r㚷n,PAd fQ,lMf р)1`n9Ci0CPN $q8H3&y3y]]K"C. Ҭ"&)-4m( W%-/Ĩ?@cҺ &,"9L~OKWz:raAB-]E9)_HA&dbGVmbރL/P+-\nA'sKXϤ['jNPn5o#Һ>gG.W ZߡyQcryjˊ1l!3 bn )f,JR@ %Z tQ]ڻ5PwʼX, fnX ,QGDN b[juI ɮk(#j2鼟qQVzޘbv)iRMWPjQT4 Phcy tp5[PpPxh,dZK{ad"_SQnC h*XF\'>!mwP}r~+ǧoSlĩ^}~1'w?19Dר$*Ъr#؏w֔b{p _}tC;f3< {1 IDAT498)ɐ'kKV>&0r x["w)!w-W1a~1h:}Kx( vy*895ْx=|D?:902˒p䍨 -_K͜܆Gb*k;$غU 'Z͝pr˅q ro? L~e75`hVQZ]lG"4)iAՋU 9anxE{)Cv.V0;".]]Ԧ͌CZ40"|S@w;fE3)ZEBZN_A1lRi",U D 9 (@b8=,Ao;hfK{Z;{Gnt 5Ai0yb*bkhҩ ~mPaOP? L%%A+fĎU^{L[1laNZϱX}IQSh!]& j>*蠣IP e_7ˆkL,OYV%0pЦaRZQʦ6š.$*=}iC=lZ/ # ˂D2lK]>ٷ#k91;X,X hZ-).}E >e_%TMC.{ ˉTÚ"%iW_ o>>\&"F z`_MZef(t/b{\sȩl& jEK)yz'umژEd?dˇO^|~SZ$FcBK/w|xdx`5VA闹U=k?Q_Ed3șx~ X+I O8(7mhCidDRJΜ<5>XG )WTXzosr9.?޲_0 vEY<8ګO9X>802lNr|X | DZT1u .XZ{Cn){=bIZB do*YM -jՖקBVnPj1b+ 8D*&pe isG;JU’19sؕg]WUJN]@^@K[,e흾#+JEúͫQݏHn1CW^8#ZĹAk׺`U ~յU{6UG&V2Uz?b+S(_zZRDžY:0P`T,h:e2v|c2CcFes %`|s%+h4o Fk,k Z; r~'1'rۍl+Ot?vl"6cu4C"QSmt KY8/䖕Ub%HW`$''f'6sq2smx'>R 9i- {xxMRs87w8Yٛx8qc;:ؔw9Kk7}FTW:" 62ab^(kjr6b!>ګ+-$d2,6cZ"w8B +Y2)ܱzklr(Hκ c81p'xQI"Kn 牙Xi2Օ()ze_,0^5!02"bWLۚ*ս%YZ`# :WU6WI9?2צ)sk<JNbECɆ6^~+ P~Q!Q`dT 6. tyvW4g2UFEUڕkKF"B]c ށXkk!-&WX$c;k$2zeF$K; h! fB m.9/Uj]u=2D49R. ^ .; [+\`+@!ok_W}tU Sk LWy@}ѤX+l";R̮c syݥ;Pb)qR@WY2 Bm֐n kFYusQKOBpZz ʨM#i;}U綑糂Z#ް1|K|/d?z@m"Nalo8Mlu18mp65|Rm/2Қ!7CU7ˇ|v0?[UŢl XS_~?|6}d#  0%c=tJCEϡrĺk|Ծ&*B 6N2s;p`'{kX g6"9ƀ%(pl^Ö@W*-Pyj6Csj8k*\ !)L9Nvq =mԝ㬔d{4:.K"տ+d伪EV/4EmYHyJ'Ǫf ҿ}{6M0/' f1Z*%K+@`J#-0u*X\of T Y$B1jvQ,j[޻H%י#2ͫHQ)hD@ 0 4 H4alȪ <,Qz ٕ˲e˖>ĖH'kyгyMJ|)Fehnǽ , J7 ᴋ2נY1]ѫFIӼ=o274Yih2t b ,m|WT?ֹ=ߞW,W\O]|KP>mTyOl_R4 4^L@ P)#~vU}a-ũ˨ ʆ<j * sX>P.kTg*r`1|zӥ~mL+(̨v{CRQe Ģ6%ᄡ _YskJDimv.t~`8t 7Nb-z8{ѩf3{qֵ˒HQښ26T8]A7D#f톏>UŞ`qx}u 5a"캷Iz§o[~_In';1+%0uÄhWOF^Qq^Lf2*PmߛUTMVfܯD>joP Z/",-s\eb{[Jdtouf5t:q_87u)CMξN"gJ؜qf Hh 1L:NY霦i#*kt?b W h5,U )-Ʒ1]$CU9|hU3-2`GtFuc)XjZ ҧf$H le7]{&\rA-(/;/۞A%nս=e~xK~uȥ?tM w؀yRuP? 7KahI\H$L똎naUux`_]3=(It$%dY tMnq4JTm\^j;%.Y:,(U c4~Ǽ Z Z+t:QU?~I_ :f1%Y2/ttzb-@0?Z$jY얙)R&cһz@MiUݠ*pOP3=GPfS qVuh 7|}i b)ݧYėU\8Bl/Y~o@A`9]N Vm J6_٣^3a@{ƵM.]~P%Z͘raGr~#^5 Cظ~c2a/u`Nw>(Q@3w(nC1MYiDv1}3RM^$(6*΂' -g'S`:S~.֍lS2U-U,EkOTbئWI.kVZ7>(YVQRi8ǘ]/mf x 5笧1\ `R0\lه|xj1ĽY'@Wc zd&V*B0*p\)>\,7msb%9 *y:)n}lF`\ n6A{:-;TtwIV*-igz".*Gbb.1u)@_:9k9ѭ; Ѷv~M<~j@)еk\we5G }+pzD{-Řk"sK>ủ+תe?&)׵*t ~i>k1/?0 MD,} |#gZLrb֫ [s.Y[)p:ˈKLޙ<> ?/x#ǭSgu$C>o$n[  2xd$vn8mxSc&= bkvL3cHc "?٬Lvg§Y.B:ǣΓ``ߔ޷$#&;xrgGrWMCĘq|RJZ&t$6ZsT090V6i>Qn5=J@t)L&:@V-n٘Mly.A$X) MVtyXk!<$Hҁ')9*6gJLU;>!~Sq.UP7(szpGHF k** K\{6 R˕*Q %47C%qyJ , S5ņY(Y,)@uzCVQ=R5hplM~ފ +F \As|z8CoLÚYd]lIsWԩK1aOVڬY#v*L_ 5[FIm*ȺrzWtQT`ftH7kVcbX7PcPn/sP9[T~u#Du`[ iTdy7Viwj=W >1XsI(@!ۗڊ/FcQzazmL( ]vwdO@zagGouJcXIPO߳]pe$o鄾sK0zL(FR=_ZRԸ4s[h/i|~u¸qdg!G7l1w—s >PÉcr f,=x<|u~N^_qbDIy d/|-ǷI.,qoqb GOToNpMR7ȫ/dNMrc1Vvϐ d.| u_'X:W2u`0B .0 N]9%V_ YeX8̇\BEz-l>HJ8`Z~>Ӹ;gxxL|8nX<0B&'> {/ \$8 EZUsY[du#lA)£yOo(}`T!Mz Djy.Y@ /Labykl ݇d1V*=5 $𢀚 ;7D&3T9 g\7(]2tG ) (ͥKF]UH8d5UX@N:`U Y"ХiB{ h u"Uk.pŚ>^Y6c8TJ]Q^ z:xus \يZ%bVI/¶E%"YY!7 t%yuľ{|Lz?%_n߰*rٶܐ΅xϿf1Ǜ"ɶ5?r'^n|y`n`L-اO|wO_oSlAj._b,yaھֺܶsP㋉s$;7ynOf{VһKQ־Idf8 ڡ' |> ^*07v8rԞlLpqcvTxĖ3ypôD-Y/ ꬹ5oP>{NO* Gzb* 3DgErsॗ/ŵ6GrMPJX ӓ1wi ǁ$Ɔ`CP{Y+5{qgX;*L2U\:Gy1pSUߓ5ڸ le2rHx9dIH؉aK&I#ewֻUx}$Zf jL8Bҫ;)ڷ,زbnV*xnl]8a!F.ՙgjv*4h$Nup83(Ib݆˪Jb[gfTṷZ]_SyWVȨ¯]}^`I.Ťunh:Q*l5Q.Fғ;iP_`tS,; tO=Kꚓd鯽2An{] w3#. gXYVDӧd֯Z\r9)3(@*qѼ–Xf\SӪ`' \5XEO`ꮋb}^uy٬5 ,[pR~ɀD>oXFGȶiz>'9q{QS,-6n.%AJ&h4cYKlXw1W>| u}k|`pw r%i IDAT2FU} l߹_xg߀FZvN? EnHr$i` M7[kWp;H=σsUKOYx8l1yg7O8Hj8ZAjX+ӌ"&sm=a0vcog; GnR ~g `5=x,*QIIX[v_*X";Tpdx"wXdu@ymJRs>UAպZO-*:X{}do6,ݞ %b]u ܞsO^-[VQd' 56-s^A%|SjM$,D88qh)dhfKG>*pK$3l+p=^'2xF|0dxAu)jG$_s~onyp3rsSg;K{z@H\"w΀?bO^Dn89V9Y*tuK?};܋5J}`G5X7 "o {sp$#AL lkKȏdkd&(`Ualn;D<_lQqn5(MWr+msRz3mn8k*1S:lVe;b2ҺLX訲Sw"'eфUk8*nmTWY0r¢z |Az.ܧ3o* @USչ]k]JE+& C.ֲϿ 4֬F AQxMA"wdOYͨ'z}zy%\ݟ|;/N"t ؁'\Li^JZmXu$H{&Ҵbie5tH>٢wEV*.B[mc+ $.]2  k;tV}|WӶL OД=\ C Tc\zt6{t6yX{u!`c.j̉k=rPi*Mʱ`6gDͣ߁@5/. ibmk2$VQV[S=e3iT+Z{i5uA kyjɒh%XYr5O-+3WVŲ\[ZUh ~TZ">kk:`kͺF-kk.\ *LsbW1 ڻ0pX238'^GnsjO(<'Iڵ{T $Mz{o/-+x0YvS朁{k'nKɰ {`k)6K<9H&9!vrGqT{4|{ 3*Cىmj1-}09qϮ2E@Pa橂nɱt '~:'D-mbU{s#vr$2l1VW hۢD=cY _'QF;e^VlbKVwT!x8;O͚*k Q]RZ&J[>Rnj`(-w3Zj#1V;- rVC85h)ګMbۍQn UØg*ڍJ mUCVUӻThsګ6d)bb 5G6z*$)^/A P XT8s"~t0YK<ňd#@'hXbYV lkv8>rgaJRf!uohpkү }W>ROŮby <_2'Kh< b3fo^ *ZfRVȸq@c"9wKll!lv^cɮ\Ь*P{ojm4TG:]dOt1ptu-bHTV:]m9qy?_v~\̝HCFvAjNeFYs .؋+  Qlm;5"I=EYsJ3Ğ) ܷ9i2:o/$.Ju81߼׃65 NJ l鏟oo|_?pd3a1v`q)AU:C6}+3vy2LHV!txe]5e]6HL=ʸ0r)KkȎsTИ]s{s2O &1cbݾ hw( ]j-Z_-lދ=ndP -.C/.7mi)6J4 p`}kU0JD ~"/4Y`B1l x=Fck|%*eƘ[BUĨWbeSBfOf1A@2TL PTrVoY{6 X o)节E8i(:UӜf{3:CMZۺgvDm[+=ɂzP ̣s1hծҴ2RR`GAJ򈤜<.>:7F+ـ%)xs": HkհKkB'xi1j P1 ?PAʼ'ҁ9(/:mXLulq5k*ϥOJuk٥P:L:zz{ELkMR,:8jJ;Xպ-bA..&")=-;Pc;ےF@Y4F2ՏװKϻ"GSysV*Ą5!۸9%ay`l":5 '+i^퓯J^^,kfU5D W$C7+FOmPC-?D rivޡfj\,; C|w[:j7ܶZ2Bvg[3+8WF4XC덫 RPX٪KyIо٠TYV`,Dl.FVH~f`$YF~5OI>%%I 7Y1^W[)hZsV2D *=@nCsN= %"#@g 5zY&לn} |'eF6n!z>Ri`X۳ lG܈}'wXw"/~xx} 7O' kH`/3?~'{J'A L".b]sm 6v "Kzn o+c:s X  “\7v&!+ܙ%I9P߱`brHR|,C+$;ʁ/ U``<7m~)680@ɤ`,`sjrgŐNH]YUA ;#*02V#Z(d9NZP9]Tby=Qft!0mT,H;{(AC+Li,1zIW$ls:E=;*v͊9bKb.h %.2D},8 N)pNKX/ sG>1>:\))avz`ňdUG}}tj() iXp\58 N*e;Mn g;*FwS6r^|峐bK~4hָ1S (Ì.P0 0zX%[aA 8`?LhsI2;+Q3[dx6q1]X D;J5׭γ1CilÝ.6[ ګrs){h_+9 $A</-b.ɂaJ. JtL@.`u,X+G 0KS[Ai'p#  ,eՊJq曆Xkz :0^KUWˮ֟H{*[q<|~c7c27`$;|-2~q@,'qG^^O|y1dɒTDl?/ 6PuHg1X[٨;')[_Mrf:9e9(-ovی(OebYxd61jo#9X;0SM݊`<ثepbLX Ùkqk/v5mR 9vӱ31\K9)vq2|ق _'HTV*1Q$xxeEդUyk+g/X+yyE$c)< k 3P{;LvH1;eeɕBjl2~<][H T3!U\RP :瀢}Ĝ)<Lx#/q[^UKlP̏\U889(Ssi[_i&jeT/Ѭ(Jv.gaKv%5p.ͬ*Ю9WeoJS?Og CJQ{uXaTp}-=3.xjy --wݯ4݀R/zңZK Y@]ҶSV!Xbf8AY Χ{Fv99*dNG1}T_C&YHKI$7)ZFݜ`@ 8,ڳThh2|` `ʚ1$u{,FOP5U;)J#G߃\tdo5Z6=_-Y}E4/p{<)8K 9ҙY:D5Ht+Y ZnVs֤ۺvaU'++iSkn7!OvL WSPv ^)w74]dw^z]t_wZb+utBrob*O 渑}xDx p _tWgYg. y.?wW-~·^)WNuIy{/vmHR5efL|e1eF IDAT܆j?Se:ņ) Kg)xO|aosTx5X< b>JBP`a >}ZUgEg 0|)R>y8D!Ǎ]sX<:Nnu7ӞoNa83$#Iҍz%'5lm•H͆TzӦPլ;*÷ l<Tx7tM8xNl$l|+OCG c%4fUT쁛@͊AG>X:rf+]V獬t剝}8e 2۪ۙC(f;i!!Ni;yˆg܉:YLe-fiDc-|4;YN*Lêqaf, ͨ@K}O:l \WzG;lҌ%0?Y"^|~Q rd\l-S0U&M.yu-p[+꾴NgNڭªx3kcC>4r~p*Axwh\ȭ`1k [vRlu.cPђ0nqp/z>i%+o?~XoKAzT ̓xO?=?aCLc"O|;ɝGe "i8Wp,r,1YbVGe&eE8%Uf+ykuR*~N5zTҫ8,_Di3ܔ#A;1ڛংq;ƬFRK֞3Pq'7|>O}c+$8x9AيtX~z*]JSTk{~`1I N niRNNقZ0]ͨ (}|Y9J}v=H`HKG*|p2BlJդ9&&tȍ"wST)hs{Dv.#pvdN/CtuO\i#_;jҺ{Wj ad'~#/oO2v$zM377; tqS2([ZSA]F2s1 ٮ²biYu6E[ 7-^GM2&>VmAK@a?X^ \ٯ믴ޤd[{WD VjtmVG`w 38|ߊ rTz:]jř{Vglu/Ǚ2_NW\ȒG%7TmDyȮVW" /J!SLkTd`FREFy_hBuj|4 V&-۱[ ڣ1LPitF&bfݗ答ghhfٙvݷQgCutMܴ}BlaQ|'[Y ʁ%O}?ןO/vg>] Šp,֝_7y6?^獍Ȣqظ݀`%u@) n揄(d3+#˫}ްBk 0e R31(HF=&9jPa'YmA J SxA;y`U%i}lI6}q/VݧNlU Sf*YfԵC,LT05+*L1x2rJ^Rq)1{/ UStDT'q[Uk2>fbk8Pif[]RpV~ˊ57lnDZ* ܎Y_?Ddn'Y-,[_KR*{{1e}j5K]٩v 0͵m:|ujmۑWT̡ͯh)]a\Xm0 \YF)6Lsؑua+#Di3 DLuS³Wf$? ,cc`0yW(.i(sxy Z0<"b_n] }m֒CI7"o[m2^o|=ox+|G$t-u?ÿ?/cOXa@B3-`c#UpXSĂqp,Ϊ1ٶK&ŌrIO,˒\чwlM{qhL!_*iL,n'N05:*^{W6Y(fre\ZIMRHvi-OdCnAױ`8$Ye) wŠ*W1aZ] 0/yJ? 'yLqHNli YTPW MDyԽhҊs)MCmƵ$"C27̣c%ͩ±Kr-߱] M#ՔDD'S xZj۰kajv'4zb@uAIcI)p ,ԔLӎZOCZwHfnHZ{ر%NCF{&-B.N}d\Li3HNać+/[?v7elnԅ|t*w/_/oXs]/FUlTb:{%FXge0n%Q2{%9$KS5=2PBPdpj52#L?GּrKd]=z-Q ~, Ԭp&)^G&DK/{Il@nѱ $ ޸a/My" ;}^Ձ+w,C4xeI|Ӷ̸Y;n_uCp"nRlQp*Ҹ5'_ڤP]Vq߮4 *bPF ZuTb}QExIwJW&F\qIX5%{gy;(Tn-Y_-69H%Mr9(=*r:HؚPhT]މx%Q/&Sq 4ݪzM@Ҏ]U320w3~??~~[pׯIQ@~)x+xVeHT!sIO;&Gy/2 a9d]|h-q:8 Zv3iT61s 3X/n'̐Vr8nəF!-u<2D-W'S3lǶ= dgp"md'9xeY{X_?&is&c#އsrH?QŐL!qk0YUЦU\IR8ŸeV1&6' cQP z%B^WZ:?rJS4Ou;ر+A?$Ja'&&ʸwd%47jF> f}Nr@llbw~X:{Kǫ4zښSdw\=bHϖFPme;?o' ֎% 2{EG %)ɃU6Vfi0W`EgiS[GO^ $fOIeKH!9孠N@L%EcY̺n+/,P% xp,$YrXyZUa_]TBNVbͫ^nCt⚵ my v2'"*ȩ)7~prNKcI&} f"q&q.U!orp#e{P!cp-X1\c-9 {3vbb1J9.ksP|<y*8$dLdҏC*tMTҩS:MY=KOҔ2 <,+m8ը) flc$wV<[- 0c]iL<N,um`yyšZ2ɷbKBq郬橾iJ]p4?5]ׅE2Җ|@U~c:Jg4 Ciwu{!]Y(BfꑩTi*XsYi2񢋢JO٘lKuSeJn`+@~M:Y*&QUdAUc0W> #K M,[L/'B@SQJ;;Sg4۬h\cs*^)[X2oPh9sI>xJ0h׫5F+zKbk?l4.؟ToP s /|4~dY/:+}'+(Lvj҉2auu-#.v5) : mLU;:؁]NhQc.Ǖ]!MU@6-4p#"ͯWAG'&a wJj1.])ҞE Kׁa#3yH] r_UW0t15^ >pǥqNGvSg}nGu0PZ/g>4-Jy6y~>b|{ox_ٿ[>͉?x<dX֘p>x_|;Nfy~ 8O8Y^ &pkɗsM∻O7L'9%t8 j4LNA>+dؓɴa '; Lw~J rJP֜gYe)K>SCX.|U3&풲x#Ҙ%[@e1c`F&>Ό-'NA +/TumEH+8]AbB ? S $QimS>K#k/X㘬XJoJf%gbKliFmiSWYzk cjr%A\-9D\8?0ep#P70>1r %%Ӧ&TIJlYXK璗mR 5kǾX,; :dZWaa%{ȕeR [Y)hw2ޭղ)ʈ.v`e] *UhQz:VJˆ\<*fV!@AKɆҭOm_42yџ֩x9x-Vzbۮ;~:*΅<=GY 7~sormޟ'KF1w9_|!ҌG>uV2r jqj^襍 6i j^YbvT6߁![d׬o3Zyؚvrt`wf!}a.^ -9(^{͆T!ֺPB]%SMl[@K u3,Ue叶2 I>@o5ĎT&u};ys.u%tKqv0ء~*V#07ַo<~wp'乣{;ׯO/O*Vg#4i^-!f6=IltVٱqjrߟmǦ@c;0|#q =&+60}vޫ&"pS@!V*$U?S< ϡ,^wnsitKF9*{HmoED.x2Hnj|%Vdc˒!:,-&Y&p1YEgLmgR+Rt3eV8%6]̱S£i*u(vQZay"/vGQ;VeBݘWq RtREhzdq&ܒfo+*bs@Mc;!h-˭-UY J+XKVث^&Irȸi@~-_{iu7HjuOjЇ0*: DmXۼދ1(XP=A[@=^19Jr1ʘf‘vj!AD*ZS}b;8ns%FNZtVݱaKIyD'j* n^kNh6~Ve.4oTpCDY./=߲\4ޕ@+VSTĬ@I i/Y"Ycd[V<@~"%)9ϓt$v9/Z~P 'Ҵ6kPP娚we`ݬH?I;znǻxQNB%Q{[-RL IDAT.|_Xwb7oY_e)~pYƶ?6\APd'?{-PkֽYCT&IyҽZj!UM2uMSíw3hf;Cj`UT#av|ڛ|bc9QVn^7u$t{rkB1Lk6ry'gl<Fpdw)֞g -٢M"`i''b&B<7jX_?$18Bz<&()?*$+5].7ctR\g*ҷF,*J6:wVuW]=3f9Xw—؋h!5'~d\mͪHL@ #ұBbQQICʃ. J vrT[˖(M? 1hOXu Z F93k-F9Tu|w`ӡc#tR;eQ)ԉN]'ht' (V,)N2MiYT0Y5RD_VķIGR(hےC Uajq2dcjmpjQek6ډbZ/QLMҾ6_Uuoֺz@LcC1gc53 ,_X{,-as2'LINI$|2O)M6 I}Ȼަy? h`9 sbU5u{+zW>j2C/Ɋg_“YmrL47h궘UlDnFp2a/*RG `&4F:D1ϛQq9 y\Xi_=IdeX>w(w0,D =Or)@N Mժ1C-K2$hn:-K!0 c&RWZy1xT>.@Q7L(|ڽ[vճIJZJUhދP )ÍрłYI* H^/lht: 3 g([Ycbg)pMjMه@Qfj."l6z_^UEP}^լ> r(]Hj}@Ip-KVj15ժ0YrglۧMAGssTd^%h]G5P9mcK Zz޽bJݏ+(S'j@k*2bSi$+%^d{ܻ[W}?U$5'.SZ3,sgDUGcˊ0JP$T(<$m]N[zjn%[zH}U]fVzQ_ϐndypukdBg;,~Mn# 7px٢]2H)ST[k=Y#?a'3yn흕aI2+|"b`HplN^bVp`i Fn5Rpslނly#orM bٱ!,k>>=QpZ ߳~Ol)]ܴ5Q]HCJLGrn5{5r!rU鳞!pM105:]`h@3J'H!ohwpln|:~໪FTfݟ:œ#-~H@K: T}vǂ.b]5oJ}D6ڸԒwԫ=Jf&YVѳAdU; fel{,bJkb&g鸋E@`.m]VEaIm&CVRB4jKpji$=.`}i+{F;*5o_2ՆW-?FtA?k\ TQPD@+"$ߺ jmX.yX]+n$ٗZ,;SRfֿƠ %5H{@uSz〔b"!}XF?{qFZn^ JBdnv*4jk$5$|˝&N"~e};?a~0! H8x_g OcA/tQrvϚnx*n:?# 7UiнDVv6bj\1t>#d,DjYo`_20DVD};79oO;% 3yf^鼊<S5y۪Bq'#!Cp9Gez}Nu6& Fng.$ @O-Y|r͊8tfoG]N' fIEZd-}pjeJc!xji?CE? hE [*Bű1μWV&jQ\uU]lbׯ#p\CųuJih ڵN_Ǻ~+`8^Җ+m% e&(J7g}X"J~N)swZo:Ho}<ງ"fC[/ R@!Aн ptn[x3ۈu/&f9@Lk\M (fdQ?TK{Nn/-VP&/vL0|:i}3%[,'=plIJ[ōs2HF /*X+)} ޭ9f-?,RJWƭדu9;iMa0+avuo\oJO)'xr\B~߼OtO|!Ow<߾_~؟  X% _$O'c߈l1 V$*|}}%wD8'"d'`?{);ұN_f܈}2r bVsm<7̩_Kk;ЫIu$LӠ4Vn"߈jyv|߾և 7\8X;yύo%g5Q. PuSfFa*z㎘9í}k2A\|)ˢ+1Yg܉U,Jg$v[hU*:); |.P5ԺM͑y0}YE߳b>9/БXkU[q1Խv8 s%_'*J\9vVsz9j V&Bw*PT҈4mَ$UWLw|f]'}lvbm,lAf& F/5Q׫Q~l:,&|_'OL \o"Җ ;unhɜ7&1Jcpb)4u,K@)6fKx$96>\e&5o*2C]2a$I616u^єUWLe-X.5L6f][1{9Vc ֺH;?p{K$Z',izW&ɃJJCPE.mՍH|fgq9.0kʆ_$Ιģg.@,n g3bH44'D8CEQ#eWvIfZwPڙ ݷ. +͝2DeﵓXB0Y"%յ5크 Beb~I:|#>']N|7럿ē YhY Tb tϸmlh$-Hgn<` INND+Ąm݇EYl|O '#'F~X!jR݌!96g#̀fY;oF*d'̒2;aXirucd2Dt%YN9mG!mU$l0S6[t$mW[)a _/p7挲jJ՟>|'5VZϮgz]o¥nLv}>= ]}ٗ.3Rn]rb&5L%n\P f,m A!?ft/PkɿދqiWq_fO (V}r5I07Nވo}V f;lzw8˒gvsϙ޷=. Uziɢ'߯$KZ;=L gv'nAY,CppI vCϪy,3xi$H5kO9"$  2ݜ@S+ !mS=p['nwrO+Hp'?YIjDI S;d؎'a%  z`!31Nvp0$P,?a8<}'k Fkb !v~#n%b(Dҡ7rbӀg$p n[.C +Έ$,!q 0_?$5PJ<[0oLv0J6@Ig73 u7y*!: '#BƖZbrd1fKfƝz-&&7,X*^`"b-$GJN i)UzE51pl.^﫶VdIUR+i5|W G%zU62ڐ4I.Y.(FRY2Yښ)M3BZV6e3 ߣ@cZØ`R/0ۚUY KO-(X@4]r| $6SSVҒ 6hIo?jk`Qw6@qC&/v1CԂJril~#m-I¨}=K؁I7`e +mJbP~;PRHԿ1A`L%]|rII/)C^EVN3j=޻e:؈3v"P_ԟjO-0j[K=oPLKz$kҙ ,ya\O27qzr1ҵorf邵|<O~#Rד͗?0Xn ZWΣZ^,/o%.Z? 2ުU]`}WQ3|#2x~f{So} Yy*\_[eg9ߓOr-]YP1&G<9X3Ur@ ]bRRlg{sH 4ՅOr9oL 0aPL y ccyvfle;۵}^̄C=pCe ɰ͘mtc}6#7i7mD2 ibd`aNo|bj{bFiOvu6dɶZ̒+VC2C%^}(SZ'y0YD6Յ,vD>F sJ~`rtj Ӆ˝ Fng%HUBuSߴ65~j.8ښixvm b(kzr͸ %^mcLgtIKnC9;cV3 {mY)~¹vZ h0'u^n4,]B|K toku7}K?E$A >hڋ-`3V2Ib,q ] 8~ R VW)9Np+0ZS-Q]FbΨ.rֺ{Y6]`:y}ƥG溳$qh @T,@Z>Ud]`7ȭQ]ŴFKeZMWk#K)+qjY]l]LU55L)lk%Ȟ9C^PõQV#_E }^f 2NSR."۷b;{,^i=)p8b[p鲳2 V{UӨuLx1zk?qcn#z\umJmiָc#L3#oNƝ6pƗ\'oO~+tȯ=yypf>A`o+i̭}a{d0I j`O2ɐrƍԾ=\ōk}Z,6:OcI ͉Dv0ܖI"Qn]v *n:cv ^G a ܎(D5,N!nrY12w#Gȣ4R}~WZXX]]5brbpe/ad:'{ӪBNlR=%_988 ~6*Ǎ12 pIWql?I#qrq{ֹx׼_llJ.IS;i vyU~ٸ i<8GgrXCڠ:cM)AXu<7–6ʊ*R. 7Xbg bSZ7 AD.y r+BL͡4K&6OM0 s)*TJ Xe`bQ6Ng^b?n I*wclF\PF) Y;<_Y[@u 3YLȸbjY-vņ.bջknT]V{(q7Jw1b{ J’DaUO2,_ߌ.x]f*`@+GSm#V{H)fjb/y,1}6|aT~w5l@߬yV vȲ{u꛶lYSv$yqC}|4wb^"d_ޚ[,6kpuJ ܐl(}n. ^6<%eܫ!6RksiW5|Tfp3Lִm"1{1>[TAd 9BEx泘YZyUb^B $;EU,FyϬ 3˽(Ǝp&fb5^&T0q(SDo8TGAFrI^)N -\չ "NZmiN{F7,K^Lq>cpdx<ߋK`x̝9&d${H!gI6 ՝A / 3Hf;זs99")A߼o(6dėΐl&g&cZIz09}T747fza1o&FAAxqr] ,r o81"1;Jidā!7~H@gb5bϼ#OEOuNvǰg$QhZ$6>^F R{SRo!qܿSդY۱!MYñ8Ĵ-xa-YdyJC)^T(2~Ű) b*jδk3 qdjSvd˒U)9AR@Up0b6lTS^ZmJPc|v_s۪8OK2rr(IUՌ"ï ဗRby}׋5|Kڑ;ʯјc0A\LZj[rIRmXee\yҌj+k"6 ؗǯL\ (lp_ݵ̋)ӬTnMn] uVv5Sy*.Q%j;aek]`-HA]kf5wlI~TJ(,  Ö0eR/9F$}T?VDP 9{NJ+VD rwj7m)<5xrKoށZސAٗmIBgh){:Ү=/`[`83,Y59Zr+|v798Ǝu~|?yz?'7kJ Tې ?-ΐ9RުuV mŮgr|'"l:aeEHyhbɨK9d9Ե`],$۲`!Vrv{('5|y .6q?<_:#p4>="71`*xf'ӟ08cum2i䑛K~׆=sZLǍ am_tF=?ºXb-3aAڃ s$"\kI[V*LfG7vo7Jd g vL0d,CbFxKTL" _3 ~#lPRGB. ]R5{d%AlR^ӯ+TŴ*{d͡|zC4VXKRרXhWF ?9?~O?'b HJ[_Wzmn,hֲy(x!!oׁ֑MGsJcһO,d l| n"!9 D$Qbm>ÈbۓǓoޟePc"93/6F68n>?y_q*]~"|1κ64$ &d,Y9O ,x?aj,6ò;?$qW&5076LN>ʰQ}六R%'e 6,O]öbW=S##.o&᫚LnTXxNƒXΰUi)?H(җ #+’MRdRiJl=eչmH>-AdIb*sCxhb v)w܌b>*sWi7+tLGNv؊]YtV L9fkX)UJՄ Ae/Hg9@0a<|yD˔uX5/ͬlTaL.W+-nJő~D$vf~VK]q)kζ]Yny.,Ȓb<ϏRGsO k}ܺv5lYªt`L59o5زò< 帤T:a[ ǂ FTƕctpL &^Q!R=}AVV0v51e`êCAJ {'ذ/lb36|q=Hn+Sf ƽՌ-'7,%7V d]2&-s__<rpv> ~pYd0HNPs.rKCxioY=5nU i>z87+#u`*ludk(njXK|fa1B]K 4f~^4F3Я0ƋEn$j,֞ĥ,(AuXQjJ:htG=gI t%, 'ePk, gjyvzv'fO/wSV@$p `pH|>>2MWEYx\ !$u[d.zҊ ҢM%J3ebA'I9ey^ݘۧuU@%e!4RJ}|600bēJM>J9ITCWͫG ?`>u.ɶ70]5Ƈ6܅,<E~Yʯ6iwm{OusBJiVk\QAw4$$dBE*@ LK~cyy\0b1lj|Q} 6Yf&FOͩ6*˛<&]xv9 ^f*b`:_[YzOOa,~bWkYΝ}%l&]+/Wj1gC6zv8c7sV צv/'t*x1Gˀ>?2@`,uAowY[=c_~~MZf~'X&V tf)Sk[٥ Rkt|EXTog1(w޿g}?@C )ٳ$c> 9Ԅ'ԹDPN~n|늈D'3zYl.R-s* {h` 'ǑS 9\Hfrpw@%K8]֖e^(o'K7jYziȢ&{uLPdSxJ`DƲ r[-.6C`҆PB%w0dveI{'u6UPԵB@ o5J떦J[γHe?dJedykBW{tƝVtmD`uhQT 3˒( to6 35͙H6.ʱڐa؜WɓtҪvֳ+`QJWҀDɔ2n A5/,c}z j uxeSin2)6I#Og]>]M;|&꧖}X Ֆ4SY/9 n^wɑ%DP\KYK{鲻n͡uz_WZ`XN ^\_Բd8B.[^dU7xJUsV`6k|^L x6HKw^gZbQ zgVҴY~tY`@uR Uث5+yu]y#wypXS+zoO墢gu 4v#vx > m۴ZMxA{wl3Xy ănl6ҾfvtxV&!SsЙj'YU%sFݤκ 팟~{~w|`C2 'uv^rYe*q[ cͰ? |Zqcbj>TmEci_+$  3g/`;\\ݛ;v bpGb%}r $]?Tz3Rfp`Xp 2e!Sw|`(H}}R,{F̐M8k2{Vz_o eEڠbB:7( 'MU AC`:}fJؕLJTJ96W]W/Cbz[9Kfݾ9Y]1\H`VbwHA W` ,;iQ=fJ i\Ѥ'F8YT$b:Y=tw,h96#+(TTZV]A,N/JZU bԥh0f$J%Pز*B ULg Lq87;RsbmH"=FZ@ժp;@lke6C,j)e\Vxj)bibIJ{]۫^"i]yӥhu="lHZC9UC|H_y=YT& +:$TG,7{g5O[ьyݘрW[q!*[s;Ks^ac55fWvhdD"3k+# h}֏:3D#m'״ԅV@AM_EMRgW;TV=}W\RUZ#}(h!a~GdT cV"t&iwuK"a0dg9-* ̒?o~__{?Utd>pxb6qA'&"!Ov>jZ ؁MunQNCY;ۯbkoBDkzmyЯc!&m^xcMc}+F ̙0T>) ۽kN1*ܜ3&&O`9 /ko/>#sOFj@ >q/%Dy$ #GzK=_zzӵ&1fWҖTmY ^Ь"^< "V%GjC,3f\'iFUzϔYZOFs̭QUڀ eiG13wJscE6LF^jCUUؠkEbC?aE$x7 êJW`>EFUUChdy'S`cV 6nV-[.P( B>*]?ZX0>lb^{KN@hP:>6 i3́qL\He^ Ngllh,@z]~Qj5BH;Q%ζDzyrhpf5V^qIa.*8ψ.VAPN]}AeeTg0;_y򬂵EWthPc(&DZ4[]fEgy H.C "U䍳N$VUa:QmCXA|ITgo*B=4w}4GR5$+lkW6c'v?&XwqTtϔN3 m_:ZL7eײ*PRGѮ68OS}ag-М` &F{Vvޗ D "S]Ʊ4ʕ$Ȩ{/ayaqcR~fWwax Qj3L|+.0 h:L;LIʇLF6FO2!CX[vөrfTz>T@DtEn]f^iʍ:(b+͚ Rgmk޼,nnlY>]kqٞDJk/MaVFVM6Ͳٟ@Jq>Zv7><zu 7"3_SsLМؑ4ր|:mOS4`RaV~Pl:ۉ#ղnQI0~RlWie ֝$7U8`(R3]NYA!MUZnaୂOF˥qQ58b8S`(ٚSc|.R!qK'm틟[L8 ޣ;;Z$A/4c7LD >-^WcP#$̖(Ò/`~6` bvBnb/|6kq$08?a1f$*8;!IQ5* P r~x,Rەڦ>+[OdŬ1X<Gk?;K-=6"$OvLK?ǻ`sH"nStj,Ko|9vM3vC6X YDIF|Ҿnmoү&2}Ķ1bj9Z(K0Ke雜_)R\x bW*s}}zE/\E>J=?1lO굻ߤ)Bm")%mAe&b`oŋ(-wjݕ^V:q[}pD>EE[VHba)J}:լtsCx$ŸN$^b/ݰVir ]uhʙƢt fLAxjH0UXsKJj3Gmr]~+O9$hH0Q:7ƼT d@ų {)en/piR3f4꯭{:hoZ+ bZ3Ō9nP. w.›QvA_@emp[)AoOT n _\43۠TWL^sE &"$]̬?g$hw -WZ -<;KxVF)X\$l^=+f :i\:+'OW|5@e0ܿ U2 fKRhpU,34nPH`gV֠:Y{؋}ʴrXs3.)e.P#V5) Z3sk?sgO~¿~}m|[!6d: {2ִMރ5t&N{%濃#s^D,&{Ainj ]X$q_q󎆝&ZW].]BZbr#t a|}3EtBU_͌SV:3 "Is0!.l`i|sݟt^_+Vo줶韦C Š FM8 ˺+jkZG]N\wNf9Ǣ۸f峘B5O8]BeV5!*Tx}g`4{oA-o'q$5$ghHI[ioݭgk\ŷfk֤뾜UQ wX8x l:' ǽ'yM~7?17.&=2]ac,cMM&x h?b:z$|Itkܡ8O]e0&0p#H{q=p: >P`: + j~@TQ;7c9s݋z6W= _j FM@kb,v8#^j,PpɂA.{AԡS򞊒-݌ E1lf.2N/07Y,RQNiv 6 n`,esTE+/W)iĭU썏.7sc(bT!h2Xs,F.GubTݕYO>%:}{ >b[}ka)MsSaŢEb4(ۮ2pxCtBj;W[hX%K pS`ٔf>q ?MeY6f~~şFN KX0W{hCC!ۧsaYNE1k:ridԁoXZ9_p*9.A$0̆* 7ڠ0HnL.yU~r5/6!E<9n^Zbw CiK]ilZ^Z&ptϟS`[ފ-`nGyJ$ksYf Z QRJV^_,y`RMZEm%FT3Cz͓\ 9{%xsãur#=Ϝ3^3_M[W0`@$r} "tngjgϮk:~ll6?K_Ӭz@+N4"E(E0r|s&C#7'kc'w~7?W?stgMX( 4kȺl3,q/xﶹm5<"a\˱&f[a]x-~ɈwԸisi/'Q;rs{)xl91d0LGc?Yz $K\6ap9"D`^>Eڳ^U7 h7%H Eit+ZmUwRILU2٣:-my7ZÛÔ֏)V2nl~P'(!ُ%Q41G0"7n7wΆ9]Ws[vhi^`J lfsh=N*}M[WTGu.LxEf"ٰi.J:vo]T IDAT{H9l+]07hTIU,<_V ^z5B.X@YT:6eBׂNVT}NOR}F it`׬5x|*03mΡr jXJqGoByZTbgٿXbahcTb1 ]~p/%9շ`ŮX0p-tM>r F &`.V9l2T$FySLo1 {x:Juh+g".:af>.߮k>#:MvsgBӌ5]ߘ5tE;_| ' ip)S1n:5q *3(V% _+{[:t} mVaT֥ 2.ā5IzIdj&oag-f,v(px  O *m1z0k"iw*HYO7O;Ƈxg=M5>EF1Pdid$ÂGv2xB% JI a$fFe+ 'ΓK~q!.6Rv1cܩsپ{3yɈ=?].0t,pD.,߬6{봢f썬 ~oЦM]Y2\5՟e{3/ :t`,RrŮ>!o* krc(=kvԥ'fS6N[&օEnV5`Yi#j53y#-1tz U8ʚ'y76ء͘F Tj+oMwږZ~Io ؙ0{7+ŗ a?#QZY=XnM$Y 5qn*`3RUes7Kd3Akm[ v( jAY+0`dЦU2pdWI6(fb:uY64FrLw cT:|Jw"0:u[mfZza ^EM/Z`yX~<=NNF+fV&1nK(P&g?^|!=ho,n2hvW/fXX=z-F tpSAiDKf`6 NT'qT {(uW4n@Zm^mJb/ݨuFT _ATmVlm5GH/U2s9\)0_ar}ΫPTk,1vYw/c ^~~|P r?z^NZbg1 g] K >[.j0@~O.;H&fIdE57X+*AYFEXgY YBA~O^JWl`"mH>4s+{-%ƂQlV\8tX1KZsaW+gƵo,&1E< /Wc3'A0q\%7QBBEa$geTK~+x Z<*؉%\b}S_$YZX.ʌ[-ԇ:!&I+ o1D+1aɚ%O(bxJSA{wGTOGSăEMdfC})B(j/ \CqF IۮڸaOoKGѥZ0=bc[I%vOQE\!v`dSZYme?13цDJx6 v^i+dԭMrgbp:T5b:E[ne#>3?C?%àOA![,&ʋ:E*Ee--!xlèԞaf5wPR2.c~[륊LaTrۺneZ󾴊ZۀWqf4lS'~@pƩgT.51{(@!zuowE5hVi}~^@wq(e1̮Kؒ:AVF2uZ.?:T$9^L5mY LU |~T j;l?ifP,mv7ږvUp I%ԵbA0&YAWn}bҋ`V],!&o}A>RKgVY^\qC\{ j\LG(h]ql&j*Zv#W L+E"oo3{V,5z\d-5,,BM@bcCb9.<`S>Xl`4=pSx .,˶g,wzky2y% E{+E @ي.61 Fl>\mvI@Y:έxZL,FyQ,+eF'iVŻ%ejS tc\Lx^jWU!] ]Z2mƷiJ=8j[[J3lkp&;U橢cf\6Q8z1foew:ZkxQ=zn,Dd.!{Wf]Q JyQqܫHH.H*TlS2_$xl=\uR.pӇcjo bytg&4 bQ鹷W!^16v\ gЦb/P@>뺛9_;\N!s L&ƫ)1U0A$Z{ ae Ia}_4xuj{֛FH; 峀k>/7BEQ@ZIνjkJlve|.S'O׬ )ռ*2"0Fئ hi|5vS@b *8볚͚*@/܄CdݓY+5OeP\Lڎ,X]; ik9gо]b(֎XruGd'c[eۮjI{䴵wLjXOs"LP㽡΍cLu֫* d`s]5J=zv3ۥ#a%3gY []/\{|aR`3`QþNp[/"Z4tm奞js1Fc"|7 h.b6ds6ysE/S@nJGr{dOX)j@$ ZZNY, !=M+Ft4ֶ+'~C]z e$) rkNCbŖDqnakC3K/ߘůmQ-kuR|+za:66f봚{$K~˿w TGm bиj T/m^;k~I&x6-mwӓbQcKW7Ɉ%F7|{2.r@NZ x5s$ً5=m?al,D,F~#Adf#h[&M(E3PQ4F:eJdalxTM͠|gfd.ńf\OSllFNfL,=*:U71>Jg5p[N{cy1I"+!xiRmTO84bQPOuەin%Ѱ2PDdvAZz{ʹ!T'z4736^4i<Hvj.)?1r*lfWfK *ź\ŀ5Z qhl*ug8bngͧu*qNV;-Ngfd9TuUKBamȋ+gg`?jM?vq|j3$nCdP$߉֘0d͕MS=I.R岉[Y'! I8mM,}?duY1Brt-k2%dE).bpĭ..FU4lhr˭E}@~FjzM͸M 8bN*|6C3>Jbsy5Jb} GȷSןsK`eb>` n%-IPt3כ̨4kvfc *Q Y b=UܶNvƩ׷L R,{vN7ݰ:.YG4DW(J:9EhFygYՍb +k}a; 4|: V;3^> x୴y#v6֪g$9%fvRпZN@jI{A{,lu,YC[;U$6PI@QgV*蓠IZe#RrZR n "JTY_}*^?(w EIzlIK3'g\ʲi:ktk!7ə8Y WS9:d-s-z| =@\^j hp_خ y;7YY=TeadnJO9`5گ={ "lyl~'{֥Bgeu']o+ʯ~xc~lJ*\olNƕ,׸prk!fXr-b,i Q;ȹf6 7 O;2+ƀ|'ʦ%y;*@! Bl-/D f{ȧ7}ay.B`gpmy7Icۅ־I@,fuኯlK )saPOs k^Ob [sLYv_iHh1$(A|bǨ{YӵUVbY]p`SqjBA;JXx8!=d6Ňld v,M:V7ۢڏʣƓndj(6R&7~{T^tX)~Xp(_~)X*2Fid :!ՠb]iF:0[:BoԽ[eqGUeԌL 52i <;]=Nb^ʶ˽`guJ:xg窤bm>Pc)uu&VWr@t=KؚunܼICyEC'=mYgkV5XWN `GUW$vbx`%)'hW =V҄l +E.mҹgC@UAV59)smHw4j0_A~ Cd$| >/:y4f$MŊ!)j (ŪIH{`7v(+B!5;׽3cOp~ͬ6ی9\:mv^c+X,7 o7v |ǁ#xL/>`)3H,CLnFLCkSšLoϹ)݀s͘r&LFJu} E;70\xrb]?G~_%f--I{EYmQ4Cu'RRbvŎVgJ ?r&D0d)r34'lsu1~[`."ur{FtBl̆p72|l{6!u1feaJV ygvԦ3 Or)93  8ՑH #^[^ vbh}X]vP).UJ ]51ih.mRSԁ"8XZLvE,hٌzEldf1 XYȒtUpB9ئҎlk\K RaUU\d 쪨3C6NjVW1'fmXknկҸJh6@5 ֜䓼Si5GjN};%Maz-!@4۪d7q>OrKk&)e~"ʋ&|_˯ 7Pda~DPmOey&5; "؛u9I%r>ʢqs+K]9#9*bzRf 2\䞬-[foۓt#bpEb33`bqc3'cXnLm,ev3pmæ2޾K@k4iFRjІe8?L_򳭮9+dm]Ҙb,7w/>Ě!c8PME\ؓ̋ |1&$ ɘr a XW*!dӈ^b%yۥf@Y!]Kcqs%bUfD$l3}zd\h,va9,jflg%rCu@-H!@ j#XW>iO :iP#aN6]¦}Vk{׭URPxG["&j^Pyqt[;,2 IDAT{,:l+h XA@ TU`6} (MS&ȐMW4&ZdIyV1hNqu*K@vˀ.o?PWYUe)!(滲7o/K ahRҙœAuL d>`grKEnNYqzl3ҨQ?D4=٤oe,KpMs=Ibvc i4^<#-mOQw;U{V^a_I>&ҿC865x89{Hs<"Y&õɫg*] "J{֛z0J_1# RiUfX哱7 ~ ]Q([4_$V$bh5iWHwȔ"VQ*IĤPo}K96vb;ѪNWJ/j5g@LXXU }&̖d kuVj& ڋv shJy3}9IƃV31v'+"lHlUK&Ok#+ ̨LVRRY;rfLcp[#Ğ4]m`!0&mwWi<Ȩ #c1޾f[$ 1Ġmƕr=qO1倰rL3ho&60mn_QOhe5rXRg:|77#g < ]۵Ӌf`RANl FMOv{GUghF2ҫx0r*vV]NrW.JP^kضtke-2yjGXzIl-u$BntLMuRV0c^S}6^)/RzL%Cxܶ3-vTDX@ďe$Ť44o&6ڣ `?Êk.ı<%;? *?ΛճLP3T!a\C?}T!%(i j'F&hCHG&(Xn% y<,؛A'!ѬiX. v܄UfCϋB.MmK=+J)L9d 6[v1QA✤ dq5c^ ; bg755 }뀲m f{ט BU"쯰$˻Z;}mݖ8S u}ؒvsxyv:Hu7\ߖ!DG~] [$3)9߱P{t]kkۡ=ǩEVF&οo/GNLJWrN^d#M*UO2&XPԵ8SkRbھ`2mf[ h؃ DbM\'"bogZDV][@O_ mRo!,H9=-!j՚˃u]YfĤn{.@[Q`o!}貄1&$6!=Tb Ca b0l hX({3];Xb4e) NuI-6;6iQE4Bk钍ykNJe T1ڴEahP͜AbOSUHu1wCDtJ9OԨu@d +hfA+Kr{.]}и{eזyȅob̪J膆r@jm= #cFB :b>4YQ"fͲQX]~j$j>e~.Ҍ:j\*h?]>VX1}Y߲e^o# lߐS6'ZIʏZe@W'.@NVᔖ%*A<~R6VjȚ_Ŏ+j/8G<Ӛ_L~.f ]O=պb9:Q* &R\5o'xM+,ᧀf/#-׍|]IW~|ӍJsA\>eǵ2u2P4GcwJ~>ׁ={;5'2YYjzFZ[ Zv_jԤ)Ԛq|S2iظ ,I̭{/ظKEj UҢw C\Ϳ_/7;>O[%W%Ű;{T6SC%Knꚶ/%#ddT%)nycBܹ:##ap){.Ɯ^o[˝۪1ў%ktyV־:\ / ĶfـTCS҇h/WܘM،q2 c6tM$ YSlh0S`mepJ*m:p߰xhv$|M+ @$v7pW{6 " LUcbӖLm,-z+bIZ "?UgaZ (P.f:40] ,a6S0ycdP6Q\$sABA5f| غȮȚ[EE H.y+V[۴/eb7=LBJ'| [M;"zA;pi@YA)]/Yyf/(05y47QAIU96uyZ!|[kŅSY`R/j;:xS&=e4w`:]ѳLb)KW;aXsG?*?h9LyTag^cbD~uֵ&0Li-d3ɖ335˔:Am)XUn /Xd*@N֢ء@/XOBixunWj :}a jNqz-D"u~?_2*zگ?t5#G:[}8 Sx&|Ą bsh_? wiI2DVa=$Dž͋q%6&k/ҿioA^GOg7E[*6vI߉j3 n*l* "\^R`,.xߟdhwM6Kc:d*"m<=S-wUetHd(mcc{vL3_"QZ`$Qf击ҵ1zEvl3*-Em6 [CZK9Rt`VOz> [toFbKd`U)6+=UmY00t4SNG5ehcŞX5,IIm Qljl0(}jJ!v3[WIk'5?ihIU%֞DCRgG };SOb׊U,`Pl{(?WKaZ'!SatX57S t['o&hX6cTcӖEz͋e᳼`[]PsNWVHZLWt *H.B>[Z[1V SAUח,@8Bū*V< NI<_3$ SR'bs !}PvѼw!R޺w t[9"B^~Fj=f[Y51 cֶaM$z-UМ%gM^/* [efΉSlDvKо-_@Qy:$b_fS^/_2y~q)5{ڛftf" v1b/PPi9ۆ(A[ ~οݟoj ͹qwg+O"xlZ#U p;e[SXŎEsD9 9Y cqqLOn Y׎HiL,%O V~~OU2as`۷غCmv-$GZSzTjTЙr6ɞ3uJ zl_}غ1sA Ger_^t&A &˛g'8sr庤ZS>ONJ.7><|}hgQ8جrvHIR{S(0gIR?yүr;X!0 YWV{~o~o|9|#$#ƈɣ3匧 "U>dCx79՝rg7o ew>sE(/({V#X{R͍*$3$`PY2,XeR葦_]Ww"m^l{|2 {ݮ{.76sXrF8LM9L>`ҞA6p1,DyV+3 ͮ\qqbX]HޔMͲ{E8D3c*KB:LipGIuU!g> *̧dgRzOU uYH۬y8,W[H)IjI6(:yiP /@v+sJ. ϡU;d ]e԰>3Sĭ5LpYggyl,'IoӧuQo{m:ꙔKU(;q~sKY Ϙ*¢ь~$ t—zfz1J 連e3nh4@\Izϴa &gdF ˹Zyvfzf@ N[&w3*h**+0KԅEZsAӾYؒ|T8Tٓ;TZ˜̬JK$i:n6uG>ӑYXy_ |Z|}~|{]oek3և~v]maTFQFek_luow'};Cjbwv~*Hkq^l3~x7'_qIZ狇yk7擋Ŏ/a,WdɜK),o_"yͻ7~oal|`pY Ov\.6/lOWbǃ+obk1.(KN&d79؟`1eɬMJ`xji:]B ؂unX4]p`Xk-G)\°݇NUwI$ؤFύ;UZ܊L`)΅rE!VX 6SFIe{nHylQ Y&"OtJڡeϖLlJ+VWnR3)l`((fTm-J483S)(o\_Z t.)%.bɣ>/eVz'T|.?AT6 bJz2*] ["YJx(zS* -^`.yYt6,f54c.96. 5F8,p>eKŎ~t{Z-SNyyW$"sDAdC+%͒ 3*upDEk^4Yй"WWYTB<ƨ5܀8vHS?{-wŠ+aAI~L㉋"Rع5p>Y9U_nȊ H$"`yyڧu5<LuP1gEdV~ri\e3`JWw5 N {qѵLM w=;6뛹(kuW=@vk'XłkV8)PEw#KTƱWi{GbU`s2 %"EN7~/7_} NJ*leQț9/u 5!̝E<H2 WĘUm_ךA(Jr]XnXOꭨI^Ͳ|f]AU9{0 py`G*pl*RIoWŢR7+((&T >V@hge:׀Zڴ2F9q,~;Q!]_ƑxF-;Q Cřpig0LRUF3^vyM ݺ*SRڗ)x*@[(0P@ |8sȹD@"͂V3qyf IDATFk7zmqߏZWZI^aT&98_C>~w&]~ߑyf% pV>nnO1l0yp`FV0/S3%3CcCsO\=^]c̒a~ ]]c;Ӥ]V WJ#uz:I&X~UC&j>c1zDQ%C;^PS]3 lKMtfǡ:T8e5?l̀=}#QݫCR6*p̊uPXk7ݝMӡۭܴع0[qAUyj{Ucm0ߒS3* m>EȧSVai0 0:h"*ltuY=> oD(k2!8KtJI^~|/z xl "˒˼XB#k˦_zu CSkVٝ2n@*l{)Kbfo2E(Z)Y^{š_̳W]z#"hG\i3ͧLCFՍhY; ':-Nj/YFcl7̪8T(R7IJWFNQ᪅!ram$V '][gu Pm{15KY K/tiT'뽦D景ϴke{Zc~Ps=n;1:`h/rzt#D19567k͑&߂X^xcŦҟ+G~o?+?ʜ촌 C= xXlEp~{`9uHfhn\^q=+ۈy,p|`d،5bOxS{yAݺrd'J!jYk3, Ɍ7/qR E`ZwB.]ZMQ J:D=Ydl|q_:JR:3ܧ)$mV*{6䬖{~a3X,*2_Q{TRbAb^-1])+0 $_\c۟q&/LYˑDVUh"QGe&";vH/Hfdɵr@9HDZJ{7ҊbsuN̲IXƶUCϢqIugF&_ ̭!+zDi{J^5 esRP:v >ľ~uJw9tځ=bER˯/R AF_'I&sd33L*fNnԡ;H+M3PMzэ$)f!榉dc`)[/rH116z޶^%K`*S@J8]0ܻHFmE~5b`vbbk}GSnjB$JPp bkd(fpE8 $hRmdElHv<tw;ruj>қМ07l4DE@5g [zon7/n^|ϋϖ k^dEtcu^[Yt[!/ȵױ l ipAqx86wF~Ͽw`CRne+COZo3.ĵՀC} MIrI7u|r2ƥ7k14'gtIfwyl1j;>G'%>M- 1SRmouø< S;16jb` KgSWƺUiG7lǥN;`>uSDǭhE\|7WVЙzo‡ZD;%=m\*M#ds汪}/WEZqIb_om0Qmm&cXujjh*EK&1S li[zYK̋Po񛪎3k7݉|{`:(]Ar)=*WQ0#m3s0#w ]sVg{b\mR9T:bL +`jS R%("խ{,Cxg7P#"ݭ}R 6-hܠD>ȯTY(au v 3AR[) In<]]~ZjYSQ)>uhN.FTh՗%˂*;5[ߗL\Nä:J-D  Qo=պd*޻xJs4ί{OEJ$G DuV"kؑOpZ}'XZM)4*~I-I\VR pИbckS66eOOYٕfLgJɏY>n0y{ DuXnWAx9q@G5D'k{PwItj]nBZ#n3+T^ӳ\rqWqg: UTXvtWpڡq@\Y5%͡il`,; ĞW1Wcʟ}1Pv̛?*#t@#S|ɚ pypc}0csbYcrSu-6nI3cb%_NI5{?dUs& LS;"o$OK,+3LTg6wµQW PXV Ѭ ʟܳ #eBbFoVc'A6+54^xb{}j |#t]*a'kpll*i!FU yv@ N 2oMgL+eO{9lM%`>:?u,ڜ'6d^ίna@ٮ]4ZlpaIjPi)bDv`M U"0඀-§J'm]Äi|iJ(: bXJzF ɑB`pkJq6{l/%MUIZMfI\,,f*j>5UCc0 S( Fq=5a&gb*+ M;e=_ҖW"ڬ{J%߯էT鐍UhՖq. Ԭzk2qiyȭ`\E2Ȳl,U-G:Y4F 1_|#1~K~ݢu3TQ:cV:٬M@ ,`nЌ<#@FfI)Pf,)P=SdC4ARϤ"4sUY*ƵC/҃FY}?whAmo`'6yXT`j83rRc<@g;b\ ctuE,{_xneLc.XʸNDnAiHԺz'1웅q/dB&#ERm 9{Le~ }__%DnEfzn7˝t',27wylzK V*}egΖ `gFWa2V6L{c]BV ˁ=e'vɯV~+^׾M`۶lLBs6нZS3*|(]U;;xfyĔJISEB,F 깭B%Nʗl;ۑTR KK*V,n}ɯڿ*Xbr u@e {p U:n$D͑abׇ nq*t'.^3[@+@>_4] ~,"GF50>2NpذNJ#il/??KW~_yA#묉x( RqZbg.%mweYe͉Akul[={5(@ I٭N# *%^w Ol,nHu 62yM/l'U cPk>Mvo=uJ{Ҫw2V9Ϗ57?}c0s2 b$_s*уI@=&ToeLna:T QČ$V'uMNw:c1RKP#SmO n7dLE@vfo ~G_% 32ڞJYIrCݝ+z1O`m"Al1ʷ `f :Ɋ |lY q1#[Ȏ a!Ո*&b=ֳ[)ߙ\;GKsG"+So"uPql5+T*h'`6{[] AGY Nد4y"T٠)$˖PIH`+oaᐾ(eT4\.`Q|>7IE g@\m,ΡUhvf35h z ]1J*l*US 3Sv9b:ם@fg+)4A lqXQMEW?}8K%o]iv15|*'5Z &RUi:SQԮ& K~)w{= f]z;C' A57ex 4 Wݗ4%3@]Ugᝥ}$+PyppNQ{uiZ5 *Y~J/B`Ə_WƟ|s~~3Qzݖjٯ1/ewv__\ WW07KN#QYD%c(3TP ,$ GJ?g(ƹ&nb"O/jͶVDhH-U)|f@5|{Vy]t ov.io 3 Մhsm9v TW+ԕkv?:S w `&sB܃RIW!P~'_%CTaO#L+~lN&#a 0a%uKkvՃ.Qx#&{GmM; rY55 L7L2JMa1e뜡QYoYi(qBXbݔP:#r 4>m%. %UBuga!)}'(M 2wg݌2{A(Q7@s,*I߶Rl I7^aue~9tfU4 i+|lXin'Q+MX) 4K֡FM~8'R9dQHbvU(s p+/ie! LjĦ(iA0ʭ Pz}W1mG Lk@Yu$`Arڧѭ5s_5O :*cWtt?ꤦ1Ti] v&yIZz|N7=s㷟7OO_O~;?kD˞g8lmԖQ9:_Ang1' h};?*0<ےPS.1u1^gٮQpM7/uuPx?jol3Z"?=Uқ*k+ڏSkl4 X~j\Sf2+_}9,+Y WArf_~$`J`m'w2iW7Cd;wrM웳c;JZFd MMWm0Y?{~⃘OxNLg hs"?(oLQq Gٌ\8Ȝ<hu mDG)cvp[{7! p rcP~1sS&SZP ]a/Au1L|;m]!KsIXABpl ĕjNs9AD{k~JYE1+)ݥ6A$Vi$J>Ѭk=khF-Jt%Ӵ IDATvQz߈۴/-8J+,UrN=da`hbsN%#O'/l-ӐpY6t{&̑\0 bw8_ hJfkvN el@>*:FbPU >YImts"rKVރ7O"5t3F1 c"j$^4b265Y-X ɶIv٦(Vۖ]ƣ>ob{B#oͬ8+ϋkCDy0\zsзTލU64p^Q%d5:arhmF=l%5]w (55Iq QdB { *~|}_>||c6Y,@)-M:)_&dxCZށK}ea a[%Y}D[$Mނ[ެgٟrqErBV_+y>ik!gI[4w53+Or,1"`$fibq;~y 1%BoGg07q# ތs6ҫ nv;%f,i9Vسm+ٓm6v(g}#<s8٫,9C1`Eia]a3Uchz٦w/ecZȲ][*@յ|K3v#ԡIڭt':4R]jd@ vìxŏĠb<9 Noq668MiPb!F76? [Wv\%%Yav`4 :@ C+58aa9;l32w7S0 X3w@ H \`˓ǁMvibԙjxIjA[Haά&KxN{!f-PyȢR5UE@A'sV`(A"@t N* +^V4`yKWn6/ EAD: y>c;$+.f09[ $zx,۩_u(VAwҮYKPI@;5|sӳS p\҆nfwNjjK%E'FuV"BJ‘^̹j( Q(QQbmEySw1,.$01>~77y5>ܾ"A.c04dJԕɰ&50{i/F}Tm' i%d~z WVEkյŪ+q1@q)G=,Wb< 5 ; luBIyCUNxT?{ Ѳ/5q[+ I،gnߓ-&X 4PI79;6';Ըx3qoD| M.2쭊+)|p"(У4pHapA__$nē:PQ",O" cdyUE#Ge24y]͜DX. y0mar9wf Fݍ Khft@Ki WKUV>f׍k\(LEZ0񣀉?5jXg5ISš D_Cv_A;^P%QJK1XL'\ %=,\,{6P 573\&$DZ_ @ }K^5VIEH+]@YpdV8y9~}//|WHgb"TGRV0ӱ1yN"MӚr`jlu[F=dtE/9Lw3k${UE&syăq5 !%$Ƌ)Yд25Wmٚ]Y,(O;É0=d# d6ć^(LsNk [ɶE}Lr:c{I̩.U 6кi[{oȗEZ LlXB)A"X ,j GBoiSl$D^%K GTh+340atޙa^UECɊE:tvmiF6*.+KJXp^ӵS.H1̃F鈧D)̈J)?LW}:&J"5l AD͊V 96+KpiE^Yu֠3v쫲nUoMfK2.gW e|&vO`K/.g 14;[.@?:8sݸ,íCX[JL e% تM>3)0,;g҇vcLUIぃ 7W#{fwo٠7ն[(\Hwu^,hwNXFǢ ;V#Tmb6X㦻J,Uj<$HHkfq)`$^7~o7 98} U~ZS pqP<%3ZdRK獛=Gv(\J{= [i6j *UjUZ=>fJPٺISHԨ%YJvHlЊ$zhڢ 2cHu0;;qSSZEeIt1+oTm}.>}/"Xf Xgh_ gn5kmo7_aegl_ɽED^&p@td0=e<Nb qiYgиDeTQuɀĜsY"j38hڔphfO$Oo^?  ׏_p7o{?.pYC[%]Չ+M{gi ~1^bdp-{/0['QmW0Eܺ\n;Aux2jf' _94ﲜJ(JO=^ 57Qӣo,\'3 YِI&;YwP\lJJc! F`kA cnɚ%Nd7op$vn;sT1d&oŸXdHqh@޳,W$SSc#Y=wxX)al(EK;gםnT/`VzJm Ɯ|a7|S-9 Oⱱ{3KA5:!p U̫6sƪ6e'CY{ɼ@ Ti3y5|_rۼμo;t+18ppX$r5оWȥA8CQj j>Fb@T^&7L/']gV5ƿRb!4jԃk] ,aJvk YsUj`Hs="Tjz|ݚE'N :3.{ZՋ L縥|%ǂGHbɘ^sqA14HcElȓSgcC&㘌qcd `1L|'p,r;# S3&iAv_ }<8Q &fM)y$BL1+_ҲŨр'ʖ-H4oFg4Kut 6 d<2s ҎڦItU`<65<- 1!1.i?9t͏NOa'ds&ֆ1u9z,XNZy&ajZvQ3|SO֖ /[QxQ"y*цQ`Z! ,ݖ۱4eRC#Ff(f1 3Y҈0 mK _ :j]i]gQ=To9VV]ލC!]v;61?|;Wi\q'u]ŒeiVm-־ٜ>O̒@݋nW5o4)Y¨g+'ز n|N0@@m퀡.JZ/3o6_qnЈ4#7r|?ܘ6YYc8T|ow9#մSAACriŰop|Çc^1&_vL~ݝ83ZHo*Qg,KU6JJ9MMTMՙ%+` >{B@Tp+YgW}+u}ެ}L_6!=I=>1-HUPp|$ wܪ;Vf쨩>HTZI^U}/zx%«nL82%J Npd/$PX10[R誁;pr Ƙp&R2J?x 8Ù^ՠ<ޭ/?˪"GhOR]283f89/a܈<5P{ v,W}nƆNYi[ \xU21* Z aba|L}^LSaT**q&CU9>V:Ah'}jxanUJd U%Wp 7ÃS#y 4ҡLCֻSdyϖNP-&v-Oc\ `{z8web<4;n Z@y1Mt 17<.[ȑ"g,N瀊^gñY9:jJ%bz}X%bKF ; HƋ3CW~+??_~ ~闋7Y;(v0y_ IDATw# B;X~Jba+>T\N^*YBs?xjNtg5DJ&\3qU7+BU; JB\:IdU\:ӿ:;##}'ߪn ]FW%vYn28Z?ZYoBǵwa(q*^a}~#ݵg@tF(~mʊ$b@}TjB7TtEPES{zb1s7W$ik:WsLWCMd"CNt00$ɣ==}t.a%'& blG/`YaM UW&:$C&]^2H{\~B28`]@ьYe d&yjڀ;G*bJRMf$^m*ۦ4b\|TDp<3jLӜiے̲fQY%-9,84v2ql씮(}n)GHnaɼI Q@@)fA O!HHkl$Ix*"_l[١1x}`c6^&P~FTwNZ[^,HY(OwWJ"2%8AǦJ60;Wqcy |>~Sn_1< '|zf-O@ST`8I^*+Q+؎z{9:z}WݰZWUAk~m^}bؖcA 5IF =(EpԭeJH+ Y 2OJLiw\!y~r%'&|r<|Q6s'5MuGIRdN-回b 3u2,(RȘ{qX ~Җ,sI,;#群X9oohJb d=xh()U "-qW,,j``64+ Qkl)TT n ҽ>50$6.RE0=fHT7\ǥuv7{N*0U˓)! H PVMv.z(ОQRW<ANNjM _xa(%ʱgnF.97,탋 6$?}6zYO&ȷuq`Vf"Ve Mbuk=[x_''W1C o|ݟpH7YR*+t+H"k򙎲J# 00R%z1 z g9o7nۚw.^n?3YjAɠ_h n3QəWl>H.:c̡lddTOPս'Q%)T#,wWU|«຃ o 3~a^ep#3b@kOCߒLalvн;5#X{|n'a-/rƢ5خLZ[ Y=8d0I2owr U( 2>vgP>ÈG);;ۮ/ΎG. KkeagEybӧP+-.ư͉}%k.&0|ks=J3nUo}fdfÌ;CeOvJR,}F(A,YؖL5t(е }UHHZ5#~x:qF>H4WZݣ2.Y3T"JWGM鲅 ra͑u+0`V&#If1nGD׵~; p$=u+LVZ")Yn횐vz7<9.ыJʫ'j6t?Q '&\ZlDO`֠=3asCI%d+J111WLb/bI?OȮUL1M~{!PscEV]4]tTGz: gܿ%׿}bƷp| ^nGoĂm$?Uaٹ5){-/(C:^L}~u\5aJFsb~3SڏxZ -zb]No6 Y`j8w)$daYn.ը$ 7|fs}TVs;-v"MiNcUr"` N˓k);+Ɣ7ܝ9έl[x&C4F -'%XTեL1{.҆|\!9qO''!sØYvU"KP6DM_ӡhȫ w"f%[ͳfWέ)p USYH6vָ T:Ŋ@&XdD@|P,J6)ƐFe-+IͳDUT TʳϢ]=Ǔ5F%@}У͛pa:yIib{R&:. =zfPnOAObf={[!Ks]N0#TYVƼj \],^ȇavT2ZZ_^4֖V>ם8+hF8AHIl!bꚮұ#G {ͨih0hJV lrOηݿ_M~15K~7ßw|?k9o=#gV9!Z7ad'4>DyY 꾄i y;V@xDC4liﶻmp?|&$FnES%r}%<~7#EQYUsW:kjtRΫ!BRbkiȍ'~g0tc g) Tܠ_ e8=88XK}Óo?z!& ̅ %q;`]:ͦq6|aأMP ) 26svד8xg6m ȱ3!fÉkiy(|pO5şi-Fq¹I~}}IaURfh18TU! Tր gP])\٤:$G:⣦^KT C™,Ӽ=ӈ X8$'f u'&!0cKձ7ѯ_@&]4,c{0aä7rUBjVW<,;[6E醠'- ,6mX`5:72bWtDI'4SG9Tp 3 ,{R{_-v:ߕjruiGF>kn;+vg 2 lVo-hVs[Svܔ`?r=yFja(ɲm اS6X=贶5b>pl E7\/.Yu5%|S=oӭ('tMb"]#=Wb{yg%Nh|XI~vA,Pɜ?;ӮZS  Ўr'0Xrz5ç_|/_ ~[#ɏGC>O! # l}e0&0 bW@dM-4SP"ƥ\f]nYH NE-U=7SkuukЇި*Y 9*p)E-k2iB'3{I>%qv;uv\<ϨmG}c &gRX0=^c9DL:X{d셻άYY|Tu#l3CU-Ü摱0{Y+31}I H66P; ׵Wv @B8 gc+ɡ&# L/fb; qTD.?=̰eSp475Zl? @ˍՄ˽ FpSP6饤jqY v*c7emDLɃ@-Z 'b2p b@CV.jIbkUGM$g.7KKwg#bY5AZ3ϩ.Tօ3<7aPץ13kK_ @cB'.ЬUˏh0TGl!hSwFwl5f/||T;h f\tZ⁲'s D5]sKK|n*oYsh{yNO )iD3Z!Ak Ol9Uw67҉A"Q3P u~ǧVc[cfe.Ƙ̸  'Na@Z<>gv P2|:)r\{/bdىJ42::ӠmrѢ0~e~~~/g?#~7?g?|z;5\ gW*@:sk\(9TTCk"}Q 0\i/IAeA̖<ǬWi>\fVccT,i_v(VRJo])1w%QRu椩Ru#4Zk=JLo/p e3eՄQ+φ9<]m|hn"ai>NF:*ذ \,~ˡ^|`5|r3#ǝG<ǐ0bZvsb,Ā?e&VD&QMAoFJroyƝ+ 7.ՍvPoPW7U8zUb$A{x.(}/ `*+}8v̢FK3XR(`";k/D֕Y%gIϒę׷W툑u'C%K]} gK(=Tq7Ksʲ~s|S~V977%刾CIPV$2NʪɖZdS:^QϧU`1;-z39zMwNgh njM˼+փN?]UIYKBULpը?eHG\v]l w dhYuS=k_9!Z6OW\(^rPsp{@, ×ƀe$d<~iQ0qθ>U${ɚ:s(JJ|$M8qoC_; c2=${DŽssAfʍč\efe}}VUd0$w-[UzFL2WegD{ Ԇ+||\i-1,LWDmȪI R ك&05݌5c34>"`Y"LV<`T,6<-)q!ͼ첼HPV4qZ[t7FR 'mU@-1lci=/`z,'ʛ1bU&`wc"}l| H.TQJ"bnm?:[QgODs-vWb^==DA@؟]T/$3ZkK]U xJ+٫lY4h^Tҡ@>#)AEHn]lAe3Ul'Zn%{9ܯ{ܿ~4];8H<ׯLot5zWQF(l$D ~ Tƭ. @7&:JI(yڋqD|fվ IDATf+@kk |}5AIL*)O6% U}bs~4s*)NDG.%;|Og~Oÿ7 篾'_|3_~??缕A[;-Kj2 E(YB"Wۢ*rFʕjSC $S=>buf>SΛ}ezTE5zk=n`1ʕ~uWb%VMtr^GUWe5S^H !53=zQb'X,`qg"i5";6^-ArQ&rzqhudۘq2``!/Q.r,m2Ѥ>66’/w,P&4729'q|hHPF策^`[=7"c xS6?6cqzr0 + cıHbׄqHcxx|rf\IB q{`^%35U'doW <`v^ m&h)+Id!(&>7gi"Flb..cs7t*F J v5DY#C(x5,Xq.Cb 4rUq1ьImVм&EW5Vy۪1b9PG%s"ػ!J0z=-J w9W{nK/|*jLn {#+^c37< О읜o/oͷ==ǯ'w$r|ge/i ȒK\΅ZK6DWʥI+]V's8±WmPj%Dy"3,uYGI6aK-Nc ,Z%Z]گ*4*vT\!ק2t 8HԱYguvrЯesNg;a72&Oa!bN eW#ygiY1`JVAwvbR%yxx"yت~ -qgn"́v= Zf`@J"y+FaϚĆh<g }0 3|}6JEEQ~peOE.@8]cJ#؏:x(㮑{Sl997*+>}.:,T{ :ô!6xwfb~g.m9X1 #읝2G֘9L7N*f89t>,q6^nH[dz<_CMe!8mH_q1\jVf.O:VYJL{j%n0jeP/Q 1.%ssxÃq85"0mE8b@rj Y0;G2+Y>[L6m^2W+t]iI.%uQNQ+XIpj\e^@Y.p&f>F:u-[]>UېweݵnSIg*/~oKEFZ m`(rQc UlmP鲺>u7TF?+ * %Qw;FMښcTf1mzoPIR^g!lsej4ZdhYKU'e D/ݕ6A)Q+Ok ?ŃXo {_+7_U3M(Pg[M;X%%DlTaO`z!1's'!!v91X+{\,Nf-*F j$% %B\`u;.W*xNBػY<59JՓ? A|/B(bL6*B"C JZq']!JU@zԨO`^L69&͇ȟz]g'WZRIG 쾫*}|_< i[/0qcy2H1I*RY۸~2Yo5;p{a>jXjNJ#߂{wcaF>|zw//?}˕ǐq>x!皐Ԇk;>!t1VkF I  SU0{q 霵mWȮq9hBD5Z^]WU `b!o1<.6K*MJ`=ϒJRO'4{0`LF&5>%;k +y\tb()weg& 1y1 omDpp;&vrdy~.r8 xIy M/vsb ܚJ:%|#{6Ĭa (O܉N]ubb2f3q{Ҧ&ǎZdy 1@e+[C11XʻuMk*$lN*//m! 2aim8/inR+9198`IsV' .Ʃ] %' FN}P:YZc"`Um$<$(]Γ@-@I8TdҷZ&3wr-jc`~5~A*_um*TB0SJët[tJʔ`d.W9r\,)ve݅,w[~=^r ljGk?M̬Xx[[K%n/*&;k)X"N}\w"WU u:SWQYzquOi@q?bJ >z.)=JVAl7]I՞T*RZߴ_I Wëk/EIPuZ3Pg5$\e m(F: lkrK|}6N\VHn,=!_®/m_w)3符;U} 5@d`"밵v l36xĦ㑳lr2p;UYS7'<AΒؗz$AyQZ^|u__%}$-e峙TZeGFu{)e}eL\&LeC`1pLr[bl8bKmǪ2w6~^ZSlb_A5CSiےzćJBleUTo^SdC}w(Y'f;MVa&$u{u7oy̎mP FmҲ9vVi"4P^ڤ:|3b<q1ɨ4pSc:_^'_ohԙxA|2Ҽ_iJ%Ik0MV帀CAs9Wh 8$)G7W}g$?ߏ뚌`۔~Ҏf<]dqZ4WwElZpgZޝ^|x2]W0h[jH{CFnjw '>Ꝓ ̞Q*/]λY,+A2ot%',(FT+3h&t_%v" }?=owrX:ɏ_~_x<Ni|jÍc'69D Uz㷇dcWR.-׮ O9:/Y@[$@Qclڇ5UrgpK1-#1\jQjWRqKs/f́ Kڵ/NK- 'Q>OBAQҌcgW\ȏ0l;ޒ,j5%eiIL܋C-KN1U۹'vōj&%˝BHו'XͻQ/5x&wƒsd`̋U@1{Po`q0"<+nJ@;PcNi5 ]:> NsĎTcX "GTǫIhaX A0]tMa9pb1;xc076YBKl-n^TtDWa]`<0;tMy78ܱ51d hKfɘ Ǣ'|Y:Ԥ<}Ҿ5`TLYM65re.3jqd8L=( fm2<m7f&nw5hDJBynF93fK=vZ6O3ɥ_W huPYZfh]S%,GukwfD-x9, mOxTtF5~gQhܝD0Ö:eiOaInihO>y(cH,3ŀwzOH.>;Pl|Z틏wVKn~VQ0n^kN%=@ <6Vb7O3%+ƓPڊ.1YxL@e{3 = ȇ4=9@>?{^wd!0; aSLRJ|1Tn?OuSZ5OlR3}3o!+)T.Vh셭xisR@O9oJh<~Kz `%Vz$`XOS-`k"Op$Q J݂lOY0K^?WZ^͜-ZkdVs8t=D^ȷIT¯F=:znaPRJPTiv0\nWIU=@ÇjMnjYv8+ ϳ&t4 vL$gsD,-+P{! xL=%ÒNN+wY?5 c< &ï`e]3k53 ZB)-rf<ċ h]UZxQYTZ\?0{BİbǺ5t%qIW?ޕ'aK@/;z3'6fJR LZkh&H*/Ƴ d F3O}VQ 'uB`\}`. ^yGf:Go?ց^VZ(1rj4Ιh OTV+:r5h\)g?`0&1o݌1 xUNFO ~ZevUݰd&_XvrY:* <<*Y lwjm8pƘLjg'*fcb!~_kSCzJ,t-cJdŶYW onXԈvhk}_r^n=AdTX۞f~,"2Uf\-rI{d%lkxK|x/t|&۔fy&DŽ-NHjʭ|*; N,_$5[u]u=sɀ(6)A%YL*.*wd\{J¼r7_m@ n}OhX%ħa9n/-7a;7n f5V>ط"yJԘ\\)NQ.+9U6]{ ?a +9dyr!W>X^M;l]B"WcEA}09mr/<,9Z7$wSBJ5;8z"oX9Y6Fکi(ؖс2 9:^0%0üJGO2f v3KSNu>vbn ed{,5B) SϧχTgH{^HzTQHzr$wJ,>Hq ʊ$EdbZߴUd*Z#`96o|ǡ>0;dͳ [0nl7FkM7[bc! ien`϶&o,>X义9{8 sxŚC6d3UCR/ 焥4Ap'ENH/ Z'91BJǒWUZ\cY-ss X`2yV<] |6q:d*5ӹH3jD+M(6 %c*02:#%.]|p r+kܜL#\ebl3 5=&y[BKFcTْҙQA NQ?,-O2$NA^giv#R Mjۨ$3B*CMJ3kX)vnhL[~ 3t!i59Un,x4,ƴA3KS:+Hl)Z N8}u.w \?Ҧv'u`/2ċLҟȐ@@xuMi J0 %4EuUG{b >@{?3\G~3t3Јg5qAUJ)Z.쒱㼀JX-1d-"w% luJKմ4Jy{ͯy/s!rx#Z 2wfx%W7!a'"dsEɊJ=_5[ zhkg_+&KZ’d;V:%Kֶ}teJj޺k>ʆu+e3S4W5.P6 eϺ+:5ViߗMRUDڠq~~Ҋ^8"۪Q[Rta/J"p;/Z ߝ_̥&3W86+<5,N6GD. ![:JX9XelπːZfJXH#Gg`iL@j/). HWL2hV`JggLLv$<<ɊV%c҉{L;Xuȹ9+`Q筂]A'dC@M,|u^e_dƛ*Vבj2+}c׽{Ӛ51 FWQj2-pz1)T"س4v=-5.H ;K$җ>=t#^1NΑ ~ޭ9$[Z3ЯI]6]Ulv>b.*d8K?0+XgTA-V(] ]bvsh`u>T2}G^"ढ़mRAk /,f1JWct ZkXE q[Y6j\D2=R>?2-/O|!_#j$ y 7~7s6eɤEIjzdǃ7n8Ɯ/oXa%VǺ?+ՐfbD m%+K.`Lp,krTEX h"IJxg#F^y@n "+:ixrۗdYbQ]I4$UU ҰWӤvWW;~͸' [,e{2~@J/sOVE@eݙ[s*ɛΞȥNfHȓ3&+y_V#l`-='c龟N6 xd)=8n4]*( ` 5)fȲtㄵ/DnqcqLTbO+eomUZVHSwp:p'G̲ƿRj[X1Q5=KzX|^QkJܸ<{U +돭|CExǒ9[|>JeV`MQrV-%H'yNr1V"5wS@UNנk[UlZeYBl0&RuYYQ [6 $3T=N%Ws*Y8-@$Q#ŀ]v`; )aIBh؃fX;{;۾׏~ί ,O!Pi YA{vڼ}?q`mx%x{k `۰e̛s:7n};9ko"y ҹw2o~.YK't;und~^\GUur^g?P3ŵ9`։]'!Ĥ+vI$ESq%5j(`j;'|ް=9֡>tNyh-y\qǒo8ܒN3X#PCjn2rC eȞR_E~U@e% )?jxռtϰZ;7<)Aಡg'Cȕ.|E__% 5LN."LLfըa.;#{&A3U*%t8dITv\SXyX`[qyUY({[l]Q\rd;_&016cNm唧&[%p ͽR8`a<:Ѝ'K僈,aB$ ;"O&e_Z$ ≮rIl<KM`*(_5y4arXG5'Uy|?;]xs!"Ձ>h.H6yig@STWkчR(Ш {8A Y\.t}UJx]V 3ScYU)P&5yQ"Ji +!jB_Ok:EWǓl?N:uS^OJOX γ: ;$I &~`($ PcE֣QؑjY@BשjM~Zxf/%AStsqƝxkOO}d8ލ Ƹ-|=3^_}Dۗ/Z;r8O:.}Qz(P[ϫչ |֬_Z:]7a֧NTw!46UD%5P薿J3֩UR\*[@qYVyjiPW곛ۦ/Jz %{>+pQy2`jѩOpڷ,1ԼhψKڡjK6@ҍ Y9M1ی'85bTee0#P%9}KIv&W1)+sak}=__%=0G A2N؋N0$iq6P8hLdD90oî|u8Cc>i GGf=oz(V9ʆ(m`39] –,r审 Uإۡi]p8ns@VӁC )[4fJ'dNŎXJwd!kxj){Tؾ3Wr,dL;zg1H'j**b6X0tt hbUMiƪ]VT!'بRAd|UCgS 8 '|~O?|f?#|/?=?{pMoucl=x6Ov3a1WI_ ( ΀llcJ.RfW)}Ut]gRKϵV˭W_Z>v%*9ӻfo@M,'X\4EO}uoXI@!$|T{~<,y90{#1oJ\T>zpf|0F2VxRZ>SÝcpOF6b^|+N,yFAuuvPס\즠*-$1)/Xi[ /iOlRfdG4VbWx){r*@` up̚96MOGwRv]@6 D.I1 zޣl} 0Ytq- 54*2JfY)YC飤L YI&VN0۟;!SC,o8PI# ~{64$a<\-]i>C{=3n!y>$q!b#'^7|0ٟr^cڰms̈CS}tC@(Y?)vˏn*+Rd 4`S3RQ-&G?irEJ:ґ]jCK֊dT}!eWm_kݕ\wGVv]ǬmƲլKʯ>ïg!U6Lf#5F.8{Lc!^|=yȣ~F}yKL'32|ܘ 8<`8DLg7c*dy)MINqX,n S}て8aj;#M.felq([[nd&퉇6r-y2w"aVcAz2rcY8, ?[`;&FVSy97a2r*I8J.6ynFvÖP+kTkqrĮGeb?l + .] _:,×&6E|g`;LHH^~i*YbXQ\gj6ϛJ1 uni m<$? xg/-Sfg1=M~5(j[&1JjtN{PsI*,] N,^Tȣe-AYN/X:njJ+k]S)^F%3UF)lԔ10W`PI2pيفW9<)^*?-]{dة*ٔH_;`5Tt^zYDu5x8Ϻ=!MMS n>eV:1>!򽶺A@9?+K7ޗV7D-kt*kpfZ tg, -tϼ}s䀹?qwURnOcч͒aJձdy5wO-xlYxNURmWl~|ƘTS!sILFҙ{*iL';Siͷuo=uIɥjw\&Ee^@Lb/)Uz.d%+=^5ڼWBȏV=}"G߮ %-1wT2÷<[90K}{WĶt"Dؕ ~r>VB FL"s4q̒/v@`/ Ԙj)A0cN=aFdZ,%M\q5t 4@5lhCgK[ c: s+*shb̜NdoCAF)'Sa LvxsWȞsSk9߈4jJ4S]e.ۧ'5Gm f)& 3DM2Knu1 ٴP9kwJ7!MҶW'>ԮbQti!Ð0QlBf#v@1nTRP%MtC3d&aț#%Gj*[3JªLg(SuXY5}Fn4Gp t@7 T}mWd IDATTLI@>GkS zKuݒP!~YEUNwr߉x#z}ltҠߩ⾉[Il@V0 Dzͪ"|TϾdhnPKK/غ_>^K/Zdi~ jHo^$-_QҤGjщ*u ۰/?=NX3o9NCeɯ%CwUH v|KWu'esW`ZHjX`1GgXծ:{Y3f^+U!a֮{=`@L -@*-2īiojD3ZkYOס+k p g5k}Uɪx7<P3:! ݻMSŨ k\+-"<Mm1򡪡%/yG5+77b%vqﮱ7[l[l8+ q扻̲gв>6G3Xb Ir}}vpcy&`r1i]Ds6":ЫGQAZgDm$>Mn/V»MG$ifՋ1\,/* ole I_:37iQllܜ:Cnf>DN;x<`6ivdl޷Gbk dV4+ӂLn̰%9 NOod?aߔndsa;܁2*ں̤I9;yRDbQw'31 d *u5C9%!~ԈC]N\vx( g%~w'MܥC]N}ZuҁW:!l>ŒubWIEHӜYx4KPωא0LnVrɴd l · ⵅ |`b@>6Rޠ^!ø*jjk>*3 4ӀL)4$}W h<^n *W|t3?9о)Ux`;h/IV,_郲FՖOA&ghfb'}zZX7k&ȯtzNևv]Kja%ՠ50e+@@It MQ7[w uLf=78%# d03+R^Z5]q;0~z￝9~G_ "WgZϏ1nμscEbfTp>`:HgCҋFh[6bfs}Ն@\#!ql8 ZˆD=Y3}[Oxo*^#犈ȭFYw BĬ`,׌q8Zyyg7'|<'+* qQ,־_>6o_yۿW~d~`V 3+nC-mUPX,_Emv V͒`q˙֞Rـ+(Aր.t ք1Ff^׼Ɏw ͆ ;xCVrOWV}I-gȥ9뽿z118xʌa=pVrΓ#͐p/<Ģc=Ccr3Yv< J5:[aqDFr3(۸1 ̔7E\şG,\گQuf0{  _rY`fى؃YټamRU1żQ2$8YRB4!f4%ërHs0#u*llAcm+U W_x:1 _{c"Cl./ԔHjVAeevI75q;N>W kxxQEʢ-#aK K^d:$'{#VAռ,PqU7 3USv7ٙ޳Ws4eA_ WӵYY;"0Hj4YX^ƣXfߏ*>]v2 4!EUm<2-PAb-ѢpNP9yNL!w- 9X1d3 (1=$q6f5h6 YMؓmxֆzfH"2T~wWf,LM ?cj91\!LG,:g7 RD=C*0D҃ e\w޹Ybb|ʲ'L&Ct/J l5$dttla,N^ױ:$_ / -֐A}S)6Fؚ^ٿn@I8tP_ki}+@B:" K_i:贤TS hR>B:N{Uۆbl _lw6/Н5=5:,j\y)NֻS(Iw->t5;py qy54f /7itG,適`)]:yٞ ~ 3h@!xOr]VBHȲ̌R D,wq0du:dN(Mx0\?PfŃ|/bWv!ʅq3l]ZʋܪoI װz^ BEi=j-KԿ[%d r\Wpwe>vm"Li%;@R>y$3Cb53h=N=jX]'Uǽs鷓96 9Wwv<ȐyuB/7̷HΡ&4ݹo8)˴~;GTпspOZC̬H37N=Io2C2t"nY~bl,2 ~H@E;͘)Zؘ K rl4b|f"#1;ɸFmDj:ZRms)ƭi.ؖUWc] - @%ԹjV]./21 l;%aj%饏8!@3 {43E)]tYn ϛbwW1v3Ea#:q9P R4S Y5#ɍ[ḧ́m5m_E.BZ"KUdPJ[+I~TťK 87J#pߊJU(K_+FI%NƉv2Y] 4'MT5hzO@TS鲜4P'bfKCB_Ux袙 bK\Ϙן~N g4mipWʷKQ6J$Bk*[^ |VH^sF`րA J{cWJ٢3R B]P,݅rK9MItWJi 6`dA7Yeg 4l gu+-Ip[tJ__QF[oz7c<]c^ _ $} .II5x̚{Mf L3|tz5ype􋉍r^G%VLĨNh .+HT;_Tq; \]%f2|fr>1*+mI>8&҉=Cl?8psm{sr~$aO1!:( !m A؛Ÿ ՛ T`bvo*3/mC`9Z(ѩ/yYX L7{u+FĐ5 4!Sv_P<'{*x~g` 4(fw3Auobg¸S,a[ŗ@f42eyn?/ pt3j &vs9ē}vV[G٦pc[r3y;p86ɠ|Yl]UiEkˮ j#o?OnW138]c;1O=qSfe,"nLU+\l{x7{'+Kڸ\w70X01*jP- >&{܈rRq=>;% ~`H5!Bn#zG{D LbӇsﳝp?uCZ&a96M>JU΀sSuyvaZndKB8aV5=[ Vl*ar1)Pi~+Zة$af7y pgTwZUNpDv"%1k˔9M鹕r~ЦtӨ&% D|*"f3>$ ⱽ0?Y3Ks#al*J/Dw5̉ۉtOKò uH.JKsWY6vS^`2,ռ];uƜ.tYC@ruZ\6;vF\c8:WJk+oڊZy6[/vdKޡf5`ix7#j_1Ȟ Q5KHk6.ل`]AI*u(Y̋^e+%FԳ(P1' UZw6r$qoe !`\s`Ҟ28Űrs18g?;$$srږoJ8K*7p0F@ܚ v3"oJ F:Wl\cj&.kɎ$g;`G&OC`[nOm0m&AY 莼87٤ݨINbsAMd E``0+!;,VLKr(KR Rʮ2sm0=8-j&X,F+j[tl"O㥃uBޒOu< /``V٫_7&(ahC0$ 1K'?Ή SKc=])S?qg~nlfipE V} \ٶ(ͯ[nť͌+WP6,4bNҙٕ6 +/濳qN*,O`^ԽwJު _XҐAE~|Q?g5QYOoT @c6C\}itpGPإml%hL{0v/ D ԼO`RxQωlz]V[׷+TW˙tfJ~նaʰ@+IoiY,`TzurZ;*`$,;eYFn/?w&iC1RzoQٮ6)MdL}[^~%_(C}4uyCetTPx+ɡ ^ӢQ9*VZfhYUiŪ5 xJ_>Yu`J{6Kv+.hϧO(߱Gb?n/tWeLT§r KGk@r4T4xuSfƲ=άJY-|аK ߰13Ux"!IxT5ߥVoRdC.1΍]YBs,ݶY0Y)ސw!gqˍs9ixIJVnrH릢uqwud ʤG!-87`=НۖPi*Cެ4q㡈3j1gc/,Ҍ420= ssG$qr32 uNСKP=gbB.4]4< ~XJGs<x]Ł5Vt`^뫮q̾n '۬WawH@ 0x`W^l+T}ospA/ى`Oxgi  ;1qbdy)P7jNkMJ c] 7Hc̃yg<8jz)?F&fPwFź ]7@`e(J,{OV,IR]*n F*ǘcr{? _~ł>Ao3<(1RNV1=Ϲx?Qٲ<1X#T-rgǩRVkQݩ~&= xvpĆqcKN8_X@8f 7ḷ(Y%.t:"#xq1Q2471=Ny yKiTUГajj`kX["G_@+wCݳ>-!̫[ٰ̄BUv(U穔i8d JаzprKQ*mWl2rpPn @b611,{b/ rra-&hnŷi0Fg ^;y*C4b7G-Z3-6) I'%*cf;o' _u5J6+"Y UƱ~Vro;_xx;QUiV dg4o߉S[xn 1l $P*jDS" ,_Aez zU~ͤx^{V5_\kܴvz.\Yf/oQde^?)J\f}k fKt':ʘ轻?G~+O"K6<ɭN3 ֻk UOc1rĔ&;H;wIɲ`z`Yr rny9'%||i@9c#Ug;9}`k.5VaD *~6r87r92Y`ƪp:_?$%>O"mmFf̬t6739ц,sk G{Z"$I27՗ڂmNۯ>: oepfɮL:yl"'^.QգQ ڴc?ي QGmebg3 VoPX\{kpLKBe@\8ZFU_z 9ংW3b\z3rR_MdYRH|NS\fNr?$΍7$TQڅshftƖ/쪚 Ξt Nlgydp3u;#GRPWMOҦ:tCiw?g&Z+f! =6c첢6б=Hϡ(U o6p/L^xL<)1BET[y. G1el?q(ٞT2Kc$Q'ScWUqs JBdK+NT!,^Xu3yŮZtOJ1jPL L@w=GʻK`Xdw"@B u:K7FDipͼ:4Q&oJ.SL6ڴUq;nbXAL6Q4|Ww()Z mt_Ldv0_ĥ ͠D/.)/OKU*A X=9!^eBvjkK.FDcW/V.-jKe{]"뮺xII @@T@{CrusmV| ZN bQU[ePﱳ* MᮆAW %o)-&9b)o!'ʦvX:]>>' f8V6ZY*`{Ob}ov#ǔ'MYy|<o;+*4w\i}|k*6;Y˯y&\z#7KD V@vfUk\C]7 W*M!% JK\E=fܒlm ܼ~Vd/_?3#?޸A>y}Zx+Xcq?%Zg.#Y3a$9v0|3ml Hn$;"f1x16wH8lm9_$Kj]$e{[Ge옐ƴ2$X& ǓĘ>? h-ః=l7`3Lש03 >TMb#}19v+baǕ28m2C`jU( V*N|+Ef&2 kb& N6hu bۼ6dw˶1uljoEX`{K+<^VРrm&UqRvg>&#t;OPYETPp`R&}I1%b{Lb1k8!"Ҷ0ʨ u񪴽}*Ԩ U$MtK ng- *dX یIջɦxm@1_X/O ҘjJ35WvES)2%-s[j"֬J6$w ˗-8Xdۻ֢C,\8ՂAYri~DJ贶8D$7 nW/HXo;q.ZFrF{7HQŸFg*LkQ_f9JZUUlk߇\BcVH0-=C rH]נ&s-hkޥU eUՙXIoDT!q{~>M&OV 0qNONrd[^$HYO"2&p[rIa˜69;䘴912 16"+Ɠ72Nv'ǃa.7wyL<nCDl{3ʇvcS]RИGf*jکf&-G*|h9k,[1',11M?큈`w' (^YwF~3taa:Iq\IGw~H@kXXkD^q˰aFyTXEeEv@uoDYOYYZK-"],;.JgW:䰄 @ir`ϊ{Zmp1QFdm"NVݾHEdk6cYݕ'?;0fm6I#ݕy>C "YjLrQHdᕑ B^ ]F|+yV%Mk+g<-0j;71eQ1s`Q}e]A9C& gjCҔ6+ iPjUnMN9O:*u:ӓ,ƑI4.Pch9m&UB1iS8{!e0/Vm'|fre1u (֫5.`(&1tgf阭~ yF]ڋn0nTRʡ࠯IvWZZTTWOt*q>F'N.rI|\/D&kk%MhC|W>Y ,=>wK\_bo27̡}ղy Ƽx;~r7ׯ}C`4CbZрqI4kD'Jw'׵|/8u{J~= V)j9):?A|](LFy gZcO Y{d#īJ=Ϛ"sɀ؛h3\1%KXDn+PvSRN-#&|s%qHXӦ5jXDO42^b Ps62&DATsɖ`ڌ{k!^Ohk-]1]>ϧ vIX4η>˗G|jfjmITS5ajx='kFo]J&.9Jt-]gj p[;+' :b`4YHD}V`3` oĽ~ Z FGo+$%]1k_ǼCGIְ'y\UM'~#>6w5Ǝwr<O]lWܘ.FsdG7n&zkMPEf }U'ayD+Nxj0vu=CkN^Io[J ~TP~Y{N?`Y|?Z%W#R䗟*F ϾrexM)팙앬86nUxדbvvsN"Tb?dU_ȅE0H{Eoi#Jp]͟bUW?'7nE^-eP]nJ4w'܎O5EoӘp>|o:QWmVfCi0%Ĕ9oY-|˯ޙ`p*yE`s1T#JV#6&Yb{;N;/5wAmRP:'`K϶/f;g62nF|XW0o r5SKރ{!1˥\ŁOF4š9`euM}bY Z]`GQM+gb@. DuWz] LWwҘf^e5<rlmRiHצʫ[l=&W D& e6,{=lj) $P+qXhI6{ [j+v,K_8T8b;QN >Af8LwZwhrO$V q7Tb{#@kzWbS$?gD>]?VsF)ӷ/tn?滄JX7[[ dz׏CϿKIvOMFi c|9z 0KzLsh\wgV>yx?y.'o ;w:׳:Jyk]0J7JhI'?I@(Jk=v 'e1ՃJj3+M>Z5hu ԇZJ6^mbf%)F՚KL 4Y[eUR LuKUЊNڰp_Oɇo!Ƭ!fC+d5x`94܉u1Q`WI#C׶F:_ȇAhHP^T|ಇXl{{CV3`1_"$Q)vMJ2f}#xcINs P?KV2_]ZO# TJƐQ<*僑O|A>yw"v9㔩KԽjC8#/rRڵJ+_˟:T2iVcVSPۚl+)w5:KO~k/KAS`o㬇>`bSud[̪`Tbk5n\dT?a;A;\: d~0y4QL-җ:F 3ɑ:/Jz 5չ$3ۢ} =$u~ ْ[zO4sWC8( ,ǧ5| E"U-&W̶RfQcvD* C;_~~_x}yKZFJBVS\ }ro'cO/ؕx֠L_XVא+HTW1¶`^>.`kգc<߄+6K d,9,D`WuM <ս7K AFR'lԠK@KN8tb@d3c٥ XhnkLSF4~:0_f<R*3=X)ima؋9a0U.0p)nLذ0.`쉶{?JۃiJ9j\1FWq*?z*W{$V9uT[c$~S>拱M@pg;)wQmbVsӴy&68*8^A&\Op5`eb¤Kxf5 7@bNJ fc O`?:«T :B*xНlfd]oJrKNU2,>8:1Pm'$f@tham?|K?jWoPf}&sw&VwZe5}hCoVNH,/Uk,ds/0$`gaDAͣ`y/34 11HY%&JJέ|lݴo~jCƖ d87X1ٺ4],GI.'hgÇq=\T.]us[ň_E =T17 nMzK,4.2,pqMSֱ.3`wƝaR֐c2v(QOic$G+,,}h=44!l;rCrϛ{1wtSZ |+^Z+aj$< -6ߐ4m.܏*&;D!+*QRݽ 9k%͝, sbL. V-J#pz#F%e|N^|o| 80n7]Y@`M>?i42fI+`hT渝AE,$O=cdKw)OV?zH)npRƾٔUc픓hzR \"r#DkX*[ut[j&mbLF HgUY2n[O*w<*f(-ݩ8,O.Ғ K?u^yf Nf5{&]ߕҊՌJF:8땐S7n-y^ %3ϻ 1XB m;S0\7!a Cx+')IB;YP VfOlT?e`9:0uATK9{7*z4@)\l;Zj-c~4YMߪB0|09Uq66)Q ;$__YO6.0M[D%Li2%K"[ŮT#9 ddVu9:NIв[@+TAn zpƤuY3*D=rURθ2j:5Yo(iT=7Q%M(?[-ɩ^@n¬nȧd&>4Ui%_Rpaˏ???!{̛ ҧfƸB{ڬ.qLc`|Y71_q3X.{Bq1|iݐsJjvk;c'6vl_걨80FNm,&nnh=?زy5-;c3w0ax@_YC;>usYiRWYۘغԦ3ñ/z dvs\1Ig7.LÃep9g*0C҂Ͱ2fNfb_g\:LjdX VTVǖG*x@eRiU](j*:X*oE}beY~b{ &3*xP!lgY-n /߉p6IHL [tbRy%Qt_~?˿k sqy 澘=e0&\{v,,XTD"Uq Uq41ol'+OR>bqG,5@[`x2Sse2>45/7W:1'X<پfP7b;O!qɽQwwe0|g,J}K@;,06uG`'ɮQD1ذm9Ŝ2դ3 KHbbT.̞%uL0r1,#rMu|cMkyl(k:UBF%.Ki07IBp+$Cs]rW@B3`Jxk鑄ge (ƕ6ٍNCc|a{2ra!И\ճ' \d֣ (רm(=4_VZh}Dw'RT{Fgobu. ҾF XTC^'5አ8* |}sH8S# IDATmgHݎ+$OV 42*jo3AW×yq{ʽ/AA;J dE%(1d6N*R#H \ioTlEdJɄ%)+ܒa|1κkId34?3Y]YMpaWE&+v 3ɊYw ^Z^K{~e/=T[} Nܼ-Ն*YWV#[rg V%.s$K󲊛|p}3?ᰟ1b:1?`6 | 9񵱱,7"e*u |`c*1Xr[b3ٶb7,OFtm3*$ʇbe_0>x-i/x`~iG}JwU4Xݍx}vWj)U,1I΢6eFU#cHGRo͐:ӭ{i$^ e?2qvA.qq#G-ʇp}-`.yvk,|PVPd{@*(m{@ 'Hsq0H-.=l8.} }ŇB^볉]HhRc;X%嵻8b6([ajVLO0nۚhSs9%ҩ)h=Ju iUרѬeUjEZ3Dg u38#/:H)u7g+`%FPui`q[L"ovyl/ F߫ ;Q"@ y,N V]{TTV w?\Vnb7,I,UPdUs@V?7drȒ1<.UĊ7Uᨓ4ks^\ **gJF!_wr4@XznW);w䖅\ƲX]VʹgJ$` sZ 0ze~#&x-=Ǵ^ ȅz~B%I]a3ہ=L KJD7 v!QuBmfYN) Fj>\@y3yH[x{[!@!יpr.r tn~(; ]:V:cF^XEU$W׊ H6vɈ,vmoQZYS/LyEp Z-vuzkv]ZfR:ӂѮ&. چPmZzIݗ"bX롎dBtxSLf#9^bnkc~㾕;&0KfðU+daLuțC2m*h>6hd$јU J, uM`%ظM3:$nCǵw9!E0GH#(6}وw7<>D&X%34TsB$%;Q]Y xSr͗~֊5ZjU'Uh,PwXʂX  *H 6ÂJ*# gFm:VyGcŴgPvTŰ=nDhs,u#>/LŶg]װ8VUz*N Z(Yl3%OREI*o;%4BҐs]"kֿߚlJ>H]ISϒw@Z&)tII wiIZjaW=Csp=ĄޢvD5s;ti))9|kmZȵAƓe]G 5c/ȹyy.}}|'Xp- [䁑5B~+6yz]#;/>0/"O|r3ط26۔Xr^{dpsIw.mX6تH=HH tk*p9W޹X6d,0TycqhY䬬}[7,l^:lH̫8r羜B_!] C=[j >tvې& L) R6@+{JRhPLKRE~] 2*fv\IAWMT145iBh=quf]tLFsq[d;U˃6򮤡B>|#%/ųãg}aӿZ\5Jy!fGزl KbxHD|eHR yn Hޟ=,#9I {%,zzt{eS&5j,%oY6 %+棖 `\kL5\16QD$ٵ}:;v=u_=+6q- ëbSVIx I8ՠxlZ۩d4G-gu?% 5vا-_ff3UەJ8=ZdqZYc6i=!tߟo,Ԟ'I7zJtd\V0Ɨ_~/?1obFܙnMiiA'C1=C}iC~+f?AfX,5`^\O2ɷȎ A] W=g6G.+ 1e_7tg';oi֓tؚ6o 7b q\wr/+"ov6d4:A}0p$K½/¶rjDHk&!Q]d5\ @w-XI/),vsQ b-xa`͞ӥK]WI.w+v"(O4X@׵,IXU5ggݛGvC:xHЅ骬J!U D۬i͋n }JB1V\jRB(i&P+ʥKᾑV%8l@bylU5^W JX2ϋMEhc}8ok8[ǡZ#*y9\'ޱe 9l]6 BJod ҵݙ_\S<"u)֏Dr+\2SVչk:_T魀zmyFT]ݩ QSo#~=GT[Q'~i8DKVUv?1NO zu.pi<"EK񍨡+ʴԪ[4nb>_?wƸtgMp5g'ofpda囝-1 &+2&Ei8S+0ҵ9c1Ƅ'Jg`-orbm)p71r~Yc_J. rF}1ǘY!Q-ܐӒgLla_dRK]61xo]Z|jCdFѐ5Ɠ KvT*L*=4DァrZ]K)37O߄ bϳav7ąۛ,7X 914mˌfjXi9XeiӂVpnx$-u(Aw1`.(b.F*rbrWm3^*Ac}5 90Sjg4Y>H{\̜-/HgF~M˨! Pm uW'"aU7Afb16e7ñ=)XBuD 'F*S=jZFE!*rfEIy c9~p(IOpvk(>Õ˱i1|*}+X݋ B&=lpX3VˣŊ-Iz(&ՠVGۘ)@r| ^ Yyq@}Jf}SFO ^bMJU $Ë=4!oV@.uNE0 c5 :F꘏QF[nDU-Jw[ۚ`&@gy4u7ivsV!gPP+;kg Ze]UK@)zkUF$KdCZFk},;=Գ2|kAɮlҮjs9dvV _g\, cA|7VUEʪecx;}a~C>/\T^m0Z=dyۘd]'5ܿ5"Zs2.U3X<j3~P3oV8#wn ɲtX/|cۖr#Y۬&E'v]8{jƭlW/H=w%ݹ+aI`X\G؉9:1Fe&0֏F.n|;U'U6N1TYY"` 6}pc쇺SR~AwI'J'6$(MrL3[i&n9}Orkr [|i6-@>UW i#FpgXɯ64@U'2`fUJ"rjF#YLD4m^APʲ٬VT4?I)\ 81/'&Cbn.!kezRr HϺ-P[0 ]Z:_ҶX8](K~:b\ ދ"scy\SҬf3A:^PdKQ8,XCEoHhկ9:(9j|j9HJ_DGxhJYoUYu 7 N8. 2Ɇ+}b~_6L;!~d$ &N#iP"Q#*=DɨN1A[-4S'^gITVz:} `x1""L% uKeb"b_C3; cqQtY&9iK=~vi&"oZkjOx]wcΒВy+IŰGQr(ĮlUyL% !}j`k[w܏vak_kE ` Հ%%ž{WũdZ&r7(Vn$#y%Ŵ\Ʊr&YHĐz~Ye%&JNMJ`??3o/_&Ia>Kɴͱ% =$yc$_ȱHHd!=nB8!|YpO>ƢX7^I$r،[POdg4r/lۄ=/r}E2 =+߰=e(̜'䬵EZ('o}h}?Ўb& 9U^`Wk\O9ʰgNM`3\[=6Dg^YLz1_U/=70xqqio6ϝ&2(MިJj_[xȪFR15 " .Y叼zmМ( 2@WT4!02kplD]S@z%GzUbKail"BCV4+wĈƊ[ifKi)O@C7ܵEs]]+j_oQ]$-H^SZc*_Y0iԦ-1et*JD[͒?YJnܱƹ.US bDFs0KrC^aO[@ͣ԰xfg=;oL6#҇lPNBu݋Q{Bwhrϖbc*`k4pۆiR+%+z}وr-زH^dVhN}l#[n)j<ѮWcK0d`㡱n\U(Ŕj{Ҳ#716K.^*Ӽ!jJǹ HrwHuCU9/jTN? VON@X=w&yaiC[qTdh퐴bF_[A=RV%*;nwI1[ &>.Q+l;-w'ɬ&9`53cT^bqѭ@gѧ|ݏ_Ŀᒝȩ{bIq>.Wۿ̔Xxh{˫޿bqg#?@ ZD|YfTF17˒LZ(ػx oC8tOI$&+^x]P¾O~kOlp~ mɉ)BME1ב}?F1En77d=VBj$S2xlb;Y3Ov&CDB-aDwuBQYC"AYST~%7T`NnފS]֬q }б] fzj[r`UM-Z^SFP[4y6Mٴ%6%6ޫ];^*̢~J ^z @K ڔA P$zٽ]oU)^KM@dk0B}'FTziNk|ge^̠chfP]\ /F4߼ga ge*uӜ?@C&\g1KoR^@brv3QdD%$,z]r&x%5p ܦS̏m;35r1]U2)@&Z-iyuC XF3q"2f IDAT/ ^N})kj[Vz'lXbu@ ~ A|{}J0% `ɜFgKؓ1.%0'*d ^# rsk5{XyɭU\~z:; TZUk^Աba @碤 %S#6 KSETX lj*7+'?kq-p) Ero_~~On_$->jM'UMy3l b'雷xڛx-r ?bKZ]sظ'=4amW~KWb|女ݰֱҌ h4bNWә!;"OvC\C`,F+W?}_s7GHa-sU:3uuO Cb͠=a<^Of$YM*&X8ˎlȋnJ~* T$|X*%a z,1bzD3W'"W}^>Aϋj& 6-yCOoZDv:%z__?,_xn|AeɈsaqw8ʻ5Ch8N}+n+_Nޛa9k>H=I&ܰڽZ"ܔ~\yĨ&`Iɴ[ٜZ[-ޙk5OX`[c~AXs(ءsu OodGVu3K^OhΪlꉍbv1o=4bėF'tN~}>ڍUو9\0bQNMi01ʼnMmr]` 1X۞0`ɾt&SFi̼m̝DRcrqHWwkJk1b("30Vl#[;s˾!)exM\j(Q?R%˹yxfjT0CoPn )+-#ppCN ){]blF#0:;>((u)6ݴw(%+@``\%#]q?7&K4j^{cEDF Z|@?@}Znϳ}>2PWuL$;M0Mr(hW([e1*ۗ)0T1̓kO+ͻEvZKb%Xu,|,Q īd?3+w%V2!N^XVEn5GY_;ݴFMbdVx 9&ca%\d֭% N NjȒdKS;B}vtɓMkdqY/J~9dVLfO5Iv^ f05@ְR>9&9dnzՔA ܴ3Am,tIZiU,xr:wgxJ"dV3?~{?noCT1̐m?mF~pMH~_1`'{mC25Q@&"⋈ >c4srip75QcӱX5`>ox?k`|!I'S-&KuMl%sҎJ-s>͢e/'=ԭtv{ NFKriW 6R/W9Crps'9&*?.^!dA(;5vKj1X4̕QU>w1&QPC_ FCf8U 9rم0MT>mhZx̠; 9)DgpטU&JklD=IAs <-ȔjRgt$L^%cR N6EͅJ | g˩7[nK$9D#ou몮ή}ک0U Q}_ Zws3ULъy6ʓtOڇۻu[Pa19ޅͶzɃ1A.,؁פ䬷$S#x[7K0n= ?ok?{J͢lK}zj,,9dw$ڊY:Aݹ cڅ^/1[Z"WiQUsSxl\7zÛڅ4/ )8 wρ)\$Jhc@{ݺT=J^`B2e[(0iD+̥?/; r3q x>77~?oPL?wg'ޏ0@[;TZ-1w_I~ o[ֱu?@ELWhv.!"w.^';Xwǁ{*y|O?qӀ=95!4gsi1 iޭm`5@U3 F e],n]+bqˍɺm\Fܪ;J`hYA}~N+y=Lw}."i~wԭ;J3Nچ|Kk̵:o _sϷk($;Hl,j -u臠O='$ϡRlo~|_=ʆ=CAt4~3aŨh[+%2kZ: y%J{05ȷTl"[:G5Z,E5~?~O/d!+U5`IРbY)>(_=NkrcpQ8 uXC2:+<\p| F> Er&lq8,)XUgO"hy˝0́l8'RLe%-%YwZº:.^LuG)F|}hg1^\rgMe.h8:ً`ie8gH⸶=:H1+12h ˗? @Ô\eդ(I̢V > 8zZ5:[}Pխ d3 -+ltkɱXg dvN(^qIOa}~½a{x[gj F _#|-?O.$Р >zP;|dWn6&--zn%oY "(6gn[ Aʹb a#~ &h`j- \ww=kWzMo$DKD]o#kyT3& "jVĒǧ҇fdr"颤$Q+;QjeO^]0+-lzܮzp.*oyhfd+2Ah[mgl)9 pQo*pT>9{gu6 Pꙩtn<~ΘQqZzfL9}?[y #]d`vaXf_G_0FLַR| u"ՄOtR2*GQ"I͕80+aef-+<9EDt>uH´*ePH%'8kHg-K38˙@)avQsgů}e~lOV&*\z @A8܋ uvQIz2w&'k?İԠ*T HLgLN&0qN,. H^hu+qQS̿'z&f'*|Ǡʧ\sbɳ ŵd}ĠzV/u-r*/ʟrɁ!IRјY.sVMpHY6EFUs&vn*1~S{1z\\)E3ȡv5>3Ų [򫛽u,n+nP9mP\mQxJ8rqu ҡ,8BpI7#,Ca!Wc<| [/ _\O->F9LEl6t :3 Ƴ$7'jcߴVfzR6юZb|A̶M4GXӝeδ!G 5}m7C<4E,0;`7%c#"rk9ohCid|kA"ĽvA!ͮ٬_T+]:MЩny{6I{#j֧arBwB3cڠvafBfG٢_\2vKf>kPh}d'v}Uֵ>ރCz-]-jk8>;Ǘf[GwL MN̺%tF}YCۨnƉDwG V Z6@,Cä= \øZwunW `맧>V.ue,ݛƒ~qnql 68Ԇhgk6j-X_T^[o ϻ0˿x<L(rQ/k$CdHwC)KE]̢]".WD$3k:wy`Z& z&ץ0# ^$$/ƉWr|P 5;S5H&+/RXH^K@pT\8"tj**7q`]2l]AxW$3yüeF/}Nj #(ofpfWf0e,{""tuZ1 giS> $HM֐1*ɵ`L(嬄ZFD3?~ Fb1uEl[\6p 780E1V3U.N*Yd8Mh$S6);r2Mj/oЇ[UQ*3` ?0ܭܺ[p@a+odנ[ @X&Ș1h[6紆\6bP[Xjy@"ߊ@\Ϸ}kr7,pl('C[Lm--~wmq+={טX- ([m>ul`};Wu~K1 6:/58f w,ru+ `wJn]=A/0mO{5oo净7ۃ/Rel} ZXx]|)I.9o_%<^i4);DbBԲ)3:iX\RiAd 3Y, qTKA}YR$'# C(pIr٪r(}(J7X֤7rC+ $ mpT>,lbH1k}4U)fvRTm芹P,MX^,VYQǨSF.\fq^laiFk'᫉(HPۚnoQ}Lw7a̺G=M_K=DUtAЄFl@Sрf@-e|0_]$M U|Dȭ56>+UL6/]a1J3-n6|=ֿp(w`oTaX\yX{uѲ헶t L ^fPVyHYJ; Ֆl=sKL=TwJ7;]"1 IDAToEvڽl,S 쉏GfGyk&NNjȥpYYW1nb$ٺN DdHfXcyuj_ɢ;r CƴFڲY\["͔mUbwlZo{]M70^⺢~5ݬ~+t>m򮖤~sNFzQZ/"BR/1Zln56?O?|=~bL5C-RqS <<(㌞Wi|E1B2Z?ˍ`K$AE_Pt'v>Yq;sPvlv~jvE. m-J 5Pjc,K)& ?Լ+Wy*QGq~ 'Uڕy1p&K+˻נf]hRӧܶIp Ry l֠ z:tC-4U/ %BM'꩓' `RBr|վVI#<#,cPu\ l <֠IQ|km\ @&n_C/Q::Od0Q|}Vu0drGړ.(b$c:yDE'39)[x8KU&i}} CH<$'1F!FqΊՇ6tE[*GIpt e4)`acQf5?J/Ƞ6ԦSޞ8no͖h%aJ4Js[gq3S4IUXu:nPt?iT;(>xO,I0FZ {/(W Yjv]'Q9"y  *1f:[cS ̮0iG$3>L2c1xh}X@- ؃,W]1g'?C~Yle5y뙘n&9 NצވwtboPں7{~a{t.Y@8vV;clf6{m@}vvmFwby CKp}<~ X {,b@U?bwlXpLYZAߨZ͠ pa&iul͘gKk1wt;mugsřY3}?'ly_ī.t{A 1NӖgu4-^1z%[~xHCOhbu[*ep }ZvyQ\Nj0vUۥCq|_ʯ.W%LAL';<IZẸ6^ >;,"~Sd?n oT]p9kx8tN%(F`zq.e Si(v]f084PuJZ_b{fbb%_t*%ϖ$<NzH뎆>wvU򔍓ʠ^y1I3)=Aqzf&cƫ%ת|w~ lFa.;a/kNErgsEUC^4^mL^i x9uv鄹 Nv1kXtZρݠT]q BMoJ֧x D]}?Ǘ//x='vV-Fɰɴr-jL}")X:uYs3K{MŬ6.Η$y,+?˕fu;f=p);mYȖle:l4p$}I}ai\!8g=oD +( *6Txٚ1Ӱzurk:Gs|o_%-.#"×lJ&,$!HttDƁ{[TR&ddtNYbһRl+y{_Ux'BXĦuMI!Myp/܎Yd Պ[yi/v9ȥ)o+`8c)EE햔KDWڔ=T:聚亦wJ̊X6og=U i-B3*f碘he2r5TPPKu7[{9 gZ2[7P6вNHV,]bql{Zg>- 66jJVh*PT5wETq]`_\5`3q81nuwAa'}@/fik[GE]۴5-]ҺHo}M6|>`jYԶЇl8iHlV{T<#=bmӶ-n4%am_+3"^N J0;xӼ/GJM >eglT|+Ftr ӹ\z uKlҾERqwW#(uukz|/=:xM7t 3t5{Msioikh.yʁob}><ރhֶhr^X#>^.e׹I ϟVซE3x;8:V8QJ["(CĊ [z&¬)̊i\>+yQ96 /Hjj19^_/=OGށ7j:z-)[NK|& 0{0<ᘬf{Y)Ke1vb\;:m+e4 >NmWK [\D؛C.+Fqjf@-Q;td^bc+!).X1ij n)rꑚPCCWJ4$ p׀eT,7->yUQ\۵QiM4% %`ٕ.3j&ޔ^7vq3֌~~@'8Ձt{fB`soVZ2 |퓘p>˷:Rf>Ձ}mj{ig'lՅa9%@HJcm'kR|'{q WۯU7fͳm eV2 Wԥ,İTk~@C:wrV5:L1NU@ $/.26L󁵆VێƝah|u Gk.T>َ-GSdO`u3y׾GwX޷dIO]KE1WfKl}k\=v?zBL~`^O-uZ'O8{ֲuzU7*u7,`1ι%/?<u G$aXRr :ŋ5 KD@&/c r-.=am-/ NB.kp7xFP?xj%fcwfaՋVpcMYZt1z/,\ 9`29y)>tD&&बf _hhގ2w htp ȆIY69j/1BE$5/gM Pڳ>t"f8F]骤%3>w$`ƄZ,d-O>JLTq81f4 41۫7-P`$tun!Uȡbn}nfmA\KD =PD%YC'@u@zP1N.f4'hꀔ|[]6:D s%4۽A&Q-BǙ;eMA tf~.=Ze%&jlnsҎ0㩟'kfYb ^:Osf7Ng().j 6u=eo ֧nX7n3߱N >z OoguzEƁ0=YJٿ%p2 B] Jft>յrٗ-7 0%PS`hx|ָi5hlk+h%Me\_^?bE|(UWT=z^?`'3CC))6#S.H9B,ۺlO)n+f- ACL[4bwǎl(̆dQmf=^ fTxkH}||砣LQO|6س=zk6ՙzjrot'Zm0Q4;uhjbfnnV me , 6߭ }d܀ŧ舮 f jhqj7+[Yvl;%w/Zk~%}9xq{A7J$)+!qk:$EhojBvֵ ,wCHP2Fj =lv˕Vi *`]*nBE^foPuB}kiܿ_))-dN>{1٬{hQzt@g)}l buC`^Rxwh;-Ⳋ9mЀpj.;"*1>~3?wնGưe#xOU_qܝUIY>S\?eU"B&W _A MAUT些$C}%N=V5C)Cr%eǘ6 XdG)bc r1O0 WxM-k} aZ#k\9ZڟǓaVQndb-ᄇ:(MҰXٓY.^z0ԚrdE}w5|Yi*x_u_Gj%?Haia`']Ս,Ia׀Xo('J~z!Kz,-S gʜc%(G&cL. "W2'4?JOfƬz?9L8siq-ޠGO WIM1: ^2jk7d5PHfHJ6[98VHZws-)&u4lRGq-jvN D^uAYnaoglnwVɞzV\X>guho6mko_->y k ߽KJC}=c-gm Gu2 6{ǫrK1v"X2`% ؂g Uje5s-1UbftQ\-Qvhҙ]D@N>3u޲k;V&v!ddDoULhX?(uv_!=,>2yʓ$֒B{0Հ?փ]f# ғhP=ɭUeLbu뚭Oc`\1֠ŹNva.ZS~lA0[EZT{I_~b0E72/`>\?{_GoW?g+ƓOpyQ,y=N|] GAnT\^N·:/R-}C,isA2W:Gϵ(+Eŵ RVpJRq-l\.O{ y;s?VY]X'4ZFQ iF YYrG:g*V! Hv JdpzLy`|O_%aOm[ &O6gK^(ؒwi^d-&̋tg %} ɘxp-PAYB!,VMLfFQY\)6wL1/Cj9A`>h ;RLR'OeưjݡX]%WZǔHE9ay-;Qb%ԱӚňB nY)-YGVOޭ0m:8nͲdǐNy`-17۹Gla&znMoe߃d~~&Yk'{4x_}QpGנ:%k <=]hh<ߠfM(!йjy~:?A{k7Cו urc_va+%a8P \n(9q IDATAL1'Wޯ9a[bh&&2KP6 hIEҶ7X RQ_Pҿ>`~Sð%Yoh77"40÷L ~# Yiff7(( ?O׿?3fk%Ru5zXG[fzq]Er.gjʉR\΃4ctȳ.W<ɰ\_ S4*5;3M.1"q819XI%gr24ܮU ??+<AۃU-/%,}n0N7:ʬg=*|BWm~_7%zL|4 "1{cwr'bw}z&eTv(q>hY\`+uiNuv,u^_]2|?_Z9>.ͻXiΊS㹾F22!^{,$_ A xL0¼5k80I\\I^U<_W^尻2iRAb~h)u@,58C: c?T(0r4@'ݼ/^pVϑ:w h /dzLMǺm$(:ZlDLUUd8FPyHF Hܛ9iT)*KmRúYu+ـ:6%FŀPiX3e=fY' 끒2sUkćS9/˃`Bu3=p$cM&^'6en˟P'~K@[6Yn T,xÍ-pVZtR䲂] 'ڏty2?'g% Vnu+Ϛ2nߥ?#6h {{ܲ~JMio0k2ݙH$3m#sdGx,r;ܑ-q|JA BfJ5U<:zj8Bf{P0ֿ7̱쀘Um`קB*L@v86R%Eھ+zieK׬;-c8MgW: ioF兩?=(=/GǾjK튤oyyϹdG*3y`6 >2=<uY ɴ"kŲy%FrѬV+Jfɯ+\/G9$spDT ͼ:lSjhÊ WXC&ܸpZbZs,FI@s(zcƣ+eAR,&{l~ZN bI4çwsQ59czN͎0Bm f䒅>!Lzb_]8og'Mߕ}yfoTjр-YlЧ! \ڪm9vwĭzpnXޟ` +ANx}JV be WQiw-Lf֖rqġt.z\'Qc)%oͥ='s$xCu!s^l^X-שnVKQ,{u6߷9'tW^$Y.%+0WJa4|nܟfA2}~kN]lG/ZX5خ V)WIefՅ}B )_B:Npf!;!$wb=d_Eɶ|A]Vfd ݡSݪ] vP+Ͽ%o߿x1u=ʪbΤx(*Ofʚ *Xu0s1^ӈ&WjI`r d\W 0Ewl^, 0fv&Yz%fP#HЂŬw&N|ܴsV uwsא;"a:g槮Nw }c^E87bmob©PDepl&lk(UgaC-V=[6ۆ˞uyMZ4 $eX!1va-ҴȎ!|ZG ;{ˢWѕ:e;nԣzJT'Hq̛Ea%y$)3" 1aXeڶ7mo6_ pƖzݬ iVEc/icc87Я\bmś5/F%HA48vVpB+B10ڵ?,)67+ )RmpsZMBj>s4&+%{<}{pR/ڷ>n߯ F'&Skm'6V}zꟻuklU\,Ks./ ;; O-Eou]HT{LE\ C7ڳN3ӿ?1Wf`ꮕ!6Xy2ʔг,B!ap9]|.:'c1)];%6%Ǚ4GqSn cer Ou+ߨB2-uoz]2*CދYbQ%o RZXib,I.0jʻza-鞮 1:lr,5tl5ѝb.4`oZ!@bثH{*0 v fDV66/kfEb[ڔf F:+Vk-N\΁̛YV/ăNhf;=2̽`kqlwd c6f}; ],DDPS9mCEFUKVpD0b0B: t &"4R 5uX&}E0wie:~/ Јo/S})T;[՞Y!رٹnضW@[zeafd7kp3Ċ=-,-/;ߌtrxMskE{$\3t"'qb]v$ N?} Wp?Qf<)}na$%MYqPhK#)?Lr_/Y>Z%z! RZjޔ5`e6æduNԐ*%r9[:@f?wfh6!KYPq`!^e}UPYS2G롬pzh o@WʢżNk5;uMk6zlyn֑ftg6C.0VE5{!!EwrlѶd^ǃ_ǿw= >(;4e%d Nq L_JYaQ0/>O\~*X0hX8N54cs*dE6HPuHcJO |eă5 .ygqIHyˋQNӺ`>2?wEx41ub]-K] F etTgz`Z/GZXK%m/7 h1 D%:;82I͔s7\ +fq7?] X}u++ $'nTawa.Mu߿Յy&甽 ]!l-*8EѶф Z~EbWjp@xw ߩm丨5OԤ p*kƗfX|GZ/WkGҶUVNIU`aƬ ͕A}9cmVY:~)=]PB 91%sL`,"N=Equ&1#s`^x>v02}gKʨU!ZxtثI rW[;7ҏ8IWa>׸fw f+K~?׿_' 'jZk$y5m{3/\@EeIƋU*=IXLl/93 aD~uXº]5wxVL'rJ˟t^Sx8Gט5QTe{1c&vttrYŢ8=iyL<VRӤdlX\?"E THk]- ,UR)! NĈMʖWLY3ٌOWb*R0CV8X\yi#EG"y41>qۊMot$3Fn),l\-},OiuVqN\%<jI dr4\6^d9ΰ|Ta60-!mk3ɴIa$%r tlTf`UfWJ2t";3Xx[b']l5T[#u¬uz oVnǃf3 ߦMk]BVII7ͦg=LP.c4;ePX d鼷VpЖ5|kmkS7 C viFv7ݾ} O'{NxS˵j^{[7Z SFzhfƜhzJXcBbr֒Jյ*O4LRI:n ;e!5ƫuf֛af̵>˸iӛؠ۴y] !tZ*um&C!7oE |J8h=`-f]d.:=9ov.kΖp i[</].]\|xKy+mt~=zb0E+@yгhx7_~K{/8H.^8DdSYICk֘'y-bՃ3!bA~eUR$ut( VY1^_$x^X8+_PJމm.vxazP9ZY0|W8{NWVɋ*lL|//*a|(^/`r+5h&+A'D]|I<㭇r|M: Q8_ȗv.I@kKԪ3䁺<y.𲖳\S=H@m@7#*Y:oK^(,CtX [Hmy* eufܚp˟[]j=uMfmmǭ oz56Rfkrq>~e{6X}<.oSR%0.d&,}8 V?ȥ Mb"dJ[J҆mV*/fEUZ`!a;`m5b%*Veht ֎Ϛ#ǡFz>oE?gE %֛cf,^Ao9"WbrjN޿C`2%i`XSOsi0̈́;fX#W 9sp:#:w?˟ ^;$( ^0[|B2-rW\Y|WQv]Ҋ5F?'9A1b,h 'E. g$ޱXnϋa1+_ Ik@{ E 6Ե=a"lfg-[;z>d@TRVj猐䘃ˌ20ybZH=@r‡^4oxrM, [C2ּj@8Z0Ŭy[U)ˮrN[jCFd!8Z +0318> g4ݠ9Y<(mxdvHѱ *e{(lD8iZPZ YL?ΦJfI^ۦ]Es=,*%öR; A aA1ώ@_y)a>KMTbO#x v0a`+`xTA}p 0m m=c}cq4T{z3uҟkO ]Z>9oHDǝ`U ~K,7{+=v~W|Sv7Tpz܍qS{5 0 U m֝bqml%j_V20r2Y@.J*=Êźu)z1]]o6Vle}&d6]a)\yiMSUlof tL{c`[ ~ I[,TI@KlA]twQN&M+a,߿rM[!e+1B`wPD@f"ee]H5;Y; @z';+Iwct v7Ɵ[x %;7R:bz -oVjéۃ7 lPђ߮c[%Mʍd7+/B} DuwOd&2Cۿ߷Aamz0?%ll17-@@BCSdң%"X}vgWUNM7[t;CrRVeYP~)r)[.y_շ~;xV>j+(uӟ܎}[Vlݕd7Xu伒R(g嶻exOukoLz=Nftsݝ=)uH2/I,.D  7_~%og),'c1 83p $\Q{ ;/e\!b੎hhp2'Y*:>χ%?a K>5qPh݈.K}0KJuf!={,Y=Z^Ӛ+-;j٨&zn0ֻ#pRb)mRrpi%(j.G䵨8.uXVxi7 h〵\,b`0']7lζ NDם++]ВJaﲳ 4om -NuSU}ҲtV셹R( ,^MgPQp}6z-G88P !kۻ.pf d]A3gv|Vx-lcjԢk0) { ʸb¹LLۮk *3`@]ݚ=熖hʢ7 #6t}<'@(@߭?{;l@0G6JA6>o%] 3Rh'kYOblp;%T d.p7japM  tf)ܘ~V  g1ZϊfϞ f^z$fp ~-S+l5©mʕl V#ec4JZ2qF[?fD %.Dl^Om9зսSl@>boRk;ր<(_)x^q  H/7y~sKi >ԛyq~\U֟YN>woi= j9C Tn[y@0-mI.*%SNn[ u?^#?7_w?z8^T 3{@= c$=_Y~H2bYL83,;E؂|e㯅q PnAjÇ8,Vˬ̊+_(T/"깗e/p|s Y4V+]'Qѫϧ5[&Jl̩:`Y`^8⭨!h=<@nF¢𩁣֘mQcKYM,{Ik3(NF[H!}a`ɣps &eRLM-$*lTl{aIT:\A܁^W sMs im/Ifd7Sl?P JmQ0,(9F݃ZE.bsijaKb Ln CL=?̛Z`j O @-@vFH [[+@Q hnw{hEoyɐ 8T8 nqm$q[q{OOYȻUT`{:#4GI(v˷ϱ͜u[sM4vn3[hAٺ?[{-6xiK.C9r? _9AE^Pg #fǖ4(hgJ2+a,u| =;ٯWǶ Ml9 pIikJ:OΏY=6\km5=ޛ(]]ҹ"!T Pv,jD#c}_Ͽw_Qq \bJI惲\5϶ۚl7aƪvz4pYrzysv I'1\V5nR>旻ZvqEAQA]g/wʂXsZ$jÎ Zw^2uc+/Z#*w. Sgv) *ҦH\DABvbXX-Pgs?w<ı|HF g-+bS5dT0\i~uewg^G$s1`:僁ŴB s xfİAZ|rkQHRR*.2Ib|AWb5-::K͞ƨҰօnfo{IJ)PNr[X'&60ҶExZtn&_}j S'wa&q}}7MISBz~u? xﵾ.j(VIÝZg n9ɚ Pa[}=F33ZG  W7G{V:>.޿^NԭwRږ<߁Yα;v4񾉻n,l ;j7 5 X;*xOTi}˟/?׷7>94aj$ ꜬP!o0 nbsڪBώ )fLt &yoip zNׂ#` u]syhQvښeq#[%b#NzH^2[bs-A. #O.הz4M'\5cyozX&/AbeM8ZyA^24XvN7||`iؕu[ݍZ=.b 1V#XrȮw$! SKX<WKM `8QdGRarcX,Aʎ)*ZB͞dYnQ͊BmSdVdurWA_t*]mCո%tkѰ`j^8s)mRiq`gͨ ,n–8*x O PXlh2Z5{mWqTnzn$#).#(6ZM6C/;ESچldߔ~ڟN9D j6MݠjͰ񏾴Yqp7-pvs}l 6Y6GL`[sEd;*LtM@y2l[~vKNt,Y1sL'onO0ڥʖ94cޚ]@hbۺ&-'Pș.*YNKdl0| tnpa=rQZ|}o?[nyk[ϜAc&*􌂊O(DF 8?pCs\G]J'YGl@{+7y%5}RZW:X?3s6,QǢꭵ Xc_7bi?~a@a6uo=bPWXM2Pa9G8 rHWx4]zܘySu;l0S:Zp d|a,O|a[}/ć\?D ?miʋvf&2?낀E92UF'37 h5ԃVZg _X ht[>}^/jknyꯧ\ڤpR5zpJN1#b/ml.ncx ZJ:H3FS bp5ײX坾%2JfE6YU-]!;{2;C mnU IDAT?4$J.4vS2 L`q<61H$ݠv̡̰p I`cJQؼ4Y]b-bWYj^{Hf7\@NiW5hW֫4:%&>.|jFAB`SZ҃M#jZ?jp6h U׬EOpxϖ+Te$nMFWK"6XT@@!ip׬坼Uƶ6~nXz>WϞs3ma[ɍ=F} O3}mMݍ.FtfDUF=%nPwO&78mIbvD[ߩ=;K9֫(B6[}\nNńT`b<{Щ>Lf #P̭#nP4$_7WPBl= Ge5Ow~\MK+b+_Z[b@A;[>OESd >+u+OM2Or,}4ZxF`Op~?ӗWgE02GF/J^u;eTvPZ>8w4JGE0lE0e\3ޱ xÝYW[ui7%sF2z!r}D&4{EC٥4.| *$rurV^ #oɶ%ڊqtB }o(bF;b BAnbcZTLHg ~rMo*.RzCf"Ur( Ĥg-E,z0gWj!՞W/ܪgFյ^` @DXLz];1&GdxV3i:G\4"Ǘv#aɲjšBvq8 =dgT'ƨGq 0_lɩ8Xp>DڸL} vblc~oZ$WOq,v5XprVQ܌t_Kn -}i0`f3`fےɰ0Pn8mզZ@ٴ6:JOF6Rr7X&zlb2y7x~ݢ`Оmٞfo@Ci)e-pՈOl.ÊuK<>:w塍_.De.﵋ y/'[S_`R<lC~*ڶ ۺO`65{k&㝯dپwńcxptiBC۱.@m gYCA.rٺ-:!?3?Ͽ7 پz?a&$gT(|ҌSKENS1i+&nꅇU|GDO"YoLIq8q8uɓ/ŭS h64 I;P oU=dυfH'dg3ꡍGWO7-!Dl~YAyIyZ"\Z, X?xZZE<[Cԙ0jt-0V\*ۦI71nە6tSĶLZdZ> [ިUgAX1k# 6x6P4f9:EZlvrqMt/pD@k؃UhzC?4%v;, ah-S'ޠeB͆)pArx`A_bNiȚ%~JV&vq*XpI-cVK{glw1ftٍe:' p[~O=εcmr XHc)q Oi8o0~Z#l_u?G˚|e|~۾B;}*{Z>%^ɽT: pŖ->~e]||6e d[/V*ܺ lVAmڌ-XlS!|ۆɹ}-J/3 f]oḿ`Bj ?e +y??:O|'5ü65=?w??/%xxut{Ze$AEi?5>>,ti 6 F)`7!y'^~1;p@Crv,Щ_?>_Ҵyl"Ai׸˟CZtݥ΂qRM< B7ݿۺ TkSIW=knK2SwZ.Z,vl{o](c*pmQaZsS*d Ѥ E8%3`HUHu"޺Д{%׎#I\@MTl]Zj3"w+3ȅ<@Ѭafnp}|49يkr;Ծwj?@~%q2? V'cpk7O;~/}M܂`q[ X Ǥ YYLsӬAIng9d r#MM^bakL[u`q2\<O{eFyu0,NLܟL6('Tu(1NE"5\4ˎInaAֲ pV8VMȏNW*M31x,߾cάuXnʟ' hsjW횃ٕSuj\jyf(C>SQ9~S=Swm8p L PLNE'"[X^P=elqw3<9(Cfdjy`yjA8'ɐ* =?z3r%fn9bSԲX.S;L2&YڋT$1%8ʃ# kx2kz4338JjMg1q3#j0M@Y$]xXoČ-ޤ<_⽜C>K0ӱ}Zi_)N2,7ՍӲsqyvVV<1_m-v;7vHIa憨L{h+73ɇcb3T kC N?1H6bA/uBl"$W{`M9}6Ϻխ-3Ȃi4j-b Ilw1~&un`w{\eu&zCN3[)lr &j_0h4Oea.P:Y.-yo]K'R wu%kbj]kT-!X| FD&g >Rw5<Қ LX'5wU .ݛ.۝_ρ7F>gX^$9xG$+XPwOzBgbu\'U.{;Xwؐwy@X{$ԍbً' Kƺ-y%x quIVsZ@ =xAddQ*i`P5բ|"Of)2%}Lձ0ɩ' h)`se׿0[2Nz!9aAo3V L W-ӂ 1%<$B(O,TɊdWlD2ejY ƃw}VQb Eh3ޛϞ0|c\Ɠ\ݺζ)kծ\K(&¤ȶ2|*hp'غl\@p@ [=4"Ӛ95_Hܺmr(=٬:ZJņ Z>P{P{SGԆ|dje=%mY*W/f5.uA[^Aڼif>eP7X[i]yeaqBDKyzWÁyLJ6*'z1jniLfikfwp Q{]{6 d~O m`Ξ]@Rf{zPNK\ f llj81K7Rv, ,s/ިZnBa[7.{܅6ՠcߒn'P,\zɲ rpkp%kkI:~|UҾ_Zj̬$=>-&1xŧ`Rdiֳ2ќVCM .t\_N~{ _/!KX_yu˅h%uLqIk 37hZk7^)c<${Fy(ZyRn/d͜[ӥ¿~gƃ7Fw u#U>G\5(hk1=9kz,#UwP sμ! !dxs1p{:u?;\'I:'=o*7`qU/ =vK:9xK2O1ٝXʵHY.94)Fw["we,77 Z%e҄g6)n-gxqvO18{\uJ֬NתrһLQNZ)a6c%!ܰ#z3ֆE,[X\ R6+4+Pޭ%F]p[jicܐ0l*miMkjLW:<.$ᘟ~PbT:%2^4o[IU:m?ņU)dVug)[o&Y`o|}f|nl;8` / |[Vw^j W?*"_WCGp}Ŋq]ǿ˅NP@Ckņ}!]ᢺ2NC;Y %be\) `q=2 9yx[׿3ÿϿe*jvh 2 wb`nl΅-P6s曮z2,d]/{(ȃ4A;Yyau*, ,oWd;g=x(&LI[7!ڊbwNW+gPvIb)N? [Oy[ɪ#>KciqW1MN@6`O2sͩ[NbIb@@ZM]KLqQjTS&YF"l;ܕp hmlm(;ȥ$b 1gPLnrnҪZtC hIdS٦h*4ͨeM gIWʭ8$O%펉Y\-4IXr6FsG.LyAO‚g5,/A\"~6zbcgK8{3 1;mf<13eOf;Mv*kdsm4F7՜>^܈hQǀ\߫A'A?{o xiZf;QRmZ7 بzmw )> MhtN-V 7ԮB^t h^f9rzf>.m?^5z}nҀnE7gKBCEC_fC{k]׾b EǾJ߉y\멻X@|'jd?\k2mщ_n<|5'_̵}Z [ƯFgZ]wĢ㴥u(iXAWvRJ\|' f=ߡ5}5w~'|܉0N 8YX>s|j۸wn܏ų"'zL-'ɵINl}nýc 3IF.œY7r$O3L:o JykC~6cDS8[7Vn HS9٦".}`XsL٭)RzA8al6K^fgYw̵٭z3lO4U|u( ,WJ;jR>M:s]V,vqIQ`vi}!@ģllĸX0%JYVZ߲m6EisHs2FMRZ[%P fADV\vfWuH^ŝf471v)A 0Іjvzch-kj5wZ{jQWVu4Ǩg]4i| U ݮOhe]U6w7:큨&Y8o h/fߋ}H/fL}]޺9gl/G|ᛙueԾ|n e&a8GO? vFdk*S*.8zPs%۶ngn Fϲ]^Jmg[] r,v5fޛIˀ ɕt5ђ&xr>^Gvџ"6tٍI(:>NcÀ:;"%moѝQu N.wT|Z}__??#_?fŁ]*= yU.cz}iҁE̓7 8G='-L @[:.7Q`ucTw>:9sv'v`k1p,{pfy9JKgVYjS<5ԕYLәqg*+p:9O~xa8+|kWY@-8<ۍI7,U"w'IڰgjI+,zA*,8Jlt 9נC IDATb[t_oLhM 3߀U:Ӧ:٬VL 3K"}n[7L'xl63\x8SiNVΪzf|o3Ha(![K]0&_-HpS彻w@׀ @Z7 spD䚰ޯ뱡5 ].V8o0Ae F4Ofbo&yskS"[u7fWef\hlC&Vb}7p~I%h&P%geCxPL,#uhV=.&7n).x WK=~?ɥXZKP~?0Bu5oϮPGgÈҗn?ܬd>yp%/uo[IjwxAɕ ]zlzر$JސYϯ~ ;$E{%`u|t LGJ>^ `86Sӿh8 >xR)d@=?9Xgc2h6嵥e52% &Л9Jv|M GuwO=Ng6Ȟ e e'kZZwo{ԍP ʎ꿭no d_%3sω`^@FX[~}0^}ӵ-i $dgI@vّ>]@z{_r"kg凪Η¢wr>k5} u,Y:Bek{/燴عP~HG'|Ea-jeo: `e`ُ]$l)n XʋwAwْL-H W|?q+n%kKvhn>fSg+^jO^xALw9?.w rMCГ=hnp[b2vcyB=a36;䒔I|ҵꊾtJYȦ5P.i+ (@6^- m}ngΖVy3FgZwr fϞ9lntT-0ĜhfUāA 604j# ۹S@&]]TF/dD~oBN@uvؖXj`Wk[zÞ\`%hQ}V]lqֵZ*îh9ggt:ל8[}>, ›JUUr["!d3tmWz.zMGjd]uoAU3p,opPdhuk.?eR>g=2oظi61AlMmuXu"j$k[3fkm˺#P[Pu?=k?5s|=oWᛯ~%ё5Y1uR#X, 0>Q#Q3xbԐc `9GiFeU(Ū)ÂypPcޘ0XO䍕@XQ!*Y`Սg)O5Y^1갇RU(ŐnHn;)z>EhHzMNV޶B#a)ԢI U6QH֩baF6 }CR A-g*%l@2J W:qtRNS]u[7z5 ۃҟMk %:$rwjEEp na&3׵!ֆ4轹5YN[VQo4 ًh84ɛڃ: lo^veK_pk`!nT|f֢X5#Yh,, lz(af.>.ՅFs 'dQ٭~}Pۀ:A )k3 ܢ Zβ-Ͷ>~\W!qdKrlf Utݎu`_ߏl?jo6{1&~\B ^WھF=2_n;(͐>R\ T!fwh," LCɗ/SkcZ$<&Ox|&g^b7Pbwнšٵ+%KPol_+#Oj|WbX7UXWg>q_7 /H»+RzPg% I2E?;FT0*c+'kHINz2eb+y^n98/3H I2V&Ee'3Cb– ͜{WJ8}ckPH>{>}`m$L&a0x8{~ 炷CZ&;Dc9ZEʬ80;ERY%Q:V|18&GmYdLR ijYd'RĬ*{` 2Q;}bKyQqZUa:T'Hh_р7D {Jzd0Ҡ#d060?iT\~7jGd/j]Sz$@eIa >La/f, "\åm[dX ftL[ Y-@ : (S~)7H)f ̖d"Z9V{-8E>u/>5QPl-6C %.\oZCٚX BAֻ'?拝w (Qr1k$V7؋Yu:3ڠygK5}m'q}Gӛ2zȱA&:7Bzz1uPnoTXǸlWX*_{w4;GM|݄WGfm/wj3}& ^כjB^@˔Z?*pxLZ~̫^=Ir-2b8>cpa.#UrZAL2*9{O ^RT>?]ln!`J|/?o~- gwOełQP +Gs- Y#k,f:fYK`ӋZp* q U͐ g~R|.,F%5<{S|z-%e7%VCEwNU'k:]X 6Fd'azg8,o$srS޼aTWX f&;QwW20D:۶7I{amQT[d^vIdqyk| F3Ze'73=,j&(SѨdavh Mtt6t 2, aw܋9)(VZ QC@ݒAm¬7ܟpLXʉ`Q5^zVv@=S!; qêb&-ʖ&*Mc^TQ7k),(|&:eDYb *;C|%ZK[%;27uA+CvbZ+uSNS 3)]Zn0Fdd n {>9zk)4#ꍩ=$^-n0ׄ1䖚1U@sY)cQh\H?OX-B ZXobY]0P̞ͮKfSS hͨWk9 zAqpѳ R7K5֠oK,l1C/K ŗƵ[+Q;=H7}s?ZPz6U marV1pwnJtERRz]LW>zc^׻f1T&w ff>x?y|-ʬYRUy`~_v}aꂿI |U`C?ú77yP?X;xX:fhn?;_{~#w1>'r oεh=fa'g-*߰:78`j.#Q^%,bu0J/YzW!\7`6Gyý 6NVp/m0f_5QvZu+,RK/`,2n(0tLb{BKI."u`L Y&Me˽Q߀ΚG1Ǥ,oxLj?X.1[,kPd!O}6cуVcZ_ cb{M+RKmX2ŌW+|k@anwy+#wsܨ,Tlfy=Rx(\MV v2^0Ͳ[ؙzFgGw66(b_ˢvYq0z`ؖJ=Sꡨػ 8g9[y%nfu#cdlMV7+n⏯n.~`_L~fƧh3s0F s l{-  Wξ*3YF_ׅu׵ߒ=8cU7Hp{O{7˜R$IR >Fy>y<9YƎ03*Н)'@\ W_(vV2 |Ė]$)AJlw `,*B?@ fYNsS 1O~//%3߈c xB3#{ *$63Q%^;Vp;<M$b!idBK7`8k>)¢ 2ϜȭY7Fx29| tSF$Aw"UxkXȳ}ꦸ%{ܩuM(wO~ SjAS5DiFR k2H6zw ّع4zG~\ ܟ?O9ThPeّInŪI]۴; 9 Sn,f Eխ$V{99lRSTM5^KbLg^ۛ^Z#C3S9ȕJ&Pj%T;LQ.,%R3i+4ঈCKZh*9k=pn%B76OS A6uH+jgsCR“j  XR1 3% :Z<޼ZUuO*=6q3qȋ:蚂~&:huẒ7zM+>5t<Ҡz1cA~g/V.B?m6_L9*nX%0խ *mI͆{l3de]7~i6l׍ivTTpy}vBf[U/*_ (Te0܏LGZ&ŖY;pq]>U[zm,P+ؚ\^s}w T>Tӹy볋-5f$v>P,gzF닾2۫o}j[}]l5V4WY^ww,qz{r²'5PpBv o|ǟOWqk@ ;ƽ2yi[ yNIѢNO*TgF eYMEqT0VJfZB,D\~3٠ dSdvMs|@,"eO'I5ȼ~q޵΍`吽Z룥o872xNl">aCY X%tHc`]5^T=~(^8:P[?Q†ۜ;Nz^?I@{t,f۽VA9d{JT/1>VGJTgy43!7N3E˥JwvK)/ri]t$6KynLdc`q7^kEm-m-6bߖ;c; 9lؿS.zZZڋCף:gw'\@VB\NǃyJ<Ħ^_Qqiq+"y3"ekW.zG@]C-xɰd#ީ| ;>.Hmw_k~ݷ|  6ԥ=c'3սt#srRCj סN8jɓ Y%Yq⳯QK˙0#X*g:͘o04՛l4ճe}?.ةt@c/ vAkQ^zhbQ)?OMX"u,}h}@t) LvP}GvJ߅e>Ƨ)0 slԹ'$d5j}-$uKXk,՝ 4ke w|g'4*ȟe=Lwbx-zcS: $ɑON )MLNl3Zיk s8sb]kC{WHY<83#D0[*襒~ck&7n'N')yrn@f"/YSCe"܃I~ZEa"3V'x-捸=t&͜!($A90&9sj09;I'I2@O;ΓƱڵT>ՅJJҌ<Yz',N"2^& Q1VN 7(SefvӰ:Xq% +}UeQ`֝0Ii5~E<?5WC.f1jqƀr8%,ћJ7/'iʦƋ1cJJ+vRVɿkŽZH1fS-6m.I8Hf"ۡJ!j>JSmzm6hEbWֆh&Sϔ?5$ph`B %䒮.q3+dQbKWjzu od,l,"괥IlZ >bp-$ϐg{ ?_ \֒jg俀AX o .qa6 6i lf%2o`35ify'm]d4 ~u_jB-n_BCk ƫ.4'zvo.PցǮN-!E 'gx&aV4ml@s*l%}2MQvcuKKFkp8 RJ$%*[\6kg>=4[j5˲1u|7}޸f́&w.Rg-ƒc P<Ӊxփɣz8ĽxS=%#u}vsL’VRp@dSC-I*~jȺmn9XF}j'҂QIƚ@]Gp+0dy[m+3r6vLްa.w1UO8++\1eob|YSn %!Gްp>F\)w!39=^?I@=i>Q&H{Zs)͘.߯S,1jODd"|idN`R@WMr &OcUMZ MZ[Z(rk} t C6J5PX0:"vyL!Qx6_ #aO{^m@%w :f_NiӐK z6hff-?{e[/suyjcZ}ԛϨ} <6g:jW]ൊ}$Ư"d즰j{p=Y)1mvE׳%:Sa$@"zb*՚)c;q|H졆w>Usks>Ks2TxufL6,>IU{IFV!FEb4TAڗZ^j)6L w[Giw>/|ǿ_篸}n^7˯C-a j(wuPqv!Fuf ])RJ2_I2BR5ßDݩc= 9PR \Ɂ= /Ze dNcd2C9xl7! uSF_ t?:2ݑ7nƉ<1ԕX,5,ui.7JFd0_O-ɒƺY3)cz;f"guUs}/j8=U7o֫0?x71.hc q}TMLqcM (> ;Z^.eK}\D0\CFEivr`P7{(<+ Fֳ?l֮V]}/Y{ h[Cހ@BfZCM5"wzA/~5 4ag;x:;v|Ń}0:7ǵbX?I~K v',P؃BP47utb̋, zF|CgHQvp/]x.Jiq̾ _'O. `-c0o~%Oo?G{'ҕ>̙#‹IU=u)ˡ~LQFT}$+I2KkJp`,mb^o8E+Y`ݒD%/ue xg!s>0,ZkzNuf Wh?i<88EuԞɪG'1TDG{g xplCl$31/r8AouGchxsFE9ĞU!k D"2l5sJWiVY/Ë,b(n3Y}3]jeH_[ǯ~H3Sl BPY֠B9~64i;ɾYm6I@(Dua>IbTJnϪ6VE]jf[h2aʸv8ek0[ʭyZB4ۜZm&`T +C%>H[_W{A g4kƬ. 4R:P8V3:evYHLVf6aN%+l]-]mj2&u~ᅦ4^f]D@on[{u{hjY~_z#/^Rdb5-`~&)67ZkX.4 ;9b뱯kӾ`{n\JZ`3 Iݲcc%W>ޕ]zhyAʒQ6%S*3̿a7|Z&Hمʎ-貾ZӼ!JLV/wzA<yǃo~%?ç~|9X$:-f,{g`3F1YS#q(`mQ>帕:5ƬaqK`2uMVj\JfF«ptMlfIiSQ¦[P~vnMpz4.x%sy<禴+.nIђU*_bίbAx3}7vX,"2p'"}HWr#_ZDY/NY+WGB_ hEڧq]S9إY;Q`([fthz5*ͧuIij?LU=Q\`4ɺ0$Ɗ\P 둖gQtwD >:,M` L1A srcM\2.E kS8^2};n+@uiXi:vlKp;=Rmw.ʺ'eb`gMİk9f`wI?4L#s(J$p8r1lF~E6{Mg ܚ`ZxTdQIڹ֌f ߤکV ɛi  &.6!,z0#, hD2{:_pel{@Kujaۺ@?/N]a'eA+B`[o=h?]NSd`Tk~O7>γط?> yWGϱ[7@ܯ{hPi} 06~ፈ7BRVOڍ^[;Tﴂ+ϥ6GtUwZ]myK4=AZ݉(d[+y^\--;jTMR].}TOS)_ƻia:wB˫fs%M>S3/袵+ E㍷~~g_M'8'cqg1[܁ao[`f [쿖+QFT}'G앉6pKyC[IۙWdOdM_;ڌ`Wj;xawn -:. CeF\zϊoOi/\P 53RxK ~DPd\*a@+!m&I>?&GnIklZw}sJ4)=k/,:덽^=7`%)}7۹Ͻ޲ե^>@yuE)n\ʚ7Y.ת용ٮ L':*#j)$=.xNM}hS-N Ī%Wm떕 E-9JLܟdMZEIU 2)5$] 'f5q_Vet4QT^IFJu:W ,S0t6 :#]<4x20JSG>Q3@YEq1=Ҕ%[7a4 :TZ*҂jTp،[G *v `dڃ?d*Z0bx#o7yZkrgnUV 2s3oeG lӝ|`m)Lt<1f]+,W2ֱY7iZ=ejØf_m@/nk̇6z_f67oKDllקt-MFS p8aʆX'W{\T)_ǃ11]?sd\*}_n~:[ 6xM }kZX"7:gg؝ @~|oO~7 D:Sg.|s=gF]:#G)ESu<0o2 OzQyPDop=q0.Iq9S 'GRs#uJ8a\IpRC ̩V/5̖}qcQ.b\p-N`^#yTݞbd~"M{ ^bE!-V^gEŜ'C9E B$19{ I 8y6dI/lxgbzxZJ@;j5HxiU]Z°[9+LUT&GB" &NZ#ZUbݣƦ7#ni&^BQ}̎$,*pMqKݴ 1bix'4]2qs:Lw2JD˧ 8\IyyKsL}kPywCkZCXؒ#TJ l2 +G!w9rؠMh8D4(tPpt`NՓ=Ď7r%CV6Ќ8-Z ÊLiXR|\baڭ J elpa-}ȜTְyϚҙdAwڌ ~ 4}>n>vuBKuhzM?O *]FC7Hϖiv Oﵶm<԰/hԶH57h*aN6PNvV3 Vs~ IDATM)3>ݢR_Z0d Z߹50LP~;Joҿzk%q=[WVGg{'"+-εfVvQ+9=:ǭ]@^7?Zh' iT<$g*c tm_||~~;O~~19#D;Q2%]0F&$|q`-Rң^ʞ605O^OՐǁ}"aYT}̅SZĜLb.2F'CJ4jdz=('YX,< "Y-\!i?&iUTyY'HjWbAٿ0{Q\~#k"ζ e&O&'.fE__%]Lθ Abh UJ9d,4h0ha܄ ebYCzwRgC޼/C]dJC{!Kqu9! eDh5{|_jaKT LP h[S{Xy`d΃.C-[$;,JJZr_(nZRŰ7^ pt QX,H:4%ayfYqv n64,ՅZV-'Af YE :`ZZdMה4DA ׿XmEɦ1:VIz,[uêeu!ﮡ2}w-Af0:h9[3w Amf^CRDa,_XH w"5L{6`0\oٌiĐ 17e+}/+7 e6g{ Ms/97(ۿbl6ɶvDm{z|kW淶UEw}`u-i@iP@xdn<ź+5EwTUK j5zom66_qkC= /X$=Axz݀0Gql8_79ki͍y]L9\c-߳֐2WB|a 9X¡>c;߂kX ¯dHY7)u%u`cR/3bL;>9eMzg6bUuws`1xNAT\Xc rXa 5$|JKZZ&/A!ܰ'g$rHĢ4dg{ *Kq\ˤ`¨YF}Lچ3kH<8?>uJ4U$>uhhw(g2PDIde"CNkX\=5Eϯ2wa8/GCam:!uQ5t{F5z̥T-18zTjaB/G Yb +xmֶck F6 =2&W z uop+1f\bl QDF ߘA,4mCUm.hC զdʰ6%5&5s59gS}$ MOK nVjZ b'uGdַN@n8ڡyytiiS Y>m |5f`YJm ^)A;tw: vq6zihrL@ۺeܥ.>> FS>h^R6oU{7ۗ+@?Q_ SB~/wxm|qv^6t%-MM Hv @k[/kg"*Zfbc]:a!>ް1 @rrfnjl f6vm-1'_6eQ$PLGIGZ5 ?;_w?(05:| SQR×b? 9H%5C-U)WCSuE=X^}[lFbR[oiENMJԫݱO8|]8 Uf*K6RWAyΚAGazDl,"z=hN|Dž <ߗ/`Yj*b'hUttkrqC K-0cȕlԮqzէh;W :,Q?b&@5(gbO>s{k>)Ө|8fd%:tIVLeJ+0uIIá'p{C2S[~Q}Yw7ԤlÙ쾗BiN͜!Z׶7 ΃W5n(?v6]p̏Tbwf~˪_}vvw6oO} 1*h:1| 1RZeUwֻab3ҭbb JZHalt{A [S^넌{}yْPpyQs[\%iTى?eogTةpvҫoj7SE Sp$ {90NŞwbQT <9_w?>jA ҇y x̅AAj |'8Ug_ĹLlxPb"fi)~;4=0{)YZ0tMZ>5ԉ]u\1#NXCSn_};icbՒvb|kDP[V6ɱ8Cʛe aY1 ukO8tt:&R.)PL7lY adxIlki I,-^ku|}6Rllgc*SL&" (.˔cav0 |`< XŴc iꖜQ1)x< <.E\SZx$6|Jjʵz%9\G {%#u(&RKW+eK)bra\j{9G f_^>ˤik#ȩ:JbF 2.j1v˟xB2mB! ;@0z_R$-ǁab{CfB,F7#aV*Q$KM7BzToD0{ )xC/ rj[3xJ|65ґbCj&}]͂ԃm2 j>pyA 1In봏`&'F=>SM ^Ke՚ձX Nkƚg5ޯM?{4}yl}z[/%luonV[ U6;Ro6[-}m˛6`GzY낦Y@{jXW_uN d҅qR2/z%)f*UZ%Ɂjzw* ȵZՖApku@zܿg6&8N޾__?G:i2XFNje(b6tl:,#/3éqAz>ˇӳe.qLFvC2;&Y#o'R#MF,Vϛ .rs7I3yћׁ 橤?׷_ Z'i{ui1L5$"1q1ʭKQZ*6I1yr @$ e^H5Bt-¦ԥ:+?xEjzIG F%Aǡ*/0E ҳmyp2.l'큯REOs ))vk3NQ\, N͢V*.S+G5s v6X ԟb,T'CB3MYIFILNą[2fr.n ,N.`zb$QX:o"L1.,\ͮt<(l9 1LaM:[Xx^ ! `qVf Czdպs(EEekn9u}^ nV[vƙg̾H)tH:&͂5Xc ^`T6`1UlH9j&wq7xnFY %B"CfŶ~Q40T(ߌyf&K4w >77tOl 5 nnӍ=^Pvw6on[VG֎357 W/aeHuvچgWޡe.v!zL~#O_~=?-=4x }yZ`~qҭo5zm$r2RMOZSOS4y0pOVJ5(r=Y左kVŁ:Y|68۵02zvUquǐ' " ;}[C⨅N3o7&d]Fh<pɴZ6 bA.8{P1ݭ2&3ۋM|7!q{66urm#Aƛ~-h\ o].٬6S}HԘOZ^0$~8y{l% *6:]tm~fXJ_6I2P*#aK=nF8}ommLXiUDM>^gGu=](g J0SW^+*:tJbIlSA"z]PaChրZԅIXRv캤M଩{ ,?uf G`l yɫ(Y?;[gW($ϨhζhsuA"3EEL!`Ig'wyd}Ϸ5_?w'87xe kLΕ2dDž͡"#qT):׋_'$ [2W*n0JE`N |U;NlmnY1Z*b8vk9m)b{2#6ԙd.\DdnM9L&.Ξ*&?6 *cβELe|W ]#U̬xEqKg@u1Y/,;o܊qJ*|<{ I#+r-H<߹E ŴnURgy@1,Wi>lR}}]__%ݾ \6L N%3XXggZs5yŹP%$mfjFjc*%cKy@N{tu=Z.ҨX~>Pw! IDAT(Wtt^a$0 ,w̓ޣ'-"b^D% \q0LzjqցclM7^# ^X^w;'U&#ǜ 얣 1~V=Q.")9iZv( Mr75D$۬j@afgl`nPSZ[L|cYvMy݌ó@56O慭@k?oZif@v#cX>ڔ~sd7 { .ڭAӃ}2IVպ-o* od_npvA Ggꚋnw@IO[QȉN~P.\^5GQƖ~[7ڸr1یh*r۰fHq :.)X+oVl|Z|knfuθDE8v5tl1bj"Sn?]h1 bQ띌%1 fwG?veCz~~?׿Oqfoҽ|HwcLCӵƢp– Y%;. qXM\$E7T mۅ+I61Ul/X'DiЌ5z.#W|%_W hR,`r/bu>h_czTԂSd´fJ; +,K%꿪 1>r.+j ѡV@Gl 7Lr|vKX946p6%HMC64=@-A26ȬZ|ae-x7p IJYjVomr8.YvwMi|jІxDgq&oY5$xjp.9Ƃ*5RWL mGуH6W7Ud:"gvm&fxfBUqlkf4aXGVL ܃7[?[pev[4g-Ino ub&%bGBŊ!Ը,z :tNqِ >q͒%C:}fE%IFy\Baު|⩒ZAgbS`r T~3ta ~7~7ODaL:=}4+{˖C^+ ;F񊑟185x:%ֽek5Z}N QOlOoݏἈZz`Ng%} ^ ^|LLrf RBdNjNE\x>΄qN .WWRR P!X:]kS&/`7pJנiCNE&7܆:|kU{ AgigzW:m*4WUϿCZא/ǺDuwD^}k].v;jSS==難q4bkAzʪk!IlC&RσCZԾhxj5 7>^sϙ~_pK߱J޸=ewgQΊ-5>8lSk!T|X'w kckJ}\}HW!ix*_ n\u/8dդ4Fi׷Mg-C\)C 5n};{ut7EZ9( +'b,%MޛH4Ɠ4lj #مu1GcSRraC Ss7q6a*9Swvq^eRA'|'B6,t֚a Q:6.[MdD^ xh@u!:&{?Nyk PSy0/Ev9i2 ag g>Lelm^8LOk_t0;W-*;4UeNAEcq ^ܯ UǦfBлi!#rտ+ ]A_ ܦ9wƑ #.zs sٵiD>-hN:u پPn:(oVG"ZbffHxakԾv2Tm3EVMB=›G$%,P:5i~C\s@H&FdPEIŃ<' 90SWn!2Irwu3tOmu(Y73Rveڎ/g9^ڹKWlUJ1&~;6&\"LYk.u5U֪xoVIoS&Z3ˆ' }&JWA{S\'h/D»J^O V%]s?E PRko7g)K3 !<>Yj; sxN+mxIESoZv{oۧk7mY5氘dj),4G2: 6) 97![eG#A35f=+)G+F(֌' tSN qQvM &m MDD4:z;Z|%KpvUcS՜-|,zKtFti8" V#^rt)F_?y9sc!hPDbؼsxpgmkƖ)q*%aJgv]~ m tbK':O}s≚'E+?cK.9c%KE9͛>ZRS3!um=#r tnY|%X7=u>4 *3m?RW*ڈ3_B s,W0 EZW3oC_>CI(Mpw6/ (MJCýزscv"Y7!HM \oծ7WQ3b\jT Jۢ"imY`vh\Kw$ںF1f5нPunJŭmMמ\h\&Lc ='zY\:Int١eT4`޿?w|?q;԰Fx C[?ov;7W*Mnh Dj*$Ǻ/Zjvp'i]>mt.HtL1q74-r$ 倫 6 ;vX5ym2}Mc{[x`7fX=HO `o5!ద'{K`0N `{ɧ> @:hމڜX9|hgX *r'iQos^V i@:7B͞3iE;oqڨ+ʹ0xL _bN0- B\+2c/o|;H v֪Ýƭw)gfqZktݭb3݃~6R7oyM"'.ڨ۵-46nDᓛ́J>IAAHLHAdBov#-tG,I<h`KcIz'j1/"dn^ U&eF ͧ{>_t0, v/;0!c.T>lď%-nڅ+CA6\\K$.Fμ z5W>}f1k6 ebKQuOUKUUUH}I/oY?Kō;_JPML`A%<{HA;ŃKJm ׫;cfBȿ >!Cڃ_Ȭq5/'-!|ӯ??w?v?G"-)ꔞ 7^|ZQ1#0^+ (0QFw)1[X=\\R1TSH]&>ژظkD0mfg!~ _GO&ʀ}J[\LW!7?+w,OGս[+uȶf6EpUk~wOo!5(j2z1E ǬT7/zC1T^ ͵95|0Ez)tQ2:-Z\czUNXn/m]so~#MU9= `z[д9oӸݮy/#}%̵sootk2=A9;\V^Jw}ާ˖Ϧ`(sOj'ha&o?? ?oӟ|~߽K9 =$Ht%Vl?zP >`nѢ`=rV8+uVE#3lMn6~b眑 l]$7D'dIÐDK::1n0e' ;55{5X8%g/5 ѧ& ۃ54:όw^')+iӟp׻ n}T[T( FInnGOr}ߩ|*EjZ_h{y9 Y}u= 0_,nj3Isd{ɿ& ZCFʸ8ɞjɾtIR[ctdhu.9b,E9 Z:#m-6JԲwA; X}yžfcY)SCk53d_[5ITwbCH4X$90+TXhoo b ܠO!J#^L\j*8\ i#wY(ErۻH |B q7-`|wէSjytd̆]Sn>6".R[hc!%=¸e`/릫H嫘ߚeŞFQJ L9WSq8!Z*l4-IۨXwX*{\UOz.--98/ Lkrz8jG6.4 ~|R F%,q)I哗` P߭SxCeQ(|T4{ءJj]®{0VV+ɠ~TA%zн]=O[ IDATŝņ8!2=ߗ%9ݓt!@9ր63E'Xɦj2["=u)c4FNZ96Ռ$!046'=~B֮O3;xqX3X*É:ةSe+̆2)_*½A D6*E-EGyrؿho'݋:OОfL]|UMi͑aĬe$ס DNC )Jړxj:lٞA.␽_Hkx(֐khG'):b]A+=g>Ŏm!su8Xuj>`N*rQfJxa[ Q phw}yGopHca?5Pݨ,@#vDkl˚p6gePxrOk9K }R0]R\.ߋ 2ȅx7&! R 7S/G~%Ю7̅Wވ߰AjRjLb|c:OY1hp#)?4 r QSB!id.Tc*u&]:H T:Ah"q{I8á<1zlk̗xG.Īmg|[DQ_ .wi\B>bB'хjR('!ݓm=_ =|fCV,H\H:com4gU,lFhekwA+L1%5(ŃfB5UU6bQ 5Wg0ގBfٔ3EM~*oX=c؅]#WrMQiRX_.jS}g nf{6joBC~.}(fHϱf5-:b,P"_^tjLLkIg `u_ګrn[ zOg5bPUvΠ{/b2C=o񜹢fǵ.xyj K|Rd$)E#?3ǿOރ Eš"o/rְd7pS v-#/Rm6Ft>i΁ I3`VX<pHxc~˻]{&5lx?VRqs#ow kmvM!dUWgsۏ 9{=Ov/l9û8gnnk([Ӛ' q{ʱ2"DbDdB6ʆ/v!a<ߦ`]niO9L͠If6T7Yޢf8q5' 9j7eMomt1i6mON~(Z[t\D%_Q5;); p1~rC(+ۓNi.'d9`ދIcKTWay<1_HdS<8k̡mT̛ [Jbe!~]߉ֽ8q>%~5Jg%{M{TMS/\"J5Yqj/FW/T2,>$#f蘜 yr>poƽc7Jj'۝_S~r`5Bj}J:C<>!bγ zMSC }Ie7r ΠNb5mw` RkȷJ#}k,_}΢8樸6r1b`)grpkv"p9xϖ ã%1 32bR}lמ:)pOCM5p"i>v IYTDП*zl^4?Yf?w~I9!7yGҘ65 |\LF_B#{-TZ`pf􈊄J1L45뱈a?~~Ұx=r+ɷGc-pme ;X!q%%knҾGkt&PYӠ$,?yږw;g{^}0Uob|xvC-nmdhwu~ KNkέ7/Qvl9, ; BϿFY OMM)0jApoF Qtw$1)Yg6Y4 Jq1D`7s URɵD | Q"TЋQi2)w+f 3cSkd)A}gܕ^G$loTn.,6 99$a/ 4| U7|hd5m _8=Ҋʤ9ME울K|&Mr R $l/iP9ˢݹ]"!3Ƙ۔`u!Ćj)8B늨)ͩuv5NNOhU<`o}7i*Ä.Q U %8Os M&R̲}}Iu ]Pbt<[m+p F *5>|^q8)[@ N/pt=G5J8ݧ~BfפkP .$R=9AB Y)-!D_j§8SͯcN@1(<ebqy3zqpg6N ffFą7/A4&Ts-.!e3$܅ReC*.}ˊه:'i%.jMse+v/_o?3w֒nIn *XBȣC.iBphs|˚-jݸ7nEI`v(hnx%'GQ0nUkaoSMhdmg#G5'SU7myso=R`ul5}ٚN%ID 9'zXB? zQM-:YqǖHM=$`v[<6ŧ5uz-\jU(w@N$q5Q Q;Q̺ mqvJƍGCb{Su7M)i)fSvbR;}6VklXeBPۊ՝5CŻ*jTbwxYǏ#dߡCӌpC~΢m.GSm9g=\NoO)G6mJ@NFAQ[E P ZxN6\tƐU\qNG VD].4*hHlm M!q:*&BU(8Z=j&ՄZc[-Mq=G/m&&C1,  5Sn>aqJWbX]êX7ħaȲjYcW% U^엏+W nx0}/1M5BT]5}V 傥wלC(-.:Aq?|5d 4]@n& T{$)x1V&j@\g EAc3gh/,|ͺv0bݸ?̿W~O|~'ϙNؾaA'xnlgQ>D[iXxͤO:L-&WeËm7XING tJmKܴ 2PmriYzgTcun|YxnlPXo7M)I_Ja3M>Ƕ 6'Lž}nㆳUwfݰP }7ƍ3cmbΆlO˗ |lʜNVK$fVD/rC5; 5H !/"I6㛪$ܶڵ%MT6s[ٌbZml=!I#=p:o,uhX7t8~]?+vBˈ8(L] /ΪS~xÿQCf=`е8DtV]>nNbWssƗ I{8ܵe)Si$&+(#-ht3(YVRIXq9*Q =5PxCMP_jx/+!JͱC\TY\(~%fh$42uxܐ{YVvHt^T kpF`V,DQ](C ősz̓y6W$lMKg ^~6oHU| }9 <ˆW3a*S\zx*q ,Iț Ws%&1q X*H (hB jpQtxi\:~·6>ּgvQҮ'0\\yUtm;S 4罾,|r_ϟ?ןwVVjC{0p8si`#KzP[՚<Xu`1͊`|Ͼq}[l`=!S9y>Ne<}:}|kEu`%vYXz<'oduZmҮ}ZXKy)3z yoZn-0 9=l͉<ػ4&z2D `fvOV}SN٤Mv'Af)ˇ=F lk+ESX}(Ϥ1&^ -z c,7Yj=RoEcT["NOfHi ֥KdPSʌ?5Wf3b,̐ 7{q`@dR,t=.k! H 3I9cqo G$^{=F9&BMGcd9mi}\ ;ö$j63X>H%D'e}).T % =fhd?u19ċ5nxcnt|EܐmJKe.޳%FŅ.ݩ=se\^.55"uPӆc9X<;ǡ.f]Y `NkZfiF<c8}!`(m ()+4gl\M:ع|xK .>QS p{^N A]l%Dcݱ2q Ck=*O#Rb\bM3&6z\Q !bT|'ɵ @MM2~TEϪl&`@ڣbIz!- XOSC`'R֯ho)JgFXM5Oql{lD}t,0 Citvřh#z2O~0\}9fI|9?/_ן~vܸ?q?y{8tnjnܵ[ӋD_s=5ԷX!-^Z@4m7OFZpZΊhD+K~ĵއ$ϓ8IÊdfsそHP_d>}ɲfQV!XCgR:O~NI=qI]d,!حiNaqlS$D:ϡ8~}۝9ܸSi-1]oY=Ny)\(rZ'gkb1򚧋UX,mP`9T{wż9re}Bvpy>[5%ZX2˃̉P,lRb]Q#ْjg?W,(\+5m>Cf)dF48m.Tl$M"CW0s뀽X:`Wml2&3SHH)g뉗fvs*0/S?84{bZ*w=KCޚ*(-̒1xSE)5 PC;|hg!r/^8ޔU3j1f$&Eqy7̚PzfK|r%aD`al 0e`KtIU|P5JYYQqŊM4آyqaei61chɉFfZJgE;D#9\"%lٮre5Qէ *K|@[_Ĺ'7!ǺncTe?E ew&(\4\B5NIB}f9m ?E .=Dz>[㥭:؁]{}lc7`:I_q@B@=leFL?OmK%znU']O'i l )׿`l5y{+yf1.zOup{{G~O?_O?;Okq_,srkxDxU9Ff}WӶ%v,AAcV-y8OZ={)$ApRK$Uш< |#g\Ka<nvIlMTʾ;7*tuKYs&+v8YOqAJ_KNve#w4_3G&탞ז>ڊqb=[B IDATcqhe'K~n -'s-8]tȆmw5T朧HǪeLzrB4$ LL6rDכ@wyNj|}m.DrYkij&/^<&Jr5ݲ*Zi!e@ h["#CQoYm+N~ϛ6Q(gQLeUzۂD9)%"/"^bd[ e:ѭBޅ$3Ol{=wvg.(db :E\~%Xby8jgtϱYu^e?]Y6(C@ؠ ?˯"vW1W#$JcW1\>タ57;tct,Ƒõ&͇:]-|AS+D}jlPaM씅5_vy*ъ;CNSoͿop xU=*'Y_Tp/a~x*O//?|w~?88pbœ掷9do|@iϿ ۙpiŁAc,Ru;VA2v,k|y'XMVI .NׄZl<_JYTg uȲ yuA-QYܒ[y>7N><ޠD5I9\ w]sι6O5v(x`T&Uo,T4>R!]]h~,Mn)ѓUBM/%ESċ2NYOER蛧}`^TӨc5lEs)pC2G6G&[& Ú/aO̜u49F kr:Dgh }2WoȖ/-m4]Z>a#K`K2e5ݔ!LuN!:aȩ=A=.NQ :=E~C}~E^ڀs ;0dluСѿWu;YxKP@1-}P9I>`<0{KcM33X+XĤi\ϯMcT"=u4FJ;lsX!MFu,ح:osB#@h)1[2z38\GYYz9TWnBo*# %OEY00-BO{{;p4:S j@Y[c3hcYRKuFYh:ZdBsCDn#и,_Un[k,-i0/d=pc:ǃ?o;_y?st)E*ʱXRW3C1é-61 !UQ~QA?CRA'uΒ4EY|2ed2Ntί%]u3P98RU>LY’|*%CXd%I]Onk3S1(k072.pi,xPH;~\3AX{1l͢.r?V`ܺ x#d2]Hj0uF^KGH٘O>;;$CX{1 k&b_Ɂc.*st)~\|$JR%)4) .F;[M[a`.+U(޲D[ 4蹩*ݺ3 tE!~fX+fmXTmqtw@loTH`,F*_y!܃mY[S/JӌˑKb>h\uD(lz9"Qn]/_2I Ҍ&tnh$g(ej6DQAL`NX֚MW!<2 __EϠ˿Li"?L|%SQGB^ >QF;\E`+mcO*Qۘ~?MG]ˢө':BxN XMaM˯^DZj,5{ܰ'J-8u䴓<l6_g/I>1t~pI>`}Q P [P E끒M&JnN E[#&d7d8ڟN\TS4XםMsRiߨuQvjۿ!|g : 7?/o??}e)yU+4w,K 5wTQ$kK3%:&s,pIƊE8EftSeOq#-{GIZS;:܅) F/cc!ӈq `6QGm,Ĝ:, UV<isY"l1edl5UK0~od-+TyK2,8_fqaXxPO"_&1ﳢ6YpƃaSàZnƾj? WZO,O*61URSZgiK^H!Hٙԥ!Wʗ6ݒi 6[mO(FH TWŨÜPp9,نmj*kOXB1w[Alv#=U\nߐYLF*p_\25[#N.hF[i#J/@u VI-%m^"-GJEݦ%+e]( l $ŧ `wĐ[$dcdo4?km".Q=ܩHrFZ`՚MѩK+yXV8#g /9J7 aeBs]My7*X #4Vr43Khl[zYVrU&zlAֈɴD@Π,!7U|pFQV6M. midB'-FKBCYSM<шW$\`X3zp6h۸)*O69a F8Pv&[k\(d,jiM%: mfH2ʼnb.EMlwsکԍ6gS4""fj@*[W (yha~h-SެRlD&1lc)Ð롕p! 'je\N L?)-߉V3Du:%#"xˏo_槟~x{v'u %!ĵRf6&k`hSRj"sE4gVK0N/"W7ts`cS=$vy TwL{Hp,P?ai!'= *;0.ɡ m~g|A܌Ne^C-jZ JoV}k2틊)y9r7L!ĺ ']٦2=I؅s2fCXifS5%Ȩ}މd/;Mf. g?3ZXG[&i8E9(#٩rmK}5ښzMr#| 4ǎM.ˈ[/·81;zWTEzRu k\XRճ%QWPHL:?5Ae%"ZXCJ ;|an6]ŶE N.Y5=ʼn~joufWR rK^2k#vPpK xr}QJPe~"K/,M( 2%g6woCx:=EWabso}dѐ]M,T}]M^}m~BXY[Ѝ!@r'X#YBňaoʹ_ n;SrShdJdCn)[)?Gmh?%'&Xn8d{izvz4vlg-z2_T.*gfC/E68w<-_(͖\6UQA"h݀y(֝ifQ>>|+wo?xn퓜.sbOroEUC5𕅯$(+b1CYi5%/53LwƧ>H榄8NVty)[8Ur3y/LYAǫgTVUҥWE/\@[( |EtWHDd~/6 :_N'>CX8;؎qv, 4'j3nDܢiT%PW#Hz67?7,?4px]4=Xm!PUojha_Õ\Bdu@wZA(ZM!{ݥ)f( G["/IG- 9[=NlB-ܝDSتt/.tpL^JJ$($TF0t)lz`RDnkar(pRF`ah(n-R&(FgyXZ~K(w `ז(Q]::~󉏧غvŗmeD7CA~˙r[> ~A-9je/ \ɠrB1KUc}ߨP;,y Z}ՍՄ$12cKC0;ʳ[?D3$4pVkoM"wC6M1;>еY]ޠ |G ֟(T zUzq}z캍M &F:!`aNU'/Yn:&@ ۑ7|LЀsl׽5'4BUٙftcHZ)݈(ҽqg (3-BKCIDЮ-1G7V>DTCjtK;va:{-FLr(]o6}ojbWAšh_yP7f%2JêހuBh@×@5kcQ7e eqPE[ĻfFaE Q֚51*M`q~^y5Om C%$ '- /E;%"KgUI7,(>@qdB{sSN^~>n4_e}A`//7~'_ע,a;g-fd $|d9ئӦj<ӘBT :'Ӑt6߬\^xQrm.vB4T1b. fo\QLmN,dČ- 0ׇHSϞ I{2{W DR '.2eSyоĐN+}&*yLWnJUùx]l8w01UbQf|+b\:yYKޜj)k\ͨѬq}ه1J52-7/ѯ@+8kOi4$oɪC-1em W*:iGӕN7-Іt!a6W:$ٚF  #!6 $l,NV3Xm(ʨEM $[ (сn&49j٥T]iEu%NK$Y!tz뺺O*pUR[ ?gQ"2+L5i!\P$؍<~cr_TmL?5uK߱k*-3RCC MW#'K oè1B&j]R.plk+- +!ۭisՌ{0kr'y%BWtW>?(K)n.,_x\kg K-zI!moa&-x"NiIToTGB <Etg\r^0̱RH&+Rʿa89s$ʩ6o* &}ȕ""?cgP^S kaI'k(G|P/!>,wPM0[#8:0'OݾA\ZlLU\\{Gb!v^UXLh2VLb}@pJԖ Eg%OSl^qΐU!).кMh3L g`9%dP6^ABm=ug hdZ'>Ecy NS,wu:ɏLJmjc1}UmfCCwGdQM;H5 WK6uvih?`>Z1^&eXw'pb%I zN>9G3HEiUPuQa/~ceHeH;iE_Y=` ʱM6/]}u}vi; W@Jbrlן~_~o<17s0d`ʓZ:[$SNB`nG"fz Je6ZRs{bpHm6t̿`/5a0 8\kUwń\ ^”m53`*xtY;`8/U'*؃E1^T\2\.Pd5r%/y#{9x%5\v/>isr|WFK6L9PQ%,y)7ac^rzs{[FT |||<$Dơ3=%gz< QlJr+o!i_~)8BdRLjjx(ぴ8U?YUx$Tɚ١Loi ͕=;5:C\U:4S9Et6."#D z`͵{cE.oDd,uiG$!T27&2!bJx榮`ۮ-[hUֿe2J(|cbUjs+]ᭂ&$ԏEΎ=2&֢v)3JC=|tEƥ/: )Ьk]huVB6F%+%oW2L:8T S!آ̓2;oK|h-kz-QX]`I즌G3,Jr2fjƭOt[ r(wvk~!gPjړl.eSUZ2X0ߟO :lr 8Ewe.zvq-n"^2Z(8KW Ćo- z(: ^0-w^i˛{nj|#ZgrZ!ɎV{F%z_L!ڮgiD^<iQ91Ɨ?/?O|}/3J8xA+PEoH pq,",. TFPLlʼnjouȭ^x/fy^l'6 GU})BḛGAe;*E:1d nMBt*&C p>Ō ĝ<%tlεMB x &y$1ǥ r naD,% >اwM hn̜aR:,8tf})ކ:v{^g̚aſ!jͳ.iъS,}>u'؟TM8rS3 zC7w],cF ٹ>eIaH(=fKSHfuw1jg$B6eޠ􉟆rj52 e9HK/ tsu}:CRa,*Lm,+puAC5%z: 8+\4f9 bcZ҃9^D]V%PDtfIҩUl*cdQa\Kaݬ6Eg'xJEuYh/k hI5TFS J 5 k1: Wt1iej)#LLhJg5'Dc#-*!@jr+W(MW-50%HuQ{aqbXBB:B}}hq,(jPmˢjRMvHpu>)Z{Gxu{b.7U$Q*mI5Qioz] id%)^*d- R5(V襊IBmAnڐV.ZkQ&2Ջ: -{\g}-3}ϤUJAeE#nb u[{H菃ۃ/?/;m2^Fw6b^Dz8҄2k+fҀR+$oJ'W F>y/ " $RfwVY> `=&*)`}E޲aO >)Z;P @/jW..W2Yk;6{c֔H؃ ϔ^Ѧ6jさvI.D0f˅*XKq3Vܽ _ : . Ĵ:MfKbZ[u c_uQK0kEn[BrԴ׽ZP|I^Jl0E?}Y]Zӌ1.,:8[ jj@!_ͳ)5W#aXhaj~dAr:˹K ٰ֪ dG(Ew{Ofӥj"؇Jlt܄OFBP]TX4|K`hmJ*㮱u0> g-.ru\c`ZFiz3CNnX+(Mm^K^/B/+ϑ0TI0~9BK"R{`3t}/=߫#֮tϐ6D%X}h;jH`k5凒 P-+ 9myn.c?Y}N(~}lS߿S$ei1ﰭg{ 9_?_ )ys'Fx-P%F-l: !l4=%kÂ*j5w XpIG{xfpNaUdNʂqk)k)|Q_qbOr^q?$'{}QV<(b AHGsP⪆ U>%٫ru Vdxq%6ZgŠʉ?IىI\z~it[像f@܂\+#PʑgAGk.g碶3Hn7RѬ..2O^|pOUO QrmRM-.`溃(Rӕ8Yig^IWkp˼>gM$-j}O7.z\0}SVN3335Qe6ss

?0+&^ۋCf7jFj`Δ͛y.20Vd@`$&pBb3v@R6sOVtZ LSBR!^]Mu`ć)dӒZN$2c^(m.Dqdd| E )Ӛ_Ž}6TlF8^DQL=Tŭt(-buMIڠP( R_us+*|֧ CM&A0v1V75rGͤEjiѕxW#;]FE`e0:l.pպ]EHQb =]@|ZPڃedpBItv3q;C#2GaLQ8[WlJ"YTh JhS_꽴jl7VKI4--4Q=KF3Λ$;$:t-u[g. we;r9PD[m`Xt>\^:(LINd=K 9ÃW:KѩENd",gw!T#T\(O,X}t`ˀLw{Q=xZLlMF X_ b[F7!{]xlj;Y?ssGoWHr;1+(si RUtjMZʺ٤oSM*`h>0{( !φ d|֢vrqXuBWCSTCHa Yu]k6E7]I˫Bϟgf{SZ?,lqHj>ZJ.y %)źyqj%wʆFVH]  <_o??m0?c)W{k bR >]ԯ=˔{df\Cׅ͞\]86N~3V N>'wZ ȳSzr(}]d~S7_ZapSoZET/ |J\zvPF0vɶݾ[7l?̱j5$$Wb&}~bWSYn w5VLj̍Jt6_E{Tas+)'Ra, Õ5h=[& mqNlHWzG*Y0dL;F.#†RX˰L[W4f#&fbm0}ɡ7&YCpR57k6nD9ɹ gbmΖϾ-k½<d0ƃ%4E`6{Cn`І\]/Uc0ƋXūFδ*2LELkfZR^[N)*˰vɼ06o4Icv]2T=KVqZ ep!>Q,c6+ɲtTPJm!4)2cME=4Nى bP,1"O(m ܶLz ئAR? lC)+F]n /iFރ`-7K͖hVJՋ|P[2ТQڟ<5v.xAՉdgb/tS:sJYzȵMN6@%/Vo %FYQ[G6f?-O|r?/Ͽ?/<ib $s8iq\:ePZxRB4)k\=/4<7#SD(CgVqY18yց`\5Mbjy'xix쫘.?2E (e0Ez-Y1sKr;NUZB/̚\{S%wf654[\ddd0bj[J1)&L]\i҃ ]*ZJt]h5UVrlCQJZ4;+}%Z23VC:U vLE>Ջic(=yZҶmb(2l#KSnnz4Po}PBqM}FSlJY}.V1=lbܰ9Z$Iij/"/B,F<F P|u"poڼ4(bFOUQ@l]l#2CN\3)cd%(Q@ >Uu?!&uj}k'Tԕ+d!VBѪ,ɲ0[uuWf{dhV#d [JRvt_8fƘyg~_xp s3a/T>% &`u.e~0+~^ZdH_̄1eJ^f2%PB i<O’NnMz]}+иY ^s2 ή'/EMS WbufY|@Ѭ&e#7juP0TRǪ%:cX$+_\𖘔m/ydcs728.!$fwK@4,TI&Ū$Fk*q eeܰyK J l<1 9_q(8`QB|V/۝hC4_XNCbf.dr42(Շ!ercd0HOVnc *\YLi-e cS +06b?ےdGpU$G?F7Y],Dr.u%fl+6":%jJA* řFqSTn1_!STPīQ@W+qnv*ǙCK`%7" I|e影̼(f%h阜D?vUj%dލ0SRϧ IDAT>zh30&~ G wpFtײNG 5GԞdFڇsǘ ~ǏxgA"z#4p_2n3) jh8hQ =Y2ؘl;5dtoSx8S e=d0dςm>-Z!Dۛwuбct3uNO8,m&|z,~ϷZhY5œ-OSv]8(:S\uLx" quZJI"쭿+t2--Սhe5wyK7k^ &cj^ҷ:U.o6`͔F%OeXmޱez6_/}s^%#>(USb ƭ7ABE[Z2@#,[=1։{{ubO~?d><q=a/-FFnoylDXe~aQ.qsRCO]DRN5bz1}蔇Zli856 gnFh`͎ [ZbZfqߺّLnʥ+BΓ-|>lۄ'np$%UT[KN.Z:|kUk%)/+/y֫ZD ,(s4ZHv#!/(8j*uoM$9. ג;FQ;LR S4OPbK̭2EM)}+gR)F6T8X8HYc#kh-~ey#Z΃ ?{?Dm=BJGEBaOfIv mr/] n{狾sPQoUK PqQn6F8;AY*ùu+4C7bO8KBT}ɑN wky24dWΦճ%vּE/FFsy /,ĭ>B`/F& ZlZ$˱ *:#)8E&;L']7}9Wk}%ltF rϿ01$aأŪwNW6{ŭa&???~~y5E5?>|;Mi-hO eau16jtpOԨ5$UHh<þ&-cӊC'6S c!*v0& e0KǬtEyǍbkE9qIJaګ-ŢntpðΐĿ<{2̖! w͋ PIm*Y RC#يZbnR ECgȫ-;=9㨪l{1΃cn}\J+\OnV%sv-BDz$Qmmef+i8ʌǤ^_X]a@r UJD.*r~K[ӡӊk9v~)d>WI$7yuIDR&z}&:ps5MR ȭDж5nID%;uN {Hb;eG|xX"Aߏ&%됖V-weûpRf`ZI\--)H\5{g]G rz8F3{Ȓ??~ɏ'6)9qԊMǓTqZCwFMT֫NIs(G{-&:Fjx<<~IFj!0Pqȅoh{6arHrm(dEP:Gb(0\Tm|_vS{*,N‘fX.vUҧx-%Z*w=k_Xϣx/eTqD%zǶdZ81 W-)zT5`C`2fQNwMm%]sփs & z)x,N#SF!ÔеgnKQ_\/4.w_pd2(&n۸\hQ-Wq~ZTy0m=m7|J;|pu='4l*bsRI,B]4wuI.&t /L[ {q0- n˛Qbb_HXEZd-uXɢ" (dwT3>Re"?tR5˾OqԀ(8rjtp\aûG;2Xcb!ZVce>)!lծɬI1usPI/KTIfp l5AFֺUW ԿCscUk;3 fYTaIqp(ߦ(_!tKP:#uy`[-Jzk;.4]-NP O#LL5&Ӧ灝Qż勑w [v3wkK/!6נy(FĮhjkX!ۜ-=蛾h:F/F/8cr[FÔ/Ҥ=B5tRvZH_E/l5梲=jꡎ8\ ]CE#=<[uBN$̰zăq&ꈂ6/j10j3!ֺ zґޞo2TʥyFtc:/AӋxEisޒ6tP][ݡF6"6$ hW{zm~ak||V Ah$d< 3b\;~緟q=y\Obd8I2Y0 !u 7Um`%Ըu8MCm-YA=>ul/ [KGZWAh:w2%GV2kvalG h #ja#bwϪׄgiUEy8pE-wy[ovy`;glbWkoׅ҂Ըfo,539Qe]ܝ!oH&nGK0j' .VCUݲ "K J zc+r/K:i6ڝȸ-*&#}VQD9g'k ;J-c*魑a[=epufJ`l_GBTR*OմH)ޑHjdbtC1M[(daf\{tϔV5VE0OjtYmQ>>{"eLeѡX0Rnsm 9ytc76)'/܄ȍ]Ʒ`ηN.`1zC?ud5HיD8gW3*4X2YS]6BhvmKKf̅rK3&^dw4%' S&^1B@7y.#Zp[O*m=$Ctf?ԾK]0oW+] I|o󛛵Ei蠣C7{sp:Fs&Y 0V3݆2/cj+& F:9vY8o GUmL:F$}i0o_DPusAMN96*5gm Oce\TnMnJןu2gT# [! ي).[c3ncbB!T/Z#)wD|/! }=S >1jN6k]Sw_xa.])f1q_d=>'6$XIDfUNaT\P޺ߜ>Xm8|@4Sf Ŋ݉|SCuԒ L#UՈsB VTn|3/.Ѿ"/qd053v|ј/wιL2ޒ<2+z2|f<PO1P~7I?f*HS%r=rqL B"Kq.;9 2bN|k6[+JbWⰟqB㍮M.X!:?tJÀAT';>"Ա ҫrUB~|w'*Lܒ;unkJ RQ4kX8ɮ]ŝtfrcƶ$R7P_s rw@ > Ew&hyRٕTS"NO V'@HȬ0 =' osӥJA学@4Q4Ĕru~K6-e=X<=LơYS7 kmNPw%Oo?y哏 u2ђ1Ly޳vez2rZ6cֳDabdavѳc6d4:7!H&cMA`5}h)$`vYf⥅3 F}0ĸ%Yb)\zaĖD2g[]bŝS|Ξc8?*:{Q9nrvq6ndL@O%F%`k+~ͩ5Re]9.)*Tq$] [ƽ/ѵN}5R]$ .M3tO38Rm`Zm(y3M*5M; ]-SDˌUϒ w=99q3$G݇_D\؜z\1Q,LfK:L3  A5(H@.R:27XS#PYQja:DycLՓjtޖ;Z2G4dkcFSFeus@\z̸{©#bk2Obˌe RVTv}ېx#wG(|tsiX*_`$Y&^ UDtqJXJw]F @F*Fvi@Ġ1Qw|oEgFUXPK98qF2nNx6I5h.rt xk,],*Ƣj$I5*$A|RM:K7Ԏ+5lh]TLScz7 oǸ(M'Vʙk#ˌs֒}C.&e_Xݪ߯Hw# IDATJmPz=/ݬew> K0Vhi2jJ:kSԔI`Mm ks&Dd`}<A<q=y| ̫>PT5ݐZe0/p11\C#zC4mvD*  !ag}dt1yJ~_C]rTzv-gF8&V9'YQdz6xhR{EA}>X,3o>TC$xq^H5lԜIlk h佱[y/ch(I!|a (rHfK ^y8ˡ\"SVƐڷ/gZ^{q!S7shN9D/rzSnv 2t~E)Li%߉ 'qdJr)Od w8šԒAB-eKE6}ZVv&Zt5]ctE{K*{🗐Ti.nF.EG:o7+-229.t)gu ^rn1_^sD5K;9)&C:Ky<qɼqɬyȳ̋LIQʍn:EK C%͔%-ކfT>[Zwq$4w8"#$Kk_G"4o @dz))_Uחk2VaҚF x^\?~?3&q}ߞMHLiqx)I!#p>nҸFQ껂:JX6/Lv/Od:5p&dmK#KmmG&eֹsm Z="54ƖW.đ2Y"Eӕo,x3`VŹ0ֽm1>7F񌗌A9M%R?n3F'V 7T@_f.#˾-2F|4 ܳ06rsh_ ܉m0xC\n$ظL^.ϋ5wɕ+nMCODlJ%5 {XO^,)/g3{,40/ԟ 2̝ɣX: #p9L,:V ji҅MHqtS+Fw%0lF$Pa9MESuF^x% srQCCm m{2߈q8f1c`KKo8[o%crcx w'ixIW ;VvwL9zݮbyWhKxqNnꌒ.b|\}2I<?,;0}zAnj:׿xGC_ԽRe-*N(N8[^4oJ4hJNAL@J P\`<-t|\eoJK \&8"V 9x?x_hU_}1KWPkۨ(Nd|CdDgY3`5dmL g`6ɡ;Cwktԥx#ra|-6-iIM2~Rܬvױxd8J~O }ˤ4d'W|!pP["bqcD UaZexrs\ic%OabUa| v5m4(:/:EW1U3}1JR<|Tq6W6g 49o dL89H#b]BsQSQC0}Qs3 B^:%!@'@6c]؄E4M]Lv rrR|B+7){;1itgQpm')-9ItewDb勴oCmTqq<>X^rS~un ;ܦyf?JdE"u&#IQ %+?&vk3؄389p̦꿼ʾ = ۟Xdl}^~ >6ˬ`P;r(c&cN/%t(|HKje _|b҃$ᕐ1˭4֙)$ՑB>d."dMSzXc^65쒢ѺkjuERE"Oo-gq 9a2Hsgk4X7a+[R(3);v2mO))sH0jucDǛ9yOѲViAӄ虗%O8kkt|@Iם zK|㹝}6׷:|׋;1_㿈k|2?._0>YOo?Ԕ7sq`o^_Zد?f/ZJb` L,y;JM 9&t-p[SVKj8H/ 3;1|2O\'z|OuU7Λl3 !a\VCy#UxZ#򰝣fSC#uȜB$Aפ]s2ñ6 b')N}0|0,!/ϔEe A~10pa ۜ޾$"UF)šu 7~".b^k?xO~\Ƽ=x~$¹" b xHFv>-\n#3%b1FhhipW4U0F& 5Fu)ᤌc8% A"tԐd=Y5i*?" *tQ*svx/,OyI܎p  2Y9>uKOv2(X4F\ڰ7}NA\O9_a#$+m1%*BcZϷ`<k-dCR p!UO)JA=9zKh_ɰYF_z1 dJrØOdawp ?=0fk2"9(bh/*qvT.䥎 TZ*@[8 3uQTNDDXZPhmdr(B4pzKbזnt9D~ZEj`c,9;!P<w` T=lV9ߡG6&(l3NNJ2n4mHd&BqCc-?լs;֞'n%ZVvԊl5ʻOb)g![j!v Eo}n6[bZoW ޻btE}x̀瓘c*|x ƀǼ'c0Le=/ƒ9_I_Cl2ϟDF4^lU9s6'i Ḍy1MK|R0p[ġmx=؊rJi)_ZR!-]bߪʣ!$N1r?ZwK .`<5`/r̖|ah\KPJ/<]jԢSw[=7? ]?0LZ[f'(;T5cq}\/>?^|/gOf,4|:m0blPݎw1F|ReiJd/Gg+av+ɸƱ3a&q]g¹[^tM,o:V5">X4NnU5?^oiX!qCe(K1rԼ`ޮ Jąd[goh fx,Ly 7iفoMuÌr^c#~VsFG-ltpx4J@1Kk;664"{+P7\kShK(_zsVC!n7d)i}G5_ͯ|HUMscZ, 3b4c\,w Y@K98"{q1Ow'sM3mR*h|PñG3ly¦SVqی1[Z5R܉:1u&7Gcj%F>0<=pAc4ZyF+ VfRz,][?hL< YtE-]F4g%eǥiZ#H V[5QZLX{(W5@j&Ue$=6'ǦZuG<߱9q u"WQK]ƒo1Y'iS9ǒpR3ضAءd7la)r:lYpSȧ΅z^ϰg0Wdg8oZ~΃!̭:&/#{q+1;n0]XFF˓Oױm} NUz3=lx *_ϨcMS,lC7Ů_B}B~J-:v~vMMǧm<}a{QSb7#]aM=g۳z9!7Ω`3e(~ީ&1΅426jl<qQ]JqأXYl>rjK7^`@|A95Q}g{-ga\[Vc: :|8iΙ Rqͬ$Uؽ;|d˹d &,jYi%[OXaj,9۲U$6T*f_hw*GRHjZOp6M7ʀ.Vѓ jLk#bo i3M2q[t,~&XiR˨p危Ud>Dï[Z=FgpGQ}ЯK?'oɟs3=? !M[o,6n.b%N$Z#B+V{>D7סC,^JR̐13=n+!B!#ί)cV?E'M8R%ʼnycm0hB}-YVD6RF3dOH6R!\" HqK좲F#R!:E{o< ui5p IDATl`re8DhS{~}3z}ƥKRŜxPF|S+a>ψ$B]IjAY 'f/ v~~$ۖZn^ Z*𩗨nvjxjX1Oa1|fEGMx3k|S sٸ:Hʼn L@,1z(gh/]`*m=̂Yg/ۈQ@2Off|_<;0W_ O'{9eo7?i7Ϲ' &6tE1χ>)">D<:gW1E,e-.gT 'dZV˥E1r0TV;U.ߣ~ilKݶrz|KfׄMGEȦ۴ryvZ!R.UՉ!`-v6tMMm  a *ӂM$No9v#SKۧRxoTw@66'AO0,Hoz:vgt3S%5~I ^o"Uç0R-Y:l[Лx(uuOe791>o E|g5wwŋ=4X5+]g>b=o܄is%6O~VQm8> Nmwyiv5w2>Zה:փg3,jaRvp q nn*֢x:ԲEȣΜmFr Qjԃo66Ŷ.m/"[D.@ClMVKͳ-)" =ԇ 6T/!uG~DBVa]F0l~شZD P*fLfX yGc$|8Ũ>M{7>&|S Jx0a~gJg6!ҳ%,Tp*C)!߾ֲ1p/ڍ矯ϥ6r0({?Af&2O0v5a缡x֫d'oV;q͉"SV:bOa5G]Ob^d}1'R$*YNf ͢[ϖ`Mк&n7 .mSvj>[{yA^ _HU̓޲7ecƴB[۾Rc.W-w<ǯ䳹y-5N3PKU FR+/F0'q #V"Pf'$D>ZaeOPO)+6uZO=ld՟N!7)q_kdrs*Y,Tߊ 56KU+SVdj;t'|-%X)V!:AQѰ]0 z#<0ݤ$QF)RDE8-?j1+?fqk[pE娨Qhijm}~?T.:gB'SqPqvcØ֡L0x}p``xv M ~PMF  #dLz#6\": 3ƭF xtv~YϦ }ST=: '\ j*8$%lKڄ߲ a$rlB+K`&;\Ea@`Gʓ_ZQF'k8G I:>`WRvKrQX?s1o1SPۿy +|k`Q|VLԩzVPy#E+hfKaޔ=lW8NJѓ'8ːG -ͻqa$]zc2^X-,'9%ccd'0tjkOuEfCwN<1~nw[Ϛ<60p=3p%h>D~á!(k5Dƶm m&(*? c'ݥ9X~RLT˝vQ bAav-E7k(^ ~ؽHCۏgC~M>EpMW-t`xH14s.>h(}ދͩV$\~?=n FQO.@R }lF-y~y:'(EU\\=Dq'fdZ큩kGјy7ޏ>NI² _߄鉙Ή+1$/ l /^XlvsL5bXP(4O|hه/0@0 xDawJNƫ8vC׀D`*y^#O~ LH^(S^{Q<`9f[[CօXD.*oK*|W^xnjBύSlu65!<;/>Ixgsmj汉T[¾ؘxciD^A^&KmAfԩ5dPfˌiD|tX}-q)C7ƮOmָtV*nhd?jbFyk6)Y.ti3̟ؖX%&5q%ˊlLĞ#C@KԿ{pQ; XX˲0ۼ\̢7͐7⤛b_j]ɳN:[a†Vk'IbdrУfP*]Xg}"pPLs0f/jM=]6>0;܉Ú7Zq`}U{JZ;-0cŪ[T cɵo5 JVQT,v>C&syIeSIM vc`)ј E+ ,8@5; ~/z l5qDn?ʺ(s VH|'=Z6L4džl6Zѵ]1/ʛVuNP0ZZ6a*E 0?0T]]U!>sEO=NkbxL8 [>Oцp^R/`xLB4-쩶(jk0nrRaՃ=34l 3hd*^/ Q ypTIͩيft%쑱r/x#Wh㹁R"z39o[~.2m=|~eK5s-).hy UdLpv UĹ7ki=lfp;G(B}8wgNWXE"+ Ci-E935pLU`R3O8ŝu3_/nl8Q"/upV}˃nAM퇃\ckDޱmCXKn44pryB Pn\q )xkX-#,l٭0ŪM&BC&[K1YW>3t]WYIuSĭ{)a`yNQM ;!Ӣ?C]r', ǫ zK=Cm>ŏn\[s6yf/Cum E||?s0?]  )بq"@pjjr96 ZdNم mɻ/c-Vtw fnLtQNSX{QyIydSV+ݱTQb0| u.TSズ~`7%;M#QHt^tnޞԘغ:бj|6N17Z|S}Sb`sʴemfB23C7Üf ȣY` (LSPQ[AHl ^/DBP+6 =X3QJM$I87O0.mʙZ[sa)gv.x]d&fpz/,Y9؟+]v*|*h߬$ t}]""tK)}c!B-Ӷgw[P#b7卿#XkJ»ҋodq dv:!UWռs޼4t |MiJ?ͼ {חV+h:U<y5 G0 'nanI?7[0Bo`n\gXpFN(]T-l$n]쑰 M7fra"̉|sҧ`&Z yEU<3څj=ʊi4m_eQ~&iؠOXV4l WS+1[)uۛaΓF9mh2#dfd#D9Wc`Cuͨc8ح*֕/ ʠ˄e3$xD XYˇ^zIK4(8ÐOj1$'(~fMA_b X}RƩJ# E>g{fALy lT&~cS@ظȼ7čX>pzsʀcD>h'+YZF^TsR248JE W\7] {? 7S+ٶo3(}Y2buO#XkBR! l]ozl `,U.]mYY * yfχIBriLЗ|,>sʂa9hkԍNdtQm_42bDi՚[=CI:XG˞R,xAK1f"ʨ3Ae}SLj\lWaJ6.yLb'eY%@hwf #%H'Z)%v˰/vBbiB۪djOV]d Ft(_ OQA7C4b,&O)퓍]A,m͠)Dh327켅}-Sޏ88O0RxMa:f o쇞71]ęt9V<HYhgPdZù<ty^<o^$MI緜il^B1K K!=؀l{\cIЇC2w\s=/|;/9&=Տ)?\oJsfWEzperg_ćsyƝo;sH 9r3f?q"rv`${\tL0&h*TExC[,u86qMF{-QsZӏ. k%7s|Ӽ JK-%zkN2txحMn%'/bxȋ"8dz{s$7nIz݈/F/FlKœIyNxo,Ic:6T>Kթ|y@-h̘Xnz|kkO n5f IHueMϐT܆|72K'A76`bcσnr6j:#eZg|£QXMf$Wmk&y/g76mOΠ{4hpU>[լdl -{S }hV'~{o mnX}G*Ũ-+Nʶ+,5][lUdòCe56]i/6Y'ougMlgW!:YWP=H 4Oi"=YKw^\H]f_ˁr 2(+Q%:x&(KqhY ֖ϣYEo a<}UEIdA]Mۛ7ѧ\iI-jd|?:-hgewF˧S0KjUW2~]T=/C:][gý6AԿ6Eӄ Th $P4~-%Ayk? d*j&eǗqU#\׭M̋TL<E=qVԩ贄kVn PXLR=VaœdC<>f-Qf9=6ώҗuN&)oCごzzfPPȤɏg-CB뇿L?gVKASO4;jJ!%VX'ԧ HG!y 4&H5LGjb0c[VKybLA;5:"LkMM\ܯ_q, v.sP-/ob7L6\SH1/.NFqjԅZBJM+z;ZW?bfQ`?m vX,]D6(ȝl5JWvاhUk@Zv0b0}FrѶ\ خ`0a߼Y`j]z3fA-NYCDK |\3^n*XU4J퍻9\̎}IFCbѾX龩Iܓd]Ë}o,m[à/?7/F>;-cVd֓mK^ll5Mb{V[TA m͌PytT.r@ڠRD7Lڛ=][րRSNw&4$JsP9\P4)H¶JPȵ<8 סcL}]Ģ cj\Sti do(n_ˁݱzewm;ZM7$iq.ul &./nrN % PCP-Rmf9iwߦD~>H=:tyYpSq:jƞVA7hY~V3 ޻F5AlA1nclF |/εFE >ǡ}@* 'dMp08Kl a]C CL"BȪBio ,iYOACsp9v4ꓒq^ܗѧ;1n0qM&~pbuPZ%o"P}=._j>{͜S:~O9_'#SvU%>ʰRVE:R+xQm]@n*x4U2b`aVPbv+9te?T9%)nfsa! 4pxS w[жhFCK^F. >=:s( :eiGѸV 6/zXѥvɶDa|b/DXŨdlԬ'/cC\쫉#ӄ\l~Fvqݺ xAD_\d ֍ԋebpdՂcuVgio߀%O`wNq6gDݔN.b5[LX-V!BtB)…=wt7Io)9݄?J&YY*H-fbw$y6]$C0\'Bn _[ٌ Õ87#m)2Jx۽I:MRf; S:A.=M͒)ꕡ8aPj`{qԜHhRQAitLGl`Rs*:'$d ֟$x/cqMTz.u GpXV79`ֈCԞk5z%7q|> .{Q:qDstrLaՃ2g/`7;>o{ef$sEy"١웩'!G6YL7D%0WIOeQխֳ`q=˧@T ؓ-ݣWB ߬>!W| 3DCg)`7U@e=~k!BafuֻiJowk{OSHgIvDlzJ*Aͺ.\a-lJɉD'`m4k).fuFM"vӶ4K+ϔ:ـ,diU77-ՒֳiTHb΢gX07{EMc:* %ld]|v3̥z{R&x3,KL5j$(nK[̤l?y5b 8۷uvk/)-6#1PpJ!zrs v3En};=U6ʪx"j@G> h fEozU%K|:Ŕ Xz/zsHr8r^^ܙEc]U&ɕ W`v3xd_`E`}/L_V3qFT~sV".tw{H`|Cq{U>x=5&u=Fhܘ?e. `bQL."dp`Wdc&ލߤl;}5R-s|܈nοdUFMyʹbyƥd# }HҾ5<&PW<;5 X@_⍘[+oձ:w.YFEE!ݥeO5r!-d DPLioz(J;#yW5)x&м 4E׎XF _DLk~ Q|>}^}#xJHc emaqJ&Bu0zmhwwp}&;]BS^;ZJ - L8&-\bA4Y2,/wd/YJv7?A#?MY;AnV^Zh(c&?Do$^,.Iy{m/5zW]:c!I7Ywyl$[a>쿮AopEùXADe?gS*jot~r7&t^kš,[ZSxׇ:ۿuf'UGw0Yu$y)񞆙2A2\AL |qWD;*rz߃Zń޿u<ČyT v%>7axdiIDrsSKN޴QWr;;&o$0ov ..qiEXgLgDا6na)y:npǁsGcهw kwu7 wR~2:MtB{ `Lo%TjCKgc sutQ=syT.m`` %z5<TejyܬqLQќ$xxoLKlOs:_lF$nUZЭCچI=fw,QIFg:l; avET97c'~w)3azw$%é2)Pd|t&K^pN}Y3A\nS8HRШ 6GBku<%͔/m10;N"f AK Qeˠjђ?Lf6QoJྫ7tduIT9Ԟ].3*LtD&DZϴ5R?,@^B|.'䂔\(6Il$([,:%2,t#Sg}c@DC6V[AͰV>|#ե K7.BoHG\nih^`x;i5S3[eNqmOV` F|0u#n6 >>@]lN'r#{Qc^|O|ߡT(Ꭼ.mn'Bpŏ')O+7e XŽxA1'Q"׻[~+R""~xd!FaZNͭWTb gGrbm攞"kq!gWO 8 /0 e[ ݛ f2B8d[q]`'7P׈'>o㜨k`|(2gwͿYP,.636St^/⫣ 0D{cy4k8BZf$bN*8l=H." ^rDdbJw-OB CEOdsw':m)-Τ{g >ħ1A_7'2`Go@)jY591~vNS?b-~}QC`EC) ;Gly3~nW1|R3۠W럨[,Qy!M=)BވMD~! d3foٯ&d( 0&z c7?z<]`Dvfkg\+]QtPpyhr[mXc 2 +ڦh*)uuefcqK'[CD2q7"/ |9^n2y=;aQJb|@.م{wL@w5:7\MC5лfɮwo';5j *ٌBB8u5H6NbGBB{PJ|ʿlԂ%*wfgk=ce՚qc RD0ǃdmLv%" owQVG)E.8:~PFa_PW zCH:c[^i!n"Ȥ^16lU*5م /l,"C'hCWA-#,9QQ~ʒ <ȡWb=76~v~W.ljnw+]9({ .ɾT:2] ^kw4S!?ݼ_8fK1.9!Z{ّFYgokÏŊ#o"5߃E4W۪R̃  NL7BYܵތi*@tPIp"ErS=?8~|ctց YŨ@ 8H.^9>NXjscN^d~m7 %}}w{ӏVz1qF.wd~2GaD/b cYζDlpq~`{=׸Q:#?lb1UMAOD6ܕhJ=;Y>w(S,ٗiaΒ2P7ܻuyɵw~ ɾ6"7>n|4dJ0.jԘ}a{_|xAe/y2 o*OaA}]MѹѺ)[x5|7ɝwuГ P<TI5 ry2ˈ6H cpo#rP%-0</} IDATu(8`w^'xuM)f?AOf.v6Q661桧A2WoQ}-@;/jL`w@|4qckCPX[YiO9yVNy#g zTPa՗4+;tRhmRqu"mcRM"w˝z6YP6%r^_O?xGhM{jRZTh@ӌij1486yhBхSTOnM؁r VoӞB'v?`'kO48' $T E_M1~6wsv1q})! HbF+]^b@jiԺ1g%il E|7E3fc-"%&vR%8A Z8˜j?9;':~a]hz؝q `S٦,LeC~0$!d*k *8>^ O?^o7ϞډQ~p|'-7Q epV7; r#_%*qx~4zl~Ѽ=tQ]b,c/QDn29W, a)1hA BtaB: nA 1Ll>&*u5F8]7"]7y6(~P ʇ@! XBԫ3hkj@C:>18.1lMD"Hʯ@l_I&Q}_Dx8ו9?(Ẏ+ަ''՘>qVZ3Z;·B-nˎI>:VRzC8퓽7nqSTb䛿/ijE`Ȥ7WQJW7y7lxqXHs̤z`ljudnV6d >fgk X&Bm5vk%EQhD-c*LʸotkyS5t68<11H3Z}g52Қ5A9,_ъ[D* ΞŎ"ފ>v@"]rR"AD='H'(KGMKAhqbV"ڶIT/Tހ{O9cS~kɰijZQN݀`x2$  UM1u*E`ۻ/i8_d HrtJSK_L*+R],֤vdӝOr ٣?3ұƶ}+ݯh2Ror}v6* OeAPT9Q08>iyE#0V-Ӌ>Q0M~`j Q:I,N L>85ڪ5$6o, eY`~LF"M1$B oHξePn6}L2: TBr1@H&`pr5?Y:d]Vc>~8]QXvq3~C79NAU1Oz#/WM |FGuGw_ bgo?1f\|o1;s||G ྾=7C ?|p^|UR/X׿{*ɁSхC/~ChBtR?[~T)%H `cTPxnzfZAqdO|r$by *|!RO6j >=AkG aDVv>S{+ yL2e'6-r:~S>{?'Wgk2| q=UR7*,Rȝī(VDRZVIJǡ'4G;7sb/M6v$À\<[|VmqZtHFsUԾ25`|*! Lv3>`~r r֣ow]XU>UbYcXB*/)ru[zEbd>QyֻhS"/|$[XS(1F@"9mQWtf8 /c_ɖ7bWc_oKr6i˼裦mkQ5XZ6ğCf=y4M~Ohb?/j'V$$2bROъ|M{W)ɸ VelA 3-O@sNޜM+"}@nTXXRٝTP;,]md@k V ܂UInH1:FoxU~a%V__)٥֧7ɧ)*DJDGʔ4zRܔ2zuHZV&/;$%wD]w{Y7l7v+,&}M[ňR_Ur#[ @X)l5P}ᇡQ%B2`T7Ԣ*jڸ7j7Tt= { I`,쵟uY+>u`K77K1p^C+j=Z@2hz_X$78dh= O, &nSiur~"l_†C:ˊDb2ұфfQT:"z' RwӨF/9sp2L2DdᏧaZO0eI6 u)mSe>%$śZ QM]ʚ]=%RE _D>,뢠h(㮛+Sɝ|a͔7{n<メD48D6d^u:V$^yg$a/KlnX@xi-8:^se +~t|`_n70*8?W5,l2pit~]Hcvьj9py㉴|!5ӉOLޜjj {s !f)(2fB~[{\pks=%:zɔ wvXKF9>L'N>,ИX.6.{ *`=]Iԁ.<bacŤj_㋌֊ZIMgH";'bLB.j}}|Me57u5v`{=X|s($oLiHas~/1[l~xub>x{z6\Nz A!I7]^jDpdwogR'Trǟ ]M-i ؝Mar8(~ީ2f/v]@xϑ>w?R{*u?4bFt>hOe;.&pwᬢR-~*+>ntՇ3хxbO#Y1.eVc$p㢝kPv66]ZlͿm9Kl]nݶKcxh8!fi~qȃZ٠wua}6^9(uP"{q8%dzW f:YgwQ:S)b ܭܶ2ެ}Ph ljId(=]P})A6:J~GZJI>Ha_@i]NltUcD #O8(k5rŽob*ߌӠN??1hJb'&_CHS<ڀ~x=<щ.M\o]ֲZ%MaBپߥJQJU1kXN2qe=Q+;Bs>|߭ b1LU*B("^O@Z@#IY`ޜwSF]G8$쭈4é,y,d)ىI5% ?OO>si NUG,(XHZzTIF3(\-P [W#hGv܄j`Q3sC"@ۘQ,h2"T}\Tts0n˦$Z;)ֹ]yOk3{zi7Xvcu+sA+|K^xMaB߾#j C m 9px*7_}*e/C=5`\ }71EElmk f1`}T~nt+\k(.F9;nsZE6'xX_z J':;?t`EhII*z$"qj2`R&S9?*C՚O}RE=VWq6}0^M Ld F{K.(dq >z /.>x?'zq :/7X~uPJNuY{yE?6埀61S:__H-nKwϕʨH3L@ jurz% FnErsv 9 朢~ZF`"u"&=aUU赹qcO~_\ȔH^',Qv)wTd0m! |W!/,()ݿ`8q+Af.TPkl-$ػZQZpQ|7݁ oI֣ډW"1,ŽŬ[{Kȵ`6t0m/Q7+U2HNFn/{]YJߑ# *CbvЃR}]lmxk3-(NA7D!]mSE ro?b>ĪYW[^R7k?g! |FD^=߇֎jfj4N+&;.t])!pFݕA;ormXFFc$ReO~ꆦ>&s=?\z޻H$YvKDTsξCc6g^++쨪AN% Ё@V^ n~LUd֛&QZܔb7cq^6%0Z NYF 䪦nĆ~4WM;X'QNsZfG÷tEWp_9r/1Q㡈Z΂Ϛ-nxtr V:xDbzJR2WLa5܈ #]@n&fi9DY*Lus C%4^JTVb{V !.oXc+؎2oaHJMC듸L(6sbi/6ڞ4(;meӊp7rvB:^ hF󫔐1$r{"r_f^@z#^^CGv`ꄜ4.";FYDه௅o46v犃آ5EҬ!}|C/>|LI\k&$SvT% ZU Hv0@ThTG,=i)"|bX`C*y|q)9حG('NE䛵kGD,QdUD<8!,egovL6؛F4%!WW&2WaHod5*_n4ZBNh+.~.Y=Ug&d@\ti5WAY}zO넿ƒBŽƪksLnI(s[ 6G4v)%I/=œhOFxdVV[\ig_s#w1Ä؛hUMeaU*a>de?J^?ǟ@KĖd Dkbpު% Q nriP&Jel9Ί74rOZ}SaQfS N7d6:co. dk=UU5͞CȣX/tOz?qU:銸BEDUb` esq|z]h޷rF'c.YTk%o?ٶF"ve9f';.D6mO_/^~a81 y{V ]U 2:[I}NrIeI|9;&փoҏͼ6{{y>``nYoA!~*}C>ObN6q(Smg* uYi8>9>a|׊MJhW11lFǢ )AJ5?E SĊn[-ݐ׻H;1~1礵K0}GD~keޤ3i;2^"XU'x*!7 L!ɻW{[bYLO1zҺ$ݭ`oBxiq|Syvriɇ&xɐ \MI!YY?ʃF"3BQy/"uf"26 AІrN IiL(U9QÉ毯bW?' IDAT>?-kKюdώJhJN2J7g {"|~;1ZABF$P=֋`X)CQjيGc!>4]S9 R 4uX8@@a>[LhB~9 2IjlWh6D޿6 N2̐d"ЂHpjTE3RYPN"ʰyղ*ir?V lˬh4dHTzeđʑfnAxf#@ K63HI(.z!n,KB ߑwB&Fbf~N~ ۑp6jFZzZ.> ^QMM٬CwڣIWp@;xU^4Vn31jNz6AFg}.uK7VɵR21 ¢o\WcRgXlp.V4(;*G:L0at":9*\({52F`b_ػ !A9%zA=F=LV[sB%U@|Iw{t#٨y2RHtдfn XV ڽVk ד"Ci Uhdl2.'rw%,57)?hX|t i$kN ֢shU./IbkxqU0uL\[t0|̧җQ3|?w,~qMZc/xr}]p<]JA ]Ce3'}UݤPA+9Lv1ޒ}-oa7n-0*bO;?Orh"ca6JCAaNpU<= !h٫3niLy{'Mk4("nu;37y-Py q 7;ObUPQ ut~ D|{MV&u ^XK?oĕ"Fvr)x3R_k2~__=t'WD;3Ӑ^n\NI.2Pgujx0C|#qMZU^vzMo$9"֓9_zk4Z\D;RV|1v" 5fO* Y1Re!'Uk+=U kQjb1֦aIt ah, "+aEvi]JH'԰h0huG> iT( IFq,%I>oH t2'څiCvmEpX ~7+otT[?ǟ@[SźK&-Ԅ, mDVU>qf#)5@2ٺVJRպyG ! 3Zժ!3:?ބ- Y E"4!/ Gw`{\l:߰`IKĠ,[A,Z%zUE@V'Ɏ.Fo\{DߛC?݀`RP钄u5VF#hRB, [{م&,l٤睥^t U 4hVjr7:Dgi|(vi݋];*'㳳U5\9@,Aپy[IٝhGػ63& ״"$,-t"vRAEXˑ+6h3P*Ԉqu5Ÿ&Ұ^$w8:m'rG=6UH~kQS7+pXu+ݳv!vulj|o^+?~?|L?tjۻ[|`Wur(+\wbͬ.r2(O$xtD~ h] >?yG#Eȋz?A"H.bdig7SLo'ayCB%inVo?؇wM5-?*snYe)07PT2_mu<惏pb 2=qgG7Q"*G;>j&QlZ`# H.I| d7gBѩ&Wv6~b#ƁEM荑Ez,}D1XA:*QіL6.0]h*vgdr^8%ީaLq[^ZJ\yfk+cI'ñ[*CHOD*WYA`]H{[@"ܗNx$*@D*.2@Ʌ᝴75v I%;\5G*ґT EZܺA qJp6# K"ߣ jrN{E*)Ȭ β"p <Cݞ&)U= T^+8qBaSS[dQ`}j5ы^X{o.f W Tqʶ퀽+B"'MF)cMiǣ^۫Aa"DwiS}?yQzЭe`b]Y 8s` 86N>!yVlDI,Ҕi3Y£;z]ũ YB 5bP|<+?XFBtt/Z(;C٨Bכšn$nLCOY963ЩIrgVzch~U*fynK'W>Q oψ WvE5Y 5g?wį'~x4u}`ZR6ڨ]W28$4`UK8%ʅBbʺCΞX^Ӎ[M f)[Iaҥ(QLG 첲o$/;D]nTb`gŐ85AʢI34YR~UuK ۍ6-. ;\u@58do;l/OwF!5F:u5/yUA@!dNX.m5F;.!.'d-!Rp<'笃C\K}K=oN!Km.poS&&2٫~tF-&\d* # 2zc \/*Jo;p<~E|(N9RXO%MbyazjSɨҠc7ɴn^tHx% 3Dŋu4LRϏq5~-W7=AF1B<C6r#JOk4vS}YQZ H,~aȼmx ъKt#r#!Fw#z]%ks( L2dKeFZqo u"x1 (9ѴRܪr 0{'Oz [6Btr K\x7b Q0af̄,* Xy.n+yDr,f(IUOТgt+n=㿔~_O}p<R'Rc+wI=nʳP!4C# 1!)6]jqpg UPҒtM+o* vt2/ o?QޜڸT^*խUv|@|UkFNT?xvz(_w6gg w-NB dcGM)ErKaLCwl@Jul L2ފJY ߷p7u裀 ߕ;o!d\L mq̈ D&}XM@|^te{}:pd%@,Ӭc9p<> vd|N'M?PQDZ w\ҫm>gUk4;BDl:{:_L541L2Lɾ5?5i<pzM&Y )t]^O|^`hQy4xJ H2Jah}ؚ'xEZ[=yf2/~  E"Q)>d45Zכ ZeϺXh RPt ;7s:`t; uG6k]>E =pQ,'rJp2#qtSwUFa^uPd7 eYčZ[vY *EFeKDM_2/ZGV~YJ0 ulorSF4:-n8bwU?oAlS C5^&q uckͰ^Dםrk/B{mQ"$ٍfcFL Va/2'a?p``ըD_''9˶#$ &A& ڣf@T "vYA5k"d;B ѕuv U*FޛG2F#đbfhuLsoFO8v4։`z&6Qo|z? -7WښʒQ0vހs*Lq<ɫq8>)2Ԉm|mNeI\oMFb$S`V]IlIP,kO`eF973ѐ6hcw4< ^ AVkUSN7'ߨvv$u˞k IDAT_úXz-Xşa,ss,6@kũ|^e⃹6/ rY"fǤMK"}m)obS;.||?#v3/b;rv­'6j61/Vy[ߓA H'C/.IǹI*x|W3MVɃL U T~%&ZQ$\_& :񼈩h􏼏ֿLgqU|:7 #+AJ-YunڈV6O4_'j'AvN/İ$d&u3f7J;աɃ ;;]+3wPY"i`g g@\uE2[4FJQ26kDKPꢸ#do-!{㑃/UVַkJ82Ji:t6R}Nڮil .owiX71eػ&3*65tԙ9ƊE}/3|O"cvVLW1N*ɢ蹸's:;r[jb "jhxm6Ԫ:Ǥ>Mox,~Ҋk>!al1zlD XY^EQK .Ҏs8'ShzKBkDZ, )in{妽A +ob)bhg1P&MZ, ]ɲүnEY T+qkx1ڂ\BDXf˰TĦc!FVAd/iwhI[⻊UZ.m3ȸJGwltw$QnvwX,cDDDx5aw7I3&NS̽ Fabzs!VB:,Sa v%I,#"jEdV gb,=Cʌxn"…X֬zF*/D{ {Wud{^ ʻUBŜu٬{)*#Jл$3_7O7Fn2*N3!&yts]JH&\\I;~4z&j }qX#iĜ YUZ!>kh 1F}'p,;ִvgr/sB>ӃI7});XW@n^cnUzO?ufRmQ/P(qn@~]7I(YkSME wfah%s<v+]D֪0Պ7)"lIvh('9HZJ ._༹VL:'u6kҬsEotnubwV:';ϴ3ŤO^?7ZXQ9spLSV\ !?B͍n'np砷3B'TyF+TW J.4k'q]4SUQ>PU."1-Fқ=Ë5({i[,%Rk'}@8XdV+x :hd'TZ XD,~n<Cx,jodszχT%y]fʞZ-ahZ׭"q} Rt-Es |s.1DB :*C3ꐕgU+Sٲ*M:ܑei0 k-[Fh)djzɖ(HԬ<Ͻ,Z/OٴzZVX'Ic)VǤy<-!jΒbv> kU]EȦoߠ ]kBv\!^ ‡(7ldf jg7W;R 4l 'bL57^M\T|Zt/SJa捯|ߚmʺ'ƫ(Ν 7Uh򳧮|_o ^&)џ֪C|TF,h,Ծ JH=kn"z@Axoa0. rG'T[P[8hUjI$x/zEY lCh5U0):[ի~{=]Ut'7m,I}дs96IFhwR%_mKݐ;` )F\/L,7V]iJXP5gÞ@.q> 3)ڌ6D,D&ͽw2FQʫ!+;Hd iM4DB@j_g#Pi&91] egw<HCW(z]C Z8a44QԳQS;z OycE/HLѨJ@3L05P02MhEAʽ6a[n!b" Zu[fw;eFȅܠ8}o'f | ]Tv@kWTm콡/v?Jo'_`诼#RsGRIpK}DIUky(C 5j3KycU_Od+;2:_p!)_h}%/+xGfE>*=+~C||~[?g812jh7wh~0j7|^|x}rUKX5IZ4cWzP\ϊ jRob-6y,-$U!-2%=g)gdSJ#Ć&Ww5+F5=>hxX]lP-Z,2XHk(ap F;Ύgn>p TeܺYN?QoJ1W U67Th$sVQC[쿂͜/F,ݿ[/jHP* Eq0~MdqIj /fw *޽I]*fQSkzyݿrIq-zKۥ™_S-Uw VESPz-֓]ٓ-PBȣ4ʴ(u c|Z+Iٓ?LD36d(Mʓޘbg SMACɾ_o[ ; QF\T Гaq&VSAQCUvv*c05byda u_HD_JׅI+v>>Tp{1d]ľ`t};bY7)zb/o"&_$ 4]IPƈ[B#YrRxojPXq{1RA;cAIWV PYh/-uq+(۟-A+V p~cf 1M?X[9s2FEcd6ts^cq[e@0ﯺ Az+@*$7.Q,'ҁV4%'*Ka,{1߫}z|o?kf%[$g1ֳB|X; M+p;/&]@SjiJdYv|IoV>{ި$g{fh^~@YHN:sE7q)Gۨnly$E/i"q]H؅F+kEzi`*5Ն$/L /\e#%┊d ]ʒ1NUpRhFJre2ft=-+Yp7*]s)j.aR(\U Gv{H ?Zm=yͰi*[+ ճó%LݠK}F.&ӍDe=HeCjm jѬ( j43>_/r|ƽ6m8>ˆBC+x&`pĺ]z*@&>U0}tV2"1=HŒ.lt-TIh5ɠ!b;6u31dRӵU JMo% Z 큶.E<"o b4ͬ(˹f_ȼޔ_;wy (~|˜Y)O<I.ör o0ro[ZOIp M&"Tyzعҏ_P ?Qņ *ջ=|O;ˁo/;*Z?jHށjG7T*OryFIXǎu4|ԁ3iZ?^5ث͖]ހL*ŭԼ`;7i /8:ҩkkt:BHD\@m|t^oXE~oe b6UmA\aADcr]7V lPy+Xc7 {a8_RrM;6R}xn,&@zo}Q1id_o$.u2 R4Ξ%CEf 9|{ScF?z멦4rܬ|#BZ@tB$MoKb~1Nki#HT=tCL|7ho:+@;,~/Ԃ/LAj#ؓYu$FI/Rz~-hIuь&B27\͘B%M};|G {!fE? _FcH TAf|'b^Nk&K q2]TJ{pF;>*9, Ċ'I 9 /7`U IDATl2 `ɹoLFռ^w)!,`Y;#U(7.%e1Cwb٫[ JnBӬ{_YȮ*?XCW!iRvYB[ݝi=^bje+8JџXK6Z@G# 4< %pxśkhV$f,LƋ8:- ָٰ]D$I }K1ߋFTDkU̎8IL u2)gY(, Y 0"HZa=,T$NcQVnjΚ7Tzu|KUuF[ja4RA 5-*d~!E0 :dmlF@w9ВJVʡ4t7RnH(RJI H[0Z8F!]fGBsgUbDZT¯\Ht\\E'H_ĊZi|Ő٪0YFw |-V~2_7-'K&͜_.U$aI9F':[&dk\M.7]1]{ҨN^ hX~Gx)άC&JE;D4qF(4YҸ|?_ڋޓk_=7A]Y+= 8nピ TX&%}j/L>`8~4?t)oZS{/te ]]RmfzI %f#oa+{9r4%󶎳_UўorĴHZ3m}-)I&!|˲߈~=. l2 wDJ;RF)ȗOC~W xئ v#ғfQ *eRr18,A';(' #B OLdZ Eo8/ɽb'I,CIӪYvwuG &$RHYu*&C}so'y_1?&/7Rn㚈 H.CUҌd>kf s7"GMw@_.jH bf_{$cAެBѡorV婖 JnF܅ l!*F c]sƒ4 Z?_R$kΚΞ_x^.mHFZ~Sh)2!bA7񓽂؛FFQkwzYuxbR/zf^+ljQHJ9ٳֳ֯ F̆ieTN¨ukgCJlPv@,TmDy{VN&8ąlU.hY 'W@H6{C ۡ.zlJ7UtW(taY' P@rQ+T.XQ!eӎGd*Wc UpRnTud%V|ZA#-Hs 3y B.";v:0u 5?XQTұ18TQ+fNg#̊ըhV=|͋:RjPI]>iLxqhH_xF`Ad'+Ddu$@%1^V>ƙ Ϭ9*jzk|nnMtp DIB. E΁C]W^ -s":YϪ:hB6=]$@zQmvJsmT=Ȫ@V~(-(՚5f0T.w5y¿=q-<BAϤI"wa:[D6wtpEb: VQ~gb+NQ R `ov b 63רsK N1ƈYIJұҒX Yr\Eo-AbxBл_ s#ㆀ.. NQոd$£ GDzEJd5@5Y[*^^w?_SY!ʼnUƵnfmZMʸ:Gɭ!Vo\u5+`Ft19QpnO^ٰ^:XF cq]e6 ?UƯp em.l  k/[DT~TlO>|<^ʮ͌~a oޒ[y=Z,?Ӕ4z hdX"Jtj0w:҃_^?%rxq/QwMEqyI?*ǻwSY^nclIt'+r~t)mEנZjPMQ.UZbLA韏~߹kmp8H,ܓv|фj1^T~d0mgi6&hNH{w| jEN{(IZ_O`^/6)J<0ƇABx^h;+龑@?y ?C:Bɑ]#G)_4^)FEd1оa4M7(&wM;1&A?Y ^ 'Ƥdʦ[t"\?h;/FۯdMO.(+G[%6.#nbw:.1Hi5,?<?,Rayl启р=X~"Mu(4Lmƍ!YqadX;'q~q|OTJo!uI;w:([N51hTѫLtg+OT֒f|-T\kC N}V0f5wh|oVlZA[ݕ~O2 drU{+I1-Bva\M5Ɣ(2JDHNRFW* 8LVMf*o bHV=H%Kx <~6 55IJHPH\E*rHSjI,ē ㆍdbnE`41FAJa=g" JD<}k&_#y,0bkЛh'`GVv;D;X<]o)1Lqy kBx\lZqDV9X/!a&v6Z?`;IXp2,%i6> *UH|~> ͛šlXisU{B 4d1dU9?iJX10Ϣ— 9K`G.VS.rK#@Uh4 TA# ea٘ ֿQSAӳnSV?>+zQ& 2y'5,?U(X0"vC{5j"3>o)cEdUiRHSz$=ο l7satLgɿWMuGM#q( L{:}_Z !1=8h~Z!ֳ4p4N;8]y6拔erۂm ADV9G1H+5pr(ҩу!Yw\86>+ޔYd<~ޯz3Y/~~+c׿a{4ε#ip:;:q_Z_;4ʪAyl3ybvHge>f t4M&|ce'?6 M8UpNtkU!*҂k?G8ΊrRh2i%١w͛9}3QhNWV5RUklٸ6tY7&`Iۙ=Z% ICTnT{m@9fTLGrxM'Orf FG ۀߗI;+w,Q+nql| Y%j8y%BխT*jYzY[# Ͳڢj Ĕ>: 0Md %OaE1򓭳I */4`"ÀIFlUޙZEk2R!z bVX ԨYkX+M( vݍIkZT Ud@wL*,,f/b7mS;Q0Q+)B4!ICj>XBϴ`E?M-7;W5INŒ ߋR>}ہsq38ѐz^4,H;QXٵWX~Ejg]5P;Q;a677-Mf*zEOG1?#K/v5_J8J:nI]hmT#$t i^w.q@/GX*iaWg:b鳒M)TXlL@B&%IIvKZ*j&yM) ͇dR?ގ˽u$EMD[CFZϧҘHT crOcե 0n[jeފ**(mmN6/6r]VF{Rr~~Ɯo"vBQP0CfITpS+袸h ٗn,fdR⭥nua2BvENꪢ3R{1v+!]hQ* GvEdM!к0}Ch=.vDM^u5Gpĉ0<m> 8$R}6E@x]dHXժkKdЃ I l@ׅEC1D! _DbeV'κVMzqD3x󆰿9`uHKqVl)A sz`Ppk_dWYd1zzzݝbiiA `eQ_) 'x1:ЅSJɰ`(LMZON]Up t8uds(97x~WM|^2\^qp5rpXCLNaqƜO$>^Ƅ)} V .A?6|>a/f;1J)A8ʟSܵAq{S؍+ ~.x kYu+ZatC>ykolƚFn/^K͓f~5P̒&ڍ"s1b$ \anPD;XJKO,&v?ѿxvjF\xc~/xڨU5eS g^RM;!J"r=@7s/ğ*Ŝ]Ez i.^f敝XhLTY ۑBV-?JT2w>9qK$ȋ9;ZCFU%~MT: k_VH\&_H(8q[tb.i 8.dA(58.dUO*Z7^K\TS*JVk ;&|!ܝˬ[b]oo47Vvr2bv'/WIn%ApSXPs골V',jb)ܫƊrY%V@bsphѬi 1$ۂ.Z ' ͅ {V!.iWXF.'U*ۆHNBmU)EЬDlG.%BjITz+]6v}ChhZ {W zSV?]\Uk2H9XM{C7}}$,)@\L\6/Bx7jZf-IG@?c ?xI_J? ֌ŬGM.dl^B@kQSwcrDoj\8]:O?hdYET+Xqmduo/<Ȋj HF2r}$|7S>hp6h郝oOнAsc^gҏǝx<15Fh:oze Djo Er'3>N?5Zq!z`Zf9s@i1~OrW !慌ΰOv8M蟜3~1? o3>+ta#r4) Qke*JX{rA9:@Ƥ_%Țox7s]5 n7i,UY6=ڰh*(x.N体tq=q7T׭bSL{,X~b_}|(uie='F*}+~A\߬Ul +$/&/f&C>ia/d]|l&H)%>Nb3_/\bp OK:Y8fpv,!eg~"xE v$͊ϭJ}u)h&Szql'7 3\mR]qR`xV$g]20u.}`Q&+;;h=m\se~hORJlE;NP7|J*MFErV50G렍ʸV1N0DdE[$Qܭb 6Xyak$vZ}εDmuԔ|1*-l%DX9-hE 2{0cVx .{Yd] ,KӺ:k#@RyЛH0^ ]J䭎]Ŋdn..@:1mc,J.*V֩6.Z\]ـDrc^ZYV@4 $3f#lb{^b T1 UKXaQ+oeZm90ND^\EF&YyM<@OٶT"g}EluV#?!l5n,ߨ⯎j=oǮRΞ}zĮ IQE|0l;~`5r%}6XOg- $xOM \&ׅU~Ft֥cɣ8.} 'vgmq5,҂ntoh4&Fy$DH6V,v;hN"'Az-$>sCC4"Xu4FUN+cߴﺰ;2>q&뛵if_ZTN&Jk|H*5H/k%5KMPDZQi&EMkv~~r.EiH:XLs]~dP~&t6y-W}k&1wArƓ{}H׋5_+ZNVTAh7C7μqcd/{!'V٥KW񚒊_Cְp5Zgѱ6KM*bu?joXm&r(>i2]*͂N2Mj@֊+ޒ#[J?x Hc=˫3Y{3gޛN 6u)QUFo&Kln )\G16*u' 8"* eL48Av44Eca4s_-Mol%U!ߖhej2e l'*A kѿwA~ݼ^I`**i z]D{Y+r#M3omCe:Ik5aK=ژ؞[D#(G\8俄R87/MDN?v"F8X/Zo׃/zѡuVKrяOdes'oܱm0^!kW)qTd43MpAJۉ&4mXu@2ju(U!'y ިu1uWI]-vy~'ɗQϼvMC7~UHvo4MF7`!/6 'ZͻQqXu!S_dE~$;CX/6J]$&r+QwG ^MKg0\K'O8y!iPAGU><]?^Q~u̽ɫ8-ثZ^ѣB\%_8_Ⱦ|?+9ƉrCX=aEs d'=& ?;~!s;hQȭ.bA]tuŖ]V?!'sN^ЄS~u3}}!/֑4 .YzB~j2ԎYytP:/dNO'NBkq\2xO0/+Y{a9u9/d- ޲YOWONE]%l9@.B7;vѪ b:W8nf񡅂PvA~,|Fr 5qF&wEV955tߵG.GGQ:pbaՅb$lk|f‰P66 1QFCjʛ h؉EZe#ZRj!Y:&r~si3 od. o)4ɛw-Z)[Pe(̛$ӫμ&w}HKeg;כ̐El"irzRJq*Yf[#q'c"$:reMy;[:LXD&_K `yec@~ ^Ǎ+`htYugMeY锊WͫsXcN?5UQWM&&m$:`؉j Dt}(1{UX늍Ȼ2mo9#lA_UIf!,6e6 ~=_JX(zR뉅OI캥&}v;$I'"j='".Mbʌp73UX/60̌0Ss_ߕ5R ߬@Or44:OhirVo`7;."r0~|2<&zBj2=B5'"֣=7w&փH'#(?> q3.E'cRPy.qٜʋϘy93H5k߀Jiuqc m!_p56hR9ZͱW_,gvo֢hgEknVky(-U'zkMoYl8NŒF"&ݑ'igg"sžij"D.z"A2]#*m(9/*Rjp[_ZjdR vDNdQ*x8E/ ZK|]yyCJx4HY9KMǽp1pRРF4h< IDATߺC(d ڰY /\iٞ ͟Ȼ'ލ\>ZJӻ'CBp|q{]6OēubY:s7^hTɸT{>X$1\f$fOɄy_i/<,tZ߉#-*>!E7#Qi2艹9-|Zi%Q]zOǤBs۔ Q\btPM`d4XFNm6X,cxhD[ުtis󅶎ȅك].Zwdb NQب%3@LhU$^M#FKNmUԭ($aJ84Y,:4[ŭjt]\(v/0Wi]^Qd-x`xn!W%(Z~lU$1*Lo4|$moYXE] o*HC[(L~>EȬ-BK'W'Uꞔ&d<_hr \xvԶJH+;Tt@.-a2,}N܉Vę^}}]j" Qd|(fQQ(8cQkJRIHmbe9ŏB\jեbzb-YcDOh0%.)I^4gkxUEOc2  ;+w|Ng^^¶U)Jpsլo:|~࿤mZ^m3N6Ğ){A>vۂ[x9~ǟIe:v~ v_8% bB lcnt1/6'-A=m u.,qZO|MzGԜKtO.#X1u}aWpѐL/3NJ\S!扠C`tۊyW/?)JOL";nD Ckdj¶qxv]ivLO<)O”7~1:37r't۞|/s,|e!x#ێ _ʼnUuqOl6D .ۄh)xݿy9_ UŧdBj0?| OEn2rtȪ Hs0D4~#x#xTuC’z!"!?Xϛ(8Z!6*/+#oa,tsGqYyܙ89P)m ̑Fuyy6CS g$Jc;xg=zyp_:0lkeYigЛ!6 FsX>Q㈊02xT[UQ= }opGlbӸ܁QA#d* aO>F8ޤ^d( E(X4雲l(MJp̓̄5E v|N}sq'jj|}ǁm;FԤO d!Isg?s/ ~2E鶱?V OsMhicF7"( xyNʉKеQ4~%6.5;%>(ߋ+O?%.&6c/(\"I^ "O$4M)5Q"_GÏV&ASǥa*#UF(U.s A^Ra(= 7""RB EKE6jԃ!t,{K+%zgH^XdYIioh+9rSI9@jLW2Rѳ볜5 +yRmĞ)(JY&J(BAW0`":B╞'\e$/U. 5шd+ۃ5 2`'(|SnmVt(Y84GS7&0ߎi0bȺp&篊 u?Y-Xls֊![wu‚aJM`@||_4҅6AX';cԓuXpjRE'E0}s" _y~6B,OoA5͜/Tu^Juk?UY+Oowo5`.VV"X}DohoXn nb*BHcKbͫui;o,N{m&Iv<灯9͞t,KK<\6 y}UKKT&ɵJB$ڪH&9dֱ]V_m֭ZUSI> iyWдӕ&`-(i;-^H4J]%H 4BZ߸;mAd΋/V"7++a 1tOΒ\d*4B7SuSt#^\d(YA=AG\Lߡ/uf0SA`RgܑS¡oK(\aI-.<++pig ɷ.6 3AaV 0 s ULcf̩g{&7EȪIF'R ?hp $t,b eV+,M0f&~-"K @N֜Ϗ?'QS ?0hYY!{jcL4angB̃kͫ]QF?vǓuItgr΂/cdƝ6#t5awQ&]*Sju׬A2Qƾ?wJ!ڝeԞ6 sG\nr:^+ ?]j5fHk{\7a&ugpddHrFuz3?H`h?pIR[ZS8f,`p3_xuR_&Wy>.60ONwlm|ohAຐPOjZ3Ǿ D= S*S=N΅:3r mH2΅+>vmIk`I@^T5t+iPpiԺ2&auaiIHX7ѕPgFReZ֕Jӝ݌dUky9W;@C׋Nx{c4ZV1/QҘMr%$:l$/N &&Ihmc<{0OO|zە ϼ#Gf!k#V^ `֞%I {1ZÚ71L<P% ȣ }!^fIb j&J#PujcH›܋e*Y-a,y((yPZed{,JwDas.I:ɍ3o.XY;:*QFOa)x*]lz}VM%#0-0$fb y2yx1wѻک^"xV"rw( $d>v#V=F*5K H @ E2^𑉪1IMi$*4wM@~}I.MakUfhVʎ9څYKuĴ 6"+$][vȝ 4p3Vg֪%飺?ϫJ 2Bg96rE x㋸.> !1=X6h1N.d**wkضy2#ŌgSOfIY:k.&E0JKă!QxŢ-eV :ԥZi8b*" iAK*xgB̩4;B%Rs +nbzȺEUx6>"Ivc-FoKpde75< F{dIGwlM% . UOD.RuAF*'-]Ray )?2didOdK.hھ397'g3fAn֚҃(ާų"YEjֱ:.\x^3ɾA,a=~#t tJ=*~-r{+9 _JW|2k%>"t}he}Wvkׅor.Κ GD^JzōO$ IDAT+zHb"5dTNCȅ"Fbヱm4M3QX,Vh3y@X=0W*JR1U23Y^ё!²F!"[ɤV$ڟsFV[`ޭ2bMgKQڤqq'?(mFSe~.bQYVAQ3 e2Kc]ƶ=7[Mr"VMBufObJ yJ\z3c3ցC(V66AC:A$dMYso@F^3zIq&-ZK}aV1z;pY>ɥhK6obmF H1fzjI]52^f|nZӳ)#R;}hq +mJjbU!tP,nFخ Tj?U4V! ;ULR9BdU\堏Z8ՈMkd`q^غܸ7,&Ov<3 ukO\7f5<+HR2mlI?d\qչ^t/\+>߭ډʅ'3/8m2S/ 6HJF2ٕ͝5$(x+^VTҲP *:C|Qx.G6[3 YY˔`Ҡ>(cor.b]lfLU4=׍ ,Su!wѷX<Ĥ| mHHLsb;?0!, z_O.$A7+~ ]'ʆael;]'K7\avVa93 |_A>bC1/3/`)kt:?v.klYD{!`Nt0UvjqXL~/oaŸlR^$hw&"ڠ)O7V󂱁{q J%+QM:o3iѨKnI~9_o͐s5Wm#<2x]ؚx<\||/cwpFyP7.D~;OĜ.=8Y&]Y4xn??HycH^4֚qrN!_X*(Mꅺbۣ$(1qPkN3cEKH;}cUCnY1"sSdVm`Ŕ0cKFZL6rrBǏJ\C +hX׆̅h/XvfpN 9s`u#a~AL!#*$GI67/z}m `-}*ƚIŦdDKi4-%I'k42L_/ـtLcEÜtO஁RzB'`rX˔ {\d{.u7B&~ED Mdg}$Dx$HDsF2gpD$*L Bڡ޺t$֠R2Ɗds>1W$s&!lJ\)d않%HJZ0HU~uPr剅6A*j2.gIVF/ȼe~ ބ LV nK$;fUuz{8qNgswSU|USPt?/`}}XyA<>J·5 etŞ^ I;NLhl9P)FpO>FoAM@79'~dOz c<|o cB+M"Y*W3C8?1 ܘT9v"4/KyԬڏXu⚾%y>1MZ?0nbx@QAZY$?n7=| }<9> yZ=hRĄkVj} Iρk=b|٤r쌏?>~#br ?civF/mVtL9˵k6 r(>_ 1v8R6LC~U5G(T^^UE)ygkzhUΥTvao"Ti u'&VVA7͐FS!Z*T'W:S'5j]t'!`k \wx+pb^a #MHْorQHszk`^ v|W|W=a,Y=)4Ziyhc+4."揄_YjoF7+/tfϡ&GY/rUЏyLxy:֔yz&VYx|N>~c׬k7ˡ]J΋umD_q-\bdxv{˷]BnO8~W~}ɩ賃 dړ8fyD9Pi6Y)+ G o _/E1t֜6q'I<fŌ?x2q1@v]xhì޴T/먵:z)A}=ɹHd Z*Sa79s{v%MȒq&]Q*CD 'W|7!jPr '6?yCN~~]̟FIq qOdb£dx}|>'),Xx&ߓFrT N2:RRZ~!`q"7ZÐ6([zF9bնAvv *?f`C_.kR1g$T4?>*]>:\^d(J5@;z'fɾ)5f+o^_'iYA3k fA\9+D:ΎFb%G?9>̞XD}'7fY^,f x;0k6˅C^"C9q>4d'][[ЎY>> Jo7WpOzu~1ΐMzB;bƚ/ tXٻػ^šS:#6-noB+h"\&7є'/4'!O챘j@ މ_> {1OXRTP ijѤk{|8*GNFlσYBb#Խy'4)BCj%Fa ?ѥfXI vErIمDxIn'{X"3(uoHF{I^ dU~y Xva c#HP03 н!VuvZ xZkh{2l_dlALaock^ .y%n&8.A RL3i? km(fnH EApHg.pNv'+nFd܁OM ;곳/Qs} r4%iOz+ |'4';q>>g:[hٓu}֘7i믊@^_/Ʈ~#[X/nt8T$<(~=zWv_iH7LA7*iOl<шxmIojUwfE=+tǁo4gٺRAu}o} ݭVqA(Nh(iq7QEʛâڸݻn5 I Ӹ<(J$ ׆I պ<0/{aE֯ TQQxb Whbr}PJyWo}rJn-B\H%WӤF vX^,[-*NhnE 뚠9HmҤ0gʯ~vj,+w/piN{:9 WR<8zZ6qE/SY~*Ć o* ڊZ5z{2j\^O)V7dƣN35oee h^QH/$ǠQmPT8Qj DD>8_Xu'rnߎjCC W@aOZ(^j&g? Ѫ=H4y]H 8q݃ozmP@V85ya냌FƅW~P 2DoASs>`jl!ZV f [d A!FBAe\ %:PI`d=8N-47Uǫd{Uxͫ}k<;M*dAXn1 o'\L$nj ^!$Uj$Q UVF_!Eɩi$lz#6ғGo )Eh=RdK47 _AHla{3E -TEm }ZS?XlΊ(n3?K5 VPe@hUV+JU 9Դ^gmU_?BaNzJNWɳU[;" @fb2WF"4r;fi8 s1i&j-JZ ]@ v"0AzCsnQ*`j5wv5Nv_J2P='"H>HFԃ2 Ag㡈o"/TOP<,u2ڈA:9*82֟ 6+*D$#DŽΉF' WHNWQB:塚I@\ w#vo*;Ud k]*uf-|͆ժ妗wϣN5 }<꺤σHeAj =^h rzPMJT` eZ'$Y\M {yq7 ĴД\kUZlI5e OWc;QE~+˟tRۘeYι!^篌 CH&S`o0lQuqeMxc]_<>> {ƅ/ADb91 }V=i>rķGG?@717?_^|DN`XC"p+FuOEJš6l^X?9=s# ?ذoFh=; 2;)MD|ӫdWWcoG$9[11NQZfHVZo$./fɡ5CYcpIj ˵ '⋵zPQgToZՈtg-+褥#}aH3D*yf#7߼dKYN:2 IFpZe' !nfa5QyH"?x^ił.BY:И;PjtVlxE`454J$YڒXahh3_G#W!.EfEǺuԪ;%"'XIBkyg5m|lE]3wwk\lQD]ɮ:PnLd Q"'N{ï{wItRPH D'ZAr Ͳ&4CM-**,ڇK507hB͏/?^c ?2:٠ C8W#oO/k }Tm^/W\5v58V~{#K=peHpb ibvV'{Kd2 hn\*WB~z9F?/|j^UzQa_ +E{*L;rz m4&]f Z/Lگw7ͪOu$-:&7;딽"m'mi^\/ZN"[Z0?HAŰ2rIp+Vj-j^êHkdNXǿ G #j,Id-K>ӗ8? f~рL'F~vhfXunE2`hCvXFfbfn:ɚ Uձ偡X~y3 bG“n@n,Q?1kӡu7%ɛzz4vn.U6)U,_?I~4 Rh[3Zo/RFYt q&֛?8̸HS5x.8L`O`BL]lҰV!U4 ׍ F7&Chbd'1XU6fZ;I6\'fzĞ5 ֐WXBI:lBEJ:_xI;`4նUe&T%hbf^^e4H\۫t{poR/7n +FZlv ҈uC.^:Z^v`flr2k5|-Ab` +gy50װZ*`4qJ^f6;! [P)ESԁB ^+R[ aVFv|Rg^l27{:MvPwi V1Ny55a7+"N$v6*BH-&tduJMjEiQ>.4x/:d(W$qۊlCd5Ib)uj"ݹ?D6Y1w4^c ?y_!i~+kC4v"eaH n Za'Z :RAԉiF?>i" ͒dw)# "tl4h,x PLmW 4Obn͎!4arVi{Z=b:kY/inv,t,P?ѴZt3Te ͍F1'k*x3W⛰^+ *ig=p8JeM8Pylk\_?y!9:q,FjFutB۸`͈Yc}`4Z5,҅ddR~(xA+z8x~k]4\3PzVxgl =?h2 8]I#:nHC[Gݳ.FeJ 8c)ؓ*й~Vc|` > 5>G$~ћFw޿#lw+ꅼCjn#vrγ ߄ziG2:݊/V0nyr4p Gw gt)/5F8*{m+2xn=駰& g/nXBW hȬ R,R,iD\ExWA>iv&Pn+*-TBB?W{ .|](nddoA[Cs \h(~{f5b/ޯf-|tE6t, ,}V4 : A|3,HnzIvEBr i*N׽bv&;mFWߖ~3b7>8psR@;Z`M렸=S,LͬX Z=i)F%2U0|=Y^Y go hFV^iqVj^^׳Ccd֦p`א]Sײj6bZć~ M; nlV,CiECLv㾨0rwԜQl޻9eݩ!ehTs&ei!@B:R Abe-ջr[H(?@w +߇4;PIJRѴZK WݴPЕP|L7'BU{V/DJZp[Q y)Vʨ7 7G<'v幜"tǔ\_}u>63 5ˎAEq07grK郡<Wa4$; a\*_k mh ڣ1WгmApt$8 櫜5 t_B|Cӄ|⇳x7֍X=9Ұ')kO}U ,$NP| ZZt[;p1쁪, wVFZy+e͎ES!c~!Z&PT( f.A#W&='4n5.0UlŅXQ;1JKm%\ИBpbȁyPCG1S+cG(_[zyhˬTQgDVϽoa[j++^WK%o3kYnf+kRۅI y>2ߍado.,n,k(His¬@FvJ%8X\8= eG?d-R+V*<0Wo;%h:+?럲M0K+l@H7:k ! ;VZ&Wq(T7d0Rk^Y,s* bYI%j YGuN_q3qתI fGE} }r7Jb1jjE5po~ ™Š9䋴̤=:n_U*f'ׅM*x>zo^ZJihEtY&'ptU|T..Ƒ}G V!\">8V9b]7ڭ}yC$Dj3oB+<Nz8cՄi5|)'S 2BXd l^@n^/~Od@:\_ICdn2ll;躋"~W޹.}0xfܘl >yUBb6GUؽYh,yrx RBv:%^ }TQq 1'w] ~uR?sm\4t֯}Pf&P`3W'}qduHEVLD7QOr)Hiu՟vT0)6/'pFE &* eBy=x D䃓'B&68*?n'vNv 8HplB qvZ'zkq^=NFf9tGyq! e$nڴ %l@'&g1d0!,`3q{cmе 0ȥ*fH8vϗFYt⫢e+o̍Tb-Ky#d$/ߛlN'=ǔajeqd+mM5}V MI1\7k/4ha@K,mZIpZ)I̓!>ՒŦ lqw\V$!-MwԤ*$DsS{i)RVlv仰4*1viq!ܥESsH%NLЍdw5phE|*PXIē-Y (akl02)5?%/FJaSJ kD2/PR2xP7Vf3Ѻ+0+84rDIAHhyh&:'vA=>Po'պ88&_;*F0bguq*qQq& hU>H}Ybb Z/樵hn 6 % 7`q; "3PBZLL+%VBV1;pS=MOѱ~*uo0\5d(aH/TD#͚7k.zaY?N /ª| !dj ׋ b]We!?P2mN^&b&""]^|LohVAhG <1u?)WqH6sMlHcK5y٢^p%p^W2&=l^wMX5f:'fi 5ǃfr1be5fRDD,2<' ''V˽!:/~&+hx7i+x]Ϳs'+—O IDATrj<~bߤ |p"`Ѡ wqz6sY}9I|qpvFWX/ΰo|;z=9m!5Q \/B7ožmRZۨ@M\jL!EpYa2Y4v/̐\c-<k'nD7ǏUn} XsUrL$8sf+ػ1o\w4KHX_b7`{. Eڃ()qVQQžMG;D(7so6jAd>@)Vdϛ'"Xk׆W԰jEo_hy,=gȉ-Cہl馨~=ވ1gQӺF|+қ"sò7yǴ%Uwڕ¬𘑫H/jz%K U9hjD׺'2i0=`+F%!юX8͔9{"Dߴv1( k;ZBJ:*pxT- Czr_/v^? Rbf`#yPxc \`lRNwdӂU lqr^TVg^R kLgБT :/v| j^|Du¾6s'?Sh&l-@㺉U]1xh|7~h>B [{"/+٢rx ;vEjArG}Ov(BHB@XqS߅VD# M&¥պ:,LB2ICkZScQ5_$NR8Qbm ? ٤ siC2#l z†7~4 ֛a;;n]rQlMQn]k5˿;D{8LngǃfdǢG1y޿x='_~۷߸;8u1:-'28Vb kW}&ȾQU޳{2_{_kHcޓ{,.7޸/̀|Z7FZ6>RșlTc؄k%IT1~`9?7qpXWl Thь뺙6ڋhl$dkIN`9ܿȼ4z3)MY?U{^.hO2@m;]P;^R)Q6Fo{񤸟w'APA:];9\[F:/od;Bmv/5P9IErNVfByCx6O${VDl{S la( ,k''xy0?דhuXJav70\v~R[9|ֽHy+&qXNKeѱXx| S$fc2,_ U9B,fv/'UHE R+G\X9qqLF%4JG?j&5D.D$Wq;bM^5 ezTridPd=Ku~&xFABph FL,)^F0Jˬ Uf]7(4]x(s;CX;,[cwJݑA[& ʓfHB!E"wjg&!@ dMUD\5ړ7D ~i Z/+Jkܬ𙢊FI4?J4er⡙ORCk7ZYOTrDŽ'.~1[=EH\K8^;UޛכV)]JMk9 Ic7q96{E9N,[10Q"E(ky MQoeBj|!*hG%H9xrIZyIЫ5+%;*b*-a~iAL(jͫ).Y!IC!]HU[B Mck|'UӼ?gA+IʃFD|UμVxO87Vyk#@+c{oy42OeePGщ$]&ZJ}lGj5C0EAeT?XB/g}N~vr-3㉌{?Ir$FM ~0o,)j'Q?73j{:vl܌FƲdSF֕z]1jb dź bqBbрewb9̤_ܑ_@kBvƏ_l?gi{ZO4*XO(-bE7 g0:A쾐x̕G#{stTN\o2I B,q2dv6/hg~GM98OEQN[ ƼBWE_ 6=aDk},= ˦^Ζ{w>颵`_p@l$nkkM>."2|4 1#>ՠ` Kv58Jo~B 'tiHNד!J~Uω68Xw󝈘"gyK8rOl+I6foz2Hf85*%n\)w)ObW4²Uhi$Ǎ+뽗@1/e`zpΡFj'L-eІO/tbS,WY;*шahIC.Xnb_{b(q~ЭtrfQ)c] 3yrҵ~㯟BNjq|9پhLw&2xflI-wioIp]k\cfAJkLJmzvlw0hnA88E4DJ:]돚GE!FX HjH(M#ٿ".N봣sȦMɾo;nxi6Ix%Ƿy_|7H;(&BxO7wӀII #丐vОjz^T]Ex-D~yM7*c̊LA@9iLE9lmVENȃSe?yͅH&A~#ߘ/gω2u][8(T"#|H,,NЃyMdd'_ g'?eO`xd腼ۿ@)sYy}wnx:b7@KNSZz5hD&Wnּ 5.C^|ўFӳL NHEƭEl8r.d4-dJzicO'5-]8œjw%9ZrAo*"]nac2cĎoL#`Wc >q+~>hljZCn"ϋXj$栃r:]f<3pѻ?M~U\}#7zBAO訯IJCQM井ǷM ɏb[NS+ךHҪiy6CV@l_om3: _u$Ah 퍝ܯGw%Du/Y.DjD7bٴ{;8Wi̔eآ䜴pw]ugϡW~gu>G`gMknD(]lƅ:7~;yw}U@0&į AАYҚ7'oI' ׂxqmAU+x]nUǠz/ҽwBbӎw%F~HN/&gg8Γ>rr<{>'X+4 e֐&;I,=}>$ct9`@g}Zz4I'BcžZb 8S4A컚Au?xteG̣JtDpY~'{u0'f Ad YZHO[ЦfoffY_D,b;DNoUȡua{|Z&4TO`=J*BYAh`ga@ZLM{MEI;>80vRV P/+af_igIpb&^8J msҲJ1HCo=[:/:Dc9ѥbSc1bl}hчR'A5[ᥓ/!7t *Qr Iͱ?J̛ch^[MDFꜵ*R-,].BB(*ZHJp;g;6+8^Mu dtEfҬ@Dڨ?eAŅӚAƒ]XZv iO$y5Gz;I)(=АZb0$[lH^QnS>GMH,k%Q-Fݖxamדԓ[lڣ7vTŠ1r M ۅiYyނW"9(كF{&N<~yl{s3@.bú!{ V$-.%r| Z_D4fBX<;;?h!w;)p~{{2`X>;Y"}9 q2%gbbe7Dq@hd_؉|Ok*T'F}gI-[W rFK, ^/;-X4d/q vb=1] Yw6K,Ivާfu| 9l \̠23T n޵PdD:vWnIUD=;.lisa;4ehL}YdcRmʯ{mCN )\wĄo?__yJPۃ '4&Ĭ.p>7P Q< dچT6Y~}a8fB<.޻0c|ǴkfOҋ9֌3U6 _ߜ9<Džw"ȹX! Zf'?r^W.F IDATD*QG׵py٩k^QUًf2!xPp3d j/? :xֈk"+xjČoUlΓDL҅}X͔aVEbx1k{r<_0zSU}{{p}?$ia'Hb1Mn\c{l`lo8c7AdgBن2F.Gz{e3ĬNr5Ȯ|MRjT:YdںNo(у;'!'n`4+IL^j5/X<'e]IPZPC[9!i^ٙgUJ[506k-%OtE!4@o 'ӕ:W1Rg#SPԉ"ȂfӋgD?s jVxeDP5  ,"'W/'9K ȡs$U7Bkؐ/[2CH|$ӑۍd}MQ 5ʃ5pkMt3qkQ jNхƐN~ ?kmYmweN1H#tbNDA('e g2Nz^U߽grͲXCe7zp^5LDo{yv}1:j:I!RqƜ%l /c:A[# ?9?i2wZK"x$ fvG쬡b:;cds3/Q*7clm#uv8b^JAzLԯH\f^VO._;7L_!&&mSt*XO|ճVtX9%7$/Xvoӳh kVcow~+ }NhِPHZыڸD!hoN7Txy3`:}نa4H .$'9X;pcC|&LM_GBwQoy31:0TsNҝmlQ!TGcaFRV mu!MMW@!ֈ6v+F6zWsa(X h4R⡶ ;j`1m r1BZTT^~N"CPbbg=OI!4Y,b mtH.&$իzն'F*9~i4պ"hUa%"PZd>`-Vh)"๕G90U' 矰]MY-!^Hh}P6j11TI+VH!B-zFҥKUgvmF ڈ,HE* U̫/K'+nrAfG*29X VZw HWl(/K) "ɨ2rKB,:*֬\!Fm|e7"Fbub>/_NkAAڍhHx32wltn,:6?σyeͱ[}d%\HZu|/lgm1~m.nN{ 6X68Vy( ej5b$6 GF;;ԣXMQY*]~8飱!4k:R0urfV bhmQLjR]"-dEGdd,WpAzhjkUYrVFy[mV%}9ΕE0qD污)ސ54 2LgȪPۜw4/<>ZlJH;'݄&66e3nf89OzYgg`NQj~{CFx`cèEb(ZeuA2MF s& 8 W's.b^9~cm3zxN߸".4 ۍv/uD;M  ΈX ELe쥊/!Tf7z-GrU Ymwd4}ѭֺ7mg7{]C#jYk` ~CF{)Hp9ɚX' {ch#`˨t pg7u`VkcʟYJ'IP,Qa7#m;XM+tyT=8]^*l7L'Nʋݶ"|Pm tLV]¤U ȗ֜!Rq*P2i+&,e`HTRP,:CQd3RAu(bw =I]X.,WXUJΊ b X3P'Pf*UAU^+OąH5&%Jƫ6ŠY\0\A$8FD*@9tSDbAS)1K\ z \H 1fdWeg~Zcڿw? K/>ɋÆt>j&m^7RZbzd yJX A:Qպp0_2ղTiJI}*&* p'Ú, 3lm?h:hcǬ#H :õ#昼!V&[cbd QU IRu¸):o7z{ܚ(qb,׿b%:3Wf +im$^&Hh9 yV; Lv!]V-9q1GPk7nB ;䤍w"^iY5hO|7%Epvڸc}7giȜ\&˃mt$׫ xČ4%<+lFoƐF?ƛo/o qr_M7yĺ8}m4nJ'1߉ 9߬e1HE(c(%dSE'}1;a(%>Foǫ!zr j^<r] _(Qdā|< [qƚ7ǍfJn}[t{]t\jt*:aL DVg?1fO<Zm0F#Xm-1I?Jel[0Z[ $W=YKG7dfQ'Jt(+`lVMm7:}{>Ʉ] QQ.)ڲ Zsڷ%0Z-iZ(Ry5*慻{GSPL5UhtBA#k0TK-xS)4z쵺|xtLU|Z)~<(lu 7+AWNZ%ŢY|uЙUɭyV8J9-AѬD,+3pf֫V( YUV+%_WjuT<.e:2h QӣndS%rК7 Y?{A;&\OXF3';ۨlKׇ*yq+uhsIp Eو\&똌~c- vaYɌ|$ъѶ >8Z's-v&j\_{r>Y\霙t Vpy17q |pI 6󘴷P&jb+hA3W+@@tE,Zp]xdh={Mhޙ>oNzR.I. ¤f;:g%U ,#Zj m~cG5dW >/n}3bx_ l4vkvCq&r~֌T#$$Ƿs{?#ӹ D'Ʌ8DJSG08HE:O$xyL d7m[2d mSۦ]KM'Oga"_7b& : \WgGc"۽?b#.w x~8ZǯrNcr],"L_Qi7UUxdE/>A` ް'26]bxzbx~~#Dž"嫗 ΰҐS* }gdGFԐ.#Ë<^,^n H^Ѯj5?/4yGGVPa?<'_g ^BjOԓo0Ά;9XTKdBɅ*\}L) JRzN7]$S;Vcm.hIiq~j 6.Y5ܩR_mBoĭӅV-Z)/^酬"EgfBl(58D=2@$ @fzQfd6YeРm.HQ7k \3qVQsJU2XZJEU1ZF[#ܱv2fV Z'ZZb!Ts\ziMWYxy-ٌ& %HdCiOJZ*QF!_+Q@5D4)`Zy?Yt&E4Xq9bѵ8aThbjCBۨ>mO?!ѲM8ĠF3<D7o,@P" ccw|)벗q_ 㝙}AZYw;vdoExR4T.9"bUEm@ԐUgFb>LݪߌSy<^x^gw vO:8 ZjךeL;pk={}>Zu /ay[$ObBDbqe`Z5)xI{F'F@ܡ:u`/8mwn}q$cG-c~ύm;igm/߈qɋyLď9] o]XyZnQKhggZo`l)xg4os kt~;I[s{h<Y^nOm饂6"gF:˽%:/,ĴɎd] %f=tg(cyQ}+82KضuκamAy]:ɖU6t#y mH8b/ng \K>hzqU1'j.z>}rC38SmgO~neר |g<̘if1J⢂{TZ~rqеUg:66٬&t%4zNԝرm{N_s60I,ӥ.BaɈowr:F۾1s}.r&Z(\atq'k kZ!E*ac(7P*:g%5P!thШ{H|\r, 1/r1R _kPfN+X~= P%k F,J'(7'bִ_aEq!j\'ZɺfǟQYY(ULfI' W?Pf+#Ve-\&dQXI@5EDP?V_J/KI#X!DYI.<^~)EC2_~~O?Zc<?DKi5Aan7^`UKE߄V^0דLsGrgtNP?RHh |$[7yGiɠņZ/i: CYH0qFuWy,s<ҳV#c`lLq$`Mc]'ex<+|,"ՌqR>ʖ9뿩Y_k2(e^'(~v`Y f}TzwƍǏħ"3cKZc WUI+=GU"u"b>6v{]j* 1ط}_hƤ AӽvҸwNr 7&Qv)+/̛p^lΓ0W^%!8יq%Wx^"lB =ˏ[}\~=?qvf>!ضUe3a4Z,kؤ5pYcQ0^L%8IߜmXn IDAT oy RȇZ'|q@WL: .}zamC{e6jVudXOd실%G{ y6 XlVLU>#rD2V"ZQldc R g{ Qق5p͜E mDzRVI-oʁ |UP^DIkYv GV|>At_HIZ5y "Ag٘MB%XZy\h"< mu|s$)QFKE R!K 'RD,u>Kk JZe<?6#W^*wba:xdUU@O11w]輸I>E3„lZж,f YuiPKRci`9ImҘ(u!"ez A \W=CW8yׁ􋼞$5i}F̅Ndh5o&.Grc_hZngaYNzou幍i ⺞@b1.i9*m `]FkxɸBujKm<8\K\&h(5L%#H\`]PzR "yTmG5-u|h`XYVLgq`m!4Y4f V2ŲH$}-|-2&k.ȝqIA=BFCmZX߄o3V,>ȕ\j\n Ca7l-~}s^+ :oܷb&$d]  ~v+ldec.ҞkXa xB(;5:/& ]KAgmHCkɹI-rj+.oj3fzB/WpVh;mk:h w;vdߪ#oU.иU,>x0Ek75,D_A!P0`w>k0KcIN l9r~$f2Eŵ? Tf*[T{>]{e8hVѶNXm+ 8[*:AYJU#-gJc CX )0]*Y =\WYt.r-^;Q<%kVZ#i%E2-n(%':[nI3+.DY_[UZcڿ㿧Kc%@:z0{+%26pF)ZʢVMvO e>h"Зnbzt&48d`Q0a  /BA2O\ǃ I,/u7 ,us"+P%D@~/q~~Qme.Em†c19'dA/t~cݸ놎cGR./;a?yc%Ib^Uy[}Ƶ 7huq>~A{۝3Y pMDNX1_ ܃;>y߯]o}Kzjs_w V#BНF0tί$ דӇ]yd{I)$ť!ip=P bNDqe1Z )s@ [ÎXi~IA_WnR2ɝ8R3{z=f]Xqܪv5@"؜*w{'|ד=sw, vΡbڍcXrr^yx\ۦq;;./zYnrel\I6ÆJd<8?O2.Zsb۱ 5bqmgEh5 wʺ$'*E8qs <9ޔagwy/C=Wämm bkY5p ͊4S`AK *DEг{G5kU zcVy NVP&ʣ4zV [anB^wk[/1(2*8 3]6oQxSRDiTeE^mH$:bDvwkԎD]*yJ9+OOnVF[:GO<7 x ׆%x$ UpG f˽Id*PG 3R$NGwh?;!э> x1w2}bqFk9 jA++UF:jJ*Ba5`4y"c]m czf~;*ͭJF c-F녏`FV/檉N+Zuiw!fb@R}p;X׋9y<9JmZU""+]\kADrq~~ir0{>dXC NΣUn }nY}ύ48kAm2˓lq rD$׬x-raJ?~GWyHUnBk{xZ Zq2  )nwIJP;4gK*'$bgnRG7r*\f+{8;Da6;˯?9YǬޭU8Ad|vI{Qh(F=?JJ J:N8iȴR+?ޓH6u.\VZOD@ƍ>2AØ^JQbY[*kSh7zh$OD@`d5ekVќM>].s I_y]^~AKcoux)%kvS$:ZZTblIkôYku"@jhQ!س@wQ'm<_/’qXj#3c'oW ;_̈qPA\hT]]y*Ykq|b4Z= .@U^`88vWLw /V OKG;} x"XvQO 5iޙYєfWݚ7,'f+1X(˱RE6Qv!1LUkkuMN+i0.Y0JZn$;tڐ5IS,.tQ2VYpj3ރjBYSXE LGT5}%NZ-e$Zȉ#Ja>'blrJĦQdjraow(5qvhb ?PZ|ϟJPX@HUaY|H/ _~©Xq@% B64wz(vX2wA&gu$gb wl+^mU#-:N^&.(" =)EujRiލ*P>J-YjY uFW4n7 V)wZZRhUsWQ CR ;OLht*ԒU&R +{&M9/6 V=eHnZE&RB%xq}#ّ^`/L"p9kMLNNn Aks'Ĭ,x*ݛYWHN_XB`Wqt z3b.qå,o_;ua\uiάJ\56ZCJb=/^/ z?Ǎ?ٯ_kjM8|agՌ~",r"}ә+ ]>OW]Z#g^vr`hoLZ}rk3|{rBM*qrY!}4}_AhͰ ƬkɌzE7ίU'z?)&V~]#/ n:HAk^5$sa^ϐ쌁uؾ1޴qGJ4#(Ju&Gz|?Ws%$IKƠWtyO#PR^br&aD,*,%*$)uYRS{7o"@)\84ļ}@z O|^MUD'.$ǺhnvД@UOt3" mUKqxb٠b/nh|A ~"Llw)MYŋ} F yt~ n>x L̋O+p؍Ȫ~AwFAsYٞjd4Yc~-1;eI\YÍ~yTnlhn^YsVLy'5/Mz21 _`] i;5tP uq$b=/M>AF6p>hƃ/7҃Ns]QU•m: |;Jڮ nw踣lb,j mf G,솝;k7{Q7ӬqG=bc\xɾBoRhB #~G?`Yy`|ͦy/87:z>ѸhQ/$_?x/YϿ2[ŮŁO`q-8tpD" iD^~%kFl0^5Ux ~W6R!]z b]Nw¢؈_A_vLXGO/RE\Rc.H#9&6&yq;`K$!vyNdM) mrM"wy&D 6E*ЀnU׬F`=81o?@XZ7VElxsdSZGT؋4%hT bjQRp!i=Vpϭ{;l2* خ. a" kՆTkjõ}Z)Uߗ-W3cu W7yI6a^N/b4 e5DEy&WyC%ed|> [(HT(4ѦAXӊ`\yU sDT]-KBEąe. b Iy7Th;Wy|{GaCL>i;ʳW$~`LufhbT8iG`w(v*s9+XM)HD;zb _ }ayD^]2#i:!ž/-2j5v!o@ïU^PM8DXK@^XW$iod҅`v~kaYB[#UTz;W.ʧLǥ剔qW~f/OYA3d(}| +_uZPBD}9Q|L;NvtGmҚy35SXY[3 1h90k׽ɳB>(vAk/@?y9`]6jVQ=Lw5WI Z'G?& y$44mM%[ےjV_WmoF3P-{ R.}Q gG6a A|a [Jɽ)}6 gf~߁v'K$ tyܢ‹7vbHx3@A<Lh'p@ WɽiR۪dK!>Cs Xde-@VVB%R J~pN5I{㬂fXbZɜeܬ3z sbgJ*5YYR\q-"CyZE _WW5&Ws3 l `7)?V~%ƍUpB읆;ɋ?q`'?xVMxEj@U~I=\*5Y+)yst{ ƾuH;'=ƩXh0}-f/Ykxi2;Zqoa7;Wk,qlZ;> eAD#6tܪ0" `ka/?p?דO5Nt14nLz+<ƍ֭<3+s u-츗-B0/?JamqCZ<Ahyp ~a&tDbʫ((HcZk u5ļ {7F@څQvB7%?xxvvA`\䪀6F|T ady ڋzg|h%z..E waQ5^XEl v {m'Al<`sUL>QBH;Zv" |)-xkOiB'dRz9˓5g$Z=׿ h`_ ~ On׏0"--˄.MW~~(ɵvzv}AjǤȑAo'H4f64Pky_bGsQ* Pt4.4 Bfe9hhBiY/XܱwVd#l*'gF*^-bJmBl8uC* Be7Hmot {ޡʟd7^wm]0#Z1#m!]*z&脃Uu૰bEZtɉf۱P8 VqD%`&FNjAIygLVऊqDf0]u!6]Zwz.2L)WKX5$ƺ6Hդg>ËG rCkafMAm4zM*ޠ=1+nu^`E=}V8œ H(t< ! Jy [^vt\]F-sV~H.)|7ҔfXڜdS;-~Bf#f k AOZKD;kmNŅzyB \}1&u=q% o^+q"b^>>P?u'  V0ڦ||ṁ 6!^ȯV)6;ЋY>=;P 糸W:0YR0ݎXOw'w!ĪGN򃼾Tz2C{bD9ȬI hڱ!pBDi!k^/j{[F&_A>,O|d0Y-Q;dq3!˛yaso7u^'Vvϋ$q]}q|~&k/w QvK[?y],0hd5.Osr1/DC{glcY99&Ҝ1wgHuwں# "0ͼYAd'Wqw/|z*쭾+Ǝse3VVP"d˄}90 IA'"y0G5QJ~wq.¸$~?vv)G. fcZ\7&ZjlȕXkX|%&cR^M"ǁ=~@ߕ½ݱQ=XV`J"Z!BX5;v{d(MDɋ͌6+;gE-]'DX 9 MdmxoK8Jia$2j)E扬/IDQ.#{ۅ2Zeݟh>QPzgM:2 )J$2q"]ԕX6z+ZEV,l4V '*}aHntd+,pum7ZE)P$t(D]@5%6%Th ؟Q̕h'[oizrczF@;վ#5_ދ9)fJ셌Ai¨cX1{_=/;oL9Y 1o iiq^!SДyÝULqɾ+]jR*4Bn\W5[]XU˗j(x˯RnA'mN*51nH >U~/ƭq+8?놶Jljb_8|1+3jhюK5մ+@;6XcЀl$b at;<(I's}N)MbqvqcH'N'1sCp)Tg 4ykI,U9+?!Y^s@%%(xVYT[yŁdcmЭ%̽qOI0aH4FZiQaA(۔M)U*,V겱ܫ"A0hEHY+u 4 );LVlaCj?ud)̙o@ HXQUI:"kF)nīxߡxc9„{87'Hk}?,3.ՙE]@ubbVlŊj~ơ>t4k0nZ77IC3Z V LNIyؚ{ X9~@6**,vf3R+2iv'ASZu[٠ŻuQZaz"/gy0Y<+ƨP"̗B4z{Gǁciq;aL_HA;*C 5Xx8D,:! qaG1 UA|h4ݠi̙,$ʩ5'TڑxNl8ߥkt c^W=iw~{xs-|J> @EJQa_1L hyWz9;_ƍ6`>g 8yGD@堵N|^H+$(f&f@=:G1xN\9rnV ($=ip-$3XYi6bE04j &{w@,D"l 2&1_ eź}Ƈed-UAXRi^Vʡ6+f iqqԝ0}=+IC . mp/0Pě\2;[0k{M RIC!'suh=1}B$Yu `Ie!\{Wi˜@Mo=V!xoaG<$  +% ^N5I08(^֖!]졀t$ o$Z;PmoMgA5Ec*Y"aX-.Хn!遧CT`B0RUlfOlh B{ j„eMHT'$R̨ =&<"ESLV&P$F|Exdx>jPߒBIaKg\t%xw?__G^(\]#v71i&`*6HSM65쉅*=,74DLI":퍹8۠;ǏCY (I9h:,߄Hhl u @q;9X/Gbmcvr;eSآq 'X6ev÷`:V5A^,6n@y2B,)_pVm_G#^i'9+k>FA^>1 b^4WyS\զ'Ѻ$|\U z;/Z3'1_惰fdáWej~M<ibC)/E-PTIƞЬ:bRB^Ng4aּ dɵ6NrNr>y8>F?*E`!c|pQ;v;jf5늺"CE=,h8 T8Yyo/3XlGA zgͯ8%9hlXsB"`e@zd:x 7@r" ?؞Od,]2NiJ-3ZX;LŅ2GHiE?Pu'JLbDbSζ?[VKY@TV"2Z:牬Wb%m3\?AWVv[XWԆx/D_I)8s>q : zK:y zl&yb>WZMF+-I3Q|1R*gyyƕ[)4s&$CsB ow1V%FP_HYbxJWx -G^,jB,/Y  )F\|cʉn&#SqI0LTj͋Op[:A=!b2=1+3y%CvoKgXl!TT (i\'%$$0G{ T!qkX@,{[:pq,5ٲ͌.ſ,ay#(DaSˁҚQZAG=S[/iºk=+%;)T5%_Nf RB1C:m+퉄QQZa6攼=6JL4\ 鯉;ER*B&;%^5ez;}\C\εjj}rhY/>qQ4!.eFcG`>R1AUc]!;3g;r ֌{R??Y#ek%> @ǝrWJYAf}dJM fǡ-\~P^)~ԃ-`ж DAš?\iؑfh\+C U-- Jn{ñeȶo;hp])̳3} 1ƅ E#[a͍$2%9%bkx;ճ}79\F-;9/|cWtlV'$ͽA 'CT@F{4lSt !Ը`M쮉.R?׋q}eDzdNČ&µۙ*IlZMYgu&FJ; !__? /yj~MH=4\z_}sNG1n/|)x̺snJK0\W\YQ2B†>ꎸh̳C5;B DEċsD"וAHB8eC Q! Zݘ\حld8$Ths^᯿Ͱ#>bxzR`T">ObM٢rclwʦ7X3 0Ƭ]JGgzV(JA9E3:S4X3q?; [lR]Y^3L=PXc=a-b93YΈZ]%0lfh0rw (豘j ܝTCd_Ic_I1%g!dE$L&9Mb/ IDATswpf@XyeY`,H^:$3+sYDa5^EadZqqC񕯄=fyAH1epjA-fI/~_6O[J3W ը%oXplD%kQ տH0M?Ǽ^ATcvL+aVP T7mQr!dp̯/}EQBf%|Ge{|/,r8??%G_ b&2.v$R6Yj4daz!b,5|tK@ZԪ27ey#1 :[}P 1@ǃohk^dKh *f<\'Nx<YL-F@P"<T$&bu fL㍻ X'%AlR- iOUsc;5GFㅍG8fY9 v^u1C] d9Y}r!#E¤ (E 3Nf sdQ* J-?F2( Y.P>j(o8=U 僶5ѹ^odHJ㑔k &o\q$v@v>A(X=Һ}2V"Rd !)fl0x_dT+V6d5 jEJ/tBkJ'm3%-UT&/wkrƆkc~¤dRhdžjo,+`,8xf_(8^~o<&5.Tu' b̎/gý[lNQy#M/'!MYұg2a;CnU-e5hrY˼4jM5˕3}A%?TsYc=L>8ӃOe) Pa3örDzxCM ]èΛA.|ϡj3<Z JPZM~hBX3qނNB!snCjB)'YbdX9JWcRB 鄠B;\TTb+` jH eDeTc`!:1l#HK+ATCfdNca%% h e.nYչzJKOo@SV4ҐՑUӖ,=ъU}e-*{U) Me"؋2¯`uAMVf9/\(ɼ0O{b r8vھ]~o4PR fZ'G:&c4AXI.<=[VmӫV Pzg*oTzmVj530-x}vȉ|lB;k)YcmK*~ځ ~''R9FިQĶ "}HVLU:eȩ`̚]+-/U"ۆՂrtL3=(*ޡ9/bzLR ݘ5jgvzVVIgJu8 .^׋!A>K =ڨ~͝Yrktۃf;c1='.[M/r̰sMIӻ:Tt9YEaj XihhL'x||buE vz1GW6ٙDh[A"H,+@iZ6_5!ύ>sYvZL~1Fִn~OHS@}}ܚCY s29 &+2͸ fkFKAS76J-Yc8;SfX)]rk_ӞQ\r@--Fhg7kfrHn9]Vt{Pʂ7ϕXF޴&rZQZЩfV"7Հ)hJ0=p>X;m0;X,SXLfp#0qYbK*+4jҨӒ4\nkfjQd%( 1V%ٔmC _wR6S+*S5q(<`t| 83>gsHj#+̫҃ܲ# e"`23Kj&dx"@F\V?1>_k'dz5/ZH`$D)vP8#IߢB7̒sX|jk!SJ3eሟ>k63W"8HkHLV8)k ț52..vˤg@`O&Sί|$L<@\KFV'FgLƘ'Ir7* c"T jňѯ 4PmĜ>W~e?Ѿm{jI9FbYg$w5:g5)Hpc_v9'>'x|r|{b^_"Ε"sgC?  GDŽy촚q})lZoI#xn[e"43`ӅNu@*E+n /}P/ '扯v z`ŰVSZ>߼Tke|y|{Қa[AzPO*;=@+ 5-K3l$ 5cPYn ӁFn;ޖ%=*{;r/N/6-/}6/g(ʺ%7HO3룳< 9Z b#-V8~s(ѿ㑬!k0`ȔGP 9ROkK>53&:S+ƴ\b 7]RI _&jʒI @LHD%6WjRSVp]1m8YLzd8%WGّb9L\wKbĥ( Z%J V=s'/,*̓vM$ IX7הZ@> (=c vwlK !l$ 'XJjw1%lM,Jm2RO49&geF߿׿QOJ n5g2$!]viȢ)G(%ozؠhTb:@5Dn)+bkR cd=/qr]칗#JbdR)exWzdM Z)!\BA r| tv8ALD1#}S\x8KYJǷlߠz?ȓ9F@1;s yW6ɶԏog yR=;bJjs(,*#7['X=mp;`^qM$&Xco +[ r<[ .\JIi .4Y&-DyDH$T^AY2ό"`N%Ð(+QE`{XoP3 lmqe~RzX?s[4x.V8yq"i Ah˖632jX\JőM&Dd4MJˌ,S3;`u7sBE‘ۏRYqFS%wHmHԜ' =ǽy>Aeb:yDn3 ܥH*aFUG(q՛0+V` 5,8U3&\V9ם<iG&d."~sKi_/L$ dejl:20Ӭ  f^h_+rK|{ȅ⦮=H_$J rǟ/7_??aêDŽA:KSJ2l2lBhnŬ%ᓹ MWIVL" u{Z/RX"BC̱564xs1u m|QZaG2;'B۟/5ؠֹ_LER,8lKzbx4Xs~ aa/<Gc &Ip3&I7.d7D]xmPq&:Y:}QtuĔ~wF@#5.51盢:;;DxBoϼ(+4 4|ZA!;jV$bb0_$sQE8avqJ~1G'L)7-hk0ERX7>im,/؞̫?saj ż*m3{`0_cFhMBK;8! gߌ)WϟX(>)r<1ՃD4IX^K5&tԢ8J b2"5^w&˙TI@-pւ0g `H 3s^\0ׂ9}!_'pN x.HUb̕,X3K_tc-ř+֒ʖRz1͖i!ax{OVtBIoIöQgZD "ŅBJD1T,{)>=}wϮj픖5aQpĻ݃$2XSe[K abIN{>52da13Cb쁮 "RiVxShd#KrT捯*$[k֐KwIn^M!lW"."YIJ_'LjJ~|zRD.If!oFK*9!{nx I!+/8A A}Tsv 졄nhL>} f-x{+XQ'X飕$E&pZ6+ 9jY'V,?o 4¯\XR1JGh&% E0厮*5=޾<(3Q$ԩ,rt,(fSB6źFBIh+Nf,'RM;2!c<r^RkJh(k,ER'sJh¬M3Zt1v$u҆a!hq5 nYJ3G^}9)>N t o6~o'gmUnPݐZ;?c?JIG\??hۃZ*G4|_le6b.)d{ӍER d/ ٘ _x$[0!3+2AW:s7E+cMaHY_+?jnEbzf *U'ke{]>>-/촏'}Z'1ڑ:e߽DžpPuϼ#Qvul$S r8,* ޜ7`H,k KA /Q&kQ;5+O3.F+Bn;`RkW6&9ϓ3/&uNkO*b8$uF0w|tF_쭦8`$i/Njl,ȠOdYwAu(b'Ea n /HN cQR>.6ҔkLHHLM +M$HV,/7XYK9kAL'G$)Lax^&I(_5 HTъj {A顏>0k5TKkv0ZbׁU*$+>r$R03# FmEh[/>ǷLHۀQu;F=ҶP1+}N|Ak__ fxn$Q=}PT-kJ$R9ޑ8uP*A"jٶu+|"#tG4y}|/Bn0CyNi,A+ItP-Y[7~y2ȍXaFH-.XG(W/ERLZKIb tyZk%/C1(KŜҌMs]c!? s +/lRz=e*yFJPT^6r`Vql5ɢOMYLcu%*,{C=i 8ZZ̥f}fت,UT&_`o,-٘(kNْ9|RIFytLiQRiY& iahPF*rZQsB6l!["K2Xώ㍈'aCQ]$[ArEcr5\CM m(&~D 7$҃4`i DDR DŘqb A) MRpnK\Pɯ2T8-1 6"e!d 4Ǹf+DuyY-C/@ж[tY`,Ȗ;fMQ%KeZ3 fp*U |Cs&n79% .ZNĢIrSҒb!( I(iT66o@'fy8+X,G(폔.牚P]3ǽ IDATQ& ATP[2g;UtUj>܆*CQe:;UdMɶNuaJ֋9)@Բa:zs]>R/lKȆ"Hn֌hz#vsgI ;jAүknlfQZbt$P`}&>=x~{TZ{`Ɂ~&H<d# œbHoWŏ ~N7C'[eg:H "0G熌l"mxJ `lmo TZ C/J_C\ANisB^i#>6b"Q'(,r5wQ(^)oP$RmX6wC'Jf. XKgGV*& 5yA+_rTVD}ߤbB`@)h)9u'#2`|3$9ռ'=SQ_=EAE)<|RL;uRg KS:}^=75V2֗gG JJ0 It=+S.)H-AH0K*"Wgb-n迻gKdFOm;!d1 ) Tj6O^Vh.TP?[ VX `+I"}%@Ӄ >Hn,IثJ9 bŸP ׁdG 0";N?[3C^5 f]` ȒkB]\$ *WE% ? DwY@AUu~P=3qUꮾuS}sse)ۋĖEiDMH葜'HRui y#i" #i1)bUMvAkftl97ݘC.hv#$j3JQK}!|)2QHR)NJ,HN U;>vX )ۖDCO ]$Tjtd^| Hpk^9HVeSp`b-S L)@L91vY@Q Bqb%PZ.qP F0hS=4ؘЅTn.*5Mj0EI4[єbxh;TCxhYIuHƦ&1zoC h"R91H[QR)BiHt0@ $6@pԍ'91w/ KrZ\L8>F%(9$UZbäDcG0;P[z'hM(O]tevvKq]R"1<>=L^ia.:R]c\%0H;E$r#yZXiՈX~ ߶4-RUBj(>cD$@*Cm#x8WЅFP/#FG%B0Xji/L|Z%Uȴy&R]C=TJ5r!#mQZhסU\7PTmJˌ(m) Ԅ&PikpE<*̈́G e%c\åCQ&T,P!0TXv`H J:zCCMߵķvЫ ]XMd VjevjK(M Ȉɖ2R-ZԠIB TX]Q8i|b45ʑ"&IY e.9br@K$b6BRcQRhAgRҝT  HtU~[YE2Z S% N} D|ˍB:{1ᢒV :،`H#$) ڈUV+ q kAKqi Smw'Rh $ܣ\DB'I0Vj]Mjd)I[rH" I2<㣣tӯהl˂v&]/}!D.&Io}$Z0T}RqhDEC~Bb8BCW2mD|sc$.#1"եF{PU%Ed-JF"T_H!`cD)Kv7JORiMEp _w\V|( 9Er` IE9EdQbݑBPH'ݘX3v2X#F@{jz(ޖfOz%UHqZ`Ne !h:y ҵ^& K24.-0FIQ-7=iZ0Lr^"mtsH ` xcL' +z`kBn *Q䱦( R4]S LA]*tQE"9RpGC*JL!) ZB\-W LZ1~uwC4ʏ`S$%T{? '(EGkqea(Z%UYBhHP`IS(.JTaQ&(K]7QZcTAnHFSkC+*L%mZ msQ,DjxCZ'&Sw@,=&8hز5@Lǖn?%75 1B{58K2 $/[=tДe,[tNjGmeX9=%%e [QHC"J4uUaK#C&ܬ(7Ew{Ԁ=jU! .9]=P[ V[ l\6O *)Сji:&Fh*H%\];dHZ|[ZE&i#IQl:faM =u;آ`xbthPPY1ZZH Ze)K3HJtm4ALLB1:J*AX]blX2@JR Tbtz"HdEi{ o UF&j%yojxAfqR)B;r-);Җ[GV}N%3-A%E?vЦ@9RxUy k#n N2 G5QKD4gbYIITIԅj@y}^r|'e c2A9M@XȄ(91'V-n('I#iEtAHp!JE[N'I61!2:6M7]Z$iЩ@VeQIr b-P{C|w2Fi?5PѥFZE\V$hBI jT C㈍gH5SL[lKSEYHz T$!hTisXMi*z4OoBwt+qmch bSmQQ) #wkԯt Ox$ՒEQ.Zö5E"]Gt-%nd=D:DH.7nY\c/6c7K ƻ L {S04RfԳ HAe[&, IJJe,q݆#Xےax![\`J FC)&YGiAtTUU2 b<:@=vE\7øJT5NR]q]M]N\6 R#t"n]:Ȕ[Db.С ^(!ŘB-D?i B8# ڪfx=)2J㭢%]"KH1b6&Zcچ: ha  uC%BqXLoV>$)ڨ 2+Q„4E]m[rH˾+*_T`Ġo%;EB0h-Dl-+1=P *X@qo0C뱠 ~\ SPa(utaFnk@@AZiB)zJ@T5B&&|V (66FBybTZwmbM>R#)'"6JU*Ov )ZPC4hڔCCԪK9TW1cjlm%-ǚkFk,R5NiZ6ִU$ 1֢LZx Pw%`0ئC(,fmF\BW-th+@o&4uDYL"@*(6 . 467Rhv!e^:~)ﱪM*ipʢQq4.Jl(KRE%VRVwp!45*)i$w[iS`vN kHxT1՘HVfUS}x%Ո"YU1116H`r%96nTb_4*K߈INEj'с ݡS1 R*y4 id*u!J@$JT^ ,b6Db·7ć8 R4J IZWRT&0[(y:A@PAfבJAk.J4Hkޢ+@Jh"K4EA7I/ B$cJ)pJ]t/bL,9\(RJI@.*T}WC =[HT]4U(HjXH(mq#*zK8#w hV zD*6d<,UYIN,ޅ-TS'iX$:5J'>P㛆BH8LYEPѦTJmhHPJc Q@Ezi'Vmhz%$)| H`=!6& #BaTs4)T8IV%u5v$(d!6bi7RRVO$˅ƏcSPT"DMNNla|"$c1lCi]Z-C*BGSt&UPk|`P2P[/F`mt&JCv%ͥ)tB.B9dQeD$b\C-}h@LcOxz.{pJ# kL&Um뺸Pt:FruORQb` IHAC99'RS`LӋ7CQ}*@vtA{\%. +kViI!P&%5ZOD401uы.BߋVd&Ir㦵'KNG)l'~ʊGyBSXUc!}:HAq"X):NA[m:BQUP$_ʠ2lI5*Ԓ aZbWkƦ$h'1.K+Q$y}MV(iST:t)m5=.ϣHĘf&bir5E[ -M,%=&j!hK6t Vƒ.8Smj.HKZ WJ4Zacoޒ$9.nc(k^2tUJnBRԤRDp!Cf\:DlbGCFV#6e_(}PRH{d=OYx|2[FLd[eMeɷjRӣ(ӥծh /gݦ1RGKiDxq?u:h_b풆4SxmQ$ŕ"Q;4"voC*TvD"y-:VPd-K *0WCp,҃ &ZhBـQ%B|Rm¨DQ}(bY!R* 8TQbMIrc#SɩhHҰAU%ix6ɴ^T`*),2 醈)("jzBF )5ZIJNS$/_iQ~.rY:jT6HZ\ 8jtHhQRHR31-*bo-QT%vFi]"7F.:Aj#`Rw; .B WQQܓKT^$ 0%r2c=1K͛$k8DqGж?[ mh>l E%kI>MBHsDMTʈ|PuaõL&d2jpɧ ̅~Xd2L&qgqӯooc؜7vy޿L,nϟomފC~ ;1r7͏~r9{z>>3.ҩqُ/gEGjߏ/?v88 Yl9n5`Vz|w|v|9iڭ8ϝ4~#+ֲц]wo~eYwXN5qo9K_c:ad6ZN=Lw_{O;pUWsםw_v! Oo~|>ckf^p^ƟA?]&&`-_z(^? {ެ޺q]cxɗp}8=r۟4:rU9CsyN+vꆛ/SNf7_ccs8>|8v]{?߷ ǩ]ivMړ+x |#%?s17s-؂nuLfmhIDAT[GIF]7z=׻9ڗN8|'O,3u?u>߱`y.^dg`.Y<49qW/ ,Ǿm+-o|=ټf}x^˭#_ܱZ vߕ`z+׿.Dz믙>\g>xrO2|;SYou‹xswnfFF9(,vvsl{\WZ揷Ҝ_eYOyYdKm]{p{x}w3ϻ>߱зeL\L]$F`u˯JpsMYwu?O|\t׹G?⹻7ta\oqo/ك7z喉Cc}_zN?i+:1h=q-_V[n ǩ/+~^E_a}F0Z)6X^7v~^ۮ\wṭw}EQ̹ll&~:Xp!K~~KtaҗÎ?WM^sA<%{Ks~{=u{^8?>~{w83k=,Nog6&~vsAƥ.=~Y_1r{w{wsϜ9z=~]g/EY޼e˖fl9w sn>~˖-{Jޥ?G&_W9. ){pOq>(|G?BUUʫȉ Dd37QF¯p]gT/<;>Orڙgs g>̶+QGQG>^?/rsVϟ8} q=}='xXDh,f]|?; gq_GYϕW_}2 &%,Z˖͹^{wy'{|y~œwx"nGq8뭻_~.=y3Y`mޚ?qӮ㮿6y`@;pAqU?} L}Uw817?p./{) <8_&ewukX|9CCkJ~}D\J)Ŧo]w5~f2/=^Q~/m!O|A &ێ]p)%^wl&Nc`7򗼄݆V5Mp.cfmpܱsu̼Tf\\bbѢL}+Ovy-ar8 x}|3'R%|Z'wŞx9Oi'^x+_Ǵ9^KFLꤓ;sQ(:<9ϰ6[rgbdd}ړ/^xzQ\r鏸sg\w)_F.߿R<N; ?wqk\`&Jҡz"[o%nwKrO׿-˖-%SdwMn}UNb{ _t:r샌!+:LLӜb}]AUM5}n'>7s7f\QG P%믿Jo-Xd CC 9%wwh•~R7 |r-x>ȎOځ/\p* q6^߾\w. V7nsHgj j?Zk6tSyt͸+Ww,1!:o9H]t Gn&'Hq ?._}bTzoa>Ak_kjbVz, u='Y=8o8_A;+W{az&W\uJ~ǝL3r1>rnu%ڝwڑ|})w╖,o`lltl.}Uw8뮻/˸ysh]C|L6۹1!G, ڕܤrrX*[YlGz[o67]to}&op͵l5lL b}7sc[v[o=9'KΤW5v gGG|}U?]r)˼z?Wʽ~+wb>_ロqvt]R> 溟tR9:a~?x}'}*q69LDih壣\y5'2o6׹%K ȯn< nsՍ7?]w]U r4^X0__=wҥKO.Oy6{|3K,/~yZ^WsY_#ís!:Lf`.2n󊗽O}$p-ܿd 'r*뮳xf||If^̤ W[Ȯ`MЦG'?i>qyp1[nɉn?o='}q8K[oe~8ڟ]?j}V׾2P33~Lw;D>({/W^SN?,:yoCQOs_=8q6lSz/_<Ƣ Do};nm>mǝw^R>7=.dFns I3<<̓wxgr2m!0u?uc|r-8C89=gw ^[w]xc;Ɵ  l*#9̳x34N'ขAIf{dMw|Kט['Y09Nz&Wḁ̆[LޙZ_=;#+b뭶O:}kl]p!jwsOU&ync6Y?f҄SG' 1}DwƦ{8ZPg>qGtQG ȣxwy`yh+^i=d2nNX|~9կ^ۘLaOqUrhgTY YguyhI6d6d5G뭷JLfbltt!cl+ d2WI7qU<ȟGkϓh˥ݹ3>v|w.d2L&24w2L&d2$y]TY6V"v#L&d2?*SfZ-V ى|7!yz<;].o&d2Lg:fbN~L͍$J;o* tөbc閙NtN'V'B6L&d2f)tEXtCreation Timeven. 09 juil. 2021 11:02:22ZA IDATxw|SO&i3-P E.D ^W?'^%W@)C˖EwKY!I#-}^!B. -B!tV QE!ά Z/hBi <kǒB!,YTIVA |Kcn] !…ਹ\V^ h, :B!+cAy- jW-s%B!f*2P1,)Y- bv Lf\&BÅk`dyK/0\.SVss)B!. Te*37(KsPsA@ K=!p$2eu֣̓Am\ !A*sM=rK_V^Nm B!]- '5Vyzl%\e83\0_ò!BnWsAHݼx[uԔKp4Ƃ8cBy8-@<<`TX;Gs_eRE!{+n yA}\ it_xAgE-_.S(csS\!iܸ-1<M/V/c.-]͵<F4h_͕k5!Bl]#V1a+ʢ֮ on K=q|j"B)-iRP@*5n:pbIej.@K@ p;Ə7YT&y<!cPUꪪǎܜ /U`X7#.sc¥h5>wӧOș'?TWj *LB!x<mma'`޼u5zuk to}ӯ:yh8h`q^xۺquU !BڇD*֮nHWn߾.SWs}4|ՒT+Ws JE{]]b !BڗXl (sx*v a f1rm55\Bpuu`[ǣ>PE}5\\n5>vLߪRB!mM$gz Zk2f4ըIk.S׫9E!b=yC?nmjvV̶{[F4(VmڄB!mFBMu-n%7Gj*^+B!M*!/8Y|7z{e&.FB!K}B Kxi2WVk]htB!/llb).^]?B cZ`5g.sr^l!B:#ØT|cv/LDFBT9iՋ [("BHg%26ޢ _UJoB!32u_.s݊㦶a)щ! ..Uބ1\(-zt"!`JL^^Mm-:=L?Μml”9HҢ !^fKn5]3uҙyD" .\ACڵ7~z u|gh42~o'P^Y:ވ_5S5!}a|}Un{n@jz:<=ڳvuڲ{ rrs'? >bT4^;<޽ӘUgy[6}~77k܌ixll#bs3!'70ft|h4t_ƌҶعg/^#-aؐFӧfd u u t9:qn%%ak7nzƎ|7aB_nl.{ iYOl VKƞJcԿHHt%@jZ}9@$ գ6zt_@Rr *CEy%ЧWONLByU%^ 6D/=WnbҷS&aİM@.HM@v]\ @t5LyEݳ<ݑ ڍ }v&5n}\~u0!ܨ*TVC$i BTƾi x+q>4Lcݚ.j"pe@zf3]t6 ___d^Ꮖf|nF̵psuA=egkQ1p9:/]\&À~M?>+1WMv-(C` ġ_"1a(*)ոu)i4~!SRZlDfvv.i??wWѡOC,+'Y8vmNlpJP*H^.c? hܼdžlԸAxxvd@jzѲ JJK㼌 >dNBP[.< }`pGB l@!󯍟jMoĦ7"Y9O:z-^hZ tljq@H_s>^^Ⱦ{pi@rJ*T*^:TG h0|hxe_}K8y<WnF??<<]0nHv_xGQXT1YA=z@.#:zNl ;-q;! {E.%- ={G7b;")9ٚVGKu;r8е8__(W̘4w,\1d&F FK||pr4z b{A-!?_xy5^~y̞ >~> ܁X6@!]@ !u.9_LFFS)ҙS֨B324_|I36<>p. jйqx@݋B!@A!BPE!btB!X]B} UU] ;B||z쩷,b֌ge`P@ ر 0/ϯ;wp'狧Ӓsq m0[\.!-T*qzBN_秽_o::N-07yyypvrj]tl׮Y&t<?b99whZ @І5&ԩ[u ĥ+WF>>FLc ݺvB򑝝oLw`΁'&ORZ,x IHȄ#jkk{ݖثpE??!=#2 ӦLǭx9{N[ʪ*ܺg''L:|>pY\v^ڴ<S&M!#܎O@tL,&Nث HУGw\֦rq9tTcKQTTkpzwaEuU5P("˱ǟAF!]:]eeeHOOpkDzF&;uky|vLGAQ\R ֦*..c 4ݻugQRZV$QEr =wCBbn' =#C ߐr.r= (>>ޘ1m23T$߹Hn^nܼ|6|9йu o*,*BZz22mmm]04<\o=Lf4?cWfTN@]Bd !1 gΝH$BPP*ziC\Ŗt??_w8˯ȹQk߲2WĈpvrwitnq-.V.prtCn^>lŶptpN\:88JVJ^' /==C ܊#0'"is9OûHGP((,ԛx;;6ܼ|jt󃏷lmmis/h⬽]VR}p-8t={tGee ƌ)ggxzz"77Dizi̬,\]]@nc O {t(z7Qs˹圷EEE.GEa@H¦A !!3+ G?D(UJ&Y`_@AAay-]@  !aayz \TVU#DfJOk0I` ..s9?t`H$vu;EEEm4E(/ƌ޽;#!12zi<|xf &$ЙlE uTWU~n``BHE~ԊK!:7O1"HeKPP<u& FzMK!B.B!+B! ("B !B.B!+7G*Bs -). !BSų]^$B !B.B!]޽cƌ/Zڶky'O۵ Bi .ĺu  !EGGcٲeHMMFyHҩ[ ~g_ǎk2?;!4O,AoH$]-BuV<3HJJB޽;:AѣGb̘1ؿ0ܹO>$qq( ѣ$ zꅝ;wjי5kϟ1m4m]GEXXR)w+WBL1w~*++Cpp0֮]];^}UTVVZyf,^"B:Nt޽O?4`„ 8t ^K^s0l0D"t70w\̛7 Nj9rDFnj`ԩ?> !GPJoh4fo]* >,BBBj*%K+W &&)))Xb7AWUU> Ərz֮]sEo߾ڵ+V\\|YRŋy&&NؤP(xꩧ䄾}",,=7?d2tl:1ưdc֭r|Xz5󟈌BNv C*r욬[PPݻw(,,D^^"ӦMáC裏Ø8q"AAA9r$1qD=ztm4!oO?5h۶mBjjDh4L><T*Q^^޾C! :mKמ={PYY ggg/ׯ#22**++lZ  KCQ8qX,ƴi0eʔHBE0j(rlڴI;p%"55YYYZ !Ntѣ8~8c)%%EEE8}tFEE6l@pp0rWm#GƸqL'qy#m?r9>ljBNt߿...9r@5Q_vµk̘1/Ƅ  U\\m۶!;;8}4R)\\\Zq97kܹ֬s P~+Wĉ'V(+o!a)={ ""ɲ3g|W^ذaV^^z!22}{9矘9sf(--1p@ož}㸞ߍ}]1ӦMCuu5}];Xd O ::ڊ[@y5mן3:@g lx5<}P]U ܾb[!B99>ވxAD*[ Q\@3it&f05}9[!B4tB!X]B!V@A!BPE!btB!X]B!V@A!BPE!btB!XMGWr=1 !a@-]B!V@A!Btڠ+""<<"ϡV9ѷo__sZTWWMw^L0~~~dӧ|MTTTp.5t χ^yp IDATX-[tt5H+0 _sexw+V@]]6M[/RXd \]]ѻwoܼyaaaرcܽ{ ,@`` ٳgIZ.@74c %4_cСr[^{5qٳ'6o)ٳgcذaM򉉉X,F||Euڷoz)9ݜX3NNNxpiӘ{o V\d2 :'Oˣ˖-?0w\TVV6{Ѩ ,@PPd2BCCdܗNt… QZZ4,_EAɓ1p@?K.ga̙x0g?~qqqشir9R)Zq`݈s=g iO-Bee%rrri&lٲVz=n݊'O"..OFϞ=[O]]Ǝ:9riii GGGpӎ;ZT%?qE,Zzx'tRpov߯;# wޜ4o<,_fqYpssk~ILLĨQ0a⩧qUͿzj9r;wDTT I&!''GEpM;v GEZZ&N]{EEE ر_ ˇ Og~){N\x`8%U} 3f֬Y7Л'0WWW?6k֬f?aAAA}siXYYYժu ľi!&'K;׬YBCCx1[oΝ$-*ܹs֖)JNciZZ.LN8dW_}Û]ܹsdދ-b_|cO>$9kײݻ3Bcĉёrc׮]߿>'.\fϞ7gas̱(1Ƃ_7/00EFF2+**b<ݽ{W<77I$v%Ƙbʕ& k}cE_8c4d4+C◾ L`C|85?҆xH4K|8JOn24|pjH5 ֭[^z 'NDZZ6aAXX֭[!C@&Gs7n|Bz?C]&;w'=?Ix;^}U̜9I+ۆ 0eNF"C$- =0HRt+W.3Bo=z@"W^عs'> }􁋋KɓFB|>.rLFeeeڵk`|رcB!y&鹞* HOO`ڢP(қ4MJVǎҥKlٲv7nᅬ%%%Xx1?7cj&Jم c1;;;Mfj}zje1>ӛmn]cuƾ{kcۯT*v {oN:Mc{Qr}l…,<<\oEKra5kzj4i,)O%݋cƌafod{K~Æ c|>Fm."uDJJ rrrggg3,55S̿1i&fcc7nvi4&M233YBB[`8p }:3f\~ҤI8|0ȑ#3fLM/Fmm-еkW|eaȑFDDޕJlڴ 3gѣOOMMEEE db/c{C:Α#GkJ+G{lsoh~,//ǚ5k0zh9shzS$co۶mزe ov.K[\~駟!Cڵk-iEb:]z5T*ܹ޽{#44U]\k=uTܹs3Ho~Փ42.ZqnݺGGG[o>(JCXXF 3gf͚WWWL: ,O?/d:jzwŋEmm-j5T*:bآMswn$!b7^1t%"55YYYz>,:8xɮEbH$X}602W@ 'pabL6M;ܺGvv6VZH ct^{ w5.//r,]CŁ,7wLr9F\M6Y\?K[ 6mN< ݻlA(MϟP`@EES]vŦMЭ[7ٳdOH$P(8WWWxxxرca){{{r$%%OMM $ 4$%%/Į]ЫW/{aðvZmo(++CTT^z%$''k׮&wvvx0ݻ僬S]A{x8{vZFTT2Je?ܒr#==n™3g0i$Ny⋰_|,x'}v?DvvuPTT 6 88rSpԵkWh4DEEq&rHj[M;Sc˱~}{HlnXZX,Rԛ^ٳgނ񇠩-ј_kobƍHIIiUy駱{ny-JcJC!!!())iv :u f˘0aۧ7/>>^)/T:2E.cXp!\<pwwرc[Μ9tTUU5YK/a̘1 T zyy _~% k.N...:u*/^TTT}HRTC]]~t>>ݝ=zl17A/^d#--Й3g?/`IIIlŬwL"0///6}t.|15}1__&Vg}Ɔ3{Ν;9ݔSNaÆibY_ZZ{癷7H$zcgfޞ1޽]o!C0\Œ^\:@zcBN-9SjEp|7烩c>7-*QcbcWkto KUHS۳HWM5XsmNs݅yo{bf0*]mPU٤Z娬Ә]{c"\IӫϴRL/z6.2qX; /!R Ud9b23K=u ywMmr=KF9`ϕJ썪Ηx=t^"+Zr9vLՍ˱ 2@25&;IĸNt.|1D1HZ}xNE|n x9Uo?Z wQY{U|\oZs2Ӝ]oϔ#1O Q=5Bb`|7ߪiN"K:c͡ l|8 2d(qX1E]8?ϻb˹ |K Ky ^^<%4֨'1p'hӤp9*Ae|D(Ԙ]˱cn\;W`{eNtگ+ j {}y|/|u., >ؙ)10-TnB<]>{U9j\N5~bآei .Z 䕫l#.ԡQOg`2iR~)վ.PcR|6@m!b gjR%`)sCdXsSkԓ7-zy-CƧ3]=.%zK/RqZ˱cn\ jbuʠAbL EW7!Uc_B Ǔ} tB >;QƩf|7N=m"DJb@1 ^+iuTk H{ i wyjl=W_>W;!'k/=E*Q51 'kx/[g.{ Fo.>3T!8Iƣ[V'-Ī%n^B|7 m+Ux"WK AD`YG\|[:k\ Cww!R cecq6Q9>=QpcxaQ`o(6ߏ~T2 awnco\qj^w9B^ dAULt(M{ד`q{ 1cLoƣͬL/M0U7c ~Fz@z'l/gLτo߻q#m)NaX[cc79oGC+r2^tjyVi㟇Kqu/ llk݇B {ƶ鵝}D)Ķ.Py{ɥ:QX\dTYߊPp \MV۵Ĵ $p c&|X:\g'OvBn3%ވ˱SPscPZAY==ۓx ^Qlm.]wԨQ2HDK'R|>;PTw,ͯZB%v_𞶈,öynJ5iQ2V(Skehoe%˕IoqY tqh;rJ!_'T7P[orRe)zo;ۇ{X[7yT]jgjM3M.qY?k)Nlwg+\l1X/#v&..ǎ%u3u\V"w;tk8ʛ-Um0AtID*7/7>lu8m!S1ZpL=m\ 5\hkNvO+;Ef:w٤_Z.ᡛ-ʎ?+tmAiMG.b#*.5j|lHpcV09N//GhWrIu;Hzu=\eC;p8<;HGKq _ IDAT *J$H^Br9vn\*j: y ;cQ`ޣ=|Cv||2(Q`z_"jZZSPbl߽< [e5JW'U}>^Mk%u 3h]?vdf[̉,Cv S,_Vӝ- P&--OW"m4~!ؠHeLb K#{rpIӒ #%A67ž˕ϛ O 6ņ0掋Աn/ܖw%1 T/T(W6AJ5db>J$c\== #׫r#Eppy˳$>",!bx; l%kP`8t wDo|9%Uj\Jƹ `j48Pb=l!Z0-nkIl;<\=m$rHD|/8t R};f]/Fa@1Z1X^B8IX|TB:J-] bݯ-/BŰ|o1\lئWI "b,?gJ1~2~N6s'Gݗ!!" "ֻ*X[*\iN.4\I:]*1J*`d(s˱ccI'8b@1a4gIE]=P݋@᭱pJ*9_=f"_^aEOrj.}tOf@aA-n_5Z% ,PQgaRj-.~m Mtž(Pеjm)vxe* Vs8zu= ,oa7 o!Pk D|oJ5-·sܐZO:_f1yg#<z v"\ gr9V` &E!6Sk1}tJ v^9i J,]#1oyJ,UB4涃t^ײX ch*bahcsǎxoc*8_-*c<_gL6 纏*?7Vc]b[+.2~]r޷##Aֺʑpv ;'GBl)X JGUsΤљH ^"DfR[G!v/v6.6wAt.2>^i^h :?BZZ!; DfǼarsj쏮2{55#BHJnd+0w[A͏VDŽB"B !BSt/!B eܻ'b.&u`mQK!BPE!g㣨vS6=! .($ E^@pE)""-$ "JQ*=RB$o;!k6u"ϗϐ9g>{̬pIB!@.!DlFSe˖eB8^qwٳg_֮uBqo.!Bpڤwޖ! wwwjԨʲK,Z.m !֭[h4DFFz\\uaܹܼywyp\2$''[;x :t\rjՊ;vkۖ2IJJ"**É[ջwoxb^uzmUv̘1VsZU2d^g}ۃdb̙4k ___j׮ͬY˻啖FZ!C ߋmۖ2z'Rzu|||hѢ۷o/պ9m0|pRSSpgfѢEL4B8+WҡC+233-߿6mCFFtN>͌38z(9{e2Ǐm۶<\͙3Geee۫?vvڥ, gUgU/QL8QEFFYvڿjرB ᄈzmʔ)Vq=vt,߱ch4*99H&)s{V}UJٶ}W*""B :4:+""B}ŶmveK[=j9 I)<$=-Un+_gBo7o;n?޷!ݭV͓GYqꞮZ-!!!@vWرc]6^^^l2~-x{{SV-&NXhz_~jժQBJjj-[Fc˖-wf!Xd˔y[ٲ_̜9zO?_eyoߟ&MСC֮]kymܹ9wwbcIOOf͚ǫWW^Ve"##Y~}fɒ%DGGYƖF#'NZj3tPԩéScٛoIDD4oޜ0sLׯ]tȌ31bmVpjVP%&&2bbccqu-sa Pl۶lW Ö\~￿z]t9sX ;jbݺu9r2h ;7ڵ+ѭ[B5jǎ_>}[o߾ڵK.}܉-`Ʋl2:D>}(_+Wҹsg|I6l؀^i&$6oLLL ƍ ##k׮Y%au!99&==K.LT2?a6ŏ طoZ'd۶m,X'ORF 6mٳgٴiԩS۷/׿xwmX)8}4'N$** Rsؽ{76bԫW+͛G^(W\lٮl)o|rzM\\:t`ԩSǦsEj P?C]5kT~RJÇ+VΜ9S`ËJժ}Y^߳g *?gΜl Ea¼iASAl{6 _OMMMUj˖-J).^4-hgʔ);t蠎?R P.\*oSœ8qB'P+WTW^U_~Vk֬9t~W P'OTJ?b P `RjƌnݺǑ7TJ)u1oY7 /&O\S~~~*)))kyO?Th2D]bRR*tݶ-ە-er̞=[*㏫Ē=z4J)Rܼy)SЮ];v իW={6z]v\|t~}QׯO޽ٶm[9~8fݻJhh(=z͛V<== !xRRRhڴiu7l؀F!<<4|}}iذcQ&OhԩSԭ[pΜ9CPP=[N/^ l*S@Odd$tڕ(ˉضiL&M4^ܽի`uvzNȭN:3#Fnn#G$334jԨQP?Ʌ f̙Yܹs?X˧~Jڵi޼yemٮl)q[Ր(]C-Xn4oޜ&MD\\5wqqa֭lܸNGnҥKms?O||<ϟ/r8@aIIIZTLJCh)jԨٳY&V__|teS,ܬݻw+V[n]\`SɄdiYबDm„ 9sUV4WWWt:^^^?3gPOInh߾= *ɓx"׷lg֬Y'X6lMز]Rĉ̛7+VF:uXj-[dԩ6"5IWþ}HLLdڴiԯ___mڴ!66ݻw~zVlܸ ?R ˹(yڪFfۗnRR~-[l[%&&~V9@ΝYrk׮{ŕt )'|5kX=z93ŵ_fM4 ?c$t:cر%:g`xzz2gΜ;]1ooo.\hSŋ&`00tPKٝ;wrnÖ29vjԨ7nܰ9Ih$++,_ڵkپ};ݻwRJ$''3o<^ʊ+nwu,YBBBرoooK}pp0(pssW^aĉlݺĹs < ! V7߂_ѵkWFɱcHIIaƍvZxG %""!۷K/cΜ99r$'Ok׮7M61}t?>k֬aіvl)Scŋ/Yx1#G}___ @tt4 W\7ްmP۶mi߾}t:ƍ_l6ӯ_?0V*ȼy۷/6ױe*LF]6z_~… XO>={eNt}xxxauhܸ1aaaL6ɓ'F\\=nRRk׮I&`֬YcZf̘1XrEkƄ 5jiӆr q7o8O[_,Z кukVʌ3HLLdժU|Ջu:,YjU^zӇK.cT>7|Æ _>+Wd˖-VR0-[dڵƻ˚5khذaڟ3g>(O?47NG5c t:]|lܸUVqBBB,>>>={ۿr _~%QQQ%gvU\wwwlB*Uڵ+kfڴiL2Ū'N{M^͘3rntoi4omeB8F)^SͥK !==!rKu!!#?Ф^>1A@4rM\SޫtsXBe˖Şl]VZ݁hDQfd2F xwm.IzzzV^͓O>IժU^z;u;rVKժU2dW^u-ZTa'w߯QǏ'++RƞKQmL&FEpp0u,]er=1cݯ_n8Nѣ%mqǿ–Gi=QNΝ[iiiԪU+zx"QQQIׯׯ_d21sL5k/kf֬Yfz)T̟?ߦ ͛;Gʕ&99zV%00ڳer܊lw%,,kҽ{w&L **uے ZWעnϸq㈏l6Znر>`feecΦM&!!;wϴi e͌1J*ѳgOK; ~`ԩ{zO@@@믰9}43f̠N:>}#G2|p/_^h/ؘs>'N/j}?~m2i$,Xʕ+ر#{q6-D9uҥߟr ~mzˮ|>|~ 󡡡oޮ*Nκhժf͢M6ܼy???"DoNIIInoooyyٸq=B,ǏӴiS*UdlY*W0a;w͍m۶믿!2⎷%9f/^̳>СCyꩧ ,_sA"##߷o'O|ի{bǎlܸђ\۷s!chFn++`㐐Ν˓O>Yd=[bNMM%00Eϙ3gҩS'Koĉ9p|K.o!Ji Һuk]Fjj*fwy0ʕ+O?_e)w ""wyz7n̮]>}:o9nnn1"~mNju)HDD˖-}Yzz=/2ժUB :Բ,z矷jgڴitŦuVݽy|DDDMZ>)UW3vXj׮aaa,[̦읽^z{m߾pn)992~viZBBB |͖mr$''S~}NJTT3f`ҥѸq|ow-jJ꥗^Bb„ ?!((T=z+s2rH1,DFAlll^3Hff&gΜ)VB  )S믗Ckq疞N͚5gK̩8իWӫW/"##Y~}E]t]xJ*Y6)Sf֬Ytm^_h7of޼y>}͛ӣGz= z`0þqϟ'<<ܦXcbb۷/vk1j(;Ưʁ8}4ǏgϞlڴɪ+wÆ s9 L8(yݸq]2x`Ο?O\\ݺuYT]wwwjժźu8rdРA;v>}:,[CѧOʗ/osiΝo#[D˖K̙32dȐ_e,nۃ7={ҨQ#&M7n `0owiۢKדf52yYp!7n`ȑ+LmI-h,u,3|pիهz 3m4Ο?_l{w&""NN8Ao+ƢL&ؼy31117m &Xaʕ+Ek,I_:uꐜs4&-B0<o(@(4QiiO;UA"##ѣRJ uՠA5c RK/:*((H^Z)TӦM… -}uRUddd:tHʕ+L /X3gJժ}Y۳g QJ)O?RS%''nrP'Oy^VZV9s&_-H͚5է~jy\ oYX^TP}~R&WmҖmo`, IDATjذay*##K/|llRۼUPR&q0-[TZV}Eے [UVOՇ~hy駟-Z(ɤR*:::zy6mXrqqQ~+8秒/11KRC6+,fǏWj۶mQFyh4ӧ.\`U&!!A*>>昜ˤRY`Qn/ n3򛊷r[V~z+_ɣ8uO̙3-W>xΟ?oՕ۳gO6lpuuE9s%1*n^...lݺ7֭ꦦҼys4iqqq6hsRuIi ~3E[.ml{m۶חٳg8췹\^ՎYYYYMwﶼ^v%#ɓ'sEׯo)?|f͚|Uy777u۩VWInh߾= <}͛m^pqqF̞=5kjժbsAj֬I@@Ν+8qxmaD8uҕSCתU FÏ?hyd2o>6lXy>I1W_}Yfܓ[j- ;rvINlj{xzz2gЦMbccٽ{7ׯ'!!غ#11iӦQ~}|}}mJjԨlf߾}6-(k=/_&mWy+Q\os\0 VS{{u;$/^Rʪ|TT111 Z`=WWWBBB,sf…ʏ;ӧsZ>[|h.Jq1uy +\Ν]՚v#p꤫( 7HVVYYY\~kײ}v˛Bpp0(&mqLT$mqe &GK$ǿN̼cݻlB||<'N`ԩ۷=z`6ׯ|gFK\9 ƌCxx8̝;#GpqVXq[+پ};/;v̙3l߾#Gr"ի :;wr~'}Y:w\}ƍǦM>}: ̟?5kXŝs&]gϦcǎtڕx7nߖzX_~ 4wȾ7..3f~zڴiC=ܹs{Wd&L`ԨQӦMoUgϞlݺ!9>b畔ڵkiҤ AAA,X5kX>U7,,iӦ1ydˆ9]h 4uTZ3f;@}nܸdO~gI|xxxa#u,m3f +V[n6mn{?0ݺu+)F_|auůmqel55d/%9}GXM;vcܴ6&& ҲeKOXX7ndժU9rΞ= `ٔ)Siժ-Z`޼yjժdeeMzӧ.]bǎTRzŬB1x`j֬IyGX|y1=|7lذrJl_)(;=Ռ9K)FW>Zw.yMDz#?~ p\PGh4k{9: op> .X~?rKO pٌ+AK!(;Nt3|lrsA oqΤU!w9B!?tY̔fJ _cIIIˋڵjҮ#,?^b0س}-Qr /&CB!Ds+)|,\KF%a~?t>Y}VkIl_^k秩^ZB!])0LL~-زY|p;fIm_nF:?$C߿LbB!c8ie{Ҍs:kӿOo›4|p0W^}`Ǐx·}5&od˶݋ݻ^6BܜDze۴AoR^=Yxm7YDv\ݻ[Wy˹J3呚ʼ|3!BL,Ю-1#aga40{TрK_֭nk9JS'w݂OH?Sd+8_B!IwEQ7j$.f՚/uFv2"jʕuHB!MNeG[n3ѬiS|}| -kSUË1l޲q2(B圲˖˗X~Y:fe.-=\̳;-55?m !pʤnn^WػRSn;;IR!!68geCRpPӧ㋬b>w(____xڶh;IB!D~Nt2lװA=8ȵɰ:jc9oB/B!9OApP>^gG󋭣Ï?Spw:Rk,U˖2 x$B!˖/5T}9o(ؾc'=We43mM=|RQ- !9eUa;W'pX>Y‡伽5]!+3tqax{.h4"{tJHLYjK!wLJHIn]|wص3gΒ'aujӢY<4nKѣ0eB!)eйl.DE dd4b0ok^Ëh !LJ;lCuwE!Bs9}4MY%V!({Nӥj1hNZlru(Bܫ.rwӑoYex1c'{Y!B[+$gKO'^l6qTw/kxֺ!F!L9EIVAӓ&uL&SYfřD=j5NBq(EVlFܚxy]Na6;Gs'yL [Yj]$BE}hp !癝\2Ot:2%{xmIt?hnšzlYh5Z-%B!UY<:O+pb^B]\(륜AeЀ34!EIBLf3I7s_eJ']~IOOr9̕0p9SoRN,yU^/!^d6L'u| ,>X_xyx r] M^Δl%B}θN}*;ENtAv_9X)B!V !B$B!pIB!@.!BK!$B!pIB!@.!BK!*I별 ID+w%ɗpFGU(^qI%J)RԔdn\JfFfPZ ^ . ΐKMNuERo&QJuqqL8/Hr .̌4+T$B؟ pL"fu5F)3zC3:ӉO=5-7_'z۞d(R.YW7f?m0/ 쏻{)o_S񧹙J*<)UA<,``el=I7Y#FedB!JüCd_`3ԯ/jIX:w<_"v?_;>ϒXl9Ƥr ZzvNdeer&ÇPۉRIoͩӧza0aaupwq9 {tƋâ1Ŵ3w9].㧟ɓ)Xu7ʿêXlB8=t퍷7O?)7Sl%C8{< rIDzV2йPD|?*E[n&Ŋeh5flUE .ZW[5biixyyg^"{^0PQã<~VlB8=tոQC&ՐإG. DO`0x"̬,iՊ*zHgfjTβ 0,[7xa^=J+Õ=l?7Pp FVǏpBʘ7nʕ+7՗`Ok\M ԿX3 {hKyi(E  =rk(fE@ \v +0s<ޡ_kiU?䝩S0 |8I^8< O ^G.|y =[O"" VbQ(S(M&0r?s+jLhpusߟ'N`~=7n\'9%]Kl* G$ŻOOLL$((Di@0|v ,V<̘!gΝgy|p>.\`sKcygzqve0SaL} >͍a0,uߟ=oshԠ}+Xb9C*1ph4\fƴY`>.тEQre+bT'xi9w~#ԯ/۶a[xyḼs-(IpS+lv3];? \ݨwYb9<oMgρ;gޟ1̖6 ڵ3?l"V,]˗"KYV̹O4n)7Sr*j(Qʬp"gt :[2ͷٹk_HT )''z Ӊ! ,O޽\?BPD2317np Z646~ C?/=?l:vhOqusʵk,c3ߺ7ƍ`ܘQCؾs~ yU fSחSY ߯+9-w=7jZV^F!R%"{ dߥfhu{V0bע^xAbgTU!=t\f*Wl6sg;jD`rAld2Yn_'tU=5MOa#iHm*wc^_+igLZJJv > [ή]?V6Q/]Gz~0+V$W6_!맟Ю-ʬN.ZHP`I?S IDAT4jsu:!+ԯ[VPgHJ!+ 4Ü3|Bq6yz|<xkdƿo.[V&rԯ:|4pB'r /\hkٳ,[Qãyo,RoQWXoOOvc*Wo4=vZvGǏ̈衄V'}ۉh,U%*[Ϭ YͱǹˆaCR.(wt2gۡôh x{yIOK%=-RS/*ef~2Ν̖ͬ-_ӱ&n= Eڭ Lf&tjլIjZz}&z`^YN璮0ͼt>szrݺc &}d' e~f3\c,xR%,'|3>ϣG~r4hj $--/68yU s\t[Ò3e+W5& ^^fJ ;~xkru='NZp̙;5#wPҿs_}b6r*G(6'{͚5"5s:/._?rCxyyXO{/Ȥf͚J6=Fbu|/7l}Go z=ePeHQŒXg\4E8ڷiԯ/:̚å+W ,W~{qX{>˴pb76f$s+Mh2PxSf7_[jUFD%[fMi:X ߷KTPW_f_%G͚xBUF gYd)Mh,ף<+*l<߿w$:f1._ɾ uk*2ndW_w[]WW/ @m09?dG/u\|4k&sקF*!D0NO<҆ϡBgT?ֿ瞴&\+vs|0=-u֝YQ֔RN=BͰz%酫[̘fYYu5LFUre 7qk>vsFE}>WeW77Z(Vy%x~h4xygagjqwFl6c%rBAͺ pqqACxy=`xw#`5sMELo%쮤\Ŗ볊6.5mN7[T5Rԛ?6WN!ĽC.a^;vнKE!Yȉ8ykެCB! $=]ʢj{')7mZT!p4IAc6HINr|B[ K6-7vub2HlmWqH%Û̴T||:!J,3- ςi@!n|-gF_.0m]l6s=~AV۴B؋$].4 ZOo|}9ao\d2uhBd2|:8/^^>r'z!!Ënrpws &$^yh]JKqH%"M@|,̒{g g{QբjqqqAH%;INZ-fY-qWIr-Iw$]®r~K8ۨpGIA qp>q/K]HF=ʹp.7_.6DJr g/HP>'Ods>qKUzj 7qMpuK8?C{'N29KUT^C\]Օ*jx]ە}Bܭ>q/KUzz: !J/vmS q7ĽL.aWfIWw-Inv'{$]B! IB!H%Bt !B8$]B!IAGhӎ ))n=`0._a%oYz=Eq6+V ""@:wLBBBY$D}BhalkU:֥3۾ȷƤcLUKԱ[{7˖YƙZ_|)S?I֭(]}ǧhd,39']nnx{{G0vH~=pt=yhjɖyx SOQvm-[FVVV*Є(O*g~/r4OСh,_a0tٛ';}L&}.={x.ѧ.*f3Kg3c'K,:W08z>CG ua|K{wӹ,f&?z[vJDBIj)+J(!W, *]Tnz BVM2HfN~$N&&g6y̙3yO}=9 } f+f|ZbNG1:j<&V)޻CУOnVVVISaSO?ʕ+=zfF_.ٰM33iE;vbS4\U*` t]IG4 23X&~&+1؜ym8 Y;mD*I$؅/Nƪ7c-psuW棴TҾydk^'iNtm-@QQ}aX~RLO0.1ƣ_^8gX0w&HР||| ̕ԯ_7oެuDΆmN(U*< ؖ-[<0ݧkt ľ5L)1J%~t ۷׷< Lűc9x!>-6UfT*))S=x9`k0"bΜ7z0h@~`b89nyf3j:̙9Οg'WMPRݟ'gcN:I&xmZ"G6AP_OzFv' _tC,'ͩB#"P*Qؼ5i~ Ap]\z!cȠX=|2 {<k֮+Wu7]węPPP;Dg6? zsr\x rDJuGu}8EKa}]&aѢEXhRSSq ̘1`Nb gg%V"56m۴q[d޸}; eeeO/Cx!: 1ƣ;m؈ńqcѶuk9~S-ZLRyKbRdnXjH$fna\o jr_WK eפ VLM] stkWUlmMdB``@O2,^wA^piͩdmN[)P*#*b Wf!jR na]L"Fcu-=gLtz[}Eȴno(6X,ԧǭ3u:Fǰ!Gr=U 9{yw58V].?ś6<}Hu1&N9wJܪ*Ak)Y*UT˵k nI=NwzRȸ]:#'/q wyCWQav'#vś hQÇcDP{WG>tm8;ik]KDDd+WtC 0t2jAT#j 2L2&șYMe ]$*] )WS2&șYMe ]$*@H7;DHRVF:E-mD]ERxz/]@~nOS(/ /Qg gc6Q= h\L/ɐdP(<b΅m-D]EV“" 0tC 0tC ۋdʢBf߁ǓJePxx? Ъ.d ga6Q1tr ?7M=/o_no_?{W}VQx2 Eۄs`(g6QW1tEAێapqۋL&?<{ ~g gc6QqN*74{ 94} wE-mD]ER*kjH}_WZ&93kD%՜BNK&l̬&2.""""`"""".""""`"""".""""`貑oSSAϾ0*2lEIIfq_Į=S&ѰaC,{l())?7V̜6lhO_bܹ>{W1;פf&Hl&/5.ruu\.\.GvǘGs =&ʍ5ۇ̜s㳓_`aUsuu1b4m? J%~t ۷׷)1X:̝Sm9(UO^^l(rSscؼ=tA.C_my6Ԃ֭ZiU џR):G͛zܩW_b_[ 7n@,]IERT-YӦڹ3&ŠG6t ANgΘ@")WT @*YgͥiQEu ۄ~2nnؾ%~&!Jqݻp{;~ĢkXysEJػڙ/r~sM+*Qc#жMHlޚo s.._y7nBYY(,Dp%rԯ_fm)^`#ѲcX j5+7,y@&a`TAy; k̘18pq~ػw( r&l- Ca\]]ma xY[XLzXa#fƍE֭"Fcu-=gL×B\V(&Mepws[B\i}Eȴno(6XZr0to˗. )ܑigjp\8m;7my8БbM^)<<@ R[U}"h-e:K*yz 57rO DDD΂I#=#7еKg!.!ё#4 ;+D`@>#‡ڻZDDD.hI q9.""""`"""".T&Zw5jDVA&Z&93kDPx^AT# WxZ&93kD[PT ET*ne?0Pr&YYMe ]$*}p 9x>ޫ/Y1nؕG}Ɣ"..=z􀟟[nUٮ/Ffݻ/ͮ] H-ebʔ)hӦ <==OرcV?V"saСhԨ;wh?}D"Axxx,yz|TwL>,{`0j08'Ɗإ|*bxS\}H$aҥtN8\#޽ѣG#33SMQQ郢*Kƍ5ܹs]vEz4eM>6;ftۻ5RZZɓ'c̘18s >K.!&&FMϞ=QPPPeEpp0ƍΒ{YY&O Alrd[&^"+<<<:Ǽ/cƜ(V*PԺi\2iƸ`ǎ!!!lRSS1{l<w}7|Ç(]ޚ;}V^޽{cӦMumfA IDATڴ‘Ռ+{H埃[l{ÇLoo*KMMEii)FYg}hԨڷoo"*pˀz!H4?QZZq[0"b Uf{Z1G`;aLܼU>*;$*v&Ao0[ngei:;i3{Lxoҏzz>- 1S5q[SߔУOԳ}_`0i4ߴY?o5J>>>~~~4ݻ>>>>uM]oƌCsf/0r*kCCjCW1F`i1S J4eܸA(v$(_RT2o߆ZкU+k$0,T* r9BC!=wi\~͚6EΝ5)=wǰCDXc/yYh шBff&v؁=zԩS޽;`ƌz*~GH$ƢM68y$:t Ք}UVؿ?fϞ ???aʔ)9r$`8uRRRb;u@}k@k Ѡ>}:Zl'}HJJBJJJo#--Mz;G͛z7Ԇ,jCWL1!*"rhx((&۷OkQ%JA By<_'U['Jqݻp{;~ĢkXy'oDקfcD`,Y/^ѽ{w\v BÆ ȑ#o`zsssCǎq @Æ !Te֭[㏝kbO^iu7 pw'zׯw={ 00}ޜq*#d* Jr ZTؓilɤRs7>!K9]z 8H9x((r9>{N/y]!!kMW͛רa;a_}}99z;{8wPcwwwCHԩ Vqen@y8p 8Pe+WTFd Nދ3[={`ٲe8v+_'..3f̀DRuLݜ{RRA@iif֭RRRPZZE5R\+WLf մ2s ]z!:"{?\\\[wWr9 kϗ/#?I>0X\f:\ryXq|}|'YT? zsr\x rׯmXnMѣJ-]{YT?}]^z | _Xv-> &(WZرcLٳ[n2e̙8z(222?#&&* cǎlꫯb˖-?۷o#%%ɘ9sfA5hۦ ޏ"JGɓsNk(..FIIIΟ?W<;C⎝8s c'ն3ֆ釼qv,O/0<<I?ÇBtb&G=ww۰Yw Ƣmň9~S-ZRi_Tv׼9/csV}I,Tj ]]퓫)WK eפ VLKm|ܽM [9af+O=FAy; S W駟[> K,ȑ#QPP-[_ԩS7ƍcҥ_gyOW\5ݻ7݋+Wbܹh׮=ʑ.X)P*#*b Wf!jR na]jUĉ\J?L]dM E߰ն3ՆL"Fcu-=gL3szx}Eȴno(6XSn^Sw7._!s/?޾~Ǫ˅gжxq&#.D9<Æ aĥ;%JunUUZ,LE+57S Egd #v錜<%$":2""""%"$NF7Lj9.m8{WȦۻ NsltndPQ*?SSl̬&r GHT ' "GVxreM3F ]!G@HJwU,Rp+#6AZmA$&t[A:Do_?\t90<D#s_T$ Oĭni)ٟ:QݤZk+EeV_0\26Z0HDഃZt(ҊCb*tLxcFjwTEEDDD ]Q/ec*G :ʥP*o V-a/S2]:B7 cBv2Pr[ [#]r1p>& ]F,Loߦ.c@F;p;hη0|VwX2М.O3'tӡn2tJCG;7]K7x۟IH_2U/Sr;C zna1MBХ =Wڜ^Ԟ\_KwҼd{KFCx `ƶN1JwjQ7UeEOO~Ñ\DDDTv ` [L`Hnx24c_;pi27Q."""9Y VWj;P dY!սODDDdKkjĜPo}H_Ҿ_۰FDDD:{ߜg55 ] (cAL߭#"""d( ^zK}KCՄ%K!F ,̘ V~3an 27XDyYhmo,DͲL""""c c!h%eTPciHٓ#`s456bN B/Qp#|ڔIDDDdȒUkurlވj6kX'0DѣAK-C9QC.5 '""""""""""""""""""""""r4K!oIENDB`aravis-0.8.34/viewer/data/meson.build000066400000000000000000000013321475431451200174540ustar00rootroot00000000000000po_dir = join_paths (meson.project_source_root (), 'po') appdata_conf = configuration_data() appdata_conf.set ('ARAVIS_API_VERSION', aravis_api_version) configure_file (input: files('org.aravis.viewer.appdata.xml.in'), output: 'org.aravis.viewer-@0@.metainfo.xml'.format (aravis_api_version), configuration: appdata_conf, install_dir: aravis_app_data_dir) desktop = i18n.merge_file (input: configure_file (input: files('org.aravis.viewer.desktop.in.in'), output: 'org.aravis.viewer.desktop.in', configuration: appdata_conf), output: 'org.aravis.viewer-@0@.desktop'.format (aravis_api_version), install: true, install_dir: aravis_desktop_dir, po_dir: po_dir, type: 'desktop') aravis-0.8.34/viewer/data/org.aravis.viewer.appdata.xml.in000066400000000000000000000027211475431451200234300ustar00rootroot00000000000000 org.aravis.viewer-@ARAVIS_API_VERSION@ CC0-1.0 LGPL-2.0-or-later Aravis Viewer

Viewer for industial camera video stream

Aravis Viewer is a simple viewer displaying video streams from USB3 and ethernet industrial cameras supporting the GENICAM specification. It allows to control basic acquisition parameters (like framerate, exposure and gain), and to save raw still images.

https://raw.githubusercontent.com/AravisProject/aravis/main/viewer/data/aravis.png https://raw.githubusercontent.com/AravisProject/aravis/main/viewer/data/aravis-video.png https://github.com/AravisProject/aravis https://github.com/AravisProject/aravis/issues https://opencollective.com/aravis-project emmanuel.pacaud@free.fr GNOME org.aravis.viewer-@ARAVIS_API_VERSION@.desktop aravis-0.8.34/viewer/data/org.aravis.viewer.desktop.in.in000066400000000000000000000010571475431451200232760ustar00rootroot00000000000000[Desktop Entry] Name=Aravis X-GNOME-FullName=Aravis Viewer Comment=Display video stream from industrial cameras Exec=arv-viewer-@ARAVIS_API_VERSION@ Terminal=false Type=Application StartupNotify=false # Translators: Do NOT translate or transliterate this text (this is an icon file name)! Icon=aravis-@ARAVIS_API_VERSION@ Categories=GNOME;AudioVideo;Video; # Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! Keywords=gigevision;usb3vision;vision;display;viewer;genicam; aravis-0.8.34/viewer/icons/000077500000000000000000000000001475431451200155155ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/README000066400000000000000000000004701475431451200163760ustar00rootroot00000000000000The source files for Aravis icons are SVG files in viewer/icons/src. The rendering to the different sizes is done manually, in order to avoid a build dependency on Inkscape. After you have modified one of the source file, please don't forget to launch ../../tools/render-icon-theme.py from the icons directory. aravis-0.8.34/viewer/icons/gnome/000077500000000000000000000000001475431451200166225ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/128x128/000077500000000000000000000000001475431451200175575ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/128x128/apps/000077500000000000000000000000001475431451200205225ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/128x128/apps/aravis-0.8.png000066400000000000000000000141531475431451200230240ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs+tEXtSoftwarewww.inkscape.org<tEXtTitleAdwaita Icon Template?tEXtAuthorGNOME Design Team`v~RtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/Tb?IDATxytT}ǿyoͦ$!$!b1}G$c7'i&4?MҴ$I;E !V[`Ďfv"HH !͠f^ͼY]{o@ $H A $H A $HUI}}di²)DzI4܇DJ jcB fby/$d=Z %ǃGv, :flƛ-~' Ro]=R4*-! "œ4gm mH mPTTcc_ m HYc( +~@` "JYaaT+@pQF"D^+to0 N+tz-T*%hJBwwΞ)k7%'*y~7@gGX /2]hA Ѡ tP\: i`L(]C[<|3̣G>#Ƥ(;ÁG"׏J _'!܉1y@4* 9e|>:=O>|YH'H$6xgo"wRexI09eΞ>w:4ga!4BZLf;(YVhuω(jH)$R$J.FuM V7oU/'Q8ql}1Z! HKQBxZ՚N~}}31vF:u=nL4!*s[6OhJ. "( dVD"Y͝,^WnnK#2 5^";;rL!B-൮+ݘ.]?Ohlۺ 7nqʤg=!yS'D'd,HRCr2Jddp^z<4X)C~^%@R f}+є J-vעt5fƆU|aݪ<|8Q L3 F kEyZv]nEECl1bԉڱs^Qǿمp0 b˙ kJ T*<^DWCRR\~_l+xO&+6MxxS$:Z:d#F)@щeDh4`0ZdNَNlݼCq^8eTjJg+ @H72yOp8+ yV5~'1RޓEM])d2)Fg gO((J$'ѪPPloO/6|98sX&L> 4MpPkT든F׎PzW\9E:iPP48.HPaYVs>ue2O~dݾߍVaUF.r$՛qt;8wˆcZs9;ț`98e# ka0D^@Q,XǨ-e8e-i?;׋eA }lvǦgcr xǏQc##3 2R9eΞ9R؆w;\6NMӰ'jzL(qL,8]6T*DY+!usAD"~toĽx^4B;"PUJgA7Tdr914D+)3#?&s2mܩOr$jJh յ%pټߍ5[8e$ +_"?aìZ\E0P5H,o ~FP*^&ՑGdRMf0R3x*%* !nZZ;%Sm}ڕUeHN6BF[Xٴ=muށ ,D"AG\5hS@QpR| p.vlA~>Wn܀jIL?E2^$.`ҕ&nۅ.GhFNܸOĄHlS8ĝZgޟG< wYQ`;XhE+ؽkkW:|L \HHW p9;ԋ%4d]0 *kIQ`Y6iF#1ჇXm:%+yz̀^V^B8.]= gͷ_]W7lo6Wa2jť@z!.i9w4BjJaHN"n?76 y%sJ Ia2R .3B7orL-I+,Vm=y4oZV0>t'Orr51Ɂr$xJ```+?䔡iU5Y\L.wbhhKE!%4~>#sвȌ<鑙 BLEZ׍^->E-4̬tL0ķg$b׏k#eݽN w{ lMh"yM)aؑe!( 8|)˲AU TΰA'QBϡ8'??G;0vgQD 4jKփr^ s +h`0 )n#BNT`hh ٽSв{]44Ȟ}#݇%`ۖho)3yDѨCJHIί•9Ǐ_]8] 4MUWB/( ؟vK ~!yfRzjs8‹ZSp~' =Kfb#}>_c† ї&HY`0OJ nEQO/U@ؿ7r"1y/$gޞ^|cNyL|蓒H̩_bI[m]<윿zG7mCRh4>wCV:@( & -[:;c-Ozioy}_­[q^'E jE us 9@|צMXeY4Xռ%UB&b6G#b5!TNY׋E/}bӋ 7E=ݻͱIf̚ѪY4E=-PUS{gGK8,2Q.(˲qU5%))DJ'$'hPQ]I{Kx\u&CL,V&GҦ3ppoņ *Dev,]d8Ds {#?R ί5jZѝI{_7E4R΀L.mBq8H$plSy%"潈ǩS-A3b7n㏿vIK_ݻ=_{<|~[PJ$8N\5jp%<*ߺ͟IsFi} A\ ]]ݸ~! &ycfUE聩,wཅՆѣD6Kp`p\5mc[ܷFArEa| /iq9]i 2R;1Sѣ>X>^=XaL+_nh0Rdw|eLjK8[`XˡiKb_xC!{(m쭕(RE%0sd+=E(. }wgΣuρ{i|BP*~n5AC ?SHM%O @(:z}AJ^%_ y oZ` )> /73 SRAQr'f"c Vzc!H(BʉL8KdrùyS6l-Ç9̈́~E SCT C5RhQINb>QF/ŝd̞);88aHd׾6_3/// )U A>B.:.,7I|>14;~~; 1n.d&M.(GX >~$^aB/ sBN<'Yĸ.~d0]υw`+Z {k =a:# l@Qi' #pxF~^'Ri5jd\FL 񩼻amZpxՠ6t?ء8ŐK;B!(eYo=5[ !_gӸNTgrr37o|!)) `mzo:ԉl=jw[0XB?!X2BI,C.rw{RbjPKp5 .x&ɠÄLN{zBR41}.Hme]E) u% V |jʫ^'ںl Kq!ò􂊊P P&/~DzP~ػw|²o@s_: |wۙuQ֭[GzOF,>_R~])y<1`h(. m;+ZR{F#Gy'@ImO>s퉧4N :HK "t.GH$ nvvnyx"αݤHv"e9b(7_`']AVylZNVQ A23Kf`tt|y㿣@Gɡj3MxJz=b1{LNN[x://4+5j.tspzr7"zol5 ,D"%Z>ZZ*5/q=>d$))>tXXB~0 7H$4٘WS).Xys^w|St,&+ ̣IENDB`aravis-0.8.34/viewer/icons/gnome/256x256/000077500000000000000000000000001475431451200175635ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/256x256/apps/000077500000000000000000000000001475431451200205265ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/256x256/apps/aravis-0.8.png000066400000000000000000000304751475431451200230350ustar00rootroot00000000000000PNG  IHDR\rfsBIT|d pHYsetEXtSoftwarewww.inkscape.org<tEXtTitleAdwaita Icon Template?tEXtAuthorGNOME Design Team`v~RtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/Tb IDATxwpIv濪jn4F{'膳jeBJRH'Bt8Pjc% 4wC3tC$@e?8FYkeUV{( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BP( BHxr-Mel00E9Q(}G+ f\>SUs}6L3UzN 1 ^QlgYw3Cj=N  W 9 # a ߰珟;/>M+KYrK(܏r1A[Pcs}r*[(15Gb9e /{ $xJ3,;̣g1,+6grE;<\cI~qWRC$1q}Y76(xe?eG`K>`<~%.]P(DsOޠifh4 lj>h*Ul^ xcHX,wY(.EF=x$1=w"9M܂cΠL)U3EGdǐl>i)&'??(,.ʝg,Œ{WH|Fx?FaJOE5ϟJ qIf?;s5/_+= Y2?N3 lJ"9}߽aF' JOC66p|]ꁢPX2X'zqf~9MJO`0R!iX̒l3 `ƍ(ɚUN GQ94GkE\c>v'X\+]1V.[p8L.5/_a]: OG (>h88%%3vv.cƍ"}{>@KT! 댛Oq@uCi4:|:` Y6@Ub9y6TCjQɊ{SvG9?1Ya4*~SwO]lߦX{x <̼)HMpMۣW 9P}FQF' OhR\6Z-y@]f 4zٔk>-E,9P}0>=S?Ya6K#7l#֑P(ʥUhʇA;]1 p8p`IF ( z6½0eݲy޾'֑[w:vG:&Of`ii0 ]$c%قKlyj4'uXkK+֭B# Xj(Zs斀mWgaI 5-ueQ+!4#&WaȪ?ܾ/_HH3dhFdiwA !xOjJ 8IV,,3Ҡd2#jm{PaCk Baby vC2yłk6 I&ad9HU}0 q7|>J,}{Sb1YrB'r|~IqR/K&J1HFZXG,_ߐ5HV<5-hDH%ZM 'X]M4Fx5P97n<|V)JO~ XRbGZzi"u[ a;I@uRۑZ!REm&d2fMrt(ԄXm\%j_>ύ@B[@-[: 6m.֜y% 4=)"+-Xfhz˗AkK+NΘȊ&3 m"AEvv؃9{h8f ~'F#ڡ";6,^B4inzr'(uةgq%y y>h⤜(Fr(.GILbK}' Շ9Jj_y`ꭨ'MߘHMv+GO1rX>}x SgL\qS/Txb_z(?~߃)S+4kWo%y˖ƿVT)g^ 4lf3鉛'ԩh4)5HIF:K[j#E#<#\yݼ;q@fΝouFfUшaU>tqU{k_aVza0aҏ5==|*1@FB alV3"c, J~׼}.N8,Z)JEqb M,B|s,޿k ?a4nGo9ul&'' Z{ɉǽ{[ pM#Q"57bvI3m "M٭ 1O>Ǿ}:&3}\[+ԴL:XM۶&^X+b 5-O hEUTAҦYaVܚaP1W\'(lACC#VWm@{S Bܸ~KiL6Z*R`N6N:j&|رkb-+\l0|bpA,[ZYuͮ OTP<FX40y,ܞXu˲V.$̩st*eǘ1JeCMrr2RRby3 mZekn:L $ 屬r(ZsiyyS`2+rSP8 )}V ۭ P}(%oRÈ+V;L&VkkN rc2:D>$BaT-߱;QUO>QьŊ9Smp't "s^<^C3zbMtX7] Ǣמ\\vL4Fp-A~iiiŚn t*z_z>cp3{^nX @lڸMI ~8^ao[4D^߂#b,2!@$޿{/7h4()v|V3^\UXa}a̞+70&%u "z&45̘9Ipt$aW;.W/uXoL6glX|;5Q4@MkܾXGoУh?wi]nB,밞u(rr=ju| +A("),.hRaX;-qp~úxɱ#N& <|toeW-&v(QC@q:]bw_ú’J}>OM ,]J%eymV+fYYbYud֜"ArRI@zNRATjZZ\( 7qMQNlܗaVk*22ũ306i  rjp`ql=^xrY~jR}բnϩQWoojQ\͘- ű,<_h'PqmO4=GIߗ7ޝZ%JCV'(A@?پu^ /nؑh+d}P\+Hp:[*fh-ذKIbA%ZEh!6v}/"I`F$5/ (C6݂wU-6*rҊUUEњ=Xph6aMs Q׿-_2ֆ[%{Pu7r1#%:4U+ףYy0eKjj_aۗ;uN?VJvGJh/_`S8{悬cJE+ۺ0f\6c>eѻ?)4eER_>Qʌ8\}XGñ(t8uA@/ܿG+2/}+y& yE¤d3!9 (WS;{`ܹ}waFҲju=jm!>4t:GdazNr*4oEK ^<v 0g^-LMB@ V"/e %yHzD444bu:RdJQO7VDzlBx$:喝xXglxCBh7ضuVEqInx}h0mjzQ zubNja-:|e'j^;88Yq=^'ϱwAb.Y/qЩ@ٴq+>y.@x=֭,[/ł>A,\H$BS\2CMMM鄽C܉pɗPXN3PVCsS3Y6<# >ܾuWT ͘ 㮿]0rQN?ay9In0 f-\H2MPzgTV.^%UǞw6%qxA32}3.W.|D<AC!:q0IJ%/^o{Ͱ v8)G8! ~`pJ-cGWR3pebӆ Gfg;k45Ŏ-ƭ 9RԿ{#W`1hxDzkDѪ](X; s%%%E2m8|8ܾG"{԰ڮʜ [Mp-`"'2aX.ݿ|VG?!+p,f+e.JKbQÐ5$Vh%o]G ÈCCAZ[Zzc eª5vU. F0OKi;Ev]زy(mO#H},òdf YӐ?x^JT 7n'p,f y}~uMt+*ͅB[]QcP56XgZ$Aјj~a[kNG7)2h@mmھXGӡXب"H pƏBfà +*Ce{p̦DUknէ߯2Pŷɓg80dDn:/@ɲl S;4ĠP`P%D)-)˃]oZf[V*@R" n޸S'YS0yڄ 'Uu )'0 (g@MU*slv^a4p:&O'(rst9mY+~z׏7uo 7K%9p8`E²1htZGcu_:_sZIGO)IX^)R"ߔw:oTq|>BSwG4wNN}yK<`T`X`Z͛kŹt߽'niOo/HI[RGW,[PSMat$6$[bIPIDAT$j0bQ=7nEDDF>444Qⱒ-&Lϛ,;t:=\.ژjhǯѨVI 39Q}hlkɢ*Q%eicOENYyJv{ I{ط%F]['Tjj^+lVL<50z}ĺbGv8:U|铧JOO>ܾuǿ/]\%8e wSɻ_iҬJg ,J};\X+=>ujk7O[cx}.35˲HXCBFF/`0|Na;۷~5ع|Sph4KW_1Hr ]J qID߉D"XY%`7J\{;]ۻ=":<CL99nOA(>AŀM>s;r\hs)A'"\8 _նX05[|x+( x.Ls_\:/^C-zXl-t% 8v$E}}R`Պ-;`wc7s46vhhl]󏫱}wm)?x]sACŜblٰX{=I $y%޽%M Mx>y۷jAxǫZXRht;{1p9xY۵ rp%h.2Z=?x@_~'OF N|=| !q!r|V,^N*ipoԏwѨ6ȌF#NS)C0>bb_u]}7PbwEk>b1K^/.i-@ְ@-qөŔC4x--Epem[a];Ȉfe=#=( ĿE"OSϸ{GrrC~&Z7u[ Y+H~ 0dXFoPxw_Iwc٭;?HƌrB ah<ɩT=.֋0=vd<\vxHO/!|$WQug%eH/KJq&ӤBN\Ap[~q!T/ ]He`ؑxS(bqxQ_w|8hy$ݣ9˲(+K~ iF@rvQ(NĖGZ-20=,ne\ۓ67'wd8=`%ϜnQ^RIH$KK(MFss3^}4)˶\Wn->x*Pdsw~HvIp((/EE(= BP( BP( BP( BP( BP( BP( BP( =c=IENDB`aravis-0.8.34/viewer/icons/gnome/32x32/000077500000000000000000000000001475431451200174035ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/32x32/apps/000077500000000000000000000000001475431451200203465ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/32x32/apps/aravis-0.8.png000066400000000000000000000030721475431451200226460ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYsItEXtSoftwarewww.inkscape.org<tEXtTitleAdwaita Icon Template?tEXtAuthorGNOME Design Team`v~RtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/TbIDATXkPU{eaY@ÄTU4 l@\2GrLӌSӇj'3SCQ!xeMC_>(+ L_Ϝy%SPe{Xj1D._F\>??F[k sSYѣCQ%JKK;g[sj7oֈUuCSw9$$Xb&J%:4AeR~;1-gBg+LJ 0X1yR3f0kNNA`[7ngr2RI!zEKW`O5Ŕ&p=FnD ] vV|b]6aa^׉R:y45Uu?kW!}o6YVεsӍ?g|L49H$\C&r >h#KtHJJrQSuJӋTZA v~#'D`aO&Qsz7vYqߓ@mjIENDB`aravis-0.8.34/viewer/icons/gnome/48x48/000077500000000000000000000000001475431451200174215ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/48x48/apps/000077500000000000000000000000001475431451200203645ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/48x48/apps/aravis-0.8.png000066400000000000000000000046441475431451200226720ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYsmhtEXtSoftwarewww.inkscape.org<tEXtTitleAdwaita Icon Template?tEXtAuthorGNOME Design Team`v~RtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/TbxIDAThkPoHU."d!Kug/ig:N?u_:m/;;;]] jE]5]oYΊ !o?_.I$~̼9?yx7ૡ$$nX.]$&Bx2sڽ 0..[H 0{e8Z_??C@QQtSyCD0F련p>R5J5B^ #)%ЀFq6. bb<}sb^#*:Y) m(TT/FPD}Ǹ#Dry.~tU,Yd:]"4]ѷm _o׎G (R[lԉJE3ǐ2;o.9;vI G.~T*[̜:~[BWDڌ̞4yؘX-%h4j>3lټ@RreFRe}dEDV{Ekv0q~ Ǐ+*ddd.;+GmRwpI=EZ,YZFB)eܹ};OOnEiY e.X `,%%%%_ܯ]. nʋ A[ VSI{D.<~ν- .D!6]]\mCeeeZO5J3 _R,(ukFg0 rK( {ٳVd떝Ѵ̬,w JV%//_΄ӧͽsk4jW]H 80Q V:;IEGo@Jϟ%RudĘOfJa0$u%̘yЁccT/gK{ltudr!(^Bךopp3I(\Z@ak! ~KzΝNyDiTgxxr#oOXX`2/ o> xy!pEnRa2?>z(U*,Rc>g.xj˸Ë?(P(FOqjU6EZ0ys!x TTmlv7w ȶ~nȡ/hii@b"6- d̞Ce oG#?XlvZ wѸC#JLFə9̺7p|Is|ozAG(0wS^U&mR_7^>%AD^хs^đ'"2-Jjlpz?Y܅٨jzzzou>;+ ۻ=/]d*KinE_oOg~.Ρ!3Oksسc!2T=tt<H&ފ h"W ŲbRFlXի-@ޟϛkm *%(~]> CNbchyBbtc'0Fô>2ܾl|}E͆mFp`yb,>&wɨ%Nk8򡡡k!`JG&X&6RaQZjUVDz]YBLcG׮}C=5iDqf뾅(S" PEEG`ASD*6VM񺥞8q"Q#~J{@5dM:Vm27?<3 IENDB`aravis-0.8.34/viewer/icons/gnome/scalable/000077500000000000000000000000001475431451200203705ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/scalable/devices/000077500000000000000000000000001475431451200220125ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/gnome/scalable/devices/aravis-fake-symbolic.svg000066400000000000000000000203351475431451200265460ustar00rootroot00000000000000 Aravis fake icon image/svg+xml Aravis fake icon Emmanuel Pacaud aravis-0.8.34/viewer/icons/gnome/scalable/devices/aravis-gigevision-symbolic.svg000066400000000000000000000453021475431451200300040ustar00rootroot00000000000000 Aravis GigE icon image/svg+xml Aravis GigE icon Emmanuel Pacaud aravis-0.8.34/viewer/icons/gnome/scalable/devices/aravis-usb3vision-symbolic.svg000066400000000000000000000262301475431451200277440ustar00rootroot00000000000000 Aravis USB3 icon image/svg+xml Aravis USB3 icon Emmanuel Pacaud aravis-0.8.34/viewer/icons/src/000077500000000000000000000000001475431451200163045ustar00rootroot00000000000000aravis-0.8.34/viewer/icons/src/aravis.svg000066400000000000000000005627511475431451200203320ustar00rootroot00000000000000 Aravis icon image/svg+xml Emmanuel Pacaud Aravis icon battery is full and there is no a/c connected. aravis-0.8.34/viewer/main.c000066400000000000000000000152671475431451200155050ustar00rootroot00000000000000/* Aravis - Digital camera library * * Copyright © 2009-2025 Emmanuel Pacaud * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Emmanuel Pacaud */ #include #include #include #include #include #include #include #include #if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11) #include #endif static char *arv_viewer_option_debug_domains = NULL; static char *arv_option_register_cache = NULL; static char *arv_option_range_check = NULL; static gboolean arv_viewer_option_auto_socket_buffer = FALSE; static gboolean arv_viewer_option_no_packet_resend = FALSE; static unsigned int arv_viewer_option_initial_packet_timeout = ARV_GV_STREAM_INITIAL_PACKET_TIMEOUT_US_DEFAULT / 1000; static unsigned int arv_viewer_option_packet_timeout = ARV_GV_STREAM_PACKET_TIMEOUT_US_DEFAULT / 1000; static unsigned int arv_viewer_option_frame_retention = ARV_GV_STREAM_FRAME_RETENTION_US_DEFAULT / 1000; static char *arv_option_uv_usb_mode = NULL; static gboolean arv_option_show_version = FALSE; static const GOptionEntry arv_viewer_option_entries[] = { { "auto-buffer-size", 'a', 0, G_OPTION_ARG_NONE, &arv_viewer_option_auto_socket_buffer, "Auto socket buffer size", NULL }, { "no-packet-resend", 'r', 0, G_OPTION_ARG_NONE, &arv_viewer_option_no_packet_resend, "No packet resend", NULL }, { "initial-packet-timeout", 'l', 0, G_OPTION_ARG_INT, &arv_viewer_option_initial_packet_timeout, "Initial packet timeout (ms)", NULL }, { "packet-timeout", 'p', 0, G_OPTION_ARG_INT, &arv_viewer_option_packet_timeout, "Packet timeout (ms)", NULL }, { "frame-retention", 'm', 0, G_OPTION_ARG_INT, &arv_viewer_option_frame_retention, "Frame retention (ms)", NULL }, { "register-cache", '\0', 0, G_OPTION_ARG_STRING, &arv_option_register_cache, "Register cache policy", "{disable|enable|debug}" }, { "range-check", '\0', 0, G_OPTION_ARG_STRING, &arv_option_range_check, "Range check policy", "{disable|enable}" }, { "usb-mode", 's', 0, G_OPTION_ARG_STRING, &arv_option_uv_usb_mode, "USB device I/O mode", "{sync|async}" }, { "debug", 'd', 0, G_OPTION_ARG_STRING, &arv_viewer_option_debug_domains, NULL, "{[:][,...]|help}" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &arv_option_show_version, "Show version", NULL }, { NULL } }; int main (int argc, char **argv) { ArvViewer *viewer; GtkIconTheme *icon_theme; int status; GOptionContext *context; GError *error = NULL; ArvRegisterCachePolicy register_cache_policy; ArvRangeCheckPolicy range_check_policy; ArvUvUsbMode usb_mode; #if GST_GL_HAVE_WINDOW_X11 && defined(GDK_WINDOWING_X11) XInitThreads (); #endif bindtextdomain (ARAVIS_GETTEXT, ARAVIS_LOCALE_DIR); bind_textdomain_codeset (ARAVIS_GETTEXT, "UTF-8"); textdomain (ARAVIS_GETTEXT); gtk_init (&argc, &argv); gst_init (&argc, &argv); icon_theme = gtk_icon_theme_get_default (); gtk_icon_theme_add_resource_path (icon_theme, "/org/aravis/viewer/icons/gnome/"); context = g_option_context_new (NULL); g_option_context_add_main_entries (context, arv_viewer_option_entries, NULL); g_option_context_add_group (context, gtk_get_option_group (TRUE)); g_option_context_add_group (context, gst_init_get_option_group ()); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_option_context_free (context); g_print ("Option parsing failed: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } g_option_context_free (context); if (arv_option_show_version) { printf ("%u.%u.%u\n", arv_get_major_version (), arv_get_minor_version (), arv_get_micro_version ()); return EXIT_SUCCESS; } if (arv_option_register_cache == NULL) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_register_cache, "disable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DISABLE; else if (g_strcmp0 (arv_option_register_cache, "enable") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_ENABLE; else if (g_strcmp0 (arv_option_register_cache, "debug") == 0) register_cache_policy = ARV_REGISTER_CACHE_POLICY_DEBUG; else { printf ("Invalid register cache policy\n"); return EXIT_FAILURE; } if (arv_option_range_check == NULL) range_check_policy = ARV_RANGE_CHECK_POLICY_DEFAULT; else if (g_strcmp0 (arv_option_range_check, "disable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DISABLE; else if (g_strcmp0 (arv_option_range_check, "enable") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_ENABLE; else if (g_strcmp0 (arv_option_range_check, "debug") == 0) range_check_policy = ARV_RANGE_CHECK_POLICY_DEBUG; else { printf ("Invalid range check policy\n"); return EXIT_FAILURE; } if (arv_option_uv_usb_mode == NULL) usb_mode = ARV_UV_USB_MODE_DEFAULT; else if (g_strcmp0 (arv_option_uv_usb_mode, "sync") == 0) usb_mode = ARV_UV_USB_MODE_SYNC; else if (g_strcmp0 (arv_option_uv_usb_mode, "async") == 0) usb_mode = ARV_UV_USB_MODE_ASYNC; else { printf ("Invalid USB device I/O mode\n"); return EXIT_FAILURE; } if (!arv_debug_enable (arv_viewer_option_debug_domains)) { if (g_strcmp0 (arv_viewer_option_debug_domains, "help") != 0) printf ("Invalid debug selection\n"); else arv_debug_print_infos (); return EXIT_FAILURE; } viewer = arv_viewer_new (); if (!ARV_IS_VIEWER (viewer)) return EXIT_FAILURE; arv_viewer_set_options (viewer, arv_viewer_option_auto_socket_buffer, !arv_viewer_option_no_packet_resend, arv_viewer_option_initial_packet_timeout, arv_viewer_option_packet_timeout, arv_viewer_option_frame_retention, register_cache_policy, range_check_policy, usb_mode); status = g_application_run (G_APPLICATION (viewer), argc, argv); g_object_unref (viewer); return status; } aravis-0.8.34/viewer/meson.build000066400000000000000000000020221475431451200165400ustar00rootroot00000000000000viewer_enabled = true aravis_desktop_dir = join_paths (get_option ('datadir'), 'applications') aravis_app_data_dir = join_paths (get_option ('datadir'), 'metainfo') aravis_icon_dir = join_paths (get_option ('datadir'), 'icons', 'hicolor') viewer_sources = [ 'main.c', 'arvviewer.c' ] viewer_headers = [ 'arvviewertypes.h', 'arvviewer.h' ] viewer_c_args = [ '-DARAVIS_LOCALE_DIR="@0@"'.format (join_paths (get_option ('prefix'), 'share', 'locale')), '-DARAVIS_GETTEXT="aravis-@0@"'.format (aravis_api_version) ] viewer_resources = gnome.compile_resources ( 'arvviewerresources', 'arvviewerresources.xml' ) executable ('arv-viewer-@0@'.format (aravis_api_version), viewer_sources, viewer_headers, viewer_resources, link_with: aravis_library, c_args: viewer_c_args, include_directories: [library_inc], dependencies: viewer_deps, install: true) install_subdir (join_paths ('icons', 'gnome'), exclude_directories: 'scalable', install_dir: aravis_icon_dir, strip_directory: true) subdir ('data')