├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── EXAMPLES.md ├── INSTALLING.md ├── LICENSE.txt ├── README.md ├── build.bat ├── build.sh ├── cmake ├── FindGLIB2.cmake ├── FindGObject.cmake ├── FindGStreamer.cmake ├── FindGStreamerPluginsBase.cmake └── MacroFindGStreamerLibrary.cmake └── src ├── config.h.in ├── gstvimbasrc.c ├── gstvimbasrc.h ├── helpers.h ├── pixelformats.c ├── pixelformats.h ├── vimba_helpers.c └── vimba_helpers.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/cmake,c,windows,linux 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=cmake,c,windows,linux 4 | 5 | ### C ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | 59 | ### CMake ### 60 | CMakeLists.txt.user 61 | CMakeCache.txt 62 | CMakeFiles 63 | CMakeScripts 64 | Testing 65 | Makefile 66 | cmake_install.cmake 67 | install_manifest.txt 68 | compile_commands.json 69 | CTestTestfile.cmake 70 | _deps 71 | CMakeUserPresets.json 72 | 73 | ### CMake Patch ### 74 | # External projects 75 | *-prefix/ 76 | 77 | ### Linux ### 78 | *~ 79 | 80 | # temporary files which can be created if a process still has a handle open of a deleted file 81 | .fuse_hidden* 82 | 83 | # KDE directory preferences 84 | .directory 85 | 86 | # Linux trash folder which might appear on any partition or disk 87 | .Trash-* 88 | 89 | # .nfs files are created when an open file is removed but is still being accessed 90 | .nfs* 91 | 92 | ### Windows ### 93 | # Windows thumbnail cache files 94 | Thumbs.db 95 | Thumbs.db:encryptable 96 | ehthumbs.db 97 | ehthumbs_vista.db 98 | 99 | # Dump file 100 | *.stackdump 101 | 102 | # Folder config file 103 | [Dd]esktop.ini 104 | 105 | # Recycle Bin used on file shares 106 | $RECYCLE.BIN/ 107 | 108 | # Windows Installer files 109 | *.cab 110 | *.msi 111 | *.msix 112 | *.msm 113 | *.msp 114 | 115 | # Windows shortcuts 116 | *.lnk 117 | 118 | # End of https://www.toptal.com/developers/gitignore/api/cmake,c,windows,linux 119 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project( 4 | gstvimbasrc 5 | VERSION 0.3.2 6 | LANGUAGES C 7 | DESCRIPTION "GStreamer source plugin for Vimba cameras" 8 | HOMEPAGE_URL "https://alliedvision.com/" 9 | ) 10 | 11 | # Turn on compiler warnings and treat them as errors 12 | if(MSVC) 13 | add_compile_options(/W4 /WX) 14 | else() 15 | add_compile_options(-Wall -Wextra -pedantic -Werror) 16 | endif() 17 | 18 | # add local cmake modules to simplify detection of dependencies 19 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 20 | 21 | add_library(${PROJECT_NAME} SHARED 22 | src/gstvimbasrc.c 23 | src/vimba_helpers.c 24 | src/pixelformats.c 25 | ) 26 | 27 | # Defines used in gstplugin.c 28 | target_compile_definitions(${PROJECT_NAME} 29 | PRIVATE 30 | HAVE_CONFIG_H 31 | ) 32 | 33 | # fill configuration file with defined values 34 | configure_file( 35 | ${PROJECT_SOURCE_DIR}/src/config.h.in config.h 36 | ) 37 | 38 | find_package(GStreamer REQUIRED COMPONENTS base) 39 | find_package(GLIB2 REQUIRED) 40 | find_package(GObject REQUIRED) 41 | 42 | target_include_directories(${PROJECT_NAME} 43 | PRIVATE 44 | ${PROJECT_BINARY_DIR} 45 | ${GSTREAMER_INCLUDE_DIR} 46 | ${GLIB2_INCLUDE_DIR} 47 | # TODO: If possible find a better way to include Vimba into CMake 48 | ${VIMBA_HOME} 49 | ) 50 | 51 | # Make linking work on Windows and Linux 52 | # TODO: find a better way to include Vimba into CMake 53 | if(WIN32) 54 | if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) 55 | set(VIMBAC_LIBRARY ${VIMBA_HOME}/VimbaC/Lib/Win64/VimbaC.lib) 56 | elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 4) 57 | set(VIMBAC_LIBRARY ${VIMBA_HOME}/VimbaC/Lib/Win32/VimbaC.lib) 58 | endif() 59 | elseif(UNIX) 60 | if(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "x86_64") 61 | set(VIMBAC_LIBRARY ${VIMBA_HOME}/VimbaC/DynamicLib/x86_64bit/libVimbaC.so) 62 | elseif(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "aarch64") 63 | set(VIMBAC_LIBRARY ${VIMBA_HOME}/VimbaC/DynamicLib/arm_64bit/libVimbaC.so) 64 | elseif(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "armv7l") 65 | set(VIMBAC_LIBRARY ${VIMBA_HOME}/VimbaC/DynamicLib/arm_32bit/libVimbaC.so) 66 | endif() 67 | endif() 68 | 69 | target_link_libraries (${PROJECT_NAME} 70 | ${GLIB2_LIBRARIES} 71 | ${GOBJECT_LIBRARIES} 72 | ${GSTREAMER_LIBRARY} 73 | ${GSTREAMER_BASE_LIBRARY} 74 | # TODO: If possible find a better way to include Vimba into CMake 75 | ${VIMBAC_LIBRARY} 76 | ) 77 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | from ubuntu:18.04 as build_base 2 | RUN apt-get update && \ 3 | apt-get install --no-install-recommends -y \ 4 | build-essential \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | # More modern CMake version needed -> Build and install ourselves 8 | # Alternatively one might download a precompiled tar.gz from Github, but building it 9 | # is platform agnostic 10 | from build_base as cmake_installer 11 | RUN apt-get update && \ 12 | apt-get install --no-install-recommends -y \ 13 | wget \ 14 | ca-certificates \ 15 | libssl-dev \ 16 | && rm -rf /var/lib/apt/lists/* 17 | 18 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.19.4/cmake-3.19.4.tar.gz && \ 19 | tar xf cmake-3.19.4.tar.gz && \ 20 | rm cmake-3.19.4.tar.gz && \ 21 | cd ./cmake-3.19.4 && \ 22 | ./bootstrap --prefix=/usr/local && \ 23 | make && \ 24 | make install 25 | 26 | # This image will perform the actual GStreamer plugin build process 27 | FROM build_base as gst-vimbasrc_builder 28 | RUN apt-get update && \ 29 | apt-get install --no-install-recommends -y \ 30 | libgstreamer1.0-dev \ 31 | libgstreamer-plugins-base1.0-dev \ 32 | && rm -rf /var/lib/apt/lists/* 33 | 34 | COPY --from=cmake_installer /usr/local /usr/local 35 | 36 | ENV VIMBA_HOME=/vimba 37 | 38 | # mount the checked out repository into this volume 39 | VOLUME ["/gst-vimbasrc"] 40 | WORKDIR /gst-vimbasrc 41 | 42 | # Follow README 43 | CMD ["sh", "build.sh"] 44 | # Plugin will be located at plugins/.libs/libgstvimba.so -------------------------------------------------------------------------------- /EXAMPLES.md: -------------------------------------------------------------------------------- 1 | # Usage Examples 2 | 3 | GStreamer provides a larges selection of plugins, which can be used to define flexible pipelines for 4 | many uses. Some examples of common goals are provided in this file. 5 | 6 | ## Saving camera frames as pictures 7 | 8 | Recording pictures from a camera and saving them to some common image format allows for quick 9 | inspections of the field of view, brightness and sharpness of the image. GStreamer provides image 10 | encoders for different image formats. The example below uses the `png` encoder. A step by step 11 | explanation of the elements in the pipeline is given below. 12 | ``` 13 | gst-launch-1.0 vimbasrc camera=DEV_1AB22D01BBB8 num-buffers=1 ! pngenc ! filesink location=out.png 14 | ``` 15 | 16 | - `vimbasrc camera=DEV_1AB22D01BBB8 num-buffers=1`: uses the `gst-vimbasrc` element to grab one 17 | single frame from the camera with the given ID and halt the pipeline afterwards 18 | - `pngenc`: takes the input image and encodes it into a `png` file 19 | - `filesink location=out.png`: writes the data it receives (the encoded `png` image) to the file 20 | `out.png` in the current working directory 21 | 22 | Similarly it is possible to save a number of camera frames to separate image files. This can be 23 | achieved by using the `multifilesink` element to save the images. 24 | ``` 25 | gst-launch-1.0 vimbasrc camera=DEV_1AB22D01BBB8 num-buffers=10 ! pngenc ! multifilesink location=out_%03d.png 26 | ``` 27 | 28 | Similarly to the previous example, this pipeline uses the `gst-vimbasrc` element to record images 29 | from the camera. Here however 10 images are recorded. The `multifilesink` saves these images to 30 | separate files, named `out_000.png`, `out_001.png`, ... , `out_009.png`. 31 | 32 | Further changes to the pipeline are possible to for example change the format of the recorded images 33 | to ensure RGB images, or adjust the exposure time of the image acquisition process. For more details 34 | see the README of the `gst-vimbasrc` element. 35 | 36 | ## Saving camera stream to a video file 37 | 38 | To save a stream of images recorded by a camera to a video file the images should be encoded in some 39 | video format and stored in an appropriate container format. This saves a lot of space compared to 40 | just saving the raw image data. This example uses `h264` encoding for the image data and saves the 41 | resulting video to an `avi` file. An explanation for the separate elements of the pipeline can be 42 | found below. 43 | ``` 44 | gst-launch-1.0 vimbasrc camera=DEV_000F315B91E2 ! video/x-raw,format=RGB ! videorate ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! x264enc ! avimux ! filesink location=output.avi 45 | ``` 46 | 47 | - `vimbasrc camera=DEV_000F315B91E2`: uses the `gst-vimbasrc` element to grab camera frames from the 48 | Vimba compatible camera with the given ID. For more information on the functionality of 49 | `gst-vimbasrc` see the README 50 | - `video/x-raw,format=RGB`: a gst capsfilter element that limits the available data formats to `RGB` 51 | to ensure color images for the resulting video stream. Without this the pipeline may negotiate 52 | grayscale images 53 | - `videorate ! video/x-raw,framerate=30/1`: `gst-vimbasrc` provides image data in a variable 54 | framerate (due to effects like possible hardware triggers or frame jitter). Because `avi` files 55 | only support fixed framerates, it needs to be modified via the `videorate` plugin. This guarantees 56 | a fixed framerate output by either copying the input data if more frames are requested than 57 | received, or dropping unnecessary frames if more frames are received than requested. 58 | - `videoconvert ! queue`: converts the input image to a compatible video format for the following 59 | element 60 | - `x264enc`: performs the encoding to h264 video 61 | - `avimux`: multiplex the incoming video stream to save it as an `avi` file 62 | - `filesink location=output.avi`: saves the resulting video into a file named `output.avi` in the 63 | current working directory 64 | 65 | ## Stream video via RTSP server 66 | 67 | RTSP (Real Time Streaming Protocol) is a network protocol designed to control streaming media 68 | servers. It allows clients to send commands such as "play" or "pause" to the server, to enable 69 | control of the media being streamed. The following example shows a minimal RTSP server using the 70 | `gst-vimbasrc` element to stream image data from a camera via the network to a client machine. This 71 | example uses Python to start a pre-implemented RTSP server that can be imported via the PyGObject 72 | package. To do this a few external dependencies must be installed. 73 | 74 | ### Dependencies 75 | 76 | The following instructions assume an Ubuntu system. On other distributions different packages may be 77 | required. It is also assumed, that a working GStreamer installation exists on the system and that 78 | `gst-vimbasrc` is available to that installation. 79 | 80 | To have access to the GStreamer RTSP Server from python the following system packages need to be 81 | installed via the `apt` package manager: 82 | - gir1.2-gst-rtsp-server-1.0 83 | - libgirepository1.0-dev 84 | - libcairo2-dev 85 | 86 | Additionally the following python package needs to be installed. it is available via the Python 87 | packaging index and can be installed as usual via `pip`: 88 | - PyGObject 89 | 90 | ### Example code 91 | 92 | The following python code will start an RTSP server on your machine listing on port `8554`. Be sure 93 | to adjust the ID of the camera you want to use in the pipeline! As before the pipeline may be 94 | adjusted to specify certain image formats to force for example color images or to change camera 95 | settings like the exposure time. 96 | 97 | ```python 98 | # import required GStreamer and GLib modules 99 | import gi 100 | gi.require_version('Gst', '1.0') 101 | gi.require_version('GstRtspServer', '1.0') 102 | from gi.repository import Gst, GLib, GstRtspServer 103 | 104 | # initialize GStreamer and start the GLib Mainloop 105 | Gst.init(None) 106 | mainloop = GLib.MainLoop() 107 | 108 | # Create the RTSP Server 109 | server = GstRtspServer.RTSPServer() 110 | mounts = server.get_mount_points() 111 | 112 | # define the pipeline to record images ad attach it to the "stream1" endpoint 113 | vimbasrc_factory = GstRtspServer.RTSPMediaFactory() 114 | vimbasrc_factory.set_launch('vimbasrc camera=DEV_1AB22D01BBB8 ! videoconvert ! x264enc speed-preset=ultrafast tune=zerolatency ! rtph264pay name=pay0') 115 | mounts.add_factory("/stream1", vimbasrc_factory) 116 | server.attach(None) 117 | 118 | mainloop.run() 119 | ``` 120 | 121 | To start the server simply save the code above to a file (e.g. `RTSP_Server.py`) and run it with 122 | your python interpreter. The RTSP server can be stopped by halting the process. This is done by 123 | pressing `CTRL + c` in the terminal that is running the python script. 124 | 125 | ### Displaying the stream 126 | 127 | After starting the python example a client must connect to the running RTSP server to receive and 128 | display the stream. This can for example be done with the VLC media player. To display the stream on 129 | the same machine that is running the RTSP server, open `rtsp://127.0.0.1:8554/stream1` as a Network 130 | Stream (in VLC open "Media" -> "Open Network Stream"). If you want to play the video on a different 131 | computer, ensure that a network connection between the two systems exists and use the IP address of 132 | the machine running the RTSP server for your Network Stream. 133 | 134 | Upon starting playback in VLC the RTSP Server will start the GStreamer pipeline that was defined in 135 | the python file and start streaming images. It may take some time for the stream to start. Stopping 136 | playback will also stop the GStreamer pipeline and close the connection to the camera being used. 137 | -------------------------------------------------------------------------------- /INSTALLING.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | As mentioned in `README.md` the installation of `vimbasrc` consists of just two necessary files that 3 | need to be placed in appropriate directories. The shared library containing the GStreamer element 4 | must be findable by GStreamer. This can be achieved by defining the `GST_PLUGIN_SYSTEM_PATH` and 5 | placing the shared library file in that directory. Additionally the VimbaC shared library must be 6 | loadable as it is used by the `vimbasrc` element. VimbaC is provided as part of the Vimba SDK. 7 | 8 | Below are more details on the installation on Linux (more specifically Ubuntu) and Windows. 9 | 10 | ## Linux (Ubuntu) 11 | If the `GST_PLUGIN_SYSTEM_PATH` variable is not defined, the default paths of the system wide 12 | GStreamer installation, as well as the `~/.local/share/gstreamer-/plugins` 13 | directory of the current user are searched. Installing the `vimbasrc` element is therefore simply a 14 | matter of placing the compiled shared library into this search path and letting GStreamer load it. 15 | 16 | ### Installation dependencies 17 | As the shared library containing the `vimbasrc` element is dynamically linked, its linked 18 | dependencies must be loadable. As GStreamer itself is likely installed system wide, the dependencies 19 | on glib and GStreamer libraries should already be satisfied. 20 | 21 | In order to satisfy the dependency on `libVimbaC.so` the shared library needs to be placed in an 22 | appropriate entry of the `LD_LIBRARY_PATH`. The exact method for this is a matter of preference and 23 | distribution dependant. On Ubuntu systems one option would be to copy `libVimbaC.so` into 24 | `/usr/local/lib` or to add the directory containing `libVimbaC.so` to the `LD_LIBRARY_PATH` by 25 | adding an appropriate `.conf` file to `/etc/ld.so.conf.d/`. 26 | 27 | Correct installation of `libVimbaC.so` can be checked, by searching for its file name in the output 28 | of `ldconfig -v` (e.g.: `ldconfig -v | grep libVimbaC.so`). Alternatively correct loading of 29 | dependent shared libraries can be checked with `ldd` (e.g. `ldd libgstvimbasrc.so`). 30 | 31 | ## Windows 32 | Adding the directory containing the compiled shared library file to the `GST_PLUGIN_SYSTEM_PATH` is 33 | the easiest way to install the `vimbasrc` element. Alternatively the file can be placed in the 34 | GStreamer installation directory of the system. The installation directory was chosen during the 35 | installation of the GStreamer runtime. The directory the plugin should be placed into is 36 | `\1.0\msvc_x86_64\lib\gstreamer-1.0` on 64 bit systems. 37 | 38 | ### Installation dependencies 39 | In addition to the installation of GStreamer runtime and placing the `vimbasrc` shared library into 40 | the GStreamer plugin search path, VimbaC needs to be loadable. This can be achieved by adding a 41 | directory containing `VimbaC.dll` to your systems `PATH` variable. With a working Vimba installation 42 | the directory `%VIMBA_HOME%VimbaC\Bin\Win64` will contain `VimbaC.dll` for 64 bit systems. 43 | 44 | ## Further information 45 | More information on the environment variables used by GStreamer to determine directories which 46 | should be searched for elements can be found in [the official 47 | documentation](https://gstreamer.freedesktop.org/documentation/gstreamer/running.html). -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the library GPL. It is 10 | numbered 2 because it goes with version 2 of the ordinary GPL.] 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | Licenses are intended to guarantee your freedom to share and change 17 | free software--to make sure the software is free for all its users. 18 | 19 | This license, the Library General Public License, applies to some 20 | specially designated Free Software Foundation software, and to any 21 | other libraries whose authors decide to use it. You can use it for 22 | your libraries, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if 34 | you distribute copies of the library, or if you modify it. 35 | 36 | For example, if you distribute copies of the library, whether gratis 37 | or for a fee, you must give the recipients all the rights that we gave 38 | you. You must make sure that they, too, receive or can get the source 39 | code. If you link a program with the library, you must provide 40 | complete object files to the recipients so that they can relink them 41 | with the library, after making changes to the library and recompiling 42 | it. And you must show them these terms so they know their rights. 43 | 44 | Our method of protecting your rights has two steps: (1) copyright 45 | the library, and (2) offer you this license which gives you legal 46 | permission to copy, distribute and/or modify the library. 47 | 48 | Also, for each distributor's protection, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | library. If the library is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original 52 | version, so that any problems introduced by others will not reflect on 53 | the original authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that companies distributing free 57 | software will individually obtain patent licenses, thus in effect 58 | transforming the program into proprietary software. To prevent this, 59 | we have made it clear that any patent must be licensed for everyone's 60 | free use or not licensed at all. 61 | 62 | Most GNU software, including some libraries, is covered by the ordinary 63 | GNU General Public License, which was designed for utility programs. This 64 | license, the GNU Library General Public License, applies to certain 65 | designated libraries. This license is quite different from the ordinary 66 | one; be sure to read it in full, and don't assume that anything in it is 67 | the same as in the ordinary license. 68 | 69 | The reason we have a separate public license for some libraries is that 70 | they blur the distinction we usually make between modifying or adding to a 71 | program and simply using it. Linking a program with a library, without 72 | changing the library, is in some sense simply using the library, and is 73 | analogous to running a utility program or application program. However, in 74 | a textual and legal sense, the linked executable is a combined work, a 75 | derivative of the original library, and the ordinary General Public License 76 | treats it as such. 77 | 78 | Because of this blurred distinction, using the ordinary General 79 | Public License for libraries did not effectively promote software 80 | sharing, because most developers did not use the libraries. We 81 | concluded that weaker conditions might promote sharing better. 82 | 83 | However, unrestricted linking of non-free programs would deprive the 84 | users of those programs of all benefit from the free status of the 85 | libraries themselves. This Library General Public License is intended to 86 | permit developers of non-free programs to use free libraries, while 87 | preserving your freedom as a user of such programs to change the free 88 | libraries that are incorporated in them. (We have not seen how to achieve 89 | this as regards changes in header files, but we have achieved it as regards 90 | changes in the actual functions of the Library.) The hope is that this 91 | will lead to faster development of free libraries. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. Pay close attention to the difference between a 95 | "work based on the library" and a "work that uses the library". The 96 | former contains code derived from the library, while the latter only 97 | works together with the library. 98 | 99 | Note that it is possible for a library to be covered by the ordinary 100 | General Public License rather than by this special one. 101 | 102 | GNU LIBRARY GENERAL PUBLIC LICENSE 103 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 104 | 105 | 0. This License Agreement applies to any software library which 106 | contains a notice placed by the copyright holder or other authorized 107 | party saying it may be distributed under the terms of this Library 108 | General Public License (also called "this License"). Each licensee is 109 | addressed as "you". 110 | 111 | A "library" means a collection of software functions and/or data 112 | prepared so as to be conveniently linked with application programs 113 | (which use some of those functions and data) to form executables. 114 | 115 | The "Library", below, refers to any such software library or work 116 | which has been distributed under these terms. A "work based on the 117 | Library" means either the Library or any derivative work under 118 | copyright law: that is to say, a work containing the Library or a 119 | portion of it, either verbatim or with modifications and/or translated 120 | straightforwardly into another language. (Hereinafter, translation is 121 | included without limitation in the term "modification".) 122 | 123 | "Source code" for a work means the preferred form of the work for 124 | making modifications to it. For a library, complete source code means 125 | all the source code for all modules it contains, plus any associated 126 | interface definition files, plus the scripts used to control compilation 127 | and installation of the library. 128 | 129 | Activities other than copying, distribution and modification are not 130 | covered by this License; they are outside its scope. The act of 131 | running a program using the Library is not restricted, and output from 132 | such a program is covered only if its contents constitute a work based 133 | on the Library (independent of the use of the Library in a tool for 134 | writing it). Whether that is true depends on what the Library does 135 | and what the program that uses the Library does. 136 | 137 | 1. You may copy and distribute verbatim copies of the Library's 138 | complete source code as you receive it, in any medium, provided that 139 | you conspicuously and appropriately publish on each copy an 140 | appropriate copyright notice and disclaimer of warranty; keep intact 141 | all the notices that refer to this License and to the absence of any 142 | warranty; and distribute a copy of this License along with the 143 | Library. 144 | 145 | You may charge a fee for the physical act of transferring a copy, 146 | and you may at your option offer warranty protection in exchange for a 147 | fee. 148 | 149 | 2. You may modify your copy or copies of the Library or any portion 150 | of it, thus forming a work based on the Library, and copy and 151 | distribute such modifications or work under the terms of Section 1 152 | above, provided that you also meet all of these conditions: 153 | 154 | a) The modified work must itself be a software library. 155 | 156 | b) You must cause the files modified to carry prominent notices 157 | stating that you changed the files and the date of any change. 158 | 159 | c) You must cause the whole of the work to be licensed at no 160 | charge to all third parties under the terms of this License. 161 | 162 | d) If a facility in the modified Library refers to a function or a 163 | table of data to be supplied by an application program that uses 164 | the facility, other than as an argument passed when the facility 165 | is invoked, then you must make a good faith effort to ensure that, 166 | in the event an application does not supply such function or 167 | table, the facility still operates, and performs whatever part of 168 | its purpose remains meaningful. 169 | 170 | (For example, a function in a library to compute square roots has 171 | a purpose that is entirely well-defined independent of the 172 | application. Therefore, Subsection 2d requires that any 173 | application-supplied function or table used by this function must 174 | be optional: if the application does not supply it, the square 175 | root function must still compute square roots.) 176 | 177 | These requirements apply to the modified work as a whole. If 178 | identifiable sections of that work are not derived from the Library, 179 | and can be reasonably considered independent and separate works in 180 | themselves, then this License, and its terms, do not apply to those 181 | sections when you distribute them as separate works. But when you 182 | distribute the same sections as part of a whole which is a work based 183 | on the Library, the distribution of the whole must be on the terms of 184 | this License, whose permissions for other licensees extend to the 185 | entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest 189 | your rights to work written entirely by you; rather, the intent is to 190 | exercise the right to control the distribution of derivative or 191 | collective works based on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library 194 | with the Library (or with a work based on the Library) on a volume of 195 | a storage or distribution medium does not bring the other work under 196 | the scope of this License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public 199 | License instead of this License to a given copy of the Library. To do 200 | this, you must alter all the notices that refer to this License, so 201 | that they refer to the ordinary GNU General Public License, version 2, 202 | instead of to this License. (If a newer version than version 2 of the 203 | ordinary GNU General Public License has appeared, then you can specify 204 | that version instead if you wish.) Do not make any other change in 205 | these notices. 206 | 207 | Once this change is made in a given copy, it is irreversible for 208 | that copy, so the ordinary GNU General Public License applies to all 209 | subsequent copies and derivative works made from that copy. 210 | 211 | This option is useful when you wish to copy part of the code of 212 | the Library into a program that is not a library. 213 | 214 | 4. You may copy and distribute the Library (or a portion or 215 | derivative of it, under Section 2) in object code or executable form 216 | under the terms of Sections 1 and 2 above provided that you accompany 217 | it with the complete corresponding machine-readable source code, which 218 | must be distributed under the terms of Sections 1 and 2 above on a 219 | medium customarily used for software interchange. 220 | 221 | If distribution of object code is made by offering access to copy 222 | from a designated place, then offering equivalent access to copy the 223 | source code from the same place satisfies the requirement to 224 | distribute the source code, even though third parties are not 225 | compelled to copy the source along with the object code. 226 | 227 | 5. A program that contains no derivative of any portion of the 228 | Library, but is designed to work with the Library by being compiled or 229 | linked with it, is called a "work that uses the Library". Such a 230 | work, in isolation, is not a derivative work of the Library, and 231 | therefore falls outside the scope of this License. 232 | 233 | However, linking a "work that uses the Library" with the Library 234 | creates an executable that is a derivative of the Library (because it 235 | contains portions of the Library), rather than a "work that uses the 236 | library". The executable is therefore covered by this License. 237 | Section 6 states terms for distribution of such executables. 238 | 239 | When a "work that uses the Library" uses material from a header file 240 | that is part of the Library, the object code for the work may be a 241 | derivative work of the Library even though the source code is not. 242 | Whether this is true is especially significant if the work can be 243 | linked without the Library, or if the work is itself a library. The 244 | threshold for this to be true is not precisely defined by law. 245 | 246 | If such an object file uses only numerical parameters, data 247 | structure layouts and accessors, and small macros and small inline 248 | functions (ten lines or less in length), then the use of the object 249 | file is unrestricted, regardless of whether it is legally a derivative 250 | work. (Executables containing this object code plus portions of the 251 | Library will still fall under Section 6.) 252 | 253 | Otherwise, if the work is a derivative of the Library, you may 254 | distribute the object code for the work under the terms of Section 6. 255 | Any executables containing that work also fall under Section 6, 256 | whether or not they are linked directly with the Library itself. 257 | 258 | 6. As an exception to the Sections above, you may also compile or 259 | link a "work that uses the Library" with the Library to produce a 260 | work containing portions of the Library, and distribute that work 261 | under terms of your choice, provided that the terms permit 262 | modification of the work for the customer's own use and reverse 263 | engineering for debugging such modifications. 264 | 265 | You must give prominent notice with each copy of the work that the 266 | Library is used in it and that the Library and its use are covered by 267 | this License. You must supply a copy of this License. If the work 268 | during execution displays copyright notices, you must include the 269 | copyright notice for the Library among them, as well as a reference 270 | directing the user to the copy of this License. Also, you must do one 271 | of these things: 272 | 273 | a) Accompany the work with the complete corresponding 274 | machine-readable source code for the Library including whatever 275 | changes were used in the work (which must be distributed under 276 | Sections 1 and 2 above); and, if the work is an executable linked 277 | with the Library, with the complete machine-readable "work that 278 | uses the Library", as object code and/or source code, so that the 279 | user can modify the Library and then relink to produce a modified 280 | executable containing the modified Library. (It is understood 281 | that the user who changes the contents of definitions files in the 282 | Library will not necessarily be able to recompile the application 283 | to use the modified definitions.) 284 | 285 | b) Accompany the work with a written offer, valid for at 286 | least three years, to give the same user the materials 287 | specified in Subsection 6a, above, for a charge no more 288 | than the cost of performing this distribution. 289 | 290 | c) If distribution of the work is made by offering access to copy 291 | from a designated place, offer equivalent access to copy the above 292 | specified materials from the same place. 293 | 294 | d) Verify that the user has already received a copy of these 295 | materials or that you have already sent this user a copy. 296 | 297 | For an executable, the required form of the "work that uses the 298 | Library" must include any data and utility programs needed for 299 | reproducing the executable from it. However, as a special exception, 300 | the source code distributed need not include anything that is normally 301 | distributed (in either source or binary form) with the major 302 | components (compiler, kernel, and so on) of the operating system on 303 | which the executable runs, unless that component itself accompanies 304 | the executable. 305 | 306 | It may happen that this requirement contradicts the license 307 | restrictions of other proprietary libraries that do not normally 308 | accompany the operating system. Such a contradiction means you cannot 309 | use both them and the Library together in an executable that you 310 | distribute. 311 | 312 | 7. You may place library facilities that are a work based on the 313 | Library side-by-side in a single library together with other library 314 | facilities not covered by this License, and distribute such a combined 315 | library, provided that the separate distribution of the work based on 316 | the Library and of the other library facilities is otherwise 317 | permitted, and provided that you do these two things: 318 | 319 | a) Accompany the combined library with a copy of the same work 320 | based on the Library, uncombined with any other library 321 | facilities. This must be distributed under the terms of the 322 | Sections above. 323 | 324 | b) Give prominent notice with the combined library of the fact 325 | that part of it is a work based on the Library, and explaining 326 | where to find the accompanying uncombined form of the same work. 327 | 328 | 8. You may not copy, modify, sublicense, link with, or distribute 329 | the Library except as expressly provided under this License. Any 330 | attempt otherwise to copy, modify, sublicense, link with, or 331 | distribute the Library is void, and will automatically terminate your 332 | rights under this License. However, parties who have received copies, 333 | or rights, from you under this License will not have their licenses 334 | terminated so long as such parties remain in full compliance. 335 | 336 | 9. You are not required to accept this License, since you have not 337 | signed it. However, nothing else grants you permission to modify or 338 | distribute the Library or its derivative works. These actions are 339 | prohibited by law if you do not accept this License. Therefore, by 340 | modifying or distributing the Library (or any work based on the 341 | Library), you indicate your acceptance of this License to do so, and 342 | all its terms and conditions for copying, distributing or modifying 343 | the Library or works based on it. 344 | 345 | 10. Each time you redistribute the Library (or any work based on the 346 | Library), the recipient automatically receives a license from the 347 | original licensor to copy, distribute, link with or modify the Library 348 | subject to these terms and conditions. You may not impose any further 349 | restrictions on the recipients' exercise of the rights granted herein. 350 | You are not responsible for enforcing compliance by third parties to 351 | this License. 352 | 353 | 11. If, as a consequence of a court judgment or allegation of patent 354 | infringement or for any other reason (not limited to patent issues), 355 | conditions are imposed on you (whether by court order, agreement or 356 | otherwise) that contradict the conditions of this License, they do not 357 | excuse you from the conditions of this License. If you cannot 358 | distribute so as to satisfy simultaneously your obligations under this 359 | License and any other pertinent obligations, then as a consequence you 360 | may not distribute the Library at all. For example, if a patent 361 | license would not permit royalty-free redistribution of the Library by 362 | all those who receive copies directly or indirectly through you, then 363 | the only way you could satisfy both it and this License would be to 364 | refrain entirely from distribution of the Library. 365 | 366 | If any portion of this section is held invalid or unenforceable under any 367 | particular circumstance, the balance of the section is intended to apply, 368 | and the section as a whole is intended to apply in other circumstances. 369 | 370 | It is not the purpose of this section to induce you to infringe any 371 | patents or other property right claims or to contest validity of any 372 | such claims; this section has the sole purpose of protecting the 373 | integrity of the free software distribution system which is 374 | implemented by public license practices. Many people have made 375 | generous contributions to the wide range of software distributed 376 | through that system in reliance on consistent application of that 377 | system; it is up to the author/donor to decide if he or she is willing 378 | to distribute software through any other system and a licensee cannot 379 | impose that choice. 380 | 381 | This section is intended to make thoroughly clear what is believed to 382 | be a consequence of the rest of this License. 383 | 384 | 12. If the distribution and/or use of the Library is restricted in 385 | certain countries either by patents or by copyrighted interfaces, the 386 | original copyright holder who places the Library under this License may add 387 | an explicit geographical distribution limitation excluding those countries, 388 | so that distribution is permitted only in or among countries not thus 389 | excluded. In such case, this License incorporates the limitation as if 390 | written in the body of this License. 391 | 392 | 13. The Free Software Foundation may publish revised and/or new 393 | versions of the Library General Public License from time to time. 394 | Such new versions will be similar in spirit to the present version, 395 | but may differ in detail to address new problems or concerns. 396 | 397 | Each version is given a distinguishing version number. If the Library 398 | specifies a version number of this License which applies to it and 399 | "any later version", you have the option of following the terms and 400 | conditions either of that version or of any later version published by 401 | the Free Software Foundation. If the Library does not specify a 402 | license version number, you may choose any version ever published by 403 | the Free Software Foundation. 404 | 405 | 14. If you wish to incorporate parts of the Library into other free 406 | programs whose distribution conditions are incompatible with these, 407 | write to the author to ask for permission. For software which is 408 | copyrighted by the Free Software Foundation, write to the Free 409 | Software Foundation; we sometimes make exceptions for this. Our 410 | decision will be guided by the two goals of preserving the free status 411 | of all derivatives of our free software and of promoting the sharing 412 | and reuse of software generally. 413 | 414 | NO WARRANTY 415 | 416 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 417 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 418 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 419 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 420 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 421 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 422 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 423 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 424 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 425 | 426 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 427 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 428 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 429 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 430 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 431 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 432 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 433 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 434 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 435 | DAMAGES. 436 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gst-vimbasrc 2 | This project contains the official GStreamer plugin to make cameras supported by Allied Visions 3 | Vimba API available as GStreamer sources. 4 | 5 | GStreamer is multimedia framework which assembles pipelines from multiple elements. Using the 6 | `vimbasrc` element it is possible to record images with industrial cameras supported by Vimba and 7 | pass them directly into these pipelines. This enables a wide variety of uses such as live displays 8 | of the image data or encoding them to a video format. 9 | 10 | ## Building 11 | A `CMakeLists.txt` file is provided that helps build the plugin. For convenience this repository 12 | also contains two scripts (`build.sh` and `build.bat`) that run the appropriate cmake commands for 13 | Linux and Windows systems respectively. They create a directory named `build` in which the project 14 | files, as well as the built binary, are placed. 15 | 16 | As the build process relies on external libraries (such as GStreamer and Vimba), paths to these 17 | libraries have to be detected. The provided build scripts take guesses (where possible) to find 18 | these directories. 19 | 20 | The Vimba installation directory is assumed to be defined by the `VIMBA_HOME` environment variable. 21 | This is the case for Windows systems, on Linux systems you may need to define this variable manually 22 | or pass it directly as parameter to CMake. 23 | 24 | If problems arise during compilation related to these external dependencies, please adjust the 25 | provided paths accordingly for your build system. 26 | 27 | ### Docker build environment (Linux only) 28 | To simplify the setup of a reproducible build environment, a `Dockerfile` based on an Ubuntu 18.04 29 | base image is provided, which when build includes all necessary dependencies, except the Vimba 30 | version against which `vimbasrc` is linked. This is added when the compile command is run by 31 | mounting a Vimba installation into the Docker container. 32 | 33 | #### Building the docker image 34 | In order to build the docker image from the `Dockerfile`, run the following command inside the 35 | directory containing it: 36 | ``` 37 | docker build -t gst-vimbasrc:18.04 . 38 | ``` 39 | 40 | #### Compiling vimbasrc using the Docker image 41 | After running the build command described above, a Docker image with the tag `gst-vimbasrc:18.04` 42 | will be created. This can be used to run the build process of the plugin. 43 | 44 | Building the plugin with this image is simply a matter of mounting the source code directory and the 45 | desired Vimba installation directory into the image at appropriate paths, and letting it run the 46 | provided `build.sh` script. The expected paths into which to mount these directories are: 47 | - **/gst-vimbasrc**: Path inside the Docker container to mount the gst-vimbasrc project 48 | - **/vimba**: Path inside the Docker container to mount the desired Vimba installation 49 | 50 | The full build command to be executed on the host would be as follows: 51 | ``` 52 | docker run --rm -it --volume /path/to/gst-vimbasrc:/gst-vimbasrc --volume /path/to/Vimba_X_Y:/vimba gst-vimbasrc:18.04 53 | ``` 54 | 55 | ## Installation 56 | GStreamer plugins become available for use in pipelines, when GStreamer is able to load the shared 57 | library containing the desired element. GStreamer typically searches the directories defined in 58 | `GST_PLUGIN_SYSTEM_PATH`. By setting this variable to a directory and placing the shared library 59 | file in it, GStreamer will pick up the `vimbasrc` element for use. 60 | 61 | The `vimbasrc` element uses VimbaC. In order to be usable the VimbaC shared library therefore needs 62 | to also be loadable when the element is started. The VimbaC library is provided as part of the Vimba 63 | SDK. 64 | 65 | More detailed installation instructions for Linux and Windows can be found in the `INSTALLING.md` 66 | file in this repository. 67 | 68 | ## Usage 69 | **The vimbasrc plugin is still in active development. Please keep the _Known issues and limitations_ 70 | in mind when specifying your GStreamer pipelines and using it** 71 | 72 | `vimbasrc` is intended for use in GStreamer pipelines. The element can be used to forward recorded 73 | frames from a Vimba compatible camera into subsequent GStreamer elements. 74 | 75 | The following pipeline can for example be used to display the recorded camera image. The 76 | `camera=` parameter needs to be adjusted to use the correct camera ID. 77 | ``` 78 | gst-launch-1.0 vimbasrc camera=DEV_1AB22D01BBB8 ! videoscale ! videoconvert ! queue ! autovideosink 79 | ``` 80 | 81 | For further usage examples also take a look at the included `EXAMPLES.md` file 82 | 83 | ### Setting camera features 84 | To adjust the image acquisition process of the camera, access to settings like the exposure time are 85 | necessary. The `vimbasrc` element provides access to these camera features in one of two ways. 86 | 1. If given, an XML file defining camera features and their corresponding values is parsed and all 87 | features contained are applied to the camera (see [Using an XML file](####Using-an-XML-file)) 88 | 2. Otherwise selected camera features can be set via properties of the `vimbasrc` element (see 89 | [Supported via GStreamer properties](####Supported-via-GStreamer-properties)) 90 | 91 | The first approach allows the user to freely modify all features the used camera supports. The 92 | second one only gives access to a small selection of camera features that are supported by many, but 93 | not all camera models. The feature names (and in case of enum features their values) follow the 94 | Standard Feature Naming Convention (SFNC) for GenICam devices. For cameras not implementing the 95 | SFNC, this may lead to errors in setting some camera features. For these devices the feature setting 96 | via a provided XML file is recommended. 97 | 98 | #### Using an XML file 99 | Providing an XML file containing the desired feature values allows access to all supported camera 100 | features. A convenient way to creating such an XML file is configuring your camera as desired with 101 | the help of the VimbaViewer and saving the current configuration as an XML file from there. The path 102 | to this file may then be passed to `vimbasrc` via the `settingsfile` property as shown below 103 | ``` 104 | gst-launch-1.0 vimbasrc camera=DEV_1AB22D01BBB8 settingsfile=path_to_settings.xml ! videoscale ! videoconvert ! queue ! autovideosink 105 | ``` 106 | 107 | **If a settings file is used no other parameters passed as element properties are applied as feature 108 | values.** This is done to prevent accidental overwriting of previously set features. One exception 109 | from this rule is the format of the recorded image data. For details on this particular feature see 110 | [Supported pixel formats](###Supported-pixel-formats). 111 | 112 | #### Supported via GStreamer properties 113 | A list of supported camera features can be found by using the `gst-inspect` tool on the `vimbasrc` 114 | element. This displays a list of available "Element Properties", which include the available camera 115 | features. **Note that these properties are only applied to their corresponding feature, if no XML 116 | settings file is passed!** 117 | 118 | For some of the exposed features camera specific restrictions in the allowed values may apply. For 119 | example the `Width`, `Height`, `OffsetX` and `OffsetY` features may only accept integer values 120 | between a minimum and a maximum value in a certain interval. In cases where the provided value could 121 | not be applied a logging message with level `WARNING` is printed (make sure that an appropriate 122 | logging level is set: e.g. `GST_DEBUG=vimbasrc:WARNING` or higher) and image acquisition will 123 | proceed with the feature values that were initially set on the camera. 124 | 125 | In addition to the camera features listed by `gst-inspect`, the pixel format the camera uses to 126 | record images can be influenced. For details on this see [Supported pixel 127 | formats](###Supported-pixel-formats). 128 | 129 | ### Supported pixel formats 130 | As the pixel format has direct impact on the layout of the image data that is moving down the 131 | GStreamer pipeline, it is necessary to ensure, that linked elements are able to correctly interpret 132 | the received data. This is done by negotiating the exchange format of two elements by finding a 133 | common data layout they both support. Supported formats are reported as `caps` of an elements pad. 134 | In order to support this standard negotiation procedure, the pixel format is therefore set depending 135 | on the negotiated data exchange format, instead of as a general element property like the other 136 | camera features. 137 | 138 | Selecting the desired format can be achieved by defining it in a GStreamer 139 | [capsfilter](https://gstreamer.freedesktop.org/documentation/coreelements/capsfilter.html) element 140 | (e.g. `video/x-raw,format=GRAY8`). A full example pipeline setting the GStreamer GRAY8 format is 141 | shown below. Selecting the GStreamer GRAY8 format will set the Mono8 Vimba Format in the used camera 142 | to record images. 143 | ``` 144 | gst-launch-1.0 vimbasrc camera=DEV_1AB22D01BBB8 ! video/x-raw,format=GRAY8 ! videoscale ! videoconvert ! queue ! autovideosink 145 | ``` 146 | 147 | Not all Vimba pixel formats can be mapped to compatible GStreamer video formats. This is especially 148 | true for the "packed" formats. The following tables provide a mapping where possible. 149 | 150 | #### GStreamer video/x-raw Formats 151 | | Vimba Format | GStreamer video/x-raw Format | Comment | 152 | |---------------------|------------------------------|-----------------------------------------------------------------------------| 153 | | Mono8 | GRAY8 | | 154 | | Mono10 | GRAY16_LE | Only the 10 least significant bits are filled. Image will appear very dark! | 155 | | Mono12 | GRAY16_LE | Only the 12 least significant bits are filled. Image will appear very dark! | 156 | | Mono14 | GRAY16_LE | Only the 14 least significant bits are filled. Image will appear very dark! | 157 | | Mono16 | GRAY16_LE | | 158 | | RGB8 | RGB | | 159 | | RGB8Packed | RGB | Legacy GigE Vision Format. Does not follow PFNC | 160 | | BGR8 | BGR | | 161 | | BGR8Packed | BGR | Legacy GigE Vision Format. Does not follow PFNC | 162 | | Argb8 | ARGB | | 163 | | Rgba8 | RGBA | | 164 | | Bgra8 | BGRA | | 165 | | Yuv411 | IYU1 | | 166 | | Yuv411Packed | IYU1 | Legacy GigE Vision Format. Does not follow PFNC | 167 | | YCbCr411_8_CbYYCrYY | IYU1 | | 168 | | Yuv422 | UYVY | | 169 | | Yuv422Packed | UYVY | Legacy GigE Vision Format. Does not follow PFNC | 170 | | YCbCr422_8_CbYCrY | UYVY | | 171 | | Yuv444 | IYU2 | | 172 | | Yuv444Packed | IYU2 | Legacy GigE Vision Format. Does not follow PFNC | 173 | | YCbCr8_CbYCr | IYU2 | | 174 | 175 | 176 | #### GStreamer video/x-bayer Formats 177 | The GStreamer `x-bayer` formats in the following table are compatible with the GStreamer 178 | [`bayer2rgb`](https://gstreamer.freedesktop.org/documentation/bayer/bayer2rgb.html) element, which 179 | is able to debayer the data into a widely accepted RGBA format. 180 | 181 | | Vimba Format | GStreamer video/x-bayer Format | 182 | |---------------------|--------------------------------| 183 | | BayerGR8 | grbg | 184 | | BayerRG8 | rggb | 185 | | BayerGB8 | gbrg | 186 | | BayerBG8 | bggr | 187 | 188 | ## Troubleshooting 189 | - The `vimbasrc` element is not loadable 190 | - Ensure that the installation of the plugin was successful and that all required dependencies are 191 | available. Installation instructions can be found in `INSTALLING.md`. To verify that the element 192 | can be loaded try to inspect it by calling `gst-inspect-1.0 vimbasrc`. 193 | - It is possible that the `vimbasrc` element is blacklisted by GStreamer. This can be determined 194 | by checking the list ob blacklisted elements with `gst-inspect-1.0 --print-blacklist`. Resetting 195 | the registry may be done by removing the file in which it is stored. It is typically saved in 196 | `~/.cache/gstreamer-1.0/registry.x86_64.bin`. The exact file name depends on the architecture of 197 | the system. For more details see [the official documentation on the 198 | registry](https://gstreamer.freedesktop.org/documentation/gstreamer/gstregistry.html) 199 | - How can I enable logging for the plugin 200 | - To enable logging set the `GST_DEBUG` environment variable to `GST_DEBUG=vimbasrc:DEBUG` or 201 | another appropriate level. For further details see [the official 202 | documentation](https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html) 203 | - The camera features are not applied correctly 204 | - Not all cameras support access to their features via the names agreed upon in the Standard 205 | Feature Naming Convention. If your camera uses different names for its features, consider [using 206 | an XML file to pass camera settings](####Using-an-XML-file) instead. 207 | - When displaying my images I only see black 208 | - This may be due to the selected pixel format. If for example a Mono10 pixel format is chosen for 209 | the camera, the resulting pixel intensities are written to 16bit fields in the used image buffer 210 | (see table in [video/x-raw formats overview](####GStreamer-video/x-raw-Formats)). Because the 211 | pixel data is only written to the least significant bits, the used pixel intensity range does 212 | not cover the expected 16bit range. If the display assumes the 16bit data range to be fully 213 | utilized, your recorded pixel intensities may be too small to show up on your display, because 214 | they are simply displayed as very dark pixels. 215 | - When displaying my images I only see green 216 | - This is possibly caused by an error in the `videoconvert` element. Try enabling error messages 217 | for all elements (`GST_DEBUG=ERROR`) to see if there is a problem with the size of the data 218 | buffer. If that is the case check the troubleshooting entry to "The `videoconvert` element 219 | complains about too small buffer size" 220 | - The `videoconvert` element complains about too small buffer size 221 | - This is most likely caused by the width of the image data not being evenly divisible by 4 as the 222 | `videoconvert` element expects. Try setting the width to a value that is evenly divisible by 4. 223 | 224 | ## Known issues and limitations 225 | - In situations where cameras submit many frames per second, visualization may slow down the 226 | pipeline and lead to a large number of incomplete frames. For incomplete frames warnings are 227 | logged. The user may select whether they want to drop incomplete frames (default behavior) or to 228 | submit them into the pipeline for processing. Incomplete frames may contain pixel intensities from 229 | old acquisitions or random data. The behavior is selectable with the `incompleteframehandling` 230 | property. 231 | - Complex camera feature setups may not be possible using the provided properties (e.g. complex 232 | trigger setups for multiple trigger selectors). For those cases it is recommended to [use an XML 233 | file to pass the camera settings](####Using-an-XML-file). 234 | - If the width of the image data pushed out of the `gst-vimbasrc` element is not evenly divisible by 235 | 4, the image data can not be transformed with the `videoconvert` element. This is caused by the 236 | `videoconvert` element expecting the width to always be a multiple of 4, or being explicitly told 237 | the stride of the image data in the buffer. This explicit stride information is currently not 238 | implemented. For now it is therefore recommended to use `width` settings that are evenly divisible 239 | by 4. 240 | 241 | ## Compatibility 242 | `vimbasrc` is currently officially supported on the following operating systems and architectures: 243 | - AMD64 (Validated on Ubuntu 20.04, Debian 10, CentOS 8.3) 244 | - ARM64 (Validated on NVIDIA L4T 32.5.1) 245 | - ARM32 (Validated on Raspberry Pi OS) 246 | - WIN64 (Validated on Win10 20H2) 247 | - WIN32 (Validated on Win10 20H2, 32Bit) 248 | 249 | The following library versions have been validated to work with vimbasrc: 250 | - Vimba 5.0 251 | - GStreamer 1.14 (NVIDIA L4T 32.5.1, Debian 10, Raspberry OS) 252 | - GStreamer 1.16 (Ubuntu 20.04, CentOS 8.3) 253 | - GStreamer 1.18 (Win10 20H2) 254 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | cmake -S . -B build -DGSTREAMER_ROOT:PATH="D:/gstreamer/1.0/msvc_x86_64" -DVIMBA_HOME:PATH="%VIMBA_HOME% " 2 | 3 | cmake --build build -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -o verbose 3 | # ============================================================================ 4 | cmake -S . -B build -DVIMBA_HOME=$VIMBA_HOME 5 | # ============================================================================ 6 | cmake --build build 7 | # ============================================================================ 8 | -------------------------------------------------------------------------------- /cmake/FindGLIB2.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the GLIB2 libraries 2 | # Once done this will define 3 | # 4 | # GLIB2_FOUND - system has glib2 5 | # GLIB2_INCLUDE_DIR - the glib2 include directory 6 | # GLIB2_LIBRARIES - glib2 library 7 | 8 | # Copyright (c) 2008 Laurent Montel, 9 | # 10 | # Redistribution and use is allowed according to the terms of the BSD license. 11 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 12 | 13 | 14 | if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) 15 | # Already in cache, be silent 16 | set(GLIB2_FIND_QUIETLY TRUE) 17 | endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) 18 | 19 | if (NOT WIN32) 20 | find_package(PkgConfig REQUIRED) 21 | pkg_check_modules(PKG_GLIB REQUIRED glib-2.0) 22 | endif(NOT WIN32) 23 | 24 | if (NOT GSTREAMER_ROOT) 25 | if (CMAKE_SIZEOF_VOID_P MATCHES "8") 26 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86_64}) 27 | else () 28 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86}) 29 | endif () 30 | endif () 31 | 32 | find_path(GLIB2_MAIN_INCLUDE_DIR glib.h 33 | PATH_SUFFIXES glib-2.0 34 | HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_INCLUDEDIR} ${GSTREAMER_ROOT}/include) 35 | 36 | # search the glibconfig.h include dir under the same root where the library is found 37 | find_library(GLIB2_LIBRARIES 38 | NAMES glib-2.0 39 | HINTS ${PKG_GLIB_LIBRARY_DIRS} ${PKG_GLIB_LIBDIR} ${GSTREAMER_ROOT}/lib) 40 | 41 | find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h 42 | PATH_SUFFIXES glib-2.0/include ../lib/glib-2.0/include 43 | HINTS ${PKG_GLIB_INCLUDE_DIRS} ${PKG_GLIB_LIBRARIES} ${CMAKE_SYSTEM_LIBRARY_PATH} ${GSTREAMER_ROOT}/lib) 44 | 45 | set(GLIB2_INCLUDE_DIR ${GLIB2_MAIN_INCLUDE_DIR}) 46 | 47 | # not sure if this include dir is optional or required 48 | # for now it is optional 49 | if(GLIB2_INTERNAL_INCLUDE_DIR) 50 | set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} ${GLIB2_INTERNAL_INCLUDE_DIR}) 51 | endif(GLIB2_INTERNAL_INCLUDE_DIR) 52 | 53 | include(FindPackageHandleStandardArgs) 54 | find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR) 55 | 56 | mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES) 57 | 58 | 59 | find_program(GLIB2_GENMARSHAL_UTIL glib-genmarshal) 60 | 61 | macro(glib2_genmarshal output_name) 62 | file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp) 63 | foreach(_declaration ${ARGN}) 64 | file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/genmarshal_tmp "${_declaration}\n") 65 | endforeach() 66 | add_custom_command( 67 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.h ${CMAKE_CURRENT_BINARY_DIR}/${output_name}.c 68 | COMMAND ${GLIB2_GENMARSHAL_UTIL} --header genmarshal_tmp > ${output_name}.h 69 | COMMAND ${GLIB2_GENMARSHAL_UTIL} --body genmarshal_tmp > ${output_name}.c 70 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 71 | ) 72 | endmacro() 73 | -------------------------------------------------------------------------------- /cmake/FindGObject.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find GObject 2 | # Once done this will define 3 | # 4 | # GOBJECT_FOUND - system has GObject 5 | # GOBJECT_INCLUDE_DIR - the GObject include directory 6 | # GOBJECT_LIBRARIES - the libraries needed to use GObject 7 | # GOBJECT_DEFINITIONS - Compiler switches required for using GObject 8 | 9 | # Copyright (c) 2006, Tim Beaulen 10 | # Copyright (c) 2008 Helio Chissini de Castro, 11 | # 12 | # Redistribution and use is allowed according to the terms of the BSD license. 13 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 14 | 15 | 16 | IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) 17 | # in cache already 18 | SET(GObject_FIND_QUIETLY TRUE) 19 | ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) 20 | SET(GObject_FIND_QUIETLY FALSE) 21 | ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) 22 | 23 | IF (NOT WIN32) 24 | FIND_PACKAGE(PkgConfig REQUIRED) 25 | # use pkg-config to get the directories and then use these values 26 | # in the FIND_PATH() and FIND_LIBRARY() calls 27 | PKG_CHECK_MODULES(PKG_GOBJECT2 REQUIRED gobject-2.0) 28 | SET(GOBJECT_DEFINITIONS ${PKG_GOBJECT2_CFLAGS}) 29 | ENDIF (NOT WIN32) 30 | 31 | if (NOT GSTREAMER_ROOT) 32 | if (CMAKE_SIZEOF_VOID_P MATCHES "8") 33 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86_64}) 34 | else () 35 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86}) 36 | endif () 37 | endif () 38 | 39 | FIND_PATH(GOBJECT_INCLUDE_DIR gobject/gobject.h 40 | HINTS ${PKG_GOBJECT2_INCLUDE_DIRS} ${PKG_GOBJECT2_INCLUDEDIR} 41 | PATHS /usr/include/glib-2.0/ ${GSTREAMER_ROOT}/include 42 | PATH_SUFFIXES glib-2.0 43 | ) 44 | 45 | FIND_LIBRARY(_GObjectLibs NAMES gobject-2.0 46 | HINTS 47 | ${PKG_GOBJECT2_LIBRARY_DIRS} 48 | ${PKG_GOBJECT2_LIBDIR} 49 | ${GSTREAMER_ROOT}/lib 50 | ) 51 | FIND_LIBRARY(_GModuleLibs NAMES gmodule-2.0 52 | HINTS 53 | ${PKG_GOBJECT2_LIBRARY_DIRS} 54 | ${PKG_GOBJECT2_LIBDIR} 55 | ${GSTREAMER_ROOT}/lib 56 | ) 57 | FIND_LIBRARY(_GThreadLibs NAMES gthread-2.0 58 | HINTS 59 | ${PKG_GOBJECT2_LIBRARY_DIRS} 60 | ${PKG_GOBJECT2_LIBDIR} 61 | ${GSTREAMER_ROOT}/lib 62 | ) 63 | FIND_LIBRARY(_GLibs NAMES glib-2.0 64 | HINTS 65 | ${PKG_GOBJECT2_LIBRARY_DIRS} 66 | ${PKG_GOBJECT2_LIBDIR} 67 | ${GSTREAMER_ROOT}/lib 68 | ) 69 | FIND_LIBRARY(_GIOLibs NAMES gio-2.0 70 | HINTS 71 | ${PKG_GOBJECT2_LIBRARY_DIRS} 72 | ${PKG_GOBJECT2_LIBDIR} 73 | ${GSTREAMER_ROOT}/lib 74 | ) 75 | 76 | SET (GOBJECT_LIBRARIES ${_GObjectLibs} ${_GModuleLibs} ${_GThreadLibs} ${_GLibs} ${_GIOLibs}) 77 | 78 | MARK_AS_ADVANCED(GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) 79 | 80 | INCLUDE(FindPackageHandleStandardArgs) 81 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GObject DEFAULT_MSG GOBJECT_INCLUDE_DIR GOBJECT_LIBRARIES) 82 | -------------------------------------------------------------------------------- /cmake/FindGStreamer.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find GStreamer 2 | # Once done this will define 3 | # 4 | # GSTREAMER_FOUND - system has GStreamer 5 | # GSTREAMER_INCLUDE_DIR - the GStreamer include directory 6 | # GSTREAMER_LIBRARY - the main GStreamer library 7 | # GSTREAMER_PLUGIN_DIR - the GStreamer plugin directory 8 | # 9 | # And for all the plugin libraries specified in the COMPONENTS 10 | # of find_package, this module will define: 11 | # 12 | # GSTREAMER__LIBRARY_FOUND - system has 13 | # GSTREAMER__LIBRARY - the library 14 | # GSTREAMER__INCLUDE_DIR - the include directory 15 | # 16 | # Copyright (c) 2010, Collabora Ltd. 17 | # @author George Kiagiadakis 18 | # 19 | # Redistribution and use is allowed according to the terms of the BSD license. 20 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 21 | 22 | if (GSTREAMER_INCLUDE_DIR AND GSTREAMER_LIBRARY) 23 | set(GStreamer_FIND_QUIETLY TRUE) 24 | else() 25 | set(GStreamer_FIND_QUIETLY FALSE) 26 | endif() 27 | 28 | set(GSTREAMER_ABI_VERSION "1.0") 29 | 30 | 31 | # Find the main library 32 | find_package(PkgConfig) 33 | 34 | if (PKG_CONFIG_FOUND) 35 | pkg_check_modules(PKG_GSTREAMER gstreamer-${GSTREAMER_ABI_VERSION}) 36 | exec_program(${PKG_CONFIG_EXECUTABLE} 37 | ARGS --variable pluginsdir gstreamer-${GSTREAMER_ABI_VERSION} 38 | OUTPUT_VARIABLE PKG_GSTREAMER_PLUGIN_DIR) 39 | endif() 40 | 41 | if (NOT GSTREAMER_ROOT) 42 | if (CMAKE_SIZEOF_VOID_P MATCHES "8") 43 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86_64}) 44 | else () 45 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86}) 46 | endif () 47 | endif () 48 | 49 | find_library(GSTREAMER_LIBRARY 50 | NAMES gstreamer-${GSTREAMER_ABI_VERSION} 51 | HINTS ${PKG_GSTREAMER_LIBRARY_DIRS} ${PKG_GSTREAMER_LIBDIR} ${GSTREAMER_ROOT}/lib ${GSTREAMER_ROOT}/lib/aarch64-linux-gnu) 52 | 53 | find_path(GSTREAMER_INCLUDE_DIR 54 | gst/gst.h 55 | HINTS ${PKG_GSTREAMER_INCLUDE_DIRS} ${PKG_GSTREAMER_INCLUDEDIR} ${GSTREAMER_ROOT}/include 56 | PATH_SUFFIXES gstreamer-${GSTREAMER_ABI_VERSION}) 57 | 58 | find_path(GSTREAMER_INCLUDE_DIR_ARCH 59 | gst/gstconfig.h 60 | HINTS ${PKG_GSTREAMER_INCLUDE_DIRS} ${PKG_GSTREAMER_INCLUDEDIR} ${GSTREAMER_ROOT}/lib/gstreamer-${GSTREAMER_ABI_VERSION}/include ${GSTREAMER_ROOT}/include/gstreamer-${GSTREAMER_ABI_VERSION} 61 | PATH_SUFFIXES gstreamer-${GSTREAMER_ABI_VERSION}) 62 | 63 | if (GSTREAMER_INCLUDE_DIR_ARCH) 64 | set(GSTREAMER_INCLUDE_DIR ${GSTREAMER_INCLUDE_DIR} ${GSTREAMER_INCLUDE_DIR_ARCH}) 65 | endif () 66 | 67 | if (EXISTS PKG_GSTREAMER_PLUGIN_DIR) 68 | set(_GSTREAMER_PLUGIN_DIR ${PKG_GSTREAMER_PLUGIN_DIR}) 69 | else() 70 | get_filename_component(_GSTREAMER_LIB_DIR ${GSTREAMER_LIBRARY} PATH) 71 | set(_GSTREAMER_PLUGIN_DIR ${_GSTREAMER_LIB_DIR}/gstreamer-${GSTREAMER_ABI_VERSION}) 72 | endif() 73 | 74 | set(GSTREAMER_PLUGIN_DIR ${_GSTREAMER_PLUGIN_DIR} 75 | CACHE PATH "The path to the gstreamer plugins installation directory") 76 | 77 | mark_as_advanced(GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIR GSTREAMER_PLUGIN_DIR) 78 | 79 | 80 | # Find additional libraries 81 | include(MacroFindGStreamerLibrary) 82 | 83 | macro(_find_gst_component _name _header) 84 | find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION}) 85 | set(_GSTREAMER_EXTRA_VARIABLES ${_GSTREAMER_EXTRA_VARIABLES} 86 | GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR) 87 | endmacro() 88 | 89 | foreach(_component ${GStreamer_FIND_COMPONENTS}) 90 | if (${_component} STREQUAL "base") 91 | _find_gst_component(BASE gstbasesink.h) 92 | elseif (${_component} STREQUAL "check") 93 | _find_gst_component(CHECK gstcheck.h) 94 | elseif (${_component} STREQUAL "controller") 95 | _find_gst_component(CONTROLLER gstcontroller.h) 96 | elseif (${_component} STREQUAL "dataprotocol") 97 | _find_gst_component(DATAPROTOCOL dataprotocol.h) 98 | elseif (${_component} STREQUAL "net") 99 | _find_gst_component(NET gstnet.h) 100 | else() 101 | message (AUTHOR_WARNING "FindGStreamerPluginsBase.cmake: Invalid component \"${_component}\" was specified") 102 | endif() 103 | endforeach() 104 | 105 | 106 | # Version check 107 | if (GStreamer_FIND_VERSION) 108 | if (PKG_GSTREAMER_FOUND) 109 | if("${PKG_GSTREAMER_VERSION}" VERSION_LESS "${GStreamer_FIND_VERSION}") 110 | message(STATUS "Found GStreamer version ${PKG_GSTREAMER_VERSION}, but at least version ${GStreamer_FIND_VERSION} is required") 111 | set(GSTREAMER_VERSION_COMPATIBLE FALSE) 112 | else() 113 | set(GSTREAMER_VERSION_COMPATIBLE TRUE) 114 | endif() 115 | elseif(GSTREAMER_INCLUDE_DIR) 116 | include(CheckCXXSourceCompiles) 117 | 118 | set(CMAKE_REQUIRED_INCLUDES ${GSTREAMER_INCLUDE_DIR}) 119 | string(REPLACE "." "," _comma_version ${GStreamer_FIND_VERSION}) 120 | # Hack to invalidate the cached value 121 | set(GSTREAMER_VERSION_COMPATIBLE GSTREAMER_VERSION_COMPATIBLE) 122 | 123 | check_cxx_source_compiles(" 124 | #define G_BEGIN_DECLS 125 | #define G_END_DECLS 126 | #include 127 | 128 | #if GST_CHECK_VERSION(${_comma_version}) 129 | int main() { return 0; } 130 | #else 131 | # error \"GStreamer version incompatible\" 132 | #endif 133 | " GSTREAMER_VERSION_COMPATIBLE) 134 | 135 | if (NOT GSTREAMER_VERSION_COMPATIBLE) 136 | message(STATUS "GStreamer ${GStreamer_FIND_VERSION} is required, but the version found is older") 137 | endif() 138 | else() 139 | # We didn't find gstreamer at all 140 | set(GSTREAMER_VERSION_COMPATIBLE FALSE) 141 | endif() 142 | else() 143 | # No version constrain was specified, thus we consider the version compatible 144 | set(GSTREAMER_VERSION_COMPATIBLE TRUE) 145 | endif() 146 | 147 | 148 | include(FindPackageHandleStandardArgs) 149 | find_package_handle_standard_args(GStreamer DEFAULT_MSG 150 | GSTREAMER_LIBRARY GSTREAMER_INCLUDE_DIR 151 | GSTREAMER_VERSION_COMPATIBLE ${_GSTREAMER_EXTRA_VARIABLES}) 152 | -------------------------------------------------------------------------------- /cmake/FindGStreamerPluginsBase.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find gst-plugins-base 2 | # Once done this will define 3 | # 4 | # GSTREAMER_PLUGINS_BASE_FOUND - system has gst-plugins-base 5 | # 6 | # And for all the plugin libraries specified in the COMPONENTS 7 | # of find_package, this module will define: 8 | # 9 | # GSTREAMER__LIBRARY_FOUND - system has 10 | # GSTREAMER__LIBRARY - the library 11 | # GSTREAMER__INCLUDE_DIR - the include directory 12 | # 13 | # Copyright (c) 2010, Collabora Ltd. 14 | # @author George Kiagiadakis 15 | # 16 | # Redistribution and use is allowed according to the terms of the BSD license. 17 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 18 | 19 | set(GSTREAMER_ABI_VERSION "1.0") 20 | 21 | 22 | # Find the pkg-config file for doing the version check 23 | find_package(PkgConfig) 24 | 25 | if (PKG_CONFIG_FOUND) 26 | pkg_check_modules(PKG_GSTREAMER_PLUGINS_BASE gstreamer-plugins-base-${GSTREAMER_ABI_VERSION}) 27 | endif() 28 | 29 | 30 | # Find the plugin libraries 31 | include(MacroFindGStreamerLibrary) 32 | 33 | macro(_find_gst_plugins_base_component _name _header) 34 | find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION}) 35 | set(_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES ${_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES} 36 | GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR) 37 | endmacro() 38 | 39 | foreach(_component ${GStreamerPluginsBase_FIND_COMPONENTS}) 40 | if (${_component} STREQUAL "app") 41 | _find_gst_plugins_base_component(APP gstappsrc.h) 42 | elseif (${_component} STREQUAL "audio") 43 | _find_gst_plugins_base_component(AUDIO audio.h) 44 | elseif (${_component} STREQUAL "cdda") 45 | _find_gst_plugins_base_component(CDDA gstcddabasesrc.h) 46 | elseif (${_component} STREQUAL "fft") 47 | _find_gst_plugins_base_component(FFT gstfft.h) 48 | elseif (${_component} STREQUAL "floatcast") 49 | _find_gst_plugins_base_component(FLOATCAST floatcast.h) 50 | elseif (${_component} STREQUAL "interfaces") 51 | _find_gst_plugins_base_component(INTERFACES xoverlay.h) 52 | elseif (${_component} STREQUAL "insertbin") 53 | _find_gst_plugins_base_component(INSERTBIN gstinsertbin.h) 54 | elseif (${_component} STREQUAL "netbuffer") 55 | _find_gst_plugins_base_component(NETBUFFER gstnetbuffer.h) 56 | elseif (${_component} STREQUAL "riff") 57 | _find_gst_plugins_base_component(RIFF riff-ids.h) 58 | elseif (${_component} STREQUAL "rtp") 59 | _find_gst_plugins_base_component(RTP gstrtpbuffer.h) 60 | elseif (${_component} STREQUAL "rtsp") 61 | _find_gst_plugins_base_component(RTSP gstrtspdefs.h) 62 | elseif (${_component} STREQUAL "sdp") 63 | _find_gst_plugins_base_component(SDP gstsdp.h) 64 | elseif (${_component} STREQUAL "tag") 65 | _find_gst_plugins_base_component(TAG tag.h) 66 | elseif (${_component} STREQUAL "pbutils") 67 | _find_gst_plugins_base_component(PBUTILS pbutils.h) 68 | elseif (${_component} STREQUAL "video") 69 | _find_gst_plugins_base_component(VIDEO video.h) 70 | else() 71 | message (AUTHOR_WARNING "FindGStreamer.cmake: Invalid component \"${_component}\" was specified") 72 | endif() 73 | endforeach() 74 | 75 | 76 | # Version check 77 | if (GStreamerPluginsBase_FIND_VERSION) 78 | if (PKG_GSTREAMER_PLUGINS_BASE_FOUND) 79 | if("${PKG_GSTREAMER_PLUGINS_BASE_VERSION}" VERSION_LESS "${GStreamerPluginsBase_FIND_VERSION}") 80 | message(STATUS "Found gst-plugins-base version ${PKG_GSTREAMER_PLUGINS_BASE_VERSION}, but at least version ${GStreamerPluginsBase_FIND_VERSION} is required") 81 | set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE FALSE) 82 | else() 83 | set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE) 84 | endif() 85 | else() 86 | # We can't make any version checks without pkg-config, just assume version is compatible and hope... 87 | set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE) 88 | endif() 89 | else() 90 | # No version constrain was specified, thus we consider the version compatible 91 | set(GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE TRUE) 92 | endif() 93 | 94 | 95 | include(FindPackageHandleStandardArgs) 96 | find_package_handle_standard_args(GStreamerPluginsBase DEFAULT_MSG 97 | GSTREAMER_PLUGINS_BASE_VERSION_COMPATIBLE 98 | ${_GSTREAMER_PLUGINS_BASE_EXTRA_VARIABLES}) 99 | -------------------------------------------------------------------------------- /cmake/MacroFindGStreamerLibrary.cmake: -------------------------------------------------------------------------------- 1 | # - macro find_gstreamer_library 2 | # 3 | # Copyright (c) 2010, Collabora Ltd. 4 | # @author George Kiagiadakis 5 | # 6 | # Redistribution and use is allowed according to the terms of the BSD license. 7 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 8 | 9 | macro(find_gstreamer_library _name _header _abi_version) 10 | string(TOLOWER ${_name} _lower_name) 11 | string(TOUPPER ${_name} _upper_name) 12 | 13 | if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR) 14 | set(_GSTREAMER_${_upper_name}_QUIET TRUE) 15 | else() 16 | set(_GSTREAMER_${_upper_name}_QUIET FALSE) 17 | endif() 18 | 19 | if (PKG_CONFIG_FOUND) 20 | pkg_check_modules(PKG_GSTREAMER_${_upper_name} gstreamer-${_lower_name}-${_abi_version}) 21 | endif() 22 | 23 | if (NOT GSTREAMER_ROOT) 24 | if (CMAKE_SIZEOF_VOID_P MATCHES "8") 25 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86_64}) 26 | else () 27 | set(GSTREAMER_ROOT $ENV{GSTREAMER_1_0_ROOT_X86}) 28 | endif () 29 | endif () 30 | 31 | find_library(GSTREAMER_${_upper_name}_LIBRARY 32 | NAMES gst${_lower_name}-${_abi_version} 33 | HINTS ${PKG_GSTREAMER_${_upper_name}_LIBRARY_DIRS} 34 | ${PKG_GSTREAMER_${_upper_name}_LIBDIR} 35 | ${GSTREAMER_ROOT}/lib 36 | ${GSTREAMER_ROOT}/lib/aarch64-linux-gnu 37 | ) 38 | 39 | find_path(GSTREAMER_${_upper_name}_INCLUDE_DIR 40 | gst/${_lower_name}/${_header} 41 | HINTS ${PKG_GSTREAMER_${_upper_name}_INCLUDE_DIRS} 42 | ${PKG_GSTREAMER_${_upper_name}_INCLUDEDIR} 43 | ${GSTREAMER_ROOT}/include 44 | PATH_SUFFIXES gstreamer-${_abi_version} 45 | ) 46 | 47 | if (GSTREAMER_${_upper_name}_LIBRARY AND GSTREAMER_${_upper_name}_INCLUDE_DIR) 48 | set(GSTREAMER_${_upper_name}_LIBRARY_FOUND TRUE) 49 | else() 50 | set(GSTREAMER_${_upper_name}_LIBRARY_FOUND FALSE) 51 | endif() 52 | 53 | if (NOT _GSTREAMER_${_upper_name}_QUIET) 54 | if (GSTREAMER_${_upper_name}_LIBRARY) 55 | message(STATUS "Found GSTREAMER_${_upper_name}_LIBRARY: ${GSTREAMER_${_upper_name}_LIBRARY}") 56 | else() 57 | message(STATUS "Could NOT find GSTREAMER_${_upper_name}_LIBRARY") 58 | endif() 59 | 60 | if (GSTREAMER_${_upper_name}_INCLUDE_DIR) 61 | message(STATUS "Found GSTREAMER_${_upper_name}_INCLUDE_DIR: ${GSTREAMER_${_upper_name}_INCLUDE_DIR}") 62 | else() 63 | message(STATUS "Could NOT find GSTREAMER_${_upper_name}_INCLUDE_DIR") 64 | endif() 65 | endif() 66 | 67 | mark_as_advanced(GSTREAMER_${_upper_name}_LIBRARY GSTREAMER_${_upper_name}_INCLUDE_DIR) 68 | endmacro() 69 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_ 2 | #define CONFIG_H_ 3 | 4 | #define VERSION "@PROJECT_VERSION@@VERSION_SUFFIX@" 5 | #define VERSION_MAJOR "@PROJECT_VERSION_MAJOR@" 6 | #define VERSION_MINOR "@PROJECT_VERSION_MINOR@" 7 | #define VERSION_PATCH "@PROJECT_VERSION_PATCH@" 8 | 9 | #define PACKAGE "@PROJECT_NAME@" 10 | #define DESCRIPTION "@PROJECT_DESCRIPTION@" 11 | #define HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@" 12 | 13 | #endif // CONFIG_H_ 14 | -------------------------------------------------------------------------------- /src/gstvimbasrc.c: -------------------------------------------------------------------------------- 1 | /* GStreamer 2 | * Copyright (C) 2021 Allied Vision Technologies GmbH 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Library General Public 6 | * License version 2.0 as published by the Free Software Foundation. 7 | * 8 | * This library is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * Library General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Library General Public 14 | * License along with this library; if not, write to the 15 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 16 | * Boston, MA 02110-1301, USA. 17 | */ 18 | /** 19 | * SECTION:element-gstvimbasrc 20 | * 21 | * The vimbasrc element does FIXME stuff. 22 | * 23 | * 24 | * Example launch line 25 | * |[ 26 | * gst-launch-1.0 -v fakesrc ! vimbasrc ! FIXME ! fakesink 27 | * ]| 28 | * FIXME Describe what the pipeline does. 29 | * 30 | */ 31 | 32 | #include "gstvimbasrc.h" 33 | #include "helpers.h" 34 | #include "vimba_helpers.h" 35 | #include "pixelformats.h" 36 | 37 | #ifdef HAVE_CONFIG_H 38 | #include "config.h" 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | // Counter variable to keep track of calls to VmbStartup() and VmbShutdown() 49 | static unsigned int vmb_open_count = 0; 50 | G_LOCK_DEFINE(vmb_open_count); 51 | 52 | GST_DEBUG_CATEGORY_STATIC(gst_vimbasrc_debug_category); 53 | #define GST_CAT_DEFAULT gst_vimbasrc_debug_category 54 | 55 | /* prototypes */ 56 | 57 | static void gst_vimbasrc_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); 58 | static void gst_vimbasrc_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); 59 | static void gst_vimbasrc_dispose(GObject *object); 60 | static void gst_vimbasrc_finalize(GObject *object); 61 | 62 | static GstCaps *gst_vimbasrc_get_caps(GstBaseSrc *src, GstCaps *filter); 63 | static gboolean gst_vimbasrc_set_caps(GstBaseSrc *src, GstCaps *caps); 64 | static gboolean gst_vimbasrc_start(GstBaseSrc *src); 65 | static gboolean gst_vimbasrc_stop(GstBaseSrc *src); 66 | 67 | static GstFlowReturn gst_vimbasrc_create(GstPushSrc *src, GstBuffer **buf); 68 | 69 | enum 70 | { 71 | PROP_0, 72 | PROP_CAMERA_ID, 73 | PROP_SETTINGS_FILENAME, 74 | PROP_EXPOSURETIME, 75 | PROP_EXPOSUREAUTO, 76 | PROP_BALANCEWHITEAUTO, 77 | PROP_GAIN, 78 | PROP_OFFSETX, 79 | PROP_OFFSETY, 80 | PROP_WIDTH, 81 | PROP_HEIGHT, 82 | PROP_TRIGGERSELECTOR, 83 | PROP_TRIGGERMODE, 84 | PROP_TRIGGERSOURCE, 85 | PROP_TRIGGERACTIVATION, 86 | PROP_INCOMPLETE_FRAME_HANDLING 87 | }; 88 | 89 | /* pad templates */ 90 | static GstStaticPadTemplate gst_vimbasrc_src_template = 91 | GST_STATIC_PAD_TEMPLATE("src", 92 | GST_PAD_SRC, 93 | GST_PAD_ALWAYS, 94 | GST_STATIC_CAPS( 95 | GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) ";" GST_BAYER_CAPS_MAKE(GST_BAYER_FORMATS_ALL))); 96 | 97 | /* Auto exposure modes */ 98 | #define GST_ENUM_EXPOSUREAUTO_MODES (gst_vimbasrc_exposureauto_get_type()) 99 | static GType gst_vimbasrc_exposureauto_get_type(void) 100 | { 101 | static GType vimbasrc_exposureauto_type = 0; 102 | static const GEnumValue exposureauto_modes[] = { 103 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 104 | {GST_VIMBASRC_AUTOFEATURE_OFF, "Exposure duration is usercontrolled using ExposureTime", "Off"}, 105 | {GST_VIMBASRC_AUTOFEATURE_ONCE, "Exposure duration is adapted once by the device. Once it has converged, it returns to the Offstate", "Once"}, 106 | {GST_VIMBASRC_AUTOFEATURE_CONTINUOUS, "Exposure duration is constantly adapted by the device to maximize the dynamic range", "Continuous"}, 107 | {0, NULL, NULL}}; 108 | if (!vimbasrc_exposureauto_type) 109 | { 110 | vimbasrc_exposureauto_type = 111 | g_enum_register_static("GstVimbasrcExposureAutoModes", exposureauto_modes); 112 | } 113 | return vimbasrc_exposureauto_type; 114 | } 115 | 116 | /* Auto white balance modes */ 117 | #define GST_ENUM_BALANCEWHITEAUTO_MODES (gst_vimbasrc_balancewhiteauto_get_type()) 118 | static GType gst_vimbasrc_balancewhiteauto_get_type(void) 119 | { 120 | static GType vimbasrc_balancewhiteauto_type = 0; 121 | static const GEnumValue balancewhiteauto_modes[] = { 122 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 123 | {GST_VIMBASRC_AUTOFEATURE_OFF, "White balancing is user controlled using BalanceRatioSelector and BalanceRatio", "Off"}, 124 | {GST_VIMBASRC_AUTOFEATURE_ONCE, "White balancing is automatically adjusted once by the device. Once it has converged, it automatically returns to the Off state", "Once"}, 125 | {GST_VIMBASRC_AUTOFEATURE_CONTINUOUS, "White balancing is constantly adjusted by the device", "Continuous"}, 126 | {0, NULL, NULL}}; 127 | if (!vimbasrc_balancewhiteauto_type) 128 | { 129 | vimbasrc_balancewhiteauto_type = 130 | g_enum_register_static("GstVimbasrcBalanceWhiteAutoModes", balancewhiteauto_modes); 131 | } 132 | return vimbasrc_balancewhiteauto_type; 133 | } 134 | 135 | /* TriggerSelector values */ 136 | #define GST_ENUM_TRIGGERSELECTOR_VALUES (gst_vimbasrc_triggerselector_get_type()) 137 | static GType gst_vimbasrc_triggerselector_get_type(void) 138 | { 139 | static GType vimbasrc_triggerselector_type = 0; 140 | static const GEnumValue triggerselector_values[] = { 141 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 142 | {GST_VIMBASRC_TRIGGERSELECTOR_UNCHANGED, "Does not change the currently applied triggerselector value on the device", "UNCHANGED"}, 143 | {GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_START, "Selects a trigger that starts the Acquisition of one or many frames according to AcquisitionMode", "AcquisitionStart"}, 144 | {GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_END, "Selects a trigger that ends the Acquisition of one or many frames according to AcquisitionMode", "AcquisitionEnd"}, 145 | {GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_ACTIVE, "Selects a trigger that controls the duration of the Acquisition of one or many frames. The Acquisition is activated when the trigger signal becomes active and terminated when it goes back to the inactive state", "AcquisitionActive"}, 146 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_START, "Selects a trigger starting the capture of one frame", "FrameStart"}, 147 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_END, "Selects a trigger ending the capture of one frame (mainly used in linescanmode)", "FrameEnd"}, 148 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_ACTIVE, "Selects a trigger controlling the duration of one frame (mainly used in linescanmode)", "FrameActive"}, 149 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_START, "Selects a trigger starting the capture of the bursts of frames in an acquisition. AcquisitionBurstFrameCount controls the length of each burst unless a FrameBurstEnd trigger is active. The total number of frames captured is also conditioned by AcquisitionFrameCount if AcquisitionMode is MultiFrame", "FrameBurstStart"}, 150 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_END, "Selects a trigger ending the capture of the bursts of frames in an acquisition", "FrameBurstEnd"}, 151 | {GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_ACTIVE, "Selects a trigger controlling the duration of the capture of the bursts of frames in an acquisition", "FrameBurstActive"}, 152 | {GST_VIMBASRC_TRIGGERSELECTOR_LINE_START, "Selects a trigger starting the capture of one Line of a Frame (mainly used in linescanmode)", "LineStart"}, 153 | {GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_START, "Selects a trigger controlling the start of the exposure of one Frame (or Line)", "ExposureStart"}, 154 | {GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_END, "Selects a trigger controlling the end of the exposure of one Frame (or Line)", "ExposureEnd"}, 155 | {GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_ACTIVE, "Selects a trigger controlling the duration of the exposure of one frame (or Line)", "ExposureActive"}, 156 | {0, NULL, NULL}}; 157 | if (!vimbasrc_triggerselector_type) 158 | { 159 | vimbasrc_triggerselector_type = 160 | g_enum_register_static("GstVimbasrcTriggerSelectorValues", triggerselector_values); 161 | } 162 | return vimbasrc_triggerselector_type; 163 | } 164 | 165 | /* TriggerMode values */ 166 | #define GST_ENUM_TRIGGERMODE_VALUES (gst_vimbasrc_triggermode_get_type()) 167 | static GType gst_vimbasrc_triggermode_get_type(void) 168 | { 169 | static GType vimbasrc_triggermode_type = 0; 170 | static const GEnumValue triggermode_values[] = { 171 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 172 | {GST_VIMBASRC_TRIGGERMODE_UNCHANGED, "Does not change the currently applied triggermode value on the device", "UNCHANGED"}, 173 | {GST_VIMBASRC_TRIGGERMODE_OFF, "Disables the selected trigger", "Off"}, 174 | {GST_VIMBASRC_TRIGGERMODE_ON, "Enable the selected trigger", "On"}, 175 | {0, NULL, NULL}}; 176 | if (!vimbasrc_triggermode_type) 177 | { 178 | vimbasrc_triggermode_type = 179 | g_enum_register_static("GstVimbasrcTriggerModeValues", triggermode_values); 180 | } 181 | return vimbasrc_triggermode_type; 182 | } 183 | 184 | /* TriggerSource values */ 185 | #define GST_ENUM_TRIGGERSOURCE_VALUES (gst_vimbasrc_triggersource_get_type()) 186 | static GType gst_vimbasrc_triggersource_get_type(void) 187 | { 188 | static GType vimbasrc_triggersource_type = 0; 189 | static const GEnumValue triggersource_values[] = { 190 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 191 | {GST_VIMBASRC_TRIGGERSOURCE_UNCHANGED, "Does not change the currently applied triggersource value on the device", "UNCHANGED"}, 192 | {GST_VIMBASRC_TRIGGERSOURCE_SOFTWARE, "Specifies that the trigger source will be generated by software using the TriggerSoftware command", "Software"}, 193 | {GST_VIMBASRC_TRIGGERSOURCE_LINE0, "Specifies which physical line (or pin) and associated I/O control block to use as external source for the trigger signal", "Line0"}, 194 | {GST_VIMBASRC_TRIGGERSOURCE_LINE1, "Specifies which physical line (or pin) and associated I/O control block to use as external source for the trigger signal", "Line1"}, 195 | {GST_VIMBASRC_TRIGGERSOURCE_LINE2, "Specifies which physical line (or pin) and associated I/O control block to use as external source for the trigger signal", "Line2"}, 196 | {GST_VIMBASRC_TRIGGERSOURCE_LINE3, "Specifies which physical line (or pin) and associated I/O control block to use as external source for the trigger signal", "Line3"}, 197 | {GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT0, "Specifies which User Output bit signal to use as internal source for the trigger", "UserOutput0"}, 198 | {GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT1, "Specifies which User Output bit signal to use as internal source for the trigger", "UserOutput1"}, 199 | {GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT2, "Specifies which User Output bit signal to use as internal source for the trigger", "UserOutput2"}, 200 | {GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT3, "Specifies which User Output bit signal to use as internal source for the trigger", "UserOutput3"}, 201 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER0_START, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter0Start"}, 202 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER1_START, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter1Start"}, 203 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER2_START, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter2Start"}, 204 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER3_START, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter3Start"}, 205 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER0_END, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter0End"}, 206 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER1_END, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter1End"}, 207 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER2_END, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter2End"}, 208 | {GST_VIMBASRC_TRIGGERSOURCE_COUNTER3_END, "Specifies which of the Counter signal to use as internal source for the trigger", "Counter3End"}, 209 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER0_START, "Specifies which Timer signal to use as internal source for the trigger", "Timer0Start"}, 210 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER1_START, "Specifies which Timer signal to use as internal source for the trigger", "Timer1Start"}, 211 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER2_START, "Specifies which Timer signal to use as internal source for the trigger", "Timer2Start"}, 212 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER3_START, "Specifies which Timer signal to use as internal source for the trigger", "Timer3Start"}, 213 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER0_END, "Specifies which Timer signal to use as internal source for the trigger", "Timer0End"}, 214 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER1_END, "Specifies which Timer signal to use as internal source for the trigger", "Timer1End"}, 215 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER2_END, "Specifies which Timer signal to use as internal source for the trigger", "Timer2End"}, 216 | {GST_VIMBASRC_TRIGGERSOURCE_TIMER3_END, "Specifies which Timer signal to use as internal source for the trigger", "Timer3End"}, 217 | {GST_VIMBASRC_TRIGGERSOURCE_ENCODER0, "Specifies which Encoder signal to use as internal source for the trigger", "Encoder0"}, 218 | {GST_VIMBASRC_TRIGGERSOURCE_ENCODER1, "Specifies which Encoder signal to use as internal source for the trigger", "Encoder1"}, 219 | {GST_VIMBASRC_TRIGGERSOURCE_ENCODER2, "Specifies which Encoder signal to use as internal source for the trigger", "Encoder2"}, 220 | {GST_VIMBASRC_TRIGGERSOURCE_ENCODER3, "Specifies which Encoder signal to use as internal source for the trigger", "Encoder3"}, 221 | {GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK0, "Specifies which Logic Block signal to use as internal source for the trigger", "LogicBlock0"}, 222 | {GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK1, "Specifies which Logic Block signal to use as internal source for the trigger", "LogicBlock1"}, 223 | {GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK2, "Specifies which Logic Block signal to use as internal source for the trigger", "LogicBlock2"}, 224 | {GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK3, "Specifies which Logic Block signal to use as internal source for the trigger", "LogicBlock3"}, 225 | {GST_VIMBASRC_TRIGGERSOURCE_ACTION0, "Specifies which Action command to use as internal source for the trigger", "Action0"}, 226 | {GST_VIMBASRC_TRIGGERSOURCE_ACTION1, "Specifies which Action command to use as internal source for the trigger", "Action1"}, 227 | {GST_VIMBASRC_TRIGGERSOURCE_ACTION2, "Specifies which Action command to use as internal source for the trigger", "Action2"}, 228 | {GST_VIMBASRC_TRIGGERSOURCE_ACTION3, "Specifies which Action command to use as internal source for the trigger", "Action3"}, 229 | {GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER0, "Specifies which Link Trigger to use as source for the trigger (received from the transport layer)", "LinkTrigger0"}, 230 | {GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER1, "Specifies which Link Trigger to use as source for the trigger (received from the transport layer)", "LinkTrigger1"}, 231 | {GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER2, "Specifies which Link Trigger to use as source for the trigger (received from the transport layer)", "LinkTrigger2"}, 232 | {GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER3, "Specifies which Link Trigger to use as source for the trigger (received from the transport layer)", "LinkTrigger3"}, 233 | {0, NULL, NULL}}; 234 | if (!vimbasrc_triggersource_type) 235 | { 236 | vimbasrc_triggersource_type = 237 | g_enum_register_static("GstVimbasrcTriggerSourceValues", triggersource_values); 238 | } 239 | return vimbasrc_triggersource_type; 240 | } 241 | 242 | /* TriggerActivation values */ 243 | #define GST_ENUM_TRIGGERACTIVATION_VALUES (gst_vimbasrc_triggeractivation_get_type()) 244 | static GType gst_vimbasrc_triggeractivation_get_type(void) 245 | { 246 | static GType vimbasrc_triggeractivation_type = 0; 247 | static const GEnumValue triggeractivation_values[] = { 248 | /* The "nick" (last entry) will be used to pass the setting value on to the Vimba FeatureEnum */ 249 | {GST_VIMBASRC_TRIGGERACTIVATION_UNCHANGED, "Does not change the currently applied triggeractivation value on the device", "UNCHANGED"}, 250 | {GST_VIMBASRC_TRIGGERACTIVATION_RISING_EDGE, "Specifies that the trigger is considered valid on the rising edge of the source signal", "RisingEdge"}, 251 | {GST_VIMBASRC_TRIGGERACTIVATION_FALLING_EDGE, "Specifies that the trigger is considered valid on the falling edge of the source signal", "FallingEdge"}, 252 | {GST_VIMBASRC_TRIGGERACTIVATION_ANY_EDGE, "Specifies that the trigger is considered valid on the falling or rising edge of the source signal", "AnyEdge"}, 253 | {GST_VIMBASRC_TRIGGERACTIVATION_LEVEL_HIGH, "Specifies that the trigger is considered valid as long as the level of the source signal is high", "LevelHigh"}, 254 | {GST_VIMBASRC_TRIGGERACTIVATION_LEVEL_LOW, "Specifies that the trigger is considered valid as long as the level of the source signal is low", "LevelLow"}, 255 | {0, NULL, NULL}}; 256 | if (!vimbasrc_triggeractivation_type) 257 | { 258 | vimbasrc_triggeractivation_type = 259 | g_enum_register_static("GstVimbasrcTriggerActivationValues", triggeractivation_values); 260 | } 261 | return vimbasrc_triggeractivation_type; 262 | } 263 | 264 | /* IncompleteFrameHandling values */ 265 | #define GST_ENUM_INCOMPLETEFRAMEHANDLING_VALUES (gst_vimbasrc_incompleteframehandling_get_type()) 266 | static GType gst_vimbasrc_incompleteframehandling_get_type(void) 267 | { 268 | static GType vimbasrc_incompleteframehandling_type = 0; 269 | static const GEnumValue incompleteframehandling_values[] = { 270 | {GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_DROP, "Drop incomplete frames", "Drop"}, 271 | {GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_SUBMIT, "Use incomplete frames and submit them to the next element for processing", "Submit"}, 272 | {0, NULL, NULL}}; 273 | if (!vimbasrc_incompleteframehandling_type) 274 | { 275 | vimbasrc_incompleteframehandling_type = 276 | g_enum_register_static("GstVimbasrcIncompleteFrameHandlingValues", incompleteframehandling_values); 277 | } 278 | return vimbasrc_incompleteframehandling_type; 279 | } 280 | 281 | /* class initialization */ 282 | 283 | G_DEFINE_TYPE_WITH_CODE(GstVimbaSrc, 284 | gst_vimbasrc, 285 | GST_TYPE_PUSH_SRC, 286 | GST_DEBUG_CATEGORY_INIT(gst_vimbasrc_debug_category, 287 | "vimbasrc", 288 | 0, 289 | "debug category for vimbasrc element")) 290 | 291 | static void gst_vimbasrc_class_init(GstVimbaSrcClass *klass) 292 | { 293 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); 294 | GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS(klass); 295 | GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS(klass); 296 | 297 | /* Setting up pads and setting metadata should be moved to base_class_init if you intend to subclass this class. */ 298 | gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass), 299 | &gst_vimbasrc_src_template); 300 | 301 | gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass), 302 | "Vimba GStreamer source", 303 | "Generic", 304 | DESCRIPTION, 305 | "Allied Vision Technologies GmbH"); 306 | 307 | gobject_class->set_property = gst_vimbasrc_set_property; 308 | gobject_class->get_property = gst_vimbasrc_get_property; 309 | gobject_class->dispose = gst_vimbasrc_dispose; 310 | gobject_class->finalize = gst_vimbasrc_finalize; 311 | base_src_class->get_caps = GST_DEBUG_FUNCPTR(gst_vimbasrc_get_caps); 312 | base_src_class->set_caps = GST_DEBUG_FUNCPTR(gst_vimbasrc_set_caps); 313 | base_src_class->start = GST_DEBUG_FUNCPTR(gst_vimbasrc_start); 314 | base_src_class->stop = GST_DEBUG_FUNCPTR(gst_vimbasrc_stop); 315 | push_src_class->create = GST_DEBUG_FUNCPTR(gst_vimbasrc_create); 316 | 317 | // Install properties 318 | g_object_class_install_property( 319 | gobject_class, 320 | PROP_CAMERA_ID, 321 | g_param_spec_string( 322 | "camera", 323 | "Camera ID", 324 | "ID of the camera images should be recorded from", 325 | "", 326 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 327 | g_object_class_install_property( 328 | gobject_class, 329 | PROP_SETTINGS_FILENAME, 330 | g_param_spec_string( 331 | "settingsfile", 332 | "Camera settings filepath", 333 | "Path to XML file containing camera settings that should be applied. All settings from this file will be applied before any other property is set. Explicitely set properties will overwrite features set from this file!", 334 | "", 335 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 336 | g_object_class_install_property( 337 | gobject_class, 338 | PROP_EXPOSURETIME, 339 | g_param_spec_double( 340 | "exposuretime", 341 | "ExposureTime feature setting", 342 | "Sets the Exposure time (in microseconds) when ExposureMode is Timed and ExposureAuto is Off. This controls the duration where the photosensitive cells are exposed to light", 343 | 0., 344 | G_MAXDOUBLE, 345 | 0., 346 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 347 | g_object_class_install_property( 348 | gobject_class, 349 | PROP_EXPOSUREAUTO, 350 | g_param_spec_enum( 351 | "exposureauto", 352 | "ExposureAuto feature setting", 353 | "Sets the auto exposure mode. The output of the auto exposure function affects the whole image", 354 | GST_ENUM_EXPOSUREAUTO_MODES, 355 | GST_VIMBASRC_AUTOFEATURE_OFF, 356 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 357 | g_object_class_install_property( 358 | gobject_class, 359 | PROP_BALANCEWHITEAUTO, 360 | g_param_spec_enum( 361 | "balancewhiteauto", 362 | "BalanceWhiteAuto feature setting", 363 | "Controls the mode for automatic white balancing between the color channels. The white balancing ratios are automatically adjusted", 364 | GST_ENUM_BALANCEWHITEAUTO_MODES, 365 | GST_VIMBASRC_AUTOFEATURE_OFF, 366 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 367 | g_object_class_install_property( 368 | gobject_class, 369 | PROP_GAIN, 370 | g_param_spec_double( 371 | "gain", 372 | "Gain feature setting", 373 | "Controls the selected gain as an absolute physical value. This is an amplification factor applied to the video signal", 374 | 0., 375 | G_MAXDOUBLE, 376 | 0., 377 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 378 | g_object_class_install_property( 379 | gobject_class, 380 | PROP_OFFSETX, 381 | g_param_spec_int( 382 | "offsetx", 383 | "OffsetX feature setting", 384 | "Horizontal offset from the origin to the region of interest (in pixels). If -1 is passed the ROI will be centered in the sensor along the horizontal axis.", 385 | -1, 386 | G_MAXINT, 387 | 0, 388 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 389 | g_object_class_install_property( 390 | gobject_class, 391 | PROP_OFFSETY, 392 | g_param_spec_int( 393 | "offsety", 394 | "OffsetY feature setting", 395 | "Vertical offset from the origin to the region of interest (in pixels). If -1 is passed the ROI will be centered in the sensor along the vertical axis.", 396 | -1, 397 | G_MAXINT, 398 | 0, 399 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 400 | g_object_class_install_property( 401 | gobject_class, 402 | PROP_WIDTH, 403 | g_param_spec_int( 404 | "width", 405 | "Width feature setting", 406 | "Width of the image provided by the device (in pixels). If no explicit value is passed the full sensor width is used.", 407 | 0, 408 | G_MAXINT, 409 | G_MAXINT, 410 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 411 | g_object_class_install_property( 412 | gobject_class, 413 | PROP_HEIGHT, 414 | g_param_spec_int( 415 | "height", 416 | "Height feature setting", 417 | "Height of the image provided by the device (in pixels). If no explicit value is passed the full sensor height is used.", 418 | 0, 419 | G_MAXINT, 420 | G_MAXINT, 421 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 422 | g_object_class_install_property( 423 | gobject_class, 424 | PROP_TRIGGERSELECTOR, 425 | g_param_spec_enum( 426 | "triggerselector", 427 | "TriggerSelector feature setting", 428 | "Selects the type of trigger to configure. Not all cameras support every trigger selector listed below. Check which selectors are supported by the used camera model", 429 | GST_ENUM_TRIGGERSELECTOR_VALUES, 430 | GST_VIMBASRC_TRIGGERSELECTOR_UNCHANGED, 431 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 432 | g_object_class_install_property( 433 | gobject_class, 434 | PROP_TRIGGERMODE, 435 | g_param_spec_enum( 436 | "triggermode", 437 | "TriggerMode feature setting", 438 | "Controls if the selected trigger is active", 439 | GST_ENUM_TRIGGERMODE_VALUES, 440 | GST_VIMBASRC_TRIGGERMODE_UNCHANGED, 441 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 442 | g_object_class_install_property( 443 | gobject_class, 444 | PROP_TRIGGERSOURCE, 445 | g_param_spec_enum( 446 | "triggersource", 447 | "TriggerSource feature setting", 448 | "Specifies the internal signal or physical input Line to use as the trigger source. The selected trigger must have its TriggerMode set to On. Not all cameras support every trigger source listed below. Check which sources are supported by the used camera model", 449 | GST_ENUM_TRIGGERSOURCE_VALUES, 450 | GST_VIMBASRC_TRIGGERSOURCE_UNCHANGED, 451 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 452 | g_object_class_install_property( 453 | gobject_class, 454 | PROP_TRIGGERACTIVATION, 455 | g_param_spec_enum( 456 | "triggeractivation", 457 | "TriggerActivation feature setting", 458 | "Specifies the activation mode of the trigger. Not all cameras support every trigger activation listed below. Check which activations are supported by the used camera model", 459 | GST_ENUM_TRIGGERACTIVATION_VALUES, 460 | GST_VIMBASRC_TRIGGERACTIVATION_UNCHANGED, 461 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 462 | g_object_class_install_property( 463 | gobject_class, 464 | PROP_INCOMPLETE_FRAME_HANDLING, 465 | g_param_spec_enum( 466 | "incompleteframehandling", 467 | "Incomplete frame handline", 468 | "Determines how the element should handle received frames where data transmission was incomplete. Incomplete frames may contain pixel intensities from old acquisitions or random data", 469 | GST_ENUM_INCOMPLETEFRAMEHANDLING_VALUES, 470 | GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_DROP, 471 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 472 | } 473 | 474 | static void gst_vimbasrc_init(GstVimbaSrc *vimbasrc) 475 | { 476 | GST_TRACE_OBJECT(vimbasrc, "init"); 477 | GST_INFO_OBJECT(vimbasrc, "gst-vimbasrc version %s", VERSION); 478 | VmbError_t result = VmbErrorSuccess; 479 | // Start the Vimba API 480 | G_LOCK(vmb_open_count); 481 | if (0 == vmb_open_count++) 482 | { 483 | result = VmbStartup(); 484 | GST_DEBUG_OBJECT(vimbasrc, "VmbStartup returned: %s", ErrorCodeToMessage(result)); 485 | if (result != VmbErrorSuccess) 486 | { 487 | GST_ERROR_OBJECT(vimbasrc, "Vimba initialization failed"); 488 | } 489 | } 490 | else 491 | { 492 | GST_DEBUG_OBJECT(vimbasrc, "VmbStartup was already called. Current open count: %u", vmb_open_count); 493 | } 494 | G_UNLOCK(vmb_open_count); 495 | 496 | // Log the used VimbaC version 497 | VmbVersionInfo_t version_info; 498 | result = VmbVersionQuery(&version_info, sizeof(version_info)); 499 | if (result == VmbErrorSuccess) 500 | { 501 | GST_INFO_OBJECT(vimbasrc, 502 | "Running with VimbaC Version %u.%u.%u", 503 | version_info.major, 504 | version_info.minor, 505 | version_info.patch); 506 | } 507 | else 508 | { 509 | GST_WARNING_OBJECT(vimbasrc, "VmbVersionQuery failed with Reason: %s", ErrorCodeToMessage(result)); 510 | } 511 | 512 | if (DiscoverGigECameras((GObject *)vimbasrc) == VmbBoolFalse) 513 | { 514 | GST_INFO_OBJECT(vimbasrc, "GigE cameras will be ignored"); 515 | } 516 | 517 | // Mark this element as a live source (disable preroll) 518 | gst_base_src_set_live(GST_BASE_SRC(vimbasrc), TRUE); 519 | gst_base_src_set_format(GST_BASE_SRC(vimbasrc), GST_FORMAT_TIME); 520 | gst_base_src_set_do_timestamp(GST_BASE_SRC(vimbasrc), TRUE); 521 | 522 | // Set property helper variables to default values 523 | GObjectClass *gobject_class = G_OBJECT_GET_CLASS(vimbasrc); 524 | 525 | vimbasrc->camera.id = g_value_dup_string( 526 | g_param_spec_get_default_value( 527 | g_object_class_find_property( 528 | gobject_class, 529 | "camera"))); 530 | vimbasrc->properties.settings_file_path = g_value_dup_string( 531 | g_param_spec_get_default_value( 532 | g_object_class_find_property( 533 | gobject_class, 534 | "settingsfile"))); 535 | vimbasrc->properties.exposuretime = g_value_get_double( 536 | g_param_spec_get_default_value( 537 | g_object_class_find_property( 538 | gobject_class, 539 | "exposuretime"))); 540 | vimbasrc->properties.exposureauto = g_value_get_enum( 541 | g_param_spec_get_default_value( 542 | g_object_class_find_property( 543 | gobject_class, 544 | "exposureauto"))); 545 | vimbasrc->properties.balancewhiteauto = g_value_get_enum( 546 | g_param_spec_get_default_value( 547 | g_object_class_find_property( 548 | gobject_class, 549 | "balancewhiteauto"))); 550 | vimbasrc->properties.gain = g_value_get_double( 551 | g_param_spec_get_default_value( 552 | g_object_class_find_property( 553 | gobject_class, 554 | "gain"))); 555 | vimbasrc->properties.offsetx = g_value_get_int( 556 | g_param_spec_get_default_value( 557 | g_object_class_find_property( 558 | gobject_class, 559 | "offsetx"))); 560 | vimbasrc->properties.offsety = g_value_get_int( 561 | g_param_spec_get_default_value( 562 | g_object_class_find_property( 563 | gobject_class, 564 | "offsety"))); 565 | vimbasrc->properties.width = g_value_get_int( 566 | g_param_spec_get_default_value( 567 | g_object_class_find_property( 568 | gobject_class, 569 | "width"))); 570 | vimbasrc->properties.height = g_value_get_int( 571 | g_param_spec_get_default_value( 572 | g_object_class_find_property( 573 | gobject_class, 574 | "height"))); 575 | vimbasrc->properties.triggerselector = g_value_get_enum( 576 | g_param_spec_get_default_value( 577 | g_object_class_find_property( 578 | gobject_class, 579 | "triggerselector"))); 580 | vimbasrc->properties.triggermode = g_value_get_enum( 581 | g_param_spec_get_default_value( 582 | g_object_class_find_property( 583 | gobject_class, 584 | "triggermode"))); 585 | vimbasrc->properties.triggersource = g_value_get_enum( 586 | g_param_spec_get_default_value( 587 | g_object_class_find_property( 588 | gobject_class, 589 | "triggersource"))); 590 | vimbasrc->properties.triggeractivation = g_value_get_enum( 591 | g_param_spec_get_default_value( 592 | g_object_class_find_property( 593 | gobject_class, 594 | "triggeractivation"))); 595 | vimbasrc->properties.incomplete_frame_handling = g_value_get_enum( 596 | g_param_spec_get_default_value( 597 | g_object_class_find_property( 598 | gobject_class, 599 | "incompleteframehandling"))); 600 | } 601 | 602 | void gst_vimbasrc_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) 603 | { 604 | GstVimbaSrc *vimbasrc = GST_vimbasrc(object); 605 | 606 | GST_DEBUG_OBJECT(vimbasrc, "set_property"); 607 | 608 | switch (property_id) 609 | { 610 | case PROP_CAMERA_ID: 611 | if (strcmp(vimbasrc->camera.id, "") != 0) 612 | { 613 | free((void *)vimbasrc->camera.id); // Free memory of old entry 614 | } 615 | vimbasrc->camera.id = g_value_dup_string(value); 616 | break; 617 | case PROP_SETTINGS_FILENAME: 618 | if (strcmp(vimbasrc->properties.settings_file_path, "") != 0) 619 | { 620 | free((void *)vimbasrc->properties.settings_file_path); // Free memory of old entry 621 | } 622 | vimbasrc->properties.settings_file_path = g_value_dup_string(value); 623 | break; 624 | case PROP_EXPOSURETIME: 625 | vimbasrc->properties.exposuretime = g_value_get_double(value); 626 | break; 627 | case PROP_EXPOSUREAUTO: 628 | vimbasrc->properties.exposureauto = g_value_get_enum(value); 629 | break; 630 | case PROP_BALANCEWHITEAUTO: 631 | vimbasrc->properties.balancewhiteauto = g_value_get_enum(value); 632 | break; 633 | case PROP_GAIN: 634 | vimbasrc->properties.gain = g_value_get_double(value); 635 | break; 636 | case PROP_OFFSETX: 637 | vimbasrc->properties.offsetx = g_value_get_int(value); 638 | break; 639 | case PROP_OFFSETY: 640 | vimbasrc->properties.offsety = g_value_get_int(value); 641 | break; 642 | case PROP_WIDTH: 643 | vimbasrc->properties.width = g_value_get_int(value); 644 | break; 645 | case PROP_HEIGHT: 646 | vimbasrc->properties.height = g_value_get_int(value); 647 | break; 648 | case PROP_TRIGGERSELECTOR: 649 | vimbasrc->properties.triggerselector = g_value_get_enum(value); 650 | break; 651 | case PROP_TRIGGERMODE: 652 | vimbasrc->properties.triggermode = g_value_get_enum(value); 653 | break; 654 | case PROP_TRIGGERSOURCE: 655 | vimbasrc->properties.triggersource = g_value_get_enum(value); 656 | break; 657 | case PROP_TRIGGERACTIVATION: 658 | vimbasrc->properties.triggeractivation = g_value_get_enum(value); 659 | break; 660 | case PROP_INCOMPLETE_FRAME_HANDLING: 661 | vimbasrc->properties.incomplete_frame_handling = g_value_get_enum(value); 662 | break; 663 | default: 664 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 665 | break; 666 | } 667 | } 668 | 669 | void gst_vimbasrc_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) 670 | { 671 | GstVimbaSrc *vimbasrc = GST_vimbasrc(object); 672 | 673 | VmbError_t result; 674 | 675 | const char *vmbfeature_value_char; 676 | double vmbfeature_value_double; 677 | VmbInt64_t vmbfeature_value_int64; 678 | 679 | GST_TRACE_OBJECT(vimbasrc, "get_property"); 680 | 681 | switch (property_id) 682 | { 683 | case PROP_CAMERA_ID: 684 | g_value_set_string(value, vimbasrc->camera.id); 685 | break; 686 | case PROP_SETTINGS_FILENAME: 687 | g_value_set_string(value, vimbasrc->properties.settings_file_path); 688 | break; 689 | case PROP_EXPOSURETIME: 690 | // TODO: Workaround for cameras with legacy "ExposureTimeAbs" feature should be replaced with a general legacy 691 | // feature name handling approach: See similar TODO above 692 | 693 | result = VmbFeatureFloatGet(vimbasrc->camera.handle, "ExposureTime", &vmbfeature_value_double); 694 | if (result == VmbErrorSuccess) 695 | { 696 | GST_DEBUG_OBJECT(vimbasrc, 697 | "Camera returned the following value for \"ExposureTime\": %f", 698 | vmbfeature_value_double); 699 | vimbasrc->properties.exposuretime = vmbfeature_value_double; 700 | } 701 | else if (result == VmbErrorNotFound) 702 | { 703 | GST_WARNING_OBJECT(vimbasrc, 704 | "Failed to get \"ExposureTime\". Return code was: %s Attempting \"ExposureTimeAbs\"", 705 | ErrorCodeToMessage(result)); 706 | result = VmbFeatureFloatGet(vimbasrc->camera.handle, "ExposureTimeAbs", &vmbfeature_value_double); 707 | if (result == VmbErrorSuccess) 708 | { 709 | GST_DEBUG_OBJECT(vimbasrc, 710 | "Camera returned the following value for \"ExposureTimeAbs\": %f", 711 | vmbfeature_value_double); 712 | vimbasrc->properties.exposuretime = vmbfeature_value_double; 713 | } 714 | else 715 | { 716 | GST_WARNING_OBJECT(vimbasrc, 717 | "Failed to read value of \"ExposureTimeAbs\" from camera. Return code was: %s", 718 | ErrorCodeToMessage(result)); 719 | } 720 | } 721 | else 722 | { 723 | GST_WARNING_OBJECT(vimbasrc, 724 | "Failed to read value of \"ExposureTime\" from camera. Return code was: %s", 725 | ErrorCodeToMessage(result)); 726 | } 727 | 728 | g_value_set_double(value, vimbasrc->properties.exposuretime); 729 | break; 730 | case PROP_EXPOSUREAUTO: 731 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "ExposureAuto", &vmbfeature_value_char); 732 | if (result == VmbErrorSuccess) 733 | { 734 | GST_DEBUG_OBJECT(vimbasrc, 735 | "Camera returned the following value for \"ExposureAuto\": %s", 736 | vmbfeature_value_char); 737 | vimbasrc->properties.exposureauto = g_enum_get_value_by_nick( 738 | g_type_class_ref(GST_ENUM_EXPOSUREAUTO_MODES), 739 | vmbfeature_value_char) 740 | ->value; 741 | } 742 | else 743 | { 744 | GST_WARNING_OBJECT(vimbasrc, 745 | "Failed to read value of \"ExposureAuto\" from camera. Return code was: %s", 746 | ErrorCodeToMessage(result)); 747 | } 748 | g_value_set_enum(value, vimbasrc->properties.exposureauto); 749 | break; 750 | case PROP_BALANCEWHITEAUTO: 751 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "BalanceWhiteAuto", &vmbfeature_value_char); 752 | if (result == VmbErrorSuccess) 753 | { 754 | GST_DEBUG_OBJECT(vimbasrc, 755 | "Camera returned the following value for \"BalanceWhiteAuto\": %s", 756 | vmbfeature_value_char); 757 | vimbasrc->properties.balancewhiteauto = g_enum_get_value_by_nick( 758 | g_type_class_ref(GST_ENUM_BALANCEWHITEAUTO_MODES), 759 | vmbfeature_value_char) 760 | ->value; 761 | } 762 | else 763 | { 764 | GST_WARNING_OBJECT(vimbasrc, 765 | "Failed to read value of \"BalanceWhiteAuto\" from camera. Return code was: %s", 766 | ErrorCodeToMessage(result)); 767 | } 768 | g_value_set_enum(value, vimbasrc->properties.balancewhiteauto); 769 | break; 770 | case PROP_GAIN: 771 | result = VmbFeatureFloatGet(vimbasrc->camera.handle, "Gain", &vmbfeature_value_double); 772 | if (result == VmbErrorSuccess) 773 | { 774 | GST_DEBUG_OBJECT(vimbasrc, 775 | "Camera returned the following value for \"Gain\": %f", 776 | vmbfeature_value_double); 777 | vimbasrc->properties.gain = vmbfeature_value_double; 778 | } 779 | else 780 | { 781 | GST_WARNING_OBJECT(vimbasrc, 782 | "Failed to read value of \"Gain\" from camera. Return code was: %s", 783 | ErrorCodeToMessage(result)); 784 | } 785 | g_value_set_double(value, vimbasrc->properties.gain); 786 | break; 787 | case PROP_OFFSETX: 788 | result = VmbFeatureIntGet(vimbasrc->camera.handle, "OffsetX", &vmbfeature_value_int64); 789 | if (result == VmbErrorSuccess) 790 | { 791 | GST_DEBUG_OBJECT(vimbasrc, 792 | "Camera returned the following value for \"OffsetX\": %lld", 793 | vmbfeature_value_int64); 794 | vimbasrc->properties.offsetx = (int)vmbfeature_value_int64; 795 | } 796 | else 797 | { 798 | GST_WARNING_OBJECT(vimbasrc, 799 | "Could not read value for \"OffsetX\". Got return code %s", 800 | ErrorCodeToMessage(result)); 801 | } 802 | g_value_set_int(value, vimbasrc->properties.offsetx); 803 | break; 804 | case PROP_OFFSETY: 805 | result = VmbFeatureIntGet(vimbasrc->camera.handle, "OffsetY", &vmbfeature_value_int64); 806 | if (result == VmbErrorSuccess) 807 | { 808 | GST_DEBUG_OBJECT(vimbasrc, 809 | "Camera returned the following value for \"OffsetY\": %lld", 810 | vmbfeature_value_int64); 811 | vimbasrc->properties.offsety = (int)vmbfeature_value_int64; 812 | } 813 | else 814 | { 815 | GST_WARNING_OBJECT(vimbasrc, 816 | "Could not read value for \"OffsetY\". Got return code %s", 817 | ErrorCodeToMessage(result)); 818 | } 819 | g_value_set_int(value, vimbasrc->properties.offsety); 820 | break; 821 | case PROP_WIDTH: 822 | result = VmbFeatureIntGet(vimbasrc->camera.handle, "Width", &vmbfeature_value_int64); 823 | if (result == VmbErrorSuccess) 824 | { 825 | GST_DEBUG_OBJECT(vimbasrc, 826 | "Camera returned the following value for \"Width\": %lld", 827 | vmbfeature_value_int64); 828 | vimbasrc->properties.width = (int)vmbfeature_value_int64; 829 | } 830 | else 831 | { 832 | GST_WARNING_OBJECT(vimbasrc, 833 | "Could not read value for \"Width\". Got return code %s", 834 | ErrorCodeToMessage(result)); 835 | } 836 | g_value_set_int(value, vimbasrc->properties.width); 837 | break; 838 | case PROP_HEIGHT: 839 | result = VmbFeatureIntGet(vimbasrc->camera.handle, "Height", &vmbfeature_value_int64); 840 | if (result == VmbErrorSuccess) 841 | { 842 | GST_DEBUG_OBJECT(vimbasrc, 843 | "Camera returned the following value for \"Height\": %lld", 844 | vmbfeature_value_int64); 845 | vimbasrc->properties.height = (int)vmbfeature_value_int64; 846 | } 847 | else 848 | { 849 | GST_WARNING_OBJECT(vimbasrc, 850 | "Could not read value for \"Height\". Got return code %s", 851 | ErrorCodeToMessage(result)); 852 | } 853 | g_value_set_int(value, vimbasrc->properties.height); 854 | break; 855 | case PROP_TRIGGERSELECTOR: 856 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "TriggerSelector", &vmbfeature_value_char); 857 | if (result == VmbErrorSuccess) 858 | { 859 | GST_DEBUG_OBJECT(vimbasrc, 860 | "Camera returned the following value for \"TriggerSelector\": %s", 861 | vmbfeature_value_char); 862 | vimbasrc->properties.exposureauto = g_enum_get_value_by_nick( 863 | g_type_class_ref(GST_ENUM_TRIGGERSELECTOR_VALUES), 864 | vmbfeature_value_char) 865 | ->value; 866 | } 867 | else 868 | { 869 | GST_WARNING_OBJECT(vimbasrc, 870 | "Failed to read value of \"TriggerSelector\" from camera. Return code was: %s", 871 | ErrorCodeToMessage(result)); 872 | } 873 | g_value_set_enum(value, vimbasrc->properties.triggerselector); 874 | break; 875 | case PROP_TRIGGERMODE: 876 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "TriggerMode", &vmbfeature_value_char); 877 | if (result == VmbErrorSuccess) 878 | { 879 | GST_DEBUG_OBJECT(vimbasrc, 880 | "Camera returned the following value for \"TriggerMode\": %s", 881 | vmbfeature_value_char); 882 | vimbasrc->properties.exposureauto = g_enum_get_value_by_nick( 883 | g_type_class_ref(GST_ENUM_TRIGGERMODE_VALUES), 884 | vmbfeature_value_char) 885 | ->value; 886 | } 887 | else 888 | { 889 | GST_WARNING_OBJECT(vimbasrc, 890 | "Failed to read value of \"TriggerMode\" from camera. Return code was: %s", 891 | ErrorCodeToMessage(result)); 892 | } 893 | g_value_set_enum(value, vimbasrc->properties.triggermode); 894 | break; 895 | case PROP_TRIGGERSOURCE: 896 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "TriggerSource", &vmbfeature_value_char); 897 | if (result == VmbErrorSuccess) 898 | { 899 | GST_DEBUG_OBJECT(vimbasrc, 900 | "Camera returned the following value for \"TriggerSource\": %s", 901 | vmbfeature_value_char); 902 | vimbasrc->properties.exposureauto = g_enum_get_value_by_nick( 903 | g_type_class_ref(GST_ENUM_TRIGGERSOURCE_VALUES), 904 | vmbfeature_value_char) 905 | ->value; 906 | } 907 | else 908 | { 909 | GST_WARNING_OBJECT(vimbasrc, 910 | "Failed to read value of \"TriggerSource\" from camera. Return code was: %s", 911 | ErrorCodeToMessage(result)); 912 | } 913 | g_value_set_enum(value, vimbasrc->properties.triggersource); 914 | break; 915 | case PROP_TRIGGERACTIVATION: 916 | result = VmbFeatureEnumGet(vimbasrc->camera.handle, "TriggerActivation", &vmbfeature_value_char); 917 | if (result == VmbErrorSuccess) 918 | { 919 | GST_DEBUG_OBJECT(vimbasrc, 920 | "Camera returned the following value for \"TriggerActivation\": %s", 921 | vmbfeature_value_char); 922 | vimbasrc->properties.exposureauto = g_enum_get_value_by_nick( 923 | g_type_class_ref(GST_ENUM_TRIGGERACTIVATION_VALUES), 924 | vmbfeature_value_char) 925 | ->value; 926 | } 927 | else 928 | { 929 | GST_WARNING_OBJECT(vimbasrc, 930 | "Failed to read value of \"TriggerActivation\" from camera. Return code was: %s", 931 | ErrorCodeToMessage(result)); 932 | } 933 | g_value_set_enum(value, vimbasrc->properties.triggeractivation); 934 | break; 935 | case PROP_INCOMPLETE_FRAME_HANDLING: 936 | g_value_set_enum(value, vimbasrc->properties.incomplete_frame_handling); 937 | break; 938 | default: 939 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 940 | break; 941 | } 942 | } 943 | 944 | void gst_vimbasrc_dispose(GObject *object) 945 | { 946 | GstVimbaSrc *vimbasrc = GST_vimbasrc(object); 947 | 948 | GST_TRACE_OBJECT(vimbasrc, "dispose"); 949 | 950 | /* clean up as possible. may be called multiple times */ 951 | 952 | G_OBJECT_CLASS(gst_vimbasrc_parent_class)->dispose(object); 953 | } 954 | 955 | void gst_vimbasrc_finalize(GObject *object) 956 | { 957 | GstVimbaSrc *vimbasrc = GST_vimbasrc(object); 958 | 959 | GST_TRACE_OBJECT(vimbasrc, "finalize"); 960 | 961 | if (vimbasrc->camera.is_connected) 962 | { 963 | VmbError_t result = VmbCameraClose(vimbasrc->camera.handle); 964 | if (result == VmbErrorSuccess) 965 | { 966 | GST_INFO_OBJECT(vimbasrc, "Closed camera %s", vimbasrc->camera.id); 967 | } 968 | else 969 | { 970 | GST_ERROR_OBJECT(vimbasrc, 971 | "Closing camera %s failed. Got error code: %s", 972 | vimbasrc->camera.id, 973 | ErrorCodeToMessage(result)); 974 | } 975 | vimbasrc->camera.is_connected = false; 976 | } 977 | 978 | G_LOCK(vmb_open_count); 979 | if (0 == --vmb_open_count) 980 | { 981 | VmbShutdown(); 982 | GST_INFO_OBJECT(vimbasrc, "Vimba API was shut down"); 983 | } 984 | else 985 | { 986 | GST_DEBUG_OBJECT(vimbasrc, "VmbShutdown not called. Current open count: %u", vmb_open_count); 987 | } 988 | G_UNLOCK(vmb_open_count); 989 | 990 | G_OBJECT_CLASS(gst_vimbasrc_parent_class)->finalize(object); 991 | } 992 | 993 | /* get caps from subclass */ 994 | static GstCaps *gst_vimbasrc_get_caps(GstBaseSrc *src, GstCaps *filter) 995 | { 996 | UNUSED(filter); // enable compilation while treating warning of unused vairable as error 997 | GstVimbaSrc *vimbasrc = GST_vimbasrc(src); 998 | 999 | GST_TRACE_OBJECT(vimbasrc, "get_caps"); 1000 | 1001 | GstCaps *caps; 1002 | caps = gst_pad_get_pad_template_caps(GST_BASE_SRC_PAD(src)); 1003 | caps = gst_caps_make_writable(caps); 1004 | 1005 | // Query the capabilities from the camera and return sensible values. If no camera is connected the template caps 1006 | // are returned 1007 | if (vimbasrc->camera.is_connected) 1008 | { 1009 | VmbInt64_t vmb_width, vmb_height; 1010 | 1011 | VmbFeatureIntGet(vimbasrc->camera.handle, "Width", &vmb_width); 1012 | VmbFeatureIntGet(vimbasrc->camera.handle, "Height", &vmb_height); 1013 | 1014 | GValue width = G_VALUE_INIT; 1015 | GValue height = G_VALUE_INIT; 1016 | 1017 | g_value_init(&width, G_TYPE_INT); 1018 | g_value_init(&height, G_TYPE_INT); 1019 | 1020 | g_value_set_int(&width, (gint)vmb_width); 1021 | 1022 | g_value_set_int(&height, (gint)vmb_height); 1023 | 1024 | GstStructure *raw_caps = gst_caps_get_structure(caps, 0); 1025 | GstStructure *bayer_caps = gst_caps_get_structure(caps, 1); 1026 | 1027 | gst_structure_set_value(raw_caps, "width", &width); 1028 | gst_structure_set_value(raw_caps, "height", &height); 1029 | gst_structure_set(raw_caps, 1030 | // TODO: Check if framerate should also be gotten from camera (e.g. as max-framerate here) 1031 | // Mark the framerate as variable because triggering might cause variable framerate 1032 | "framerate", GST_TYPE_FRACTION, 0, 1, 1033 | NULL); 1034 | 1035 | gst_structure_set_value(bayer_caps, "width", &width); 1036 | gst_structure_set_value(bayer_caps, "height", &height); 1037 | gst_structure_set(bayer_caps, 1038 | // TODO: Check if framerate should also be gotten from camera (e.g. as max-framerate here) 1039 | // Mark the framerate as variable because triggering might cause variable framerate 1040 | "framerate", GST_TYPE_FRACTION, 0, 1, 1041 | NULL); 1042 | 1043 | // Query supported pixel formats from camera and map them to GStreamer formats 1044 | GValue pixel_format_raw_list = G_VALUE_INIT; 1045 | g_value_init(&pixel_format_raw_list, GST_TYPE_LIST); 1046 | 1047 | GValue pixel_format_bayer_list = G_VALUE_INIT; 1048 | g_value_init(&pixel_format_bayer_list, GST_TYPE_LIST); 1049 | 1050 | GValue pixel_format = G_VALUE_INIT; 1051 | g_value_init(&pixel_format, G_TYPE_STRING); 1052 | 1053 | // Add all supported GStreamer format string to the reported caps 1054 | for (unsigned int i = 0; i < vimbasrc->camera.supported_formats_count; i++) 1055 | { 1056 | g_value_set_static_string(&pixel_format, vimbasrc->camera.supported_formats[i]->gst_format_name); 1057 | // TODO: Should this perhaps be done via a flag in vimba_gst_format_matches? 1058 | if (starts_with(vimbasrc->camera.supported_formats[i]->vimba_format_name, "Bayer")) 1059 | { 1060 | gst_value_list_append_value(&pixel_format_bayer_list, &pixel_format); 1061 | } 1062 | else 1063 | { 1064 | gst_value_list_append_value(&pixel_format_raw_list, &pixel_format); 1065 | } 1066 | } 1067 | gst_structure_set_value(raw_caps, "format", &pixel_format_raw_list); 1068 | gst_structure_set_value(bayer_caps, "format", &pixel_format_bayer_list); 1069 | } 1070 | 1071 | GST_DEBUG_OBJECT(vimbasrc, "returning caps: %s", gst_caps_to_string(caps)); 1072 | 1073 | return caps; 1074 | } 1075 | 1076 | /* notify the subclass of new caps */ 1077 | static gboolean gst_vimbasrc_set_caps(GstBaseSrc *src, GstCaps *caps) 1078 | { 1079 | GstVimbaSrc *vimbasrc = GST_vimbasrc(src); 1080 | 1081 | GST_TRACE_OBJECT(vimbasrc, "set_caps"); 1082 | 1083 | GST_DEBUG_OBJECT(vimbasrc, "caps requested to be set: %s", gst_caps_to_string(caps)); 1084 | 1085 | // TODO: save to assume that "format" is always exactly one format and not a list? gst_caps_is_fixed might otherwise 1086 | // be a good check and gst_caps_normalize could help make sure of it 1087 | GstStructure *structure; 1088 | structure = gst_caps_get_structure(caps, 0); 1089 | const char *gst_format = gst_structure_get_string(structure, "format"); 1090 | GST_DEBUG_OBJECT(vimbasrc, 1091 | "Looking for matching vimba pixel format to GSreamer format \"%s\"", 1092 | gst_format); 1093 | 1094 | const char *vimba_format = NULL; 1095 | for (unsigned int i = 0; i < vimbasrc->camera.supported_formats_count; i++) 1096 | { 1097 | if (strcmp(gst_format, vimbasrc->camera.supported_formats[i]->gst_format_name) == 0) 1098 | { 1099 | vimba_format = vimbasrc->camera.supported_formats[i]->vimba_format_name; 1100 | GST_DEBUG_OBJECT(vimbasrc, "Found matching vimba pixel format \"%s\"", vimba_format); 1101 | break; 1102 | } 1103 | } 1104 | if (vimba_format == NULL) 1105 | { 1106 | GST_ERROR_OBJECT(vimbasrc, 1107 | "Could not find a matching vimba pixel format for GStreamer format \"%s\"", 1108 | gst_format); 1109 | return FALSE; 1110 | } 1111 | 1112 | // Apply the requested caps to appropriate camera settings 1113 | VmbError_t result; 1114 | // Changing the pixel format can not be done while images are acquired 1115 | result = stop_image_acquisition(vimbasrc); 1116 | 1117 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, 1118 | "PixelFormat", 1119 | vimba_format); 1120 | if (result != VmbErrorSuccess) 1121 | { 1122 | GST_ERROR_OBJECT(vimbasrc, 1123 | "Could not set \"PixelFormat\" to \"%s\". Got return code \"%s\"", 1124 | vimba_format, 1125 | ErrorCodeToMessage(result)); 1126 | return FALSE; 1127 | } 1128 | 1129 | // width and height are always the value that is already written on the camera because get_caps only reports that 1130 | // value. Setting it here is not necessary as the feature values are controlled via properties of the element. 1131 | 1132 | // Buffer size needs to be increased if the new payload size is greater than the old one because that means the 1133 | // previously allocated buffers are not large enough. We simply check the size of the first buffer because they were 1134 | // all allocated with the same size 1135 | VmbInt64_t new_payload_size; 1136 | result = VmbFeatureIntGet(vimbasrc->camera.handle, "PayloadSize", &new_payload_size); 1137 | if (vimbasrc->frame_buffers[0].bufferSize < new_payload_size || result != VmbErrorSuccess) 1138 | { 1139 | // Also reallocate buffers if PayloadSize could not be read because it might have increased 1140 | GST_DEBUG_OBJECT(vimbasrc, 1141 | "PayloadSize increased. Reallocating frame buffers to ensure enough space"); 1142 | revoke_and_free_buffers(vimbasrc); 1143 | result = alloc_and_announce_buffers(vimbasrc); 1144 | } 1145 | if (result == VmbErrorSuccess) 1146 | { 1147 | result = start_image_acquisition(vimbasrc); 1148 | } 1149 | 1150 | return result == VmbErrorSuccess ? TRUE : FALSE; 1151 | } 1152 | 1153 | /* start and stop processing, ideal for opening/closing the resource */ 1154 | static gboolean gst_vimbasrc_start(GstBaseSrc *src) 1155 | { 1156 | GstVimbaSrc *vimbasrc = GST_vimbasrc(src); 1157 | 1158 | GST_TRACE_OBJECT(vimbasrc, "start"); 1159 | 1160 | // Prepare queue for filled frames from which vimbasrc_create can take them 1161 | vimbasrc->filled_frame_queue = g_async_queue_new(); 1162 | 1163 | VmbError_t result; 1164 | 1165 | // TODO: Error handling 1166 | if (!vimbasrc->camera.is_connected) 1167 | { 1168 | result = open_camera_connection(vimbasrc); 1169 | if (result != VmbErrorSuccess) 1170 | { 1171 | // Can't connect to camera. Abort execution by returning FALSE. This stops the pipeline! 1172 | return FALSE; 1173 | } 1174 | } 1175 | 1176 | // Load settings from given file if a path was given (settings_file_path is not empty) 1177 | if (strcmp(vimbasrc->properties.settings_file_path, "") != 0) 1178 | { 1179 | GST_WARNING_OBJECT(vimbasrc, 1180 | "\"%s\" was given as settingsfile. Other feature settings passed as element properties will be ignored!", 1181 | vimbasrc->properties.settings_file_path); 1182 | result = VmbCameraSettingsLoad(vimbasrc->camera.handle, 1183 | vimbasrc->properties.settings_file_path, 1184 | NULL, 1185 | 0); 1186 | if (result != VmbErrorSuccess) 1187 | { 1188 | GST_ERROR_OBJECT(vimbasrc, 1189 | "Could not load settings from file \"%s\". Got error code %s", 1190 | vimbasrc->properties.settings_file_path, 1191 | ErrorCodeToMessage(result)); 1192 | } 1193 | } 1194 | else 1195 | { 1196 | // If no settings file is given, apply the passed properties as feature settings instead 1197 | GST_DEBUG_OBJECT(vimbasrc, "No settings file given. Applying features from element properties instead"); 1198 | result = apply_feature_settings(vimbasrc); 1199 | } 1200 | 1201 | result = alloc_and_announce_buffers(vimbasrc); 1202 | if (result == VmbErrorSuccess) 1203 | { 1204 | result = start_image_acquisition(vimbasrc); 1205 | } 1206 | 1207 | // Is this necessary? 1208 | if (result == VmbErrorSuccess) 1209 | { 1210 | gst_base_src_start_complete(src, GST_FLOW_OK); 1211 | } 1212 | else 1213 | { 1214 | GST_ERROR_OBJECT(vimbasrc, "Could not start acquisition. Experienced error: %s", ErrorCodeToMessage(result)); 1215 | gst_base_src_start_complete(src, GST_FLOW_ERROR); 1216 | } 1217 | 1218 | // TODO: Is this enough error handling? 1219 | return result == VmbErrorSuccess ? TRUE : FALSE; 1220 | } 1221 | 1222 | static gboolean gst_vimbasrc_stop(GstBaseSrc *src) 1223 | { 1224 | GstVimbaSrc *vimbasrc = GST_vimbasrc(src); 1225 | 1226 | GST_TRACE_OBJECT(vimbasrc, "stop"); 1227 | 1228 | stop_image_acquisition(vimbasrc); 1229 | 1230 | revoke_and_free_buffers(vimbasrc); 1231 | 1232 | // Unref the filled frame queue so it is deleted properly 1233 | g_async_queue_unref(vimbasrc->filled_frame_queue); 1234 | 1235 | return TRUE; 1236 | } 1237 | 1238 | /* ask the subclass to create a buffer */ 1239 | static GstFlowReturn gst_vimbasrc_create(GstPushSrc *src, GstBuffer **buf) 1240 | { 1241 | GstVimbaSrc *vimbasrc = GST_vimbasrc(src); 1242 | 1243 | GST_TRACE_OBJECT(vimbasrc, "create"); 1244 | 1245 | bool submit_frame = false; 1246 | VmbFrame_t *frame; 1247 | do 1248 | { 1249 | // Wait until we can get a filled frame (added to queue in vimba_frame_callback) 1250 | frame = NULL; 1251 | GstStateChangeReturn ret; 1252 | GstState state; 1253 | do 1254 | { 1255 | // Try to get a filled frame for 10 microseconds 1256 | frame = g_async_queue_timeout_pop(vimbasrc->filled_frame_queue, 10); 1257 | // Get the current state of the element. Should return immediately since we are not doing ASYNC state changes 1258 | // but wait at most for 100 nanoseconds 1259 | ret = gst_element_get_state(GST_ELEMENT(vimbasrc), &state, NULL, 100); // timeout is given in nanoseconds 1260 | UNUSED(ret); 1261 | if (state != GST_STATE_PLAYING) 1262 | { 1263 | // The src should not create any more data. Stop waiting for frame and do not fill buf 1264 | GST_INFO_OBJECT(vimbasrc, "Element state is no longer \"GST_STATE_PLAYING\". Aborting create call."); 1265 | return GST_FLOW_FLUSHING; 1266 | } 1267 | } while (frame == NULL); 1268 | // We got a frame. Check receive status and handle incomplete frames according to 1269 | // vimbasrc->properties.incomplete_frame_handling 1270 | if (frame->receiveStatus == VmbFrameStatusIncomplete) 1271 | { 1272 | GST_WARNING_OBJECT(vimbasrc, 1273 | "Received frame with ID \"%llu\" was incomplete", frame->frameID); 1274 | if (vimbasrc->properties.incomplete_frame_handling == GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_SUBMIT) 1275 | { 1276 | GST_DEBUG_OBJECT(vimbasrc, 1277 | "Submitting incomplete frame because \"incompleteframehandling\" requested it"); 1278 | submit_frame = true; 1279 | } 1280 | else 1281 | { 1282 | // frame should be dropped -> requeue vimba buffer here since image data will not be used 1283 | GST_DEBUG_OBJECT(vimbasrc, "Dropping incomplete frame and requeueing buffer to capture queue"); 1284 | VmbCaptureFrameQueue(vimbasrc->camera.handle, frame, &vimba_frame_callback); 1285 | } 1286 | } 1287 | else 1288 | { 1289 | GST_TRACE_OBJECT(vimbasrc, "frame was complete"); 1290 | submit_frame = true; 1291 | } 1292 | } while (!submit_frame); 1293 | 1294 | // Prepare output buffer that will be filled with frame data 1295 | GstBuffer *buffer = gst_buffer_new_and_alloc(frame->bufferSize); 1296 | 1297 | // copy over frame data into the GStreamer buffer 1298 | // TODO: Investigate if we can work without copying to improve performance? 1299 | gst_buffer_fill( 1300 | buffer, 1301 | 0, 1302 | frame->buffer, 1303 | frame->bufferSize); 1304 | 1305 | // requeue frame after we copied the image data for Vimba to use again 1306 | VmbCaptureFrameQueue(vimbasrc->camera.handle, frame, &vimba_frame_callback); 1307 | 1308 | // Set filled GstBuffer as output to pass down the pipeline 1309 | *buf = buffer; 1310 | 1311 | return GST_FLOW_OK; 1312 | } 1313 | 1314 | static gboolean plugin_init(GstPlugin *plugin) 1315 | { 1316 | 1317 | /* FIXME Remember to set the rank if it's an element that is meant to be autoplugged by decodebin. */ 1318 | return gst_element_register(plugin, "vimbasrc", GST_RANK_NONE, 1319 | GST_TYPE_vimbasrc); 1320 | } 1321 | 1322 | GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, 1323 | GST_VERSION_MINOR, 1324 | vimbasrc, 1325 | DESCRIPTION, 1326 | plugin_init, 1327 | VERSION, 1328 | "LGPL", 1329 | PACKAGE, 1330 | HOMEPAGE_URL) 1331 | 1332 | /** 1333 | * @brief Opens the connection to the camera given by the ID passed as vimbasrc property and stores the resulting handle 1334 | * 1335 | * @param vimbasrc Provides access to the camera ID and holds the resulting handle 1336 | * @return VmbError_t Return status indicating errors if they occurred 1337 | */ 1338 | VmbError_t open_camera_connection(GstVimbaSrc *vimbasrc) 1339 | { 1340 | VmbError_t result = VmbCameraOpen(vimbasrc->camera.id, VmbAccessModeFull, &vimbasrc->camera.handle); 1341 | if (result == VmbErrorSuccess) 1342 | { 1343 | VmbCameraInfo_t camera_info; 1344 | VmbCameraInfoQuery(vimbasrc->camera.id, &camera_info, sizeof(camera_info)); 1345 | GST_INFO_OBJECT(vimbasrc, 1346 | "Successfully opened camera %s (model \"%s\" on interface \"%s\")", 1347 | vimbasrc->camera.id, 1348 | camera_info.modelName, 1349 | camera_info.interfaceIdString); 1350 | 1351 | // Set the GeV packet size to the highest possible value if a GigE camera is used 1352 | if (VmbErrorSuccess == VmbFeatureCommandRun(vimbasrc->camera.handle, "GVSPAdjustPacketSize")) 1353 | { 1354 | VmbBool_t is_command_done = VmbBoolFalse; 1355 | do 1356 | { 1357 | if (VmbErrorSuccess != VmbFeatureCommandIsDone(vimbasrc->camera.handle, 1358 | "GVSPAdjustPacketSize", 1359 | &is_command_done)) 1360 | { 1361 | break; 1362 | } 1363 | } while (VmbBoolFalse == is_command_done); 1364 | } 1365 | vimbasrc->camera.is_connected = true; 1366 | map_supported_pixel_formats(vimbasrc); 1367 | } 1368 | else 1369 | { 1370 | GST_ERROR_OBJECT(vimbasrc, 1371 | "Could not open camera %s. Got error code: %s", 1372 | vimbasrc->camera.id, 1373 | ErrorCodeToMessage(result)); 1374 | vimbasrc->camera.is_connected = false; 1375 | // TODO: List available cameras in this case? 1376 | // TODO: Can we signal an error to the pipeline to stop immediately? 1377 | } 1378 | vimbasrc->camera.is_acquiring = false; 1379 | return result; 1380 | } 1381 | 1382 | /** 1383 | * @brief Applies the values defiend in the vimbasrc properties to their corresponding Vimba camera features 1384 | * 1385 | * @param vimbasrc Provides access to the camera handle used for the Vimba calls and holds the desired values for the 1386 | * modified features 1387 | * @return VmbError_t Return status indicating errors if they occurred 1388 | */ 1389 | VmbError_t apply_feature_settings(GstVimbaSrc *vimbasrc) 1390 | { 1391 | bool was_acquiring = vimbasrc->camera.is_acquiring; 1392 | if (vimbasrc->camera.is_acquiring) 1393 | { 1394 | GST_DEBUG_OBJECT(vimbasrc, "Camera was acquiring. Stopping to change feature settings"); 1395 | stop_image_acquisition(vimbasrc); 1396 | } 1397 | GEnumValue *enum_entry; 1398 | 1399 | // exposure time 1400 | // TODO: Workaround for cameras with legacy "ExposureTimeAbs" feature should be replaced with a general legacy 1401 | // feature name handling approach: A static table maps each property, e.g. "exposuretime", to a list of (feature 1402 | // name, set function, get function) pairs, e.g. [("ExposureTime", setExposureTime, getExposureTime), 1403 | // ("ExposureTimeAbs", setExposureTimeAbs, getExposureTimeAbs)]. On startup, the feature list of the connected 1404 | // camera obtained from VmbFeaturesList() is used to determine which set/get function to use. 1405 | 1406 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"ExposureTime\" to %f", vimbasrc->properties.exposuretime); 1407 | VmbError_t result = VmbFeatureFloatSet(vimbasrc->camera.handle, "ExposureTime", vimbasrc->properties.exposuretime); 1408 | if (result == VmbErrorSuccess) 1409 | { 1410 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1411 | } 1412 | else if (result == VmbErrorNotFound) 1413 | { 1414 | GST_WARNING_OBJECT(vimbasrc, 1415 | "Failed to set \"ExposureTime\" to %f. Return code was: %s Attempting \"ExposureTimeAbs\"", 1416 | vimbasrc->properties.exposuretime, 1417 | ErrorCodeToMessage(result)); 1418 | result = VmbFeatureFloatSet(vimbasrc->camera.handle, "ExposureTimeAbs", vimbasrc->properties.exposuretime); 1419 | if (result == VmbErrorSuccess) 1420 | { 1421 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1422 | } 1423 | else 1424 | { 1425 | GST_WARNING_OBJECT(vimbasrc, 1426 | "Failed to set \"ExposureTimeAbs\" to %f. Return code was: %s", 1427 | vimbasrc->properties.exposuretime, 1428 | ErrorCodeToMessage(result)); 1429 | } 1430 | } 1431 | else 1432 | { 1433 | GST_WARNING_OBJECT(vimbasrc, 1434 | "Failed to set \"ExposureTime\" to %f. Return code was: %s", 1435 | vimbasrc->properties.exposuretime, 1436 | ErrorCodeToMessage(result)); 1437 | } 1438 | 1439 | // Exposure Auto 1440 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_EXPOSUREAUTO_MODES), vimbasrc->properties.exposureauto); 1441 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"ExposureAuto\" to %s", enum_entry->value_nick); 1442 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "ExposureAuto", enum_entry->value_nick); 1443 | if (result == VmbErrorSuccess) 1444 | { 1445 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1446 | } 1447 | else 1448 | { 1449 | GST_WARNING_OBJECT(vimbasrc, 1450 | "Failed to set \"ExposureAuto\" to %s. Return code was: %s", 1451 | enum_entry->value_nick, 1452 | ErrorCodeToMessage(result)); 1453 | } 1454 | 1455 | // Auto whitebalance 1456 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_BALANCEWHITEAUTO_MODES), 1457 | vimbasrc->properties.balancewhiteauto); 1458 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"BalanceWhiteAuto\" to %s", enum_entry->value_nick); 1459 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "BalanceWhiteAuto", enum_entry->value_nick); 1460 | if (result == VmbErrorSuccess) 1461 | { 1462 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1463 | } 1464 | else 1465 | { 1466 | GST_WARNING_OBJECT(vimbasrc, 1467 | "Failed to set \"BalanceWhiteAuto\" to %s. Return code was: %s", 1468 | enum_entry->value_nick, 1469 | ErrorCodeToMessage(result)); 1470 | } 1471 | 1472 | // gain 1473 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"Gain\" to %f", vimbasrc->properties.gain); 1474 | result = VmbFeatureFloatSet(vimbasrc->camera.handle, "Gain", vimbasrc->properties.gain); 1475 | if (result == VmbErrorSuccess) 1476 | { 1477 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1478 | } 1479 | else 1480 | { 1481 | GST_WARNING_OBJECT(vimbasrc, 1482 | "Failed to set \"Gain\" to %f. Return code was: %s", 1483 | vimbasrc->properties.gain, 1484 | ErrorCodeToMessage(result)); 1485 | } 1486 | 1487 | result = set_roi(vimbasrc); 1488 | 1489 | result = apply_trigger_settings(vimbasrc); 1490 | 1491 | if (was_acquiring) 1492 | { 1493 | GST_DEBUG_OBJECT(vimbasrc, "Camera was acquiring before changing feature settings. Restarting."); 1494 | result = start_image_acquisition(vimbasrc); 1495 | } 1496 | 1497 | return result; 1498 | } 1499 | 1500 | /** 1501 | * @brief Helper function to set Width, Height, OffsetX and OffsetY feature in correct order to define the region of 1502 | * interest (ROI) on the sensor. 1503 | * 1504 | * The values for setting the ROI are defined as GStreamer properties of the vimbasrc element. If INT_MAX are used for 1505 | * the width/height property (the default value) the full corresponding sensor size for that feature is used. 1506 | * 1507 | * @param vimbasrc Provides access to the camera handle used for the Vimba calls and holds the desired values for the 1508 | * modified features 1509 | * @return VmbError_t Return status indicating errors if they occurred 1510 | */ 1511 | VmbError_t set_roi(GstVimbaSrc *vimbasrc) 1512 | { 1513 | // TODO: Improve error handling (Perhaps more explicit allowed values are enough?) Early exit on errors? 1514 | 1515 | // Reset OffsetX and OffsetY to 0 so that full sensor width is usable for width/height 1516 | VmbError_t result; 1517 | GST_DEBUG_OBJECT(vimbasrc, "Temporarily resetting \"OffsetX\" and \"OffsetY\" to 0"); 1518 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "OffsetX", 0); 1519 | if (result != VmbErrorSuccess) 1520 | { 1521 | GST_WARNING_OBJECT(vimbasrc, 1522 | "Failed to set \"OffsetX\" to 0. Return code was: %s", 1523 | ErrorCodeToMessage(result)); 1524 | } 1525 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "OffsetY", 0); 1526 | if (result != VmbErrorSuccess) 1527 | { 1528 | GST_WARNING_OBJECT(vimbasrc, 1529 | "Failed to set \"OffsetY\" to 0. Return code was: %s", 1530 | ErrorCodeToMessage(result)); 1531 | } 1532 | 1533 | VmbInt64_t vmb_width; 1534 | result = VmbFeatureIntRangeQuery(vimbasrc->camera.handle, "Width", NULL, &vmb_width); 1535 | 1536 | // Set Width to full sensor if no explicit width was set 1537 | if (vimbasrc->properties.width == INT_MAX) 1538 | { 1539 | GST_DEBUG_OBJECT(vimbasrc, 1540 | "Setting \"Width\" to full width. Got sensor width \"%lld\" (Return Code %s)", 1541 | vmb_width, 1542 | ErrorCodeToMessage(result)); 1543 | g_object_set(vimbasrc, "width", (int)vmb_width, NULL); 1544 | } 1545 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"Width\" to %d", vimbasrc->properties.width); 1546 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "Width", vimbasrc->properties.width); 1547 | if (result == VmbErrorSuccess) 1548 | { 1549 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1550 | } 1551 | else 1552 | { 1553 | GST_WARNING_OBJECT(vimbasrc, 1554 | "Failed to set \"Width\" to value \"%d\". Return code was: %s", 1555 | vimbasrc->properties.width, 1556 | ErrorCodeToMessage(result)); 1557 | } 1558 | 1559 | VmbInt64_t vmb_height; 1560 | result = VmbFeatureIntRangeQuery(vimbasrc->camera.handle, "Height", NULL, &vmb_height); 1561 | // Set Height to full sensor if no explicit height was set 1562 | if (vimbasrc->properties.height == INT_MAX) 1563 | { 1564 | GST_DEBUG_OBJECT(vimbasrc, 1565 | "Setting \"Height\" to full height. Got sensor height \"%lld\" (Return Code %s)", 1566 | vmb_height, 1567 | ErrorCodeToMessage(result)); 1568 | g_object_set(vimbasrc, "height", (int)vmb_height, NULL); 1569 | } 1570 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"Height\" to %d", vimbasrc->properties.height); 1571 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "Height", vimbasrc->properties.height); 1572 | if (result == VmbErrorSuccess) 1573 | { 1574 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1575 | } 1576 | else 1577 | { 1578 | GST_WARNING_OBJECT(vimbasrc, 1579 | "Failed to set \"Height\" to value \"%d\". Return code was: %s", 1580 | vimbasrc->properties.height, 1581 | ErrorCodeToMessage(result)); 1582 | } 1583 | // offsetx 1584 | if (vimbasrc->properties.offsetx == -1) { 1585 | VmbInt64_t vmb_offsetx = (vmb_width - vimbasrc->properties.width) >> 1; 1586 | GST_DEBUG_OBJECT(vimbasrc, "ROI centering along x-axis requested. Calculated offsetx=%lld", 1587 | vmb_offsetx); 1588 | g_object_set(vimbasrc, "offsetx", (int) vmb_offsetx, NULL); 1589 | } 1590 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"OffsetX\" to %d", vimbasrc->properties.offsetx); 1591 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "OffsetX", vimbasrc->properties.offsetx); 1592 | if (result == VmbErrorSuccess) 1593 | { 1594 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1595 | } 1596 | else 1597 | { 1598 | GST_WARNING_OBJECT(vimbasrc, 1599 | "Failed to set \"OffsetX\" to value \"%d\". Return code was: %s", 1600 | vimbasrc->properties.offsetx, 1601 | ErrorCodeToMessage(result)); 1602 | } 1603 | 1604 | // offsety 1605 | if (vimbasrc->properties.offsety == -1) { 1606 | VmbInt64_t vmb_offsety = (vmb_height - vimbasrc->properties.height) >> 1; 1607 | GST_DEBUG_OBJECT(vimbasrc, "ROI centering along y-axis requested. Calculated offsety=%lld", 1608 | vmb_offsety); 1609 | g_object_set(vimbasrc, "offsety", (int)vmb_offsety, NULL); 1610 | } 1611 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"OffsetY\" to %d", vimbasrc->properties.offsety); 1612 | result = VmbFeatureIntSet(vimbasrc->camera.handle, "OffsetY", vimbasrc->properties.offsety); 1613 | if (result == VmbErrorSuccess) 1614 | { 1615 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1616 | } 1617 | else 1618 | { 1619 | GST_WARNING_OBJECT(vimbasrc, 1620 | "Failed to set \"OffsetY\" to value \"%d\". Return code was: %s", 1621 | vimbasrc->properties.offsety, 1622 | ErrorCodeToMessage(result)); 1623 | } 1624 | return result; 1625 | } 1626 | 1627 | /** 1628 | * @brief Helper function to apply values to TriggerSelector, TriggerMode, TriggerSource and TriggerActivation in the 1629 | * correct order 1630 | * 1631 | * Trigger settings are always applied in the order 1632 | * 1. TriggerSelector 1633 | * 2. TriggerActivation 1634 | * 3. TriggerSource 1635 | * 4. TriggerMode 1636 | * 1637 | * @param vimbasrc Provides access to the camera handle used for the Vimba calls and holds the desired values for the 1638 | * modified features 1639 | * @return VmbError_t Return status indicating errors if they occurred 1640 | */ 1641 | VmbError_t apply_trigger_settings(GstVimbaSrc *vimbasrc) 1642 | { 1643 | GST_DEBUG_OBJECT(vimbasrc, "Applying trigger settings"); 1644 | 1645 | VmbError_t result = VmbErrorSuccess; 1646 | GEnumValue *enum_entry; 1647 | 1648 | // TODO: Should the function start by disabling triggering for all TriggerSelectors to make sure only one is 1649 | // enabled after the function is done? 1650 | 1651 | // TriggerSelector 1652 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_TRIGGERSELECTOR_VALUES), 1653 | vimbasrc->properties.triggerselector); 1654 | if (enum_entry->value == GST_VIMBASRC_TRIGGERSELECTOR_UNCHANGED) 1655 | { 1656 | GST_DEBUG_OBJECT(vimbasrc, 1657 | "\"TriggerSelector\" is set to %s. Not changing camera value", enum_entry->value_nick); 1658 | } 1659 | else 1660 | { 1661 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"TriggerSelector\" to %s", enum_entry->value_nick); 1662 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "TriggerSelector", enum_entry->value_nick); 1663 | if (result == VmbErrorSuccess) 1664 | { 1665 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1666 | } 1667 | else 1668 | { 1669 | GST_ERROR_OBJECT(vimbasrc, 1670 | "Failed to set \"TriggerSelector\" to %s. Return code was: %s", 1671 | enum_entry->value_nick, 1672 | ErrorCodeToMessage(result)); 1673 | if (result == VmbErrorInvalidValue) 1674 | { 1675 | log_available_enum_entries(vimbasrc, "TriggerSelector"); 1676 | } 1677 | } 1678 | } 1679 | 1680 | // TriggerActivation 1681 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_TRIGGERACTIVATION_VALUES), 1682 | vimbasrc->properties.triggeractivation); 1683 | if (enum_entry->value == GST_VIMBASRC_TRIGGERACTIVATION_UNCHANGED) 1684 | { 1685 | GST_DEBUG_OBJECT(vimbasrc, 1686 | "\"TriggerActivation\" is set to %s. Not changing camera value", enum_entry->value_nick); 1687 | } 1688 | else 1689 | { 1690 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"TriggerActivation\" to %s", enum_entry->value_nick); 1691 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "TriggerActivation", enum_entry->value_nick); 1692 | if (result == VmbErrorSuccess) 1693 | { 1694 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1695 | } 1696 | else 1697 | { 1698 | GST_ERROR_OBJECT(vimbasrc, 1699 | "Failed to set \"TriggerActivation\" to %s. Return code was: %s", 1700 | enum_entry->value_nick, 1701 | ErrorCodeToMessage(result)); 1702 | if (result == VmbErrorInvalidValue) 1703 | { 1704 | log_available_enum_entries(vimbasrc, "TriggerActivation"); 1705 | } 1706 | } 1707 | } 1708 | 1709 | // TriggerSource 1710 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_TRIGGERSOURCE_VALUES), 1711 | vimbasrc->properties.triggersource); 1712 | if (enum_entry->value == GST_VIMBASRC_TRIGGERSOURCE_UNCHANGED) 1713 | { 1714 | 1715 | GST_DEBUG_OBJECT(vimbasrc, 1716 | "\"TriggerSource\" is set to %s. Not changing camera value", enum_entry->value_nick); 1717 | } 1718 | else 1719 | { 1720 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"TriggerSource\" to %s", enum_entry->value_nick); 1721 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "TriggerSource", enum_entry->value_nick); 1722 | if (result == VmbErrorSuccess) 1723 | { 1724 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1725 | } 1726 | else 1727 | { 1728 | GST_ERROR_OBJECT(vimbasrc, 1729 | "Failed to set \"TriggerSource\" to %s. Return code was: %s", 1730 | enum_entry->value_nick, 1731 | ErrorCodeToMessage(result)); 1732 | if (result == VmbErrorInvalidValue) 1733 | { 1734 | log_available_enum_entries(vimbasrc, "TriggerSource"); 1735 | } 1736 | } 1737 | } 1738 | 1739 | // TriggerMode 1740 | enum_entry = g_enum_get_value(g_type_class_ref(GST_ENUM_TRIGGERMODE_VALUES), 1741 | vimbasrc->properties.triggermode); 1742 | if (enum_entry->value == GST_VIMBASRC_TRIGGERMODE_UNCHANGED) 1743 | { 1744 | GST_DEBUG_OBJECT(vimbasrc, 1745 | "\"TriggerMode\" is set to %s. Not changing camera value", enum_entry->value_nick); 1746 | } 1747 | else 1748 | { 1749 | GST_DEBUG_OBJECT(vimbasrc, "Setting \"TriggerMode\" to %s", enum_entry->value_nick); 1750 | result = VmbFeatureEnumSet(vimbasrc->camera.handle, "TriggerMode", enum_entry->value_nick); 1751 | if (result == VmbErrorSuccess) 1752 | { 1753 | GST_DEBUG_OBJECT(vimbasrc, "Setting was changed successfully"); 1754 | } 1755 | else 1756 | { 1757 | GST_ERROR_OBJECT(vimbasrc, 1758 | "Failed to set \"TriggerMode\" to %s. Return code was: %s", 1759 | enum_entry->value_nick, 1760 | ErrorCodeToMessage(result)); 1761 | } 1762 | } 1763 | 1764 | return result; 1765 | } 1766 | 1767 | /** 1768 | * @brief Gets the PayloadSize from the connected camera, allocates and announces frame buffers for capturing 1769 | * 1770 | * @param vimbasrc Provides the camera handle used for the Vimba calls and holds the frame buffers 1771 | * @return VmbError_t Return status indicating errors if they occurred 1772 | */ 1773 | VmbError_t alloc_and_announce_buffers(GstVimbaSrc *vimbasrc) 1774 | { 1775 | VmbInt64_t payload_size; 1776 | VmbError_t result = VmbFeatureIntGet(vimbasrc->camera.handle, "PayloadSize", &payload_size); 1777 | if (result == VmbErrorSuccess) 1778 | { 1779 | GST_DEBUG_OBJECT(vimbasrc, "Got \"PayloadSize\" of: %llu", payload_size); 1780 | GST_DEBUG_OBJECT(vimbasrc, "Allocating and announcing %d vimba frames", NUM_VIMBA_FRAMES); 1781 | for (int i = 0; i < NUM_VIMBA_FRAMES; i++) 1782 | { 1783 | vimbasrc->frame_buffers[i].buffer = (unsigned char *)malloc((VmbUint32_t)payload_size); 1784 | if (NULL == vimbasrc->frame_buffers[i].buffer) 1785 | { 1786 | result = VmbErrorResources; 1787 | break; 1788 | } 1789 | vimbasrc->frame_buffers[i].bufferSize = (VmbUint32_t)payload_size; 1790 | vimbasrc->frame_buffers[i].context[0] = vimbasrc->filled_frame_queue; 1791 | 1792 | // Announce Frame 1793 | result = VmbFrameAnnounce(vimbasrc->camera.handle, 1794 | &vimbasrc->frame_buffers[i], 1795 | (VmbUint32_t)sizeof(VmbFrame_t)); 1796 | if (result != VmbErrorSuccess) 1797 | { 1798 | free(vimbasrc->frame_buffers[i].buffer); 1799 | memset(&vimbasrc->frame_buffers[i], 0, sizeof(VmbFrame_t)); 1800 | break; 1801 | } 1802 | } 1803 | } 1804 | return result; 1805 | } 1806 | 1807 | /** 1808 | * @brief Revokes frame buffers, frees their memory and overwrites old pointers with 0 1809 | * 1810 | * @param vimbasrc Provides the camera handle used for the Vimba calls and the frame buffers 1811 | */ 1812 | void revoke_and_free_buffers(GstVimbaSrc *vimbasrc) 1813 | { 1814 | for (int i = 0; i < NUM_VIMBA_FRAMES; i++) 1815 | { 1816 | if (NULL != vimbasrc->frame_buffers[i].buffer) 1817 | { 1818 | VmbFrameRevoke(vimbasrc->camera.handle, &vimbasrc->frame_buffers[i]); 1819 | free(vimbasrc->frame_buffers[i].buffer); 1820 | memset(&vimbasrc->frame_buffers[i], 0, sizeof(VmbFrame_t)); 1821 | } 1822 | } 1823 | } 1824 | 1825 | /** 1826 | * @brief Starts the capture engine, queues Vimba frames and runs the AcquisitionStart command feature. Frame buffers 1827 | * must be allocated before running this function. 1828 | * 1829 | * @param vimbasrc Provides the camera handle used for the Vimba calls and access to the queued frame buffers 1830 | * @return VmbError_t Return status indicating errors if they occurred 1831 | */ 1832 | VmbError_t start_image_acquisition(GstVimbaSrc *vimbasrc) 1833 | { 1834 | // Start Capture Engine 1835 | GST_DEBUG_OBJECT(vimbasrc, "Starting the capture engine"); 1836 | VmbError_t result = VmbCaptureStart(vimbasrc->camera.handle); 1837 | if (result == VmbErrorSuccess) 1838 | { 1839 | GST_DEBUG_OBJECT(vimbasrc, "Queueing the vimba frames"); 1840 | for (int i = 0; i < NUM_VIMBA_FRAMES; i++) 1841 | { 1842 | // Queue Frame 1843 | result = VmbCaptureFrameQueue(vimbasrc->camera.handle, &vimbasrc->frame_buffers[i], &vimba_frame_callback); 1844 | if (VmbErrorSuccess != result) 1845 | { 1846 | break; 1847 | } 1848 | } 1849 | 1850 | if (VmbErrorSuccess == result) 1851 | { 1852 | // Start Acquisition 1853 | GST_DEBUG_OBJECT(vimbasrc, "Running \"AcquisitionStart\" feature"); 1854 | result = VmbFeatureCommandRun(vimbasrc->camera.handle, "AcquisitionStart"); 1855 | VmbBool_t acquisition_start_done = VmbBoolFalse; 1856 | do 1857 | { 1858 | if (VmbErrorSuccess != VmbFeatureCommandIsDone(vimbasrc->camera.handle, 1859 | "AcquisitionStart", 1860 | &acquisition_start_done)) 1861 | { 1862 | break; 1863 | } 1864 | } while (VmbBoolFalse == acquisition_start_done); 1865 | vimbasrc->camera.is_acquiring = true; 1866 | } 1867 | } 1868 | return result; 1869 | } 1870 | 1871 | /** 1872 | * @brief Runs the AcquisitionStop command feature, stops the capture engine and flushes the capture queue 1873 | * 1874 | * @param vimbasrc Provides the camera handle which is used for the Vimba function calls 1875 | * @return VmbError_t Return status indicating errors if they occurred 1876 | */ 1877 | VmbError_t stop_image_acquisition(GstVimbaSrc *vimbasrc) 1878 | { 1879 | // Stop Acquisition 1880 | GST_DEBUG_OBJECT(vimbasrc, "Running \"AcquisitionStop\" feature"); 1881 | VmbError_t result = VmbFeatureCommandRun(vimbasrc->camera.handle, "AcquisitionStop"); 1882 | VmbBool_t acquisition_stop_done = VmbBoolFalse; 1883 | do 1884 | { 1885 | if (VmbErrorSuccess != VmbFeatureCommandIsDone(vimbasrc->camera.handle, 1886 | "AcquisitionStop", 1887 | &acquisition_stop_done)) 1888 | { 1889 | break; 1890 | } 1891 | } while (VmbBoolFalse == acquisition_stop_done); 1892 | vimbasrc->camera.is_acquiring = false; 1893 | 1894 | // Stop Capture Engine 1895 | GST_DEBUG_OBJECT(vimbasrc, "Stopping the capture engine"); 1896 | result = VmbCaptureEnd(vimbasrc->camera.handle); 1897 | 1898 | // Flush the capture queue 1899 | GST_DEBUG_OBJECT(vimbasrc, "Flushing the capture queue"); 1900 | VmbCaptureQueueFlush(vimbasrc->camera.handle); 1901 | 1902 | return result; 1903 | } 1904 | 1905 | void VMB_CALL vimba_frame_callback(const VmbHandle_t camera_handle, VmbFrame_t *frame) 1906 | { 1907 | UNUSED(camera_handle); // enable compilation while treating warning of unused vairable as error 1908 | GST_TRACE("Got Frame"); 1909 | g_async_queue_push(frame->context[0], frame); // context[0] holds vimbasrc->filled_frame_queue 1910 | 1911 | // requeueing the frame is done after it was consumed in vimbasrc_create 1912 | } 1913 | 1914 | /** 1915 | * @brief Get the Vimba pixel formats the camera supports and create a mapping of them to compatible GStreamer formats 1916 | * (stored in vimbasrc->camera.supported_formats) 1917 | * 1918 | * @param vimbasrc provides the camera handle and holds the generated mapping 1919 | */ 1920 | void map_supported_pixel_formats(GstVimbaSrc *vimbasrc) 1921 | { 1922 | // get number of supported formats from the camera 1923 | VmbUint32_t camera_format_count; 1924 | VmbFeatureEnumRangeQuery( 1925 | vimbasrc->camera.handle, 1926 | "PixelFormat", 1927 | NULL, 1928 | 0, 1929 | &camera_format_count); 1930 | 1931 | // get the vimba format string supported by the camera 1932 | const char **supported_formats = malloc(camera_format_count * sizeof(char *)); 1933 | VmbFeatureEnumRangeQuery( 1934 | vimbasrc->camera.handle, 1935 | "PixelFormat", 1936 | supported_formats, 1937 | camera_format_count, 1938 | NULL); 1939 | 1940 | GST_DEBUG_OBJECT(vimbasrc, "Camera returned %d supported formats", camera_format_count); 1941 | VmbBool_t is_available; 1942 | for (unsigned int i = 0; i < camera_format_count; i++) 1943 | { 1944 | VmbFeatureEnumIsAvailable(vimbasrc->camera.handle, "PixelFormat", supported_formats[i], &is_available); 1945 | if (is_available) 1946 | { 1947 | const VimbaGstFormatMatch_t *format_map = gst_format_from_vimba_format(supported_formats[i]); 1948 | if (format_map != NULL) 1949 | { 1950 | GST_DEBUG_OBJECT(vimbasrc, 1951 | "Vimba format \"%s\" corresponds to GStreamer format \"%s\"", 1952 | supported_formats[i], 1953 | format_map->gst_format_name); 1954 | vimbasrc->camera.supported_formats[vimbasrc->camera.supported_formats_count] = format_map; 1955 | vimbasrc->camera.supported_formats_count++; 1956 | } 1957 | else 1958 | { 1959 | GST_DEBUG_OBJECT(vimbasrc, 1960 | "No corresponding GStreamer format found for vimba format \"%s\"", 1961 | supported_formats[i]); 1962 | } 1963 | } 1964 | else 1965 | { 1966 | GST_DEBUG_OBJECT(vimbasrc, "Reported format \"%s\" is not available", supported_formats[i]); 1967 | } 1968 | } 1969 | free((void *)supported_formats); 1970 | } 1971 | 1972 | void log_available_enum_entries(GstVimbaSrc *vimbasrc, const char *feat_name) 1973 | { 1974 | VmbUint32_t trigger_source_count; 1975 | VmbFeatureEnumRangeQuery( 1976 | vimbasrc->camera.handle, 1977 | feat_name, 1978 | NULL, 1979 | 0, 1980 | &trigger_source_count); 1981 | 1982 | const char **trigger_source_values = malloc(trigger_source_count * sizeof(char *)); 1983 | VmbFeatureEnumRangeQuery( 1984 | vimbasrc->camera.handle, 1985 | feat_name, 1986 | trigger_source_values, 1987 | trigger_source_count, 1988 | NULL); 1989 | 1990 | VmbBool_t is_available; 1991 | GST_ERROR_OBJECT(vimbasrc, "The following values for the \"%s\" feature are available", feat_name); 1992 | for (unsigned int i = 0; i < trigger_source_count; i++) 1993 | { 1994 | VmbFeatureEnumIsAvailable(vimbasrc->camera.handle, 1995 | feat_name, 1996 | trigger_source_values[i], 1997 | &is_available); 1998 | if (is_available) 1999 | { 2000 | GST_ERROR_OBJECT(vimbasrc, " %s", trigger_source_values[i]); 2001 | } 2002 | } 2003 | 2004 | free((void *)trigger_source_values); 2005 | } 2006 | -------------------------------------------------------------------------------- /src/gstvimbasrc.h: -------------------------------------------------------------------------------- 1 | /* GStreamer 2 | * Copyright (C) 2021 Allied Vision Technologies GmbH 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Library General Public 6 | * License version 2.0 as published by the Free Software Foundation. 7 | * 8 | * This library is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | * Library General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Library General Public 14 | * License along with this library; if not, write to the 15 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 16 | * Boston, MA 02110-1301, USA. 17 | */ 18 | 19 | #ifndef _GST_vimbasrc_H_ 20 | #define _GST_vimbasrc_H_ 21 | 22 | #include "pixelformats.h" 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | G_BEGIN_DECLS 33 | 34 | #define GST_TYPE_vimbasrc (gst_vimbasrc_get_type()) 35 | #define GST_vimbasrc(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_vimbasrc, GstVimbaSrc)) 36 | #define GST_vimbasrc_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_vimbasrc, GstVimbaSrcClass)) 37 | #define GST_IS_vimbasrc(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_vimbasrc)) 38 | #define GST_IS_vimbasrc_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_vimbasrc)) 39 | 40 | /* Allowed values for "Auto" camera Features */ 41 | typedef enum 42 | { 43 | GST_VIMBASRC_AUTOFEATURE_OFF, 44 | GST_VIMBASRC_AUTOFEATURE_ONCE, 45 | GST_VIMBASRC_AUTOFEATURE_CONTINUOUS 46 | } GstVimbasrcAutoFeatureValue; 47 | 48 | // Possible values for TriggerSelector feature 49 | typedef enum 50 | { 51 | GST_VIMBASRC_TRIGGERSELECTOR_UNCHANGED, 52 | GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_START, 53 | GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_END, 54 | GST_VIMBASRC_TRIGGERSELECTOR_ACQUISITION_ACTIVE, 55 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_START, 56 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_END, 57 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_ACTIVE, 58 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_START, 59 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_END, 60 | GST_VIMBASRC_TRIGGERSELECTOR_FRAME_BURST_ACTIVE, 61 | GST_VIMBASRC_TRIGGERSELECTOR_LINE_START, 62 | GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_START, 63 | GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_END, 64 | GST_VIMBASRC_TRIGGERSELECTOR_EXPOSURE_ACTIVE 65 | } GstVimbasrcTriggerSelectorValue; 66 | 67 | // Possible values for TriggerMode feature 68 | typedef enum 69 | { 70 | GST_VIMBASRC_TRIGGERMODE_UNCHANGED, 71 | GST_VIMBASRC_TRIGGERMODE_OFF, 72 | GST_VIMBASRC_TRIGGERMODE_ON 73 | } GstVimbasrcTriggerModeValue; 74 | 75 | // Possible values for the TriggerSource feature 76 | typedef enum 77 | { 78 | GST_VIMBASRC_TRIGGERSOURCE_UNCHANGED, 79 | GST_VIMBASRC_TRIGGERSOURCE_SOFTWARE, 80 | GST_VIMBASRC_TRIGGERSOURCE_LINE0, 81 | GST_VIMBASRC_TRIGGERSOURCE_LINE1, 82 | GST_VIMBASRC_TRIGGERSOURCE_LINE2, 83 | GST_VIMBASRC_TRIGGERSOURCE_LINE3, 84 | GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT0, 85 | GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT1, 86 | GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT2, 87 | GST_VIMBASRC_TRIGGERSOURCE_USER_OUTPUT3, 88 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER0_START, 89 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER1_START, 90 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER2_START, 91 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER3_START, 92 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER0_END, 93 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER1_END, 94 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER2_END, 95 | GST_VIMBASRC_TRIGGERSOURCE_COUNTER3_END, 96 | GST_VIMBASRC_TRIGGERSOURCE_TIMER0_START, 97 | GST_VIMBASRC_TRIGGERSOURCE_TIMER1_START, 98 | GST_VIMBASRC_TRIGGERSOURCE_TIMER2_START, 99 | GST_VIMBASRC_TRIGGERSOURCE_TIMER3_START, 100 | GST_VIMBASRC_TRIGGERSOURCE_TIMER0_END, 101 | GST_VIMBASRC_TRIGGERSOURCE_TIMER1_END, 102 | GST_VIMBASRC_TRIGGERSOURCE_TIMER2_END, 103 | GST_VIMBASRC_TRIGGERSOURCE_TIMER3_END, 104 | GST_VIMBASRC_TRIGGERSOURCE_ENCODER0, 105 | GST_VIMBASRC_TRIGGERSOURCE_ENCODER1, 106 | GST_VIMBASRC_TRIGGERSOURCE_ENCODER2, 107 | GST_VIMBASRC_TRIGGERSOURCE_ENCODER3, 108 | GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK0, 109 | GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK1, 110 | GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK2, 111 | GST_VIMBASRC_TRIGGERSOURCE_LOGIC_BLOCK3, 112 | GST_VIMBASRC_TRIGGERSOURCE_ACTION0, 113 | GST_VIMBASRC_TRIGGERSOURCE_ACTION1, 114 | GST_VIMBASRC_TRIGGERSOURCE_ACTION2, 115 | GST_VIMBASRC_TRIGGERSOURCE_ACTION3, 116 | GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER0, 117 | GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER1, 118 | GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER2, 119 | GST_VIMBASRC_TRIGGERSOURCE_LINK_TRIGGER3 120 | } GstVimbasrcTriggerSourceValue; 121 | 122 | // Possible values for TriggerActivation feature 123 | typedef enum 124 | { 125 | GST_VIMBASRC_TRIGGERACTIVATION_UNCHANGED, 126 | GST_VIMBASRC_TRIGGERACTIVATION_RISING_EDGE, 127 | GST_VIMBASRC_TRIGGERACTIVATION_FALLING_EDGE, 128 | GST_VIMBASRC_TRIGGERACTIVATION_ANY_EDGE, 129 | GST_VIMBASRC_TRIGGERACTIVATION_LEVEL_HIGH, 130 | GST_VIMBASRC_TRIGGERACTIVATION_LEVEL_LOW 131 | } GstVimbasrcTriggerActivationValue; 132 | 133 | // Implemented handling approaches for incomplete frames 134 | typedef enum 135 | { 136 | GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_DROP, 137 | GST_VIMBASRC_INCOMPLETE_FRAME_HANDLING_SUBMIT 138 | } GstVimbasrcIncompleteFrameHandlingValue; 139 | 140 | typedef struct _GstVimbaSrc GstVimbaSrc; 141 | typedef struct _GstVimbaSrcClass GstVimbaSrcClass; 142 | 143 | #define NUM_VIMBA_FRAMES 3 144 | 145 | struct _GstVimbaSrc 146 | { 147 | GstPushSrc base_vimbasrc; 148 | 149 | struct 150 | { 151 | char *id; 152 | VmbHandle_t handle; 153 | VmbUint32_t supported_formats_count; 154 | // TODO: This overallocates since no camera will actually support all possible format matches. Allocate and fill 155 | // at runtime? 156 | const VimbaGstFormatMatch_t *supported_formats[NUM_FORMAT_MATCHES]; 157 | bool is_connected; 158 | bool is_acquiring; 159 | } camera; 160 | struct 161 | { 162 | char *settings_file_path; 163 | double exposuretime; 164 | int exposureauto; 165 | int balancewhiteauto; 166 | double gain; 167 | int offsetx; 168 | int offsety; 169 | int width; 170 | int height; 171 | int triggerselector; 172 | int triggermode; 173 | int triggersource; 174 | int triggeractivation; 175 | int incomplete_frame_handling; 176 | } properties; 177 | 178 | VmbFrame_t frame_buffers[NUM_VIMBA_FRAMES]; 179 | // queue in which filled Vimba frames are placed in the vimba_frame_callback (attached to each queued frame at 180 | // frame->context[0]) 181 | GAsyncQueue *filled_frame_queue; 182 | }; 183 | 184 | struct _GstVimbaSrcClass 185 | { 186 | GstPushSrcClass base_vimbasrc_class; 187 | }; 188 | 189 | GType gst_vimbasrc_get_type(void); 190 | 191 | G_END_DECLS 192 | 193 | VmbError_t open_camera_connection(GstVimbaSrc *vimbasrc); 194 | VmbError_t apply_feature_settings(GstVimbaSrc *vimbasrc); 195 | VmbError_t set_roi(GstVimbaSrc *vimbasrc); 196 | VmbError_t apply_trigger_settings(GstVimbaSrc *vimbasrc); 197 | VmbError_t alloc_and_announce_buffers(GstVimbaSrc *vimbasrc); 198 | void revoke_and_free_buffers(GstVimbaSrc *vimbasrc); 199 | VmbError_t start_image_acquisition(GstVimbaSrc *vimbasrc); 200 | VmbError_t stop_image_acquisition(GstVimbaSrc *vimbasrc); 201 | void VMB_CALL vimba_frame_callback(const VmbHandle_t cameraHandle, VmbFrame_t *pFrame); 202 | void map_supported_pixel_formats(GstVimbaSrc *vimbasrc); 203 | void log_available_enum_entries(GstVimbaSrc *vimbasrc, const char *feat_name); 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /src/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H_ 2 | #define HELPERS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Dummy use for currently unused objects to allow compilation while treating warnings as errors 8 | #define UNUSED(x) (void)(x) 9 | 10 | bool starts_with(const char *str, const char *prefix) 11 | { 12 | return strncmp(str, prefix, strlen(prefix)) == 0; 13 | } 14 | 15 | #endif // HELPERS_H_ -------------------------------------------------------------------------------- /src/pixelformats.c: -------------------------------------------------------------------------------- 1 | #include "pixelformats.h" 2 | #include 3 | #include 4 | 5 | const VimbaGstFormatMatch_t *gst_format_from_vimba_format(const char *vimba_format) 6 | { 7 | for (unsigned int i = 0; i < NUM_FORMAT_MATCHES; i++) 8 | { 9 | if (strcmp(vimba_format, vimba_gst_format_matches[i].vimba_format_name) == 0) 10 | { 11 | return &vimba_gst_format_matches[i]; 12 | } 13 | } 14 | return NULL; 15 | } 16 | 17 | // TODO: There may be multiple vimba format entries for the same gst_format. How to handle this? Currently the first hit 18 | // for the gst_format is returned and the rest ignored. 19 | const VimbaGstFormatMatch_t *vimba_format_from_gst_format(const char *gst_format) 20 | { 21 | for (unsigned int i = 0; i < NUM_FORMAT_MATCHES; i++) 22 | { 23 | if (strcmp(gst_format, vimba_gst_format_matches[i].gst_format_name) == 0) 24 | { 25 | return &vimba_gst_format_matches[i]; 26 | } 27 | } 28 | return NULL; 29 | } -------------------------------------------------------------------------------- /src/pixelformats.h: -------------------------------------------------------------------------------- 1 | #ifndef PIXELFORMATS_H_ 2 | #define PIXELFORMATS_H_ 3 | 4 | #include 5 | 6 | // Helper as GStreamer only provides these macros for x-raw formats 7 | #define GST_BAYER_FORMATS_ALL "{ bggr, grbg, gbrg, rggb }" 8 | 9 | #define GST_BAYER_CAPS_MAKE(format) \ 10 | "video/x-bayer, " \ 11 | "format = (string) " format ", " \ 12 | "width = " GST_VIDEO_SIZE_RANGE ", " \ 13 | "height = " GST_VIDEO_SIZE_RANGE ", " \ 14 | "framerate = " GST_VIDEO_FPS_RANGE 15 | 16 | typedef struct 17 | { 18 | const char *vimba_format_name; 19 | const char *gst_format_name; 20 | } VimbaGstFormatMatch_t; 21 | 22 | // TODO: Check if same capitalization as below for the vimba capabilities is guaranteed 23 | static VimbaGstFormatMatch_t vimba_gst_format_matches[] = { 24 | {"Mono8", "GRAY8"}, 25 | {"Mono10", "GRAY16_LE"}, 26 | {"Mono12", "GRAY16_LE"}, 27 | {"Mono14", "GRAY16_LE"}, 28 | {"Mono16", "GRAY16_LE"}, 29 | {"RGB8", "RGB"}, 30 | {"RGB8Packed", "RGB"}, 31 | {"BGR8", "BGR"}, 32 | {"BGR8Packed", "BGR"}, 33 | {"Argb8", "ARGB"}, 34 | {"Rgba8", "RGBA"}, 35 | {"Bgra8", "BGRA"}, 36 | {"Yuv411", "IYU1"}, 37 | {"YUV411Packed", "IYU1"}, 38 | {"YCbCr411_8_CbYYCrYY", "IYU1"}, 39 | {"Yuv422", "UYVY"}, 40 | {"YUV422Packed", "UYVY"}, 41 | {"YCbCr422_8_CbYCrY", "UYVY"}, 42 | {"Yuv444", "IYU2"}, 43 | {"YUV444Packed", "IYU2"}, 44 | {"YCbCr8_CbYCr", "IYU2"}, 45 | {"BayerGR8", "grbg"}, 46 | {"BayerRG8", "rggb"}, 47 | {"BayerGB8", "gbrg"}, 48 | {"BayerBG8", "bggr"}}; 49 | #define NUM_FORMAT_MATCHES (sizeof(vimba_gst_format_matches) / sizeof(vimba_gst_format_matches[0])) 50 | 51 | // lookup supported gst cap by format string from camera 52 | const VimbaGstFormatMatch_t *gst_format_from_vimba_format(const char *vimba_format); 53 | 54 | // lookup camera format string by negotiated gst cap 55 | const VimbaGstFormatMatch_t *vimba_format_from_gst_format(const char *gst_format); 56 | 57 | #endif // PIXELFORMATS_H_ -------------------------------------------------------------------------------- /src/vimba_helpers.c: -------------------------------------------------------------------------------- 1 | #include "vimba_helpers.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | // 8 | // Translates Vimba error codes to readable error messages 9 | // 10 | // Parameters: 11 | // [in] eError The error code to be converted to string 12 | // 13 | // Returns: 14 | // A descriptive string representation of the error code 15 | // 16 | const char *ErrorCodeToMessage(VmbError_t eError) 17 | { 18 | switch (eError) 19 | { 20 | case VmbErrorSuccess: 21 | return "Success."; 22 | case VmbErrorInternalFault: 23 | return "Unexpected fault in VmbApi or driver."; 24 | case VmbErrorApiNotStarted: 25 | return "API not started."; 26 | case VmbErrorNotFound: 27 | return "Not found."; 28 | case VmbErrorBadHandle: 29 | return "Invalid handle "; 30 | case VmbErrorDeviceNotOpen: 31 | return "Device not open."; 32 | case VmbErrorInvalidAccess: 33 | return "Invalid access."; 34 | case VmbErrorBadParameter: 35 | return "Bad parameter."; 36 | case VmbErrorStructSize: 37 | return "Wrong DLL version."; 38 | case VmbErrorMoreData: 39 | return "More data returned than memory provided."; 40 | case VmbErrorWrongType: 41 | return "Wrong type."; 42 | case VmbErrorInvalidValue: 43 | return "Invalid value."; 44 | case VmbErrorTimeout: 45 | return "Timeout."; 46 | case VmbErrorOther: 47 | return "Other error."; 48 | case VmbErrorResources: 49 | return "Resource not available."; 50 | case VmbErrorInvalidCall: 51 | return "Invalid call."; 52 | case VmbErrorNoTL: 53 | return "TL not loaded."; 54 | case VmbErrorNotImplemented: 55 | return "Not implemented."; 56 | case VmbErrorNotSupported: 57 | return "Not supported."; 58 | default: 59 | return "Unknown"; 60 | } 61 | } 62 | 63 | // Purpose: Discovers GigE cameras if GigE TL is present. 64 | // Discovery is switched on only once so that the API can detect all currently connected cameras. 65 | VmbBool_t DiscoverGigECameras(GObject *object) 66 | { 67 | VmbError_t result = VmbErrorSuccess; 68 | VmbBool_t isGigE = VmbBoolFalse; 69 | 70 | VmbBool_t ret = VmbBoolFalse; 71 | 72 | // Is Vimba connected to a GigE transport layer? 73 | result = VmbFeatureBoolGet(gVimbaHandle, "GeVTLIsPresent", &isGigE); 74 | if (VmbErrorSuccess == result) 75 | { 76 | if (VmbBoolTrue == isGigE) 77 | { 78 | // Set the waiting duration for discovery packets to return. If not set the default of 150 ms is used. 79 | result = VmbFeatureIntSet(gVimbaHandle, "GeVDiscoveryAllDuration", 250); 80 | if (VmbErrorSuccess == result) 81 | { 82 | // Send discovery packets to GigE cameras and wait 250 ms until they are answered 83 | result = VmbFeatureCommandRun(gVimbaHandle, "GeVDiscoveryAllOnce"); 84 | if (VmbErrorSuccess == result) 85 | { 86 | ret = VmbBoolTrue; 87 | } 88 | else 89 | { 90 | GST_WARNING_OBJECT(object, 91 | "Could not ping GigE cameras over the network. Reason: %s", 92 | ErrorCodeToMessage(result)); 93 | } 94 | } 95 | else 96 | { 97 | GST_WARNING_OBJECT(object, 98 | "Could not set the discovery waiting duration. Reason: %s", 99 | ErrorCodeToMessage(result)); 100 | } 101 | } 102 | else 103 | { 104 | GST_INFO_OBJECT(object, "Vimba is not connected to a GigE transport layer"); 105 | } 106 | } 107 | else 108 | { 109 | GST_WARNING_OBJECT(object, 110 | "Could not query Vimba for the presence of a GigE transport layer. Reason: %s", 111 | ErrorCodeToMessage(result)); 112 | } 113 | 114 | return ret; 115 | } 116 | -------------------------------------------------------------------------------- /src/vimba_helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef VIMBA_HELPERS_H_ 2 | #define VIMBA_HELPERS_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | const char *ErrorCodeToMessage(VmbError_t eError); 9 | 10 | VmbBool_t DiscoverGigECameras(GObject *object); 11 | 12 | #endif // VIMBA_HELPERS_H_ 13 | --------------------------------------------------------------------------------