From 9d0a7f9ced205aa0054057a54ebba78abfd01369 Mon Sep 17 00:00:00 2001 From: svcmobrel-release Date: Tue, 5 Dec 2023 20:47:45 -0800 Subject: [PATCH] Updating prebuilts and/or headers 2a8f761ddbd0acd63d41333cfd33c0130a68f986 - libv4l2_nvargus/nvargusv4l2.cpp 592bab81439af0cb17984e0b0e1d1b4b1a498ea2 - libv4l2_nvargus/nvargusv4l2_nvqueue.cpp 138a72695eb516d82dba17bf87260fe5a692ec74 - libv4l2_nvargus/nvargusv4l2_ioctl.cpp 55cebd96a410e6ff9c74c0e9b1a7ec68cf7ee4d5 - libv4l2_nvargus/libv4l2_nvargus.cpp 6d69d9bce88ea97989ac2ecf80df794a8c066623 - libv4l2_nvargus/nvargusv4l2_argus.cpp 08591e4e7c932d134ab3b5de5608f79817f25b10 - libv4l2_nvargus/Makefile 1b22fa15e66aeb472a541aa4fa9ee68b6f631fdf - libv4l2_nvargus/nvargusv4l2_context.cpp 62ff489fd24d7e62098d8bd0b681f017a3f1ed89 - libv4l2_nvargus/nvargusv4l2_os.cpp e5896289dc5cf1a1be97cd111bef4715314a657d - libv4l2_nvargus/README 25353aa29a931dfc9f52054852b50d690716a64c - libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h e0025b2f87abda4cd744e356e26cef85246a1fdc - libv4l2_nvargus/inc/nvargusv4l2_os.h 1522ea0088250fb7ce0c9f77c75dbca5d0f511f4 - libv4l2_nvargus/inc/nvargusv4l2.h 553b571fc97552659847ad0b2bc6e34384724d29 - libv4l2_nvargus/inc/nvargusv4l2_context.h d1c5f6e8cd188bbd39c99c32d4e4e68ee85e0f8b - libv4l2_nvargus/inc/nvargusv4l2_argus.h ebcc91d051727ba390d71db0887f788ffcfd8091 - libv4l2_nvargus/inc/nvargusv4l2_ioctl.h Change-Id: I08281700e9f5d70d8e09159b75fc376a293fead1 --- commitFile.txt | 17 + libv4l2_nvargus/Makefile | 125 +++ libv4l2_nvargus/README | 52 + libv4l2_nvargus/inc/nvargusv4l2.h | 47 + libv4l2_nvargus/inc/nvargusv4l2_argus.h | 90 ++ libv4l2_nvargus/inc/nvargusv4l2_context.h | 274 +++++ libv4l2_nvargus/inc/nvargusv4l2_ioctl.h | 113 +++ libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h | 54 + libv4l2_nvargus/inc/nvargusv4l2_os.h | 146 +++ libv4l2_nvargus/libv4l2_nvargus.cpp | 320 ++++++ libv4l2_nvargus/nvargusv4l2.cpp | 113 +++ libv4l2_nvargus/nvargusv4l2_argus.cpp | 1101 +++++++++++++++++++++ libv4l2_nvargus/nvargusv4l2_context.cpp | 322 ++++++ libv4l2_nvargus/nvargusv4l2_ioctl.cpp | 1089 ++++++++++++++++++++ libv4l2_nvargus/nvargusv4l2_nvqueue.cpp | 208 ++++ libv4l2_nvargus/nvargusv4l2_os.cpp | 287 ++++++ push_info.txt | 1 + 17 files changed, 4359 insertions(+) create mode 100644 commitFile.txt create mode 100644 libv4l2_nvargus/Makefile create mode 100644 libv4l2_nvargus/README create mode 100644 libv4l2_nvargus/inc/nvargusv4l2.h create mode 100644 libv4l2_nvargus/inc/nvargusv4l2_argus.h create mode 100644 libv4l2_nvargus/inc/nvargusv4l2_context.h create mode 100644 libv4l2_nvargus/inc/nvargusv4l2_ioctl.h create mode 100644 libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h create mode 100644 libv4l2_nvargus/inc/nvargusv4l2_os.h create mode 100644 libv4l2_nvargus/libv4l2_nvargus.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2_argus.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2_context.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2_ioctl.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2_nvqueue.cpp create mode 100644 libv4l2_nvargus/nvargusv4l2_os.cpp create mode 100644 push_info.txt diff --git a/commitFile.txt b/commitFile.txt new file mode 100644 index 0000000..ee27a5a --- /dev/null +++ b/commitFile.txt @@ -0,0 +1,17 @@ +Updating prebuilts and/or headers + +2a8f761ddbd0acd63d41333cfd33c0130a68f986 - libv4l2_nvargus/nvargusv4l2.cpp +592bab81439af0cb17984e0b0e1d1b4b1a498ea2 - libv4l2_nvargus/nvargusv4l2_nvqueue.cpp +138a72695eb516d82dba17bf87260fe5a692ec74 - libv4l2_nvargus/nvargusv4l2_ioctl.cpp +55cebd96a410e6ff9c74c0e9b1a7ec68cf7ee4d5 - libv4l2_nvargus/libv4l2_nvargus.cpp +6d69d9bce88ea97989ac2ecf80df794a8c066623 - libv4l2_nvargus/nvargusv4l2_argus.cpp +08591e4e7c932d134ab3b5de5608f79817f25b10 - libv4l2_nvargus/Makefile +1b22fa15e66aeb472a541aa4fa9ee68b6f631fdf - libv4l2_nvargus/nvargusv4l2_context.cpp +62ff489fd24d7e62098d8bd0b681f017a3f1ed89 - libv4l2_nvargus/nvargusv4l2_os.cpp +e5896289dc5cf1a1be97cd111bef4715314a657d - libv4l2_nvargus/README +25353aa29a931dfc9f52054852b50d690716a64c - libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h +e0025b2f87abda4cd744e356e26cef85246a1fdc - libv4l2_nvargus/inc/nvargusv4l2_os.h +1522ea0088250fb7ce0c9f77c75dbca5d0f511f4 - libv4l2_nvargus/inc/nvargusv4l2.h +553b571fc97552659847ad0b2bc6e34384724d29 - libv4l2_nvargus/inc/nvargusv4l2_context.h +d1c5f6e8cd188bbd39c99c32d4e4e68ee85e0f8b - libv4l2_nvargus/inc/nvargusv4l2_argus.h +ebcc91d051727ba390d71db0887f788ffcfd8091 - libv4l2_nvargus/inc/nvargusv4l2_ioctl.h diff --git a/libv4l2_nvargus/Makefile b/libv4l2_nvargus/Makefile new file mode 100644 index 0000000..95dab3e --- /dev/null +++ b/libv4l2_nvargus/Makefile @@ -0,0 +1,125 @@ +############################################################################### +# +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# makefile for libv4l2_nvargus library +# +############################################################################### + +LIBRARY_NAME:= libv4l2_nvargus.so + +# Clear the flags from env +CPPFLAGS := +LDFLAGS := +LIBS := + +INSTALL_DIR?=/usr/lib/aarch64-linux-gnu/tegra/ +LINK_DIR?=/usr/lib/aarch64-linux-gnu/libv4l/plugins/nv/ + +# Verbose flag +ifeq ($(VERBOSE), 1) +AT = +else +AT = @ +endif + +# ARM ABI of the target platform +TEGRA_ARMABI ?= aarch64-linux-gnu + +# Location of the target rootfs +ifeq ($(shell uname -m), aarch64) +TARGET_ROOTFS := +CROSS_COMPILE := +else +CROSS_COMPILE ?= aarch64-unknown-linux-gnu- +ifeq ($(TARGET_ROOTFS),) +$(error "TARGET_ROOTFS" is not set. Please set TARGET_ROOTFS if you are cross-compiling.) +endif +endif + +ARGUS_UTILS_GTK_PATH := $(TARGET_ROOTFS)/usr/src/jetson_multimedia_api/argus/samples/utils/gtk +ARGUS_UTILS_SRC_PATH := $(TARGET_ROOTFS)/usr/src/jetson_multimedia_api/argus/samples/utils + +CC = $(AT) $(CROSS_COMPILE)gcc +CPP = $(AT) $(CROSS_COMPILE)gcc +RM = $(AT) $(CROSS_COMPILE)rm -f + +PKGS := gtk+-3.0 \ + glx \ + glib-2.0 + +CPPFLAGS += \ + -I"$(TARGET_ROOTFS)/usr/include" \ + -I"$(TARGET_ROOTFS)/usr/include/$(TEGRA_ARMABI)" \ + -I"$(TARGET_ROOTFS)/usr/src/argus/include" \ + -I"$(TARGET_ROOTFS)/usr/src/jetson_multimedia_api/argus/samples/utils" \ + -I"$(TARGET_ROOTFS)/usr/src/jetson_multimedia_api/argus/samples/utils/gtk" \ + -I"$(TARGET_ROOTFS)/usr/src/jetson_multimedia_api/include" \ + -I./inc + +CPPFLAGS += \ + -pthread \ + -fno-rtti -std=gnu++14 -Wall -fPIC + +CPPFLAGS += `pkg-config --cflags $(PKGS)` + +LIBS += \ + -shared -lnvv4l2 -lEGL -lGLESv2 -lX11 -lpthread -Wl,--no-undefined \ + -lnvbufsurface -lnvbufsurftransform -lnvargus_socketclient -ldl -lstdc++ -lm + +LDFLAGS += -L"/usr/lib" \ + -L"/usr/lib/$(TEGRA_ARMABI)/" \ + -L"/usr/lib/$(TEGRA_ARMABI)/tegra" + +LIBS += `pkg-config --libs $(PKGS)` + +SRCS := \ + libv4l2_nvargus.cpp \ + nvargusv4l2.cpp \ + nvargusv4l2_context.cpp \ + nvargusv4l2_ioctl.cpp \ + nvargusv4l2_argus.cpp \ + nvargusv4l2_os.cpp \ + nvargusv4l2_nvqueue.cpp \ + $(wildcard $(ARGUS_UTILS_GTK_PATH)/*.cpp) \ + $(ARGUS_UTILS_SRC_PATH)/nvmmapi/NvNativeBuffer.cpp \ + $(filter-out $(ARGUS_UTILS_SRC_PATH)/CUDAHelper.cpp, \ + $(wildcard $(ARGUS_UTILS_SRC_PATH)/*.cpp)) + +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +all: $(LIBRARY_NAME) + +.o: .cpp + @echo "Compiling: $(CPP) $(CPPFLAGS) $<" + $(CPP) $(CPPFLAGS) -c $< + +$(LIBRARY_NAME): $(OBJS) + @echo "Linking: $@" + $(CPP) -o $@ $(OBJS) $(CPPFLAGS) $(LIBS) $(LDFLAGS) + +.PHONY: install +install: $(LIBRARY_NAME) + sudo cp -vp $(LIBRARY_NAME) $(INSTALL_DIR) + sudo ln -sf $(INSTALL_DIR)$(LIBRARY_NAME) $(LINK_DIR)$(LIBRARY_NAME) + +clean: + $(RM) *.o $(LIBRARY_NAME) *~ diff --git a/libv4l2_nvargus/README b/libv4l2_nvargus/README new file mode 100644 index 0000000..97e5371 --- /dev/null +++ b/libv4l2_nvargus/README @@ -0,0 +1,52 @@ +***************************************************************************** + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * +***************************************************************************** + +LIBV4L2_NVARGUS API is a V4L2 API interface written over Libargus Camera to support +capture and record v4l2 camera application development. + +API_NOTE: + 1. For V4L2 DMABUF Memory buffers, additional VIDIOC_QUERYBUF ioctl is also required + to have proper mapping between application allocated buffers to Libargus buffers. + +LIBV4L2_NVARGUS API includes: + 1. Source files of the written V4L2 interface, where respective v4l2_ioctl calls + are mapped to LibArgus calls. + 2. README + +Pre-requisites: + 1. Install "jetson_multimedia_api" package from latest Jetpack release. + 2. Install below packages: + $ sudo apt-get install autoconf automake libtool curl make g++ unzip \ + pkg-config libx11-dev libgtk-3-dev libjpeg-dev libgstreamer1.0-dev + +Steps for copying sources on target: + 1. Copy the tar file libv4l2_nvargus_src.tbz2 to the booted Jetson device. + 2. Run the following command on the Jetson device to install sources: + $ sudo tar -xpf libv4l2_nvargus_src.tbz2 -C . + +Steps to build sources: + $ cd libv4l2_nvargus_src + $ make [-jN] # Optional flag -jN for parallel build where N is the job count + $ sudo make install + NOTE: above command will install libv4l2_nvargus.so at "/usr/lib/aarch64-linux-gnu/tegra/" + To install at different location run "$ sudo INSTALL_DIR= make install" diff --git a/libv4l2_nvargus/inc/nvargusv4l2.h b/libv4l2_nvargus/inc/nvargusv4l2.h new file mode 100644 index 0000000..839c2a7 --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* This file is the public interface for using the libargusV4L2. +libargusv4l2 implements the V4L2 behavior in userspace and hence +uses all the structure/macro definitions from standard V4L2 header file +i.e videodev2.h +Standard system calls like open(), ioctl() and close() are replaced +with below APIs. +*/ + +#ifndef __NVARGUSV4L2_H__ +#define __NVARGUSV4L2_H__ + +/* Equivalent to the standard open() system call on a V4L2 Device. + Camera indices do not have one-to-one map with devnode indices. + Creates an instance and returns a fd equivalent to the client if successful, + -1 if error. +*/ +int ArgusV4L2_Open(const int camera_index, int flags); + +/* Equivalent to the standard close() system call on a V4L2 Device.*/ +int ArgusV4L2_Close(int fd); + +/* Equivalent to the standard ioctl() system call on a V4L2 Device.*/ +int ArgusV4L2_Ioctl(int fd, unsigned long cmd, ...); + +#endif /* __NVARGUSV4L2_H__ */ diff --git a/libv4l2_nvargus/inc/nvargusv4l2_argus.h b/libv4l2_nvargus/inc/nvargusv4l2_argus.h new file mode 100644 index 0000000..f2a1718 --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2_argus.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVARGUSV4L2_HELPER_H__ +#define __NVARGUSV4L2_HELPER_H__ + +#include "nvargusv4l2_context.h" +#include "nvargusv4l2_ioctl.h" + +#define TIMEOUT_FOUR_SECONDS 4000000000 +#define TIMEOUT_TWO_SECONDS 2000000000 + +extern Argus::CameraProvider *g_cameraProvider; +extern Argus::ICameraProvider *g_iCameraProvider; + +/* Returns the Argus status code in string */ +const char* getStatusString(Argus::Status status); + +/* Reads boolean variable atomically */ +bool v4l2_cam_atomic_read_bool(v4l2_camera_context* ctx, bool *var); + +/* Writes boolean variable atomically */ +void v4l2_cam_atomic_write_bool(v4l2_camera_context* ctx, bool *var, bool value); + +/* Return the value of the variable atomically */ +uint32_t v4l2_cam_atomic_get(v4l2_camera_context *ctx, uint32_t *var); + +/* Release Argus::Buffer with the given index to the stream */ +int32_t ReleaseStreamBuffer(v4l2_camera_context *ctx, int32_t index); + +/* Recieves pointer to the filled Argus::Buffer from the stream */ +Argus::Buffer* AcquireStreamBuffer(v4l2_camera_context *ctx, uint32_t timeout); + +/* Initializes the Argus::OutputStream */ +int32_t initialize_outputstream(v4l2_camera_context *ctx, cam_params *argus_params); + +/* Set all the Argus related properties and settings */ +int32_t v4l2_cam_set_argus_settings(v4l2_camera_context *ctx, uint32_t ctrl_id); + +/* Sets Frame Rate */ +int32_t set_framerate(v4l2_camera_context *ctx, float frame_duration); + +/* Sets Denoise Settings */ +int32_t v4l2_cam_set_denoise_settings(v4l2_camera_context *ctx, uint32_t ctrl_id, + struct denoise_values *ctrl_value); + +/* Allocates all capture queues */ +int32_t allocate_all_capture_queues(v4l2_camera_context *ctx); + +/* Allocates buffers in the memory (MMAP) */ +int32_t allocate_capture_buffers(v4l2_camera_context *ctx, cam_params *argus_params); + +/* Allocate argus buffers for DMABUF memory */ +int32_t allocate_argus_buffers(v4l2_camera_context *ctx, cam_params *argus_params, int32_t dmabuf_fd, uint32_t buffer_idx); + +/* Deallocates the previously alloacted buffers */ +void free_cap_buffers(v4l2_camera_context *ctx); + +/* Query the buffer */ +int32_t query_cam_buffers(v4l2_camera_context *ctx, cam_params *argus_params, uint64_t *psizes); + +/* Close camera context */ +void v4l2_close_argus_context(v4l2_context *v4l2_ctx); + +/* Queues the empty capture plane buffers to Argus */ +int32_t nvargus_enqueue_instream_buffers_from_capture_inQ(v4l2_camera_context *ctx, int32_t idx); + +/* Callback function to the argus_thread */ +void argus_capture_thread_func(void *args); + +#endif diff --git a/libv4l2_nvargus/inc/nvargusv4l2_context.h b/libv4l2_nvargus/inc/nvargusv4l2_context.h new file mode 100644 index 0000000..d7ca2ec --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2_context.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVARGUSV4L2_CONTEXT_H__ +#define __NVARGUSV4L2_CONTEXT_H__ + +#include +#include "nvbufsurface.h" +#include "nvargusv4l2_os.h" +#include "nvargusv4l2_nvqueue.h" +#include "linux/videodev2.h" +#include "v4l2_nv_extensions.h" +#include "Thread.h" +#include +#include +#include "EGLGlobal.h" + +#define MAX_CONTEXTS 32 +#define FRAMESIZE_MIN_WIDTH 48 +#define FRAMESIZE_MIN_HEIGHT 48 +#define MAX_OP_BUFFERS 32 +#define MIN_OP_BUFFERS 6 +#define MAX_PLANES 4 + +#ifdef _DEBUG +#define DEBUG_LOGS 0 +#else +#define DEBUG_LOGS 0 +#endif + +#define DBG_PRINT(...) \ + do { \ + if (DEBUG_LOGS) \ + fprintf(stderr, "LIBV4L2ARGUS: " __VA_ARGS__); \ + } while (0) + +#define REL_PRINT(...) \ + do { \ + if (runtime_logs_enabled) \ + { \ + fprintf(stderr, "(tid) : %x ", (unsigned int) pthread_self()); \ + fprintf(stderr, "LIBV4L2ARGUS: " __VA_ARGS__); \ + } \ + } while (0) + +#define ERROR_PRINT(...) \ + do { \ + fprintf(stderr, "LIBV4L2ARGUS ERROR: " __VA_ARGS__); \ + } while (0) + +#define V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(buffer_type) \ + if (buffer_type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { \ + printf("Unsupported buffer type\n"); \ + return EINVAL; } + +#define V4L2_MEMORY_TYPE_SUPPORTED_OR_ERROR(memory_type) \ + if (memory_type != V4L2_MEMORY_MMAP && memory_type != V4L2_MEMORY_DMABUF) { \ + printf("Unsupported memory type\n"); \ + return EINVAL; } + +extern Argus::CameraProvider *g_cameraProvider; +extern Argus::ICameraProvider *g_iCameraProvider; +extern std::vector g_cameraDevices; + +/* This structure identifies an instance which is created when + * argusv4l2_open() is called + */ +typedef struct v4l2_context_rec { + NvMutexHandle ioctl_mutex; + + /* Pointer to the camera context */ + void *actual_context; +} v4l2_context; + +/* Structure to store Argus color format information */ +typedef struct { + uint32_t width, height; + /* V4L2 fourcc */ + uint32_t pixelformat; + Argus::PixelFormat argus_format; + /* color depth */ + uint32_t bitdepth; +} cam_params; + +/* The structure store the information needed for V4L2_ENUM_FRAMESIZES. + * This is used to store minimum & maximum width height, frame duration + * supported for a given format. + */ +typedef struct { + uint32_t min_width; + uint32_t min_height; + uint32_t max_width; + uint32_t max_height; + uint32_t bitdepth; + float frame_duration; + float minGainRange; + float maxGainRange; + uint64_t minExposureTimeRange; + uint64_t maxExposureTimeRange; +} argusv4l2_sensormode; + +/* Defines v4l2_buffer */ +typedef struct camerav4l2_buffer_rec { + /* Buffer Index*/ + int32_t buffer_id; + /* Surface List params for allocated buffer.*/ + NvBufSurfaceParams surf_params; + /* Represent state of buffer*/ + uint32_t flags; + /* Repesent that buffer structure is allocated or not*/ + uint32_t allocated; + /* Stores the buf_fd returned by nvbuf_utils API */ + uint32_t buf_fd; + /* Memory representation of argus buffer mapped to the NvMMBuffer FD */ + Argus::Buffer* argus_buffer; +} camerav4l2_buffer; + +/* The strcuture stores metadata retrieved from acquired buffer */ +struct argusframe_metadata { + bool aeLocked; + bool colorCorrectionMatrixEnable; + bool toneMapCurveEnabled; + float ispDigitalGain; + float sceneLux; + float sensorAnalogGain; + int32_t focuserPosition; + uint32_t awbCct; + uint32_t id; + uint32_t sensorSensitivity; + uint64_t frameDuration; + uint64_t frameReadoutTime; + uint64_t sensorExposureTime; + uint64_t sensorTimestamp; + Argus::AeState aeState; + Argus::AwbState awbState; + + argusframe_metadata() + : aeState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "AE_STATE_UNKNOWN") + , awbState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "AWB_STATE_UNKNOWN") + {} +}; + +/* The structure stores the Argus controls values */ +typedef struct argus_settings_rec { + bool awbLock; + bool aeLock; + bool enableColorSaturation; + int32_t sensorModeIdx; + uint32_t denoiseMode; + uint32_t edgeEnhanceMode; + uint32_t aeAntibandingMode; + uint32_t awbMode; + float frameDuration; + float edgeEnhanceStrength; + float denoiseStrength; + float exposureCompensation; + float colorSaturation; + Argus::Range ispDigitalGainRange; + Argus::Range gainRange; + Argus::Range exposureTimeRange; + Argus::IAutoControlSettings *autoControlSettingsPtr; + Argus::IDenoiseSettings *denoiseSettingsPtr; + Argus::IEdgeEnhanceSettings *EESettingsPtr; +} cam_settings; + +/* This structure defines the camera context structure. +*/ +typedef struct v4l2_camera_context_rec { + + /* Context mutex, used for simultaneous access from multiple threads.*/ + NvMutexHandle context_mutex; + /* Stats mutex, used for atomic update/read/access statistics */ + NvMutexHandle stats_mutex; + /* Semaphore for dqbuf on capture plane */ + NvSemaphoreHandle acquirebuf_sema; + + /* Queue for buffer indices which are returned from Argus, + ready to be DQBUF'ed on capture plane */ + NvQueueHandle capplane_Q; + /* Thread for argus capture processing */ + NvThreadHandle argus_capture_thread; + + bool blocking_mode; + bool stream_on; + bool error_flag; + /* This flag if set indicates that Argus is in STOP state */ + bool camera_state_stopped; + /* Number of buffers on capture plane for V4L2 */ + uint32_t capture_buffer_count; + /* Number of queued buffer */ + uint32_t num_queued_capture_buffers; + /* Either DMABUF or MMAP */ + uint32_t capture_memory_type; + + cam_params argus_params; + cam_settings argus_settings; + camerav4l2_buffer cap_buffers[MAX_OP_BUFFERS]; + argusframe_metadata *frame_metadata[MAX_OP_BUFFERS]; + void* inbuf_mapped_address[MAX_OP_BUFFERS][MAX_PLANES]; + + /* Argus Members */ + Argus::Status m_cameraStatus; + Argus::UniqueObj m_captureSession; + Argus::ICaptureSession *m_iCaptureSession; + Argus::IEventProvider *m_iEventProvider; + Argus::IEventQueue *m_iEventQueue; + Argus::UniqueObj m_eventQueue; + Argus::ICameraProperties *m_iCameraProperties; + Argus::UniqueObj m_streamSettings; + Argus::IBufferOutputStreamSettings *m_iStreamSettings; + Argus::UniqueObj m_outputStream; + Argus::IBufferOutputStream *m_iStream; + Argus::UniqueObj m_request; + Argus::IRequest *m_iRequest; + Argus::ISourceSettings *m_iSourceSettings; + const Argus::CaptureMetadata *m_captureMetadata; + std::vector argusv4l2_sensormodes; + std::vector m_sensorModes; + std::vector m_eventTypes; + +} v4l2_camera_context; + +/* Returns a context from a fd */ +v4l2_context* v4l2_get_context(int32_t fd); + +int initialize_camera_provider(); + +/* Queries and fills the data of all avaiable sensor modes from Argus */ +void v4l2_argus_fill_sensor_modes(std::vector sensorModes, + std::vector *argusv4l2_sensormodes); + +/* Initializes argus environment when a valid context gets created */ +int32_t initialize_arguscamera(v4l2_camera_context *camera_ctx, uint32_t camera_index); + +/* Sets argus default parameters */ +void v4l2_argus_set_defaults(cam_params *argus_defaults, cam_settings *argus_settings); + +/* Opens a new context */ +int32_t v4l2_open_camera_context(uint32_t camera_index, int flags); + +/* Closes the contexts and destroys all the memory associated with it.*/ +void v4l2_close_camera_context(int32_t fd); + +/* Tries to acquire the global mutex to avoid two open/close contexts at a time */ +void v4l2_lock_global_mutex(void); + +/* Releases the global mutex to avoid two open/close contexts at a time */ +void v4l2_unlock_global_mutex(void); + +/* This flag is set if /tmp/argusv4l2_logs file exists. This is checked + when the library is loaded. This mechanism provides conditional logging + in the same binary based on environment parameters.*/ +extern bool runtime_logs_enabled; + +#endif /* __NVARGUSV4L2_CONTEXT_H__ */ diff --git a/libv4l2_nvargus/inc/nvargusv4l2_ioctl.h b/libv4l2_nvargus/inc/nvargusv4l2_ioctl.h new file mode 100644 index 0000000..f4daea0 --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2_ioctl.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVARGUSV4L2_IOCTL_H__ +#define __NVARGUSV4L2_IOCTL_H__ + +#include "nvargusv4l2_context.h" +#include "nvargusv4l2_argus.h" + +#define ALIGN_BYTES 255 +#define MEM_ALIGN_16(X) (((X) + 0xF)&~0xF) + +#define MIN_EXPOSURE_COMPENSATION -2.0 +#define MAX_EXPOSURE_COMPENSATION 2.0 +#define MIN_EE_STRENGTH -1.0 +#define MAX_EE_STRENGTH 1.0 +#define MIN_DENOISE_STRENGTH -1.0 +#define MAX_DENOISE_STRENGTH 1.0 +#define MIN_DIGITAL_GAIN 1 +#define MAX_DIGITAL_GAIN 256 +#define MIN_COLOR_SATURATION 0.0 +#define MAX_COLOR_SATURATION 2.0 + +extern Argus::CameraProvider *g_cameraProvider; +extern Argus::ICameraProvider *g_iCameraProvider; + +/* Structure to map corresponding v4l2 format and + * Argus pixel format. + */ +struct argusv4l2_fmt_struct +{ + /* Color format description */ + char name[32]; + /* Argus pixel format */ + const Argus::PixelFormat argus_format; + /* V4L2 fourcc */ + uint32_t fourcc; + /* Number of planes */ + uint32_t num_planes; + /* bytes per pixel */ + uint32_t bpp; +}; + +/* Entry structure which is added/removed in the queues */ +typedef struct _q_entry +{ + /* Index of the buffer */ + uint32_t index; + /* Size of the buffer */ + uint32_t size; + /* Timestamp */ + uint64_t timestamp; + /* FD */ + uint32_t fd; + /* Stores the config store id of this buffer */ + uint32_t config_store; +} q_entry; + +/* IOCTL calls for Argus camera */ +int32_t nvargus_ioctl(int32_t fd, unsigned long cmd, void *arg); + +int32_t vidioc_cam_querycap(int32_t fd, struct v4l2_capability *caps); + +int32_t vidioc_cam_enum_fmt(int32_t fd, struct v4l2_fmtdesc *fmt); + +int32_t vidioc_cam_enum_framesizes(int32_t fd, struct v4l2_frmsizeenum *frame); + +int32_t vidioc_cam_enum_frameintervals(int32_t fd, struct v4l2_frmivalenum *frameival); + +int32_t vidioc_cam_g_fmt(int32_t fd, struct v4l2_format *format); + +int32_t vidioc_cam_s_fmt(int32_t fd, struct v4l2_format *format); + +int32_t vidioc_cam_reqbufs(int32_t fd, struct v4l2_requestbuffers *reqbufs); + +int32_t vidioc_cam_streamon(int32_t fd, unsigned int *type); + +int32_t vidioc_cam_streamoff(int32_t fd, unsigned int *type); + +int32_t vidioc_cam_qbuf(int32_t fd, struct v4l2_buffer *buffer); + +int32_t vidioc_cam_dqbuf(int32_t fd, struct v4l2_buffer *buffer); + +int32_t vidioc_cam_querybuf(int32_t fd, v4l2_buffer *buffer); + +int32_t vidioc_cam_expbuf(int32_t fd, struct v4l2_exportbuffer *export_buffer); + +int32_t vidioc_cam_s_extctrls(int32_t fd, struct v4l2_ext_controls *ctrl); + +int32_t vidic_cam_g_extctrls(int32_t fd, struct v4l2_ext_controls *ctrl); + +int32_t vidioc_cam_sparm(int32_t fd, struct v4l2_streamparm *streamparms); + +#endif /* __NVARGUSV4L2_IOCTL_H__ */ diff --git a/libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h b/libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h new file mode 100644 index 0000000..380b7bc --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2_nvqueue.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef __ARGUSV4L2_NVQUEUE_H__ +#define __ARGUSV4L2_NVQUEUE_H__ + +#include +#include "nvargusv4l2_os.h" + +typedef struct NvQueueRec +{ + NvMutexHandle mutexLock; /* mutex lock for queue */ + u_int32_t maxEntries; /* maximum number of allowed entries */ + u_int32_t entrySize; /* size of individual entry */ + u_int32_t pushIndex; /* index of where to push entry */ + u_int32_t popIndex; /* index of where to grab entry */ + u_int8_t *pEntryList; /* pointer to beginning entry */ +} NvQueue; + +typedef struct NvQueueRec *NvQueueHandle; + +int32_t NvQueueCreate(NvQueueHandle *phQueue, u_int32_t maxEntries, u_int32_t entrySize); + +int32_t NvQueueEnQ(NvQueueHandle hQueue, void *pElem); + +int32_t NvQueueDeQ(NvQueueHandle hQueue, void *pElem); + +int32_t NvQueuePeek(NvQueueHandle hQueue, void *pElem); + +int32_t NvQueuePeekEntry(NvQueueHandle hQueue, void *pElem, u_int32_t entry); + +void NvQueueDestroy(NvQueueHandle *phQueue); + +u_int32_t NvQueueGetNumEntries(NvQueueHandle hQueue); + +#endif /* __ARGUSV4L2_NVQUEUE_H__ */ diff --git a/libv4l2_nvargus/inc/nvargusv4l2_os.h b/libv4l2_nvargus/inc/nvargusv4l2_os.h new file mode 100644 index 0000000..7a7618e --- /dev/null +++ b/libv4l2_nvargus/inc/nvargusv4l2_os.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVARGUSV4L2_OS_H__ +#define __NVARGUSV4L2_OS_H__ + +#include + +typedef struct NvMutex_ *NvMutexHandle; +typedef struct NvSemaphore_ *NvSemaphoreHandle; +typedef struct NvThread_ *NvThreadHandle; + + +/** Entry point for a thread. + */ +typedef void (*NvThreadFunction)(void *args); + +/** + * Allocates a new process-local mutex. + * + * @note Mutexes can be locked recursively; if a thread owns the lock, + * it can lock it again as long as it unlocks it an equal number of times. + * + * @param mutex The mutex to initialize. + * + * @return \a ENOMEM, or one of common error codes on + * failure. + */ +int32_t NvMutexCreate(NvMutexHandle *mutex); + +/** Locks the given unlocked mutex. + * + * @param mutex The mutex to lock; note it is a recursive lock. + * + * @return \a EINVAL, or one of common error codes on + * failure. + */ +int32_t NvMutexAcquire(NvMutexHandle mutex); + +/** Unlocks a locked mutex. + * + * @param mutex The mutex to unlock. + * + * @return \a EINVAL, or one of common error codes on + * failure. + */ +int32_t NvMutexRelease(NvMutexHandle mutex); + +/** Frees the resources held by a mutex. + * + * Mutexes will be destroyed only when last reference has gone away. + * + * @param mutex The mutex to destroy. + * + * @return \a EINVAL, or one of common error codes on + * failure. + */ +int32_t NvMutexDestroy(NvMutexHandle mutex); + +/** Creates a counting semaphore. + * + * @param semaphore A pointer to the semaphore to initialize. + * @param value The initial semaphore value. + * + * @retval 0 on success, or the appropriate error code. + */ +int32_t NvSemaphoreCreate(NvSemaphoreHandle *semaphore, u_int32_t value); + +/** Waits until the semaphore value becomes non-zero, then + * decrements the value and returns. + * + * @param semaphore The semaphore to wait for. + */ +void NvSemaphoreWait(NvSemaphoreHandle semaphore); + +/** Increments the semaphore value. + * + * @param semaphore The semaphore to signal. + */ +void NvSemaphoreSignal(NvSemaphoreHandle semaphore); + +/** Frees resources held by the semaphore. + * + * Semaphores are reference counted across the multiprocesses, + * and will be destroyed when the last reference has + * gone away. + * + * @param semaphore The semaphore to destroy. + * + * @retval 0 on success, or the appropriate error code. + */ +int32_t NvSemaphoreDestroy(NvSemaphoreHandle semaphore); + +/** Creates a thread. + * + * @param function The thread entry point. + * @param args A pointer to the thread arguments. + * @param [out] thread A pointer to the result thread ID structure. + * + * @retval 0 on success, or the appropriate error code. + */ +int32_t NvThreadCreate(NvThreadFunction function, void *args, + NvThreadHandle *thread); + +/** Assigns the given name to the given thread. + * + * @param thread The thread to assign the name to. + * @param name thread name string. + */ +int32_t NvThreadSetName(NvThreadHandle thread, const char *name); + +/** Waits for the given thread to exit. + * + * The joined thread will be destroyed automatically. All OS resources + * will be reclaimed. + * + * @param thread The thread to wait for. + */ +void NvThreadJoin(NvThreadHandle thread); + +/** Returns current thread ID. + * + * @retval ThreadId + */ +u_int64_t NvGetCurrentThreadId(void); + +#endif /* __NVARGUSV4L2_OS_H__ */ diff --git a/libv4l2_nvargus/libv4l2_nvargus.cpp b/libv4l2_nvargus/libv4l2_nvargus.cpp new file mode 100644 index 0000000..3dffe67 --- /dev/null +++ b/libv4l2_nvargus/libv4l2_nvargus.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libv4l-plugin.h" +#include "nvargusv4l2.h" +#include "nvargusv4l2_context.h" +#include "nvargusv4l2_ioctl.h" + +#if HAVE_VISIBILITY +#define PLUGIN_PUBLIC __attribute__ ((visibility("default"))) +#else +#define PLUGIN_PUBLIC +#endif + +#define NVARGUS_DEV_STRING "/dev/video" +#define MAX_CAMERA_NODE_LIMITS 64 + +#define LOG_Q_DQ(fmt, args...) \ + do { \ + if (profile_enabled_flag) \ + { \ + struct timeval start; \ + struct timezone tzp; \ + gettimeofday(&start, &tzp); \ + FILE* pFile = fopen("/tmp/profile_argusbuffer_logs.txt", "a"); \ + fprintf(pFile, "\n%x: %lu: "fmt, (unsigned int) pthread_self(), (1000000 * start.tv_sec + start.tv_usec),##args); \ + fclose(pFile); \ + } \ + } while (0) + +struct nvargus_plugin_ctx +{ + int nvv4l2argus_fd; + int fd_blocking_mode; +}; + +uint32_t profile_enabled_flag = 0; + +void __attribute__((constructor)) libv4l2_nvargus_init(void); +void __attribute__((destructor)) libv4l2_nvargus_deinit(void); + +void *plugin_init(int fd); +void plugin_close(void *dev_ops_priv); +int plugin_ioctl(void *dev_ops_priv, int fd, + unsigned long int cmd, void *arg); + +void __attribute__((constructor)) libv4l2_nvargus_init(void) +{ + struct stat file_stats; + + if(stat("/tmp/libv4l2argusPlugin_profile", &file_stats) == 0) + profile_enabled_flag = 1; + else + profile_enabled_flag = 0; +} + +void __attribute__((destructor)) libv4l2_nvargus_deinit(void) +{ +} + +typedef struct +{ + int32_t device_index; + int32_t argus_index; + bool is_usb; +} camera_type; + +class CameraNode +{ +public: + static CameraNode& GetCameraNode() + { + static CameraNode s_instance; + return s_instance; + } + + static int GetIndex(int node_index) + { + return GetCameraNode().GetIndexInternal(node_index); + } + void operator=(CameraNode const&) = delete; + +private: + CameraNode() {} + + int GetIndexInternal(int node_index); + + int QueryNode(int node_index, char* device_path); + + camera_type m_cameraNodes[MAX_CAMERA_NODE_LIMITS] = + {{-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, {-1, -1, false}, + }; + +}; + +int CameraNode::GetIndexInternal(int node_index) +{ + int index; + int argus_index = 0; + + if (node_index < 0) + return -1; + + for (index = 0; index <= node_index; index++) + { + if (m_cameraNodes[index].device_index == -1) + { + char camera_path[128]; + m_cameraNodes[index].device_index = index; + snprintf(camera_path, sizeof(camera_path), "%s%d", NVARGUS_DEV_STRING, index); + if (QueryNode(m_cameraNodes[index].device_index, camera_path) != 0) + continue; + if (!m_cameraNodes[index].is_usb) + { + m_cameraNodes[index].argus_index = argus_index++; + } + } + } + if (!m_cameraNodes[node_index].is_usb) + return m_cameraNodes[node_index].argus_index; + + return -1; +} + +int CameraNode::QueryNode(int node_index, char* device_path) +{ + struct v4l2_capability v4l2_caps; + char usb[] = "usb"; + + int32_t camera_fd = open(device_path, O_RDWR); + if (camera_fd < 0) + { + fprintf(stderr, "Failed to open %s: %s\n", device_path, + strerror(errno)); + return -1; + } + + memset(&v4l2_caps, 0, sizeof(struct v4l2_capability)); + if (ioctl(camera_fd, VIDIOC_QUERYCAP, &v4l2_caps)) + { + fprintf(stderr, "%s: not a v4l2 node\n", device_path); + errno = ENOTTY; + close(camera_fd); + return -1; + } + + if(strstr((char*)v4l2_caps.bus_info, usb) != NULL) + m_cameraNodes[node_index].is_usb = true; + + close(camera_fd); + return 0; +} + +void *plugin_init(int fd) +{ + struct stat sb; + char str_proc_fd[512]; + char device_path[512]; + int index = 0; + int flags = 0; + int camera_index = -1; + ssize_t nbytes; + struct nvargus_plugin_ctx *plugin = NULL; + + memset(str_proc_fd, 0, sizeof(str_proc_fd)); + memset(device_path, 0, sizeof(device_path)); + + if (fstat(fd, &sb) == -1) + { + perror("stat"); + errno = EINVAL; + return 0; + } + + snprintf(str_proc_fd, sizeof(str_proc_fd), "/proc/self/fd/%d", fd); + nbytes = readlink(str_proc_fd, device_path, sizeof(device_path)); + if (nbytes == -1) + { + perror("readlink"); + errno = EINVAL; + return NULL; + } + + flags = fcntl(fd, F_GETFL, 0); + + /* Check the valid camera node "/dev/videox" */ + if (strncmp(NVARGUS_DEV_STRING, device_path, 10) == 0) + { + /* To know the type of video node being opened + * by the application, an additional call is made to + * the camera driver. For USB or other unsupported devices, + * we return without initializing the argus instance. + */ + + std::string argus_pathname = device_path; + + try { + camera_index = stoi(argus_pathname.substr(10, + argus_pathname.length() - 10)); + } + catch (...) { + perror("Error in getting camera_index\n"); + return NULL; + } + + int32_t argus_index = CameraNode::GetIndex(camera_index); + if (argus_index == -1) + return NULL; + + // For Blocking implementation + if (flags != -1) + { + printf("Opening in BLOCKING MODE\n"); + } + + index = ArgusV4L2_Open(argus_index, flags); + if (index == -1) + { + perror("ArgusV4L2_Open failed"); + errno = EINVAL; + return NULL; + } + plugin = (nvargus_plugin_ctx *)calloc(1, sizeof(*plugin)); + if (!plugin) + { + perror("Unable to allocate memory for plugin"); + errno = ENOMEM; + return NULL; + } + // CI for Blocking mode only + plugin->fd_blocking_mode = 1; + plugin->nvv4l2argus_fd = index; + } + else + return NULL; + + return (void *)plugin; +} + +int plugin_ioctl(void *dev_ops_priv, int fd, + unsigned long int cmd, void *arg) +{ + int ret_val; + struct nvargus_plugin_ctx *plugin; + + plugin = (struct nvargus_plugin_ctx *)dev_ops_priv; + + ret_val = ArgusV4L2_Ioctl(plugin->nvv4l2argus_fd, cmd, arg); + + return ret_val; +} + +void plugin_close(void *dev_ops_priv) +{ + if (dev_ops_priv == NULL) + return; + struct nvargus_plugin_ctx *ctx = (struct nvargus_plugin_ctx *)dev_ops_priv; + ArgusV4L2_Close(ctx->nvv4l2argus_fd); + free(ctx); +} + +#ifdef __cplusplus +extern "C" +#endif +PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = +{ + .init = &plugin_init, + .close = &plugin_close, + .ioctl = &plugin_ioctl, +}; diff --git a/libv4l2_nvargus/nvargusv4l2.cpp b/libv4l2_nvargus/nvargusv4l2.cpp new file mode 100644 index 0000000..203191c --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/videodev2.h" +#include "nvargusv4l2_os.h" +#include "nvargusv4l2.h" +#include "nvargusv4l2_context.h" +#include "nvargusv4l2_ioctl.h" + +/* Default node path + */ +#define V4L2_ARGUS_DEV_NODE "/dev/video0" + +/* A global mutex used to protect simultaneous access + * while opening or closing the contexts + */ +NvMutexHandle global_mutex = NULL; + +bool runtime_logs_enabled = false; +static void *hLibHandle = NULL; + +void __attribute__((constructor)) nvargus_init(void); +void __attribute__((destructor)) nvargus_deinit(void); + +void __attribute__((constructor)) nvargus_init(void) +{ + struct stat file_stats; + if (stat("/tmp/argusv4l2_logs", &file_stats) == 0) + runtime_logs_enabled = true; + + hLibHandle = dlopen("libnvargus.so", RTLD_LAZY); + if (NULL == hLibHandle) + REL_PRINT("Error opening libnvargus.so in %s\n", __FUNCTION__); + + if (NvMutexCreate(&global_mutex) != 0) + REL_PRINT("Error creating the global mutex\n"); +} + +void __attribute__((destructor)) nvargus_deinit(void) +{ + NvMutexDestroy(global_mutex); + if (hLibHandle) + dlclose(hLibHandle); +} + +int32_t ArgusV4L2_Open(const int camera_index, int32_t flags) +{ + DBG_PRINT("Enter %s Camera Index %d\n", __FUNCTION__, camera_index); + + return v4l2_open_camera_context(camera_index, flags); +} + +int32_t ArgusV4L2_Close(int32_t fd) +{ + DBG_PRINT("Enter %s FD %d \n", __FUNCTION__, fd); + v4l2_close_camera_context(fd); + return 0; +} + +int32_t ArgusV4L2_Ioctl(int32_t fd, unsigned long cmd, ...) +{ + void *arg; + v4l2_context *ctx = v4l2_get_context(fd); + va_list listPointer; + + va_start(listPointer, cmd); + arg = va_arg(listPointer, void *); + va_end(listPointer); + + if ((ctx == NULL) || (arg == NULL)) + { + errno = EINVAL; + return -1; + } + + NvMutexAcquire(ctx->ioctl_mutex); + errno = nvargus_ioctl(fd, cmd, arg); + NvMutexRelease(ctx->ioctl_mutex); + + if (errno != 0) + return -1; + return errno; +} diff --git a/libv4l2_nvargus/nvargusv4l2_argus.cpp b/libv4l2_nvargus/nvargusv4l2_argus.cpp new file mode 100644 index 0000000..ccbb6aa --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2_argus.cpp @@ -0,0 +1,1101 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "nvargusv4l2_os.h" +#include "nvargusv4l2_nvqueue.h" +#include "nvbufsurface.h" +#include "nvargusv4l2_argus.h" + +static ArgusSamples::EGLDisplayHolder eglDisplay(true); + +const char* getStatusString(Argus::Status status) +{ + switch (status) + { + case Argus::STATUS_OK: return "OK"; + case Argus::STATUS_INVALID_PARAMS: return "INVALID_PARAMS"; + case Argus::STATUS_INVALID_SETTINGS: return "INVALID_SETTINGS"; + case Argus::STATUS_UNAVAILABLE: return "UNAVAILABLE"; + case Argus::STATUS_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; + case Argus::STATUS_UNIMPLEMENTED: return "UNIMPLEMENTED"; + case Argus::STATUS_TIMEOUT: return "TIMEOUT"; + case Argus::STATUS_CANCELLED: return "CANCELLED"; + case Argus::STATUS_DISCONNECTED: return "DISCONNECTED"; + case Argus::STATUS_END_OF_STREAM: return "END_OF_STREAM"; + default: return "BAD STATUS"; + } +} + +bool v4l2_cam_atomic_read_bool(v4l2_camera_context* ctx, bool *var) +{ + bool value = false; + NvMutexAcquire(ctx->stats_mutex); + value = *var; + NvMutexRelease(ctx->stats_mutex); + return value; +} + +void v4l2_cam_atomic_write_bool(v4l2_camera_context* ctx, bool *var, bool value) +{ + NvMutexAcquire(ctx->stats_mutex); + *var = value; + NvMutexRelease(ctx->stats_mutex); +} + +uint32_t v4l2_cam_atomic_get(v4l2_camera_context *ctx, uint32_t *var) +{ + uint32_t temp = 0; + NvMutexAcquire(ctx->stats_mutex); + temp = *var; + NvMutexRelease(ctx->stats_mutex); + return temp; +} + +int32_t initialize_outputstream(v4l2_camera_context *ctx, cam_params *argus_params) +{ + int32_t err = 0; + int32_t best_match = -1; + std::vector argusv4l2_sensormode = ctx->argusv4l2_sensormodes; + DBG_PRINT("CAM_CTX(%p): Initializing Argus::OutputStream\n", ctx); + + if (NvMutexCreate(&ctx->stats_mutex) != 0) + { + REL_PRINT("CAM_CTX(%p) Error creating stats mutex\n", ctx); + } + + err = NvSemaphoreCreate(&ctx->acquirebuf_sema, 0); + if (err) + { + REL_PRINT("CAM_CTX(%p) Failed to create acquirebuf_sema for thread\n", ctx); + return err; + } + /* Initialize streamsettings */ + ctx->m_streamSettings = Argus::UniqueObj( + ctx->m_iCaptureSession->createOutputStreamSettings(Argus::STREAM_TYPE_BUFFER, &ctx->m_cameraStatus)); + ctx->m_iStreamSettings = + Argus::interface_cast(ctx->m_streamSettings); + if (!ctx->m_iStreamSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to get stream settings: %s\n", + ctx, getStatusString(ctx->m_cameraStatus)); + return -1; + } + ctx->m_iStreamSettings->setBufferType(Argus::BUFFER_TYPE_EGL_IMAGE); + ctx->m_iStreamSettings->setMetadataEnable(true); + ctx->m_iStreamSettings->setSyncType(Argus::SYNC_TYPE_NONE); + // TODO: synctype EGL and the significance to make the initialization + + ctx->m_outputStream = Argus::UniqueObj( + ctx->m_iCaptureSession->createOutputStream(ctx->m_streamSettings.get(), &ctx->m_cameraStatus)); + ctx->m_iStream = + Argus::interface_cast(ctx->m_outputStream); + if (!ctx->m_iStream) + { + REL_PRINT("CAM_CTX(%p) Failed to get Buffer OutputStream: %s\n", + ctx, getStatusString(ctx->m_cameraStatus)); + return -1; + } + + ctx->m_request.reset(ctx->m_iCaptureSession->createRequest()); + ctx->m_iRequest = Argus::interface_cast(ctx->m_request); + if (!ctx->m_iRequest) + { + REL_PRINT("CAM_CTX(%p) Failed to create Request\n", ctx); + return -1; + } + ctx->m_iRequest->enableOutputStream(ctx->m_outputStream.get()); + + /* Get source settings pointer */ + ctx->m_iSourceSettings = + Argus::interface_cast(ctx->m_iRequest->getSourceSettings()); + + /* Get Control Setttings pointer */ + ctx->argus_settings.autoControlSettingsPtr = + Argus::interface_cast(ctx->m_iRequest->getAutoControlSettings()); + + /* Find best match */ + for (uint32_t idx = 0; idx < argusv4l2_sensormode.size(); idx++) + { + if (ctx->argus_settings.sensorModeIdx == -1) + { + if (argus_params->width <= argusv4l2_sensormode[idx].max_width && + argus_params->height <= argusv4l2_sensormode[idx].max_height && + ctx->argus_settings.frameDuration >= argusv4l2_sensormode[idx].frame_duration) + { + if (best_match == -1) + best_match = idx; + else if (argusv4l2_sensormode[best_match].frame_duration >= argusv4l2_sensormode[idx].frame_duration || + argusv4l2_sensormode[best_match].max_width >= argusv4l2_sensormode[idx].max_width) + best_match = idx; + } + } + } + if (best_match == -1) + { + DBG_PRINT("CAM_CTX(%p): Requested settings not supported. Switching to default camera mode\n", ctx); + ctx->argus_settings.sensorModeIdx = 0; + } + else + ctx->argus_settings.sensorModeIdx = best_match; + + ctx->argus_settings.frameDuration = + argusv4l2_sensormode[ctx->argus_settings.sensorModeIdx].frame_duration; + + Argus::SensorMode *sensorMode = ctx->m_sensorModes[ctx->argus_settings.sensorModeIdx]; + if (!sensorMode) + { + REL_PRINT("CAM_CTX(%p) Invalid SensorMode\n", ctx); + return -1; + } + ctx->m_iSourceSettings->setSensorMode(sensorMode); + ctx->m_iSourceSettings->setFrameDurationRange(Argus::Range(ctx->argus_settings.frameDuration)); + + return 0; +} + +int32_t v4l2_cam_set_argus_settings(v4l2_camera_context *ctx, uint32_t ctrl_id) +{ + DBG_PRINT("CAM_CTX(%p): %s: ENTER\n", ctx, __func__); + cam_settings *pArgusSettings = &ctx->argus_settings; + Argus::IAutoControlSettings *pACSettings = pArgusSettings->autoControlSettingsPtr; + + switch (ctrl_id) + { + case V4L2_CID_ARGUS_SENSOR_MODE: + { + uint32_t sensor_mode_id = pArgusSettings->sensorModeIdx; + Argus::SensorMode *sensorMode = ctx->m_sensorModes[sensor_mode_id]; + ctx->m_iSourceSettings->setSensorMode(sensorMode); + if (pArgusSettings->frameDuration < ctx->argusv4l2_sensormodes[sensor_mode_id].frame_duration) + { + DBG_PRINT("CAM_CTX(%p): Resetting the min frame duration for mode %d\n", ctx, sensor_mode_id); + pArgusSettings->frameDuration = ctx->argusv4l2_sensormodes[sensor_mode_id].frame_duration; + ctx->m_iSourceSettings->setFrameDurationRange(Argus::Range(pArgusSettings->frameDuration)); + } + } + break; + case V4L2_CID_ARGUS_DENOISE_MODE: + { + Argus::IDenoiseSettings *denoiseSettings = + Argus::interface_cast(ctx->m_request); + if (!denoiseSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to get DenoiseSettings interface\n", ctx); + return EINVAL; + } + pArgusSettings->denoiseSettingsPtr = denoiseSettings; + switch (pArgusSettings->denoiseMode) + { + case V4L2_ARGUS_DENOISE_MODE_OFF: + denoiseSettings->setDenoiseMode(Argus::DENOISE_MODE_OFF); + break; + case V4L2_ARGUS_DENOISE_MODE_FAST: + denoiseSettings->setDenoiseMode(Argus::DENOISE_MODE_FAST); + break; + case V4L2_ARGUS_DENOISE_MODE_HIGH_QUALITY: + denoiseSettings->setDenoiseMode(Argus::DENOISE_MODE_HIGH_QUALITY); + break; + default: + REL_PRINT("CAM_CTX(%p) Invalid Value. Setting to Denoise mode 0 as default\n", ctx); + denoiseSettings->setDenoiseMode(Argus::DENOISE_MODE_OFF); + pArgusSettings->denoiseMode = V4L2_ARGUS_DENOISE_MODE_OFF; + } + } + break; + case V4L2_CID_ARGUS_DENOISE_STRENGTH: + { + Argus::Status errStatus = Argus::STATUS_OK; + Argus::IDenoiseSettings *denoiseSettings = + Argus::interface_cast(ctx->m_request); + if (!denoiseSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to get DenoiseSettings interface\n", ctx); + return EINVAL; + } + pArgusSettings->denoiseSettingsPtr = denoiseSettings; + errStatus = denoiseSettings->setDenoiseStrength(pArgusSettings->denoiseStrength); + if (errStatus != Argus::STATUS_OK) + { + REL_PRINT("CAM_CTX(%p) Failed to get DenoiseSettings interface\n", ctx); + return EINVAL; + } + } + break; + case V4L2_CID_ARGUS_EE_MODE: + { + Argus::IEdgeEnhanceSettings *eeSettings = + Argus::interface_cast(ctx->m_request); + if (!eeSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to get EdgeEnhanceSettings interface\n", ctx); + return EINVAL; + } + pArgusSettings->EESettingsPtr = eeSettings; + switch (pArgusSettings->edgeEnhanceMode) + { + case V4L2_ARGUS_EDGE_ENHANCE_MODE_OFF: + eeSettings->setEdgeEnhanceMode(Argus::EDGE_ENHANCE_MODE_OFF); + break; + case V4L2_ARGUS_EDGE_ENHANCE_MODE_FAST: + eeSettings->setEdgeEnhanceMode(Argus::EDGE_ENHANCE_MODE_FAST); + break; + case V4L2_ARGUS_EDGE_ENHANCE_MODE_HIGH_QUALITY: + eeSettings->setEdgeEnhanceMode(Argus::EDGE_ENHANCE_MODE_HIGH_QUALITY); + break; + default: + REL_PRINT("CAM_CTX(%p) Invalid Value. Setting to EE mode 0 as default\n", ctx); + eeSettings->setEdgeEnhanceMode(Argus::EDGE_ENHANCE_MODE_OFF); + pArgusSettings->edgeEnhanceMode = V4L2_ARGUS_EDGE_ENHANCE_MODE_OFF; + } + } + break; + case V4L2_CID_ARGUS_EE_STRENGTH: + { + Argus::Status errStatus = Argus::STATUS_OK; + Argus::IEdgeEnhanceSettings *eeSettings = + Argus::interface_cast(ctx->m_request); + if (!eeSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to get EdgeEnhanceSettings interface\n", ctx); + return EINVAL; + } + pArgusSettings->EESettingsPtr = eeSettings; + errStatus = eeSettings->setEdgeEnhanceStrength(pArgusSettings->edgeEnhanceStrength); + if (errStatus != Argus::STATUS_OK) + { + REL_PRINT("CAM_CTX(%p) Failed to get DenoiseSettings interface\n", ctx); + return EINVAL; + } + } + break; + case V4L2_CID_ARGUS_AE_ANTIBANDING_MODE: + { + switch (pArgusSettings->aeAntibandingMode) + { + case V4L2_ARGUS_AE_ANTIBANDING_MODE_OFF: + pACSettings->setAeAntibandingMode(Argus::AE_ANTIBANDING_MODE_OFF); + break; + case V4L2_ARGUS_AE_ANTIBANDING_MODE_AUTO: + pACSettings->setAeAntibandingMode(Argus::AE_ANTIBANDING_MODE_AUTO); + break; + case V4L2_ARGUS_AE_ANTIBANDING_MODE_50HZ: + pACSettings->setAeAntibandingMode(Argus::AE_ANTIBANDING_MODE_50HZ); + break; + case V4L2_ARGUS_AE_ANTIBANDING_MODE_60HZ: + pACSettings->setAeAntibandingMode(Argus::AE_ANTIBANDING_MODE_60HZ); + break; + default: + pACSettings->setAeAntibandingMode(Argus::AE_ANTIBANDING_MODE_OFF); + pArgusSettings->aeAntibandingMode = V4L2_ARGUS_AE_ANTIBANDING_MODE_OFF; + } + } + break; + case V4L2_CID_ARGUS_AUTO_WHITE_BALANCE_MODE: + { + switch (pArgusSettings->awbMode) + { + case V4L2_ARGUS_AWB_MODE_OFF: + pACSettings->setAwbMode(Argus::AWB_MODE_OFF); + break; + case V4L2_ARGUS_AWB_MODE_AUTO: + pACSettings->setAwbMode(Argus::AWB_MODE_AUTO); + break; + case V4L2_ARGUS_AWB_MODE_INCANDESCENT: + pACSettings->setAwbMode(Argus::AWB_MODE_INCANDESCENT); + break; + case V4L2_ARGUS_AWB_MODE_FLUORESCENT: + pACSettings->setAwbMode(Argus::AWB_MODE_FLUORESCENT); + break; + case V4L2_ARGUS_AWB_MODE_WARM_FLUORESCENT: + pACSettings->setAwbMode(Argus::AWB_MODE_WARM_FLUORESCENT); + break; + case V4L2_ARGUS_AWB_MODE_DAYLIGHT: + pACSettings->setAwbMode(Argus::AWB_MODE_DAYLIGHT); + break; + case V4L2_ARGUS_AWB_MODE_CLOUDY_DAYLIGHT: + pACSettings->setAwbMode(Argus::AWB_MODE_CLOUDY_DAYLIGHT); + break; + case V4L2_ARGUS_AWB_MODE_TWILIGHT: + pACSettings->setAwbMode(Argus::AWB_MODE_TWILIGHT); + break; + case V4L2_ARGUS_AWB_MODE_SHADE: + pACSettings->setAwbMode(Argus::AWB_MODE_SHADE); + break; + case V4L2_ARGUS_AWB_MODE_MANUAL: + pACSettings->setAwbMode(Argus::AWB_MODE_MANUAL); + break; + default: + pACSettings->setAwbMode(Argus::AWB_MODE_OFF); + pArgusSettings->aeAntibandingMode = V4L2_ARGUS_AWB_MODE_OFF; + } + break; + } + case V4L2_CID_3A_LOCK: + { + if (pArgusSettings->aeLock) + pACSettings->setAeLock(true); + else + pACSettings->setAeLock(false); + + if (pArgusSettings->awbLock) + pACSettings->setAwbLock(true); + else + pACSettings->setAwbLock(false); + } + break; + case V4L2_CID_ARGUS_EXPOSURE_COMPENSATION: + { + if (pArgusSettings->exposureCompensation > MAX_EXPOSURE_COMPENSATION) + pArgusSettings->exposureCompensation = MAX_EXPOSURE_COMPENSATION; + + if (pArgusSettings->exposureCompensation < MIN_EXPOSURE_COMPENSATION) + pArgusSettings->exposureCompensation = MIN_EXPOSURE_COMPENSATION; + + pACSettings->setExposureCompensation(pArgusSettings->exposureCompensation); + } + break; + case V4L2_CID_ARGUS_ISP_DIGITAL_GAIN_RANGE: + pACSettings->setIspDigitalGainRange(pArgusSettings->ispDigitalGainRange); + break; + case V4L2_CID_ARGUS_COLOR_SATURATION: + { + pACSettings->setColorSaturationEnable(pArgusSettings->enableColorSaturation); + pACSettings->setColorSaturation(pArgusSettings->colorSaturation); + } + break; + case V4L2_CID_ARGUS_GAIN_RANGE: + ctx->m_iSourceSettings->setGainRange(pArgusSettings->gainRange); + break; + case V4L2_CID_ARGUS_EXPOSURE_TIME_RANGE: + ctx->m_iSourceSettings->setExposureTimeRange(pArgusSettings->exposureTimeRange); + break; + default: + break; + } + DBG_PRINT("CAM_CTX(%p) :%s: EXIT \n", ctx, __func__); + return 0; +} + +int32_t set_framerate(v4l2_camera_context *ctx, float frame_duration) +{ + if (ctx->argus_settings.frameDuration > frame_duration) + { + REL_PRINT("CAM_CTX(%p) Frame rate is greater than supported\n", ctx); + return EINVAL; + } + + ctx->m_iSourceSettings->setFrameDurationRange(Argus::Range(frame_duration)); + ctx->argus_settings.frameDuration = frame_duration; + return 0; +} + +int32_t ReleaseStreamBuffer(v4l2_camera_context *ctx, int32_t index) +{ + Argus::Buffer *buffer = ctx->cap_buffers[index].argus_buffer; + Argus::Status status = ctx->m_iStream->releaseBuffer(buffer); + if (status != Argus::STATUS_OK) + { + REL_PRINT("CAM_CTX(%p) Failed to release buffer: %s\n", + ctx, getStatusString(status)); + return EINVAL; + } + return 0; +} + +Argus::Buffer* AcquireStreamBuffer(v4l2_camera_context *ctx, uint32_t timeout) +{ + Argus::Status status = Argus::STATUS_OK; + Argus::Buffer *buffer = ctx->m_iStream->acquireBuffer(timeout, &status); + if (status == Argus::STATUS_END_OF_STREAM) + { + REL_PRINT("CAM_CTX(%p) %s signalled from the stream\n", + ctx, getStatusString(status)); + return NULL; + } + if (status != Argus::STATUS_OK) + { + REL_PRINT("CAM_CTX(%p) Failed to acquire buffer %s\n", + ctx, getStatusString(status)); + return NULL; + } + return buffer; +} + +static void FreeInputBuffers(camerav4l2_buffer *pBuffer, uint32_t buffer_idx, + v4l2_camera_context *ctx) +{ + NvBufSurfaceParams *pMMSurface = &pBuffer->surf_params; + NvBufSurfacePlaneParams *pPlanes = &pMMSurface->planeParams; + int32_t ret = 0; + uint32_t i = 0; + + /* Surfaces are freed only if we are not in DMABUF memory type */ + if (ctx->capture_memory_type != V4L2_MEMORY_DMABUF) + { + for (i = 0; i < pPlanes->num_planes; i++) + { + if (ctx->inbuf_mapped_address[buffer_idx][i] != NULL) + { + REL_PRINT("CAM_CTX(%p) NvRmMemUnmapping %d bytes surface %d plane %d address %p \n", + ctx, (pPlanes->pitch[i] * pPlanes->height[i]), + buffer_idx, i, + ctx->inbuf_mapped_address[buffer_idx][i]); + NvBufSurface *nvbuf_surf = 0; + + ret = NvBufSurfaceFromFd((int)pBuffer->buf_fd, + (void **)(&nvbuf_surf)); + if (ret != 0) + REL_PRINT("CAM_CTX(%p) Failed to Get NvBufSurface from FD\n", ctx); + + ret = NvBufSurfaceUnMap(nvbuf_surf, 0, i); + if (ret != 0) + REL_PRINT("CAM_CTX(%p) Failed to Unmap NvBufSurface\n", ctx); + ctx->inbuf_mapped_address[buffer_idx][i] = NULL; + + if (i == (pPlanes->num_planes - 1)) + { + ret = NvBufSurfaceDestroy(nvbuf_surf); + if (ret != 0) + REL_PRINT("CAM_CTX(%p) Failed to destroy NvBufSurface\n", ctx); + pBuffer->buf_fd = 0; + } + } + } + + if (pBuffer->buf_fd) + { + NvBufSurface *nvbuf_surf = 0; + + ret = NvBufSurfaceFromFd((int)pBuffer->buf_fd, + (void **)(&nvbuf_surf)); + if (ret != 0) + REL_PRINT("CAM_CTX(%p) Failed to Get NvBufSurface from FD\n", ctx); + + ret = NvBufSurfaceDestroy(nvbuf_surf); + if (ret != 0) + REL_PRINT("CAM_CTX(%p) Failed to destroy NvBufSurface\n", ctx); + pBuffer->buf_fd = 0; + } + pMMSurface->bufferDesc = 0; + /* Delete the allocated Argus::Buffers*/ + if (ctx->cap_buffers[buffer_idx].argus_buffer && ctx->cap_buffers[buffer_idx].allocated) + { + REL_PRINT("CAM_CTX(%p) Deleting the Argus Buffer at index %d\n", ctx, buffer_idx); + ctx->cap_buffers[buffer_idx].argus_buffer->destroy(); + } + + } + else + { + /* Delete the allocated Argus::Buffers*/ + if (ctx->cap_buffers[buffer_idx].argus_buffer && ctx->cap_buffers[buffer_idx].allocated) + { + REL_PRINT("CAM_CTX(%p) Deleting the Argus Buffer at index %d\n", ctx, buffer_idx); + ctx->cap_buffers[buffer_idx].argus_buffer->destroy(); + } + } + return; +} + +static void argus_cleanup_main(v4l2_camera_context *ctx, cam_params *params) +{ + REL_PRINT("CAM_CTX(%p) Cleaning up Argus \n", ctx); + if (ctx) + { + /* Cancel requests, explicitly stopping repeat captures */ + if (ctx->m_iCaptureSession) + { + ctx->m_iCaptureSession->cancelRequests(); + ctx->m_iCaptureSession->stopRepeat(); + ctx->m_iCaptureSession->waitForIdle(); + } + + /* Destroy the stream */ + if (ctx->m_streamSettings) + ctx->m_streamSettings.reset(); + if (ctx->m_request) + ctx->m_request.reset(); + if (ctx->m_outputStream) + ctx->m_outputStream.reset(); + if (ctx->m_eventQueue) + ctx->m_eventQueue.reset(); + + /* Shutdown Argus */ + if (ctx->m_captureSession) + ctx->m_captureSession.reset(); + + ctx->m_sensorModes.clear(); + ctx->m_eventTypes.clear(); + + ctx->m_iCaptureSession = NULL; + ctx->m_iEventProvider = NULL; + ctx->m_iEventQueue = NULL; + ctx->m_iCameraProperties = NULL; + ctx->m_iStreamSettings = NULL; + ctx->m_iStream = NULL; + ctx->m_iRequest = NULL; + ctx->m_iSourceSettings = NULL; + } +} + +void v4l2_close_argus_context(v4l2_context *v4l2_ctx) +{ + v4l2_camera_context *ctx = (v4l2_camera_context *)v4l2_ctx->actual_context; + REL_PRINT("CAM_CTX(%p) Cleaning up camera context \n", ctx); + + NvMutexAcquire(ctx->context_mutex); + eglDisplay.cleanup(); + free_cap_buffers(ctx); + argus_cleanup_main(ctx, &ctx->argus_params); + NvQueueDestroy(&ctx->capplane_Q); + + NvSemaphoreDestroy(ctx->acquirebuf_sema); + + NvMutexRelease(ctx->context_mutex); + NvMutexDestroy(ctx->context_mutex); + NvMutexDestroy(ctx->stats_mutex); + free(ctx); + + REL_PRINT("CAM_CTX(%p) Done deleting camera context \n", ctx); +} + +int32_t allocate_all_capture_queues(v4l2_camera_context *ctx) +{ + int32_t err = 0; + + DBG_PRINT("CAM_CTX(%p): Allocating capture queues\n", ctx); + + if (ctx->capplane_Q) + NvQueueDestroy(&ctx->capplane_Q); + + err = NvQueueCreate(&ctx->capplane_Q, ctx->capture_buffer_count, + sizeof(q_entry)); + if (err) + { + REL_PRINT("CAM_CTX(%p) Failed to create capture OutQ\n", ctx); + return EINVAL; + } + + return 0; +} + +/* + * Allocates the requested number of capture plane buffers along with argus buffers. + * It also destroys previously allocated buffers, if any, which exceeds + * the requested number. + */ + +int32_t allocate_capture_buffers(v4l2_camera_context *ctx, cam_params *argus_params) +{ + uint32_t i = 0; + int32_t ret = 0; + NvBufSurfaceAllocateParams alloc_params = {{0}}; + NvBufSurface *nvbuf_surf = NULL; + Argus::Status buffer_status = Argus::STATUS_OK; + + if (eglDisplay.get() == EGL_NO_DISPLAY) + { + if (!eglDisplay.initialize()) + { + REL_PRINT("CAM_CTX(%p) Failed to initialize EGLDisplay\n", ctx); + return EINVAL; + } + } + + /* Get Buffer Setttings for BufferOutputStream */ + Argus::UniqueObj settings(ctx->m_iStream->createBufferSettings()); + Argus::IEGLImageBufferSettings *iBufferSettings = + Argus::interface_cast(settings); + if (!iBufferSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to create Buffer Settings\n", ctx); + return EINVAL; + } + + for (i = 0; i < MAX_OP_BUFFERS; ++i) + { + if (i < ctx->capture_buffer_count) + { + /* To check if mapped in userspace*/ + if ((ctx->cap_buffers[i].flags) & (V4L2_BUF_FLAG_MAPPED)) + { + REL_PRINT("CAM_CTX(%p) Buffer %d already mapped in userspace\n", ctx, i); + return EBUSY; + } + if (ctx->cap_buffers[i].allocated && + !(v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped))) + { + REL_PRINT("CAM_CTX(%p) Buffer %d is in use\n", ctx, i); + return EBUSY; + } + if (!ctx->cap_buffers[i].allocated) + { + alloc_params.params.width = argus_params->width; + alloc_params.params.height = argus_params->height; + alloc_params.params.layout = NVBUF_LAYOUT_PITCH; + alloc_params.params.colorFormat = NVBUF_COLOR_FORMAT_NV12; + alloc_params.params.memType = NVBUF_MEM_SURFACE_ARRAY; + alloc_params.memtag = NvBufSurfaceTag_CAMERA; + + ret = NvBufSurfaceAllocate(&nvbuf_surf, 1, &alloc_params); + if (ret != 0) + { + REL_PRINT("CAM_CTX(%p) Failed to create NvBufSurface\n", ctx); + return ENOMEM; + } + nvbuf_surf->numFilled = 1; + memcpy(&ctx->cap_buffers[i].surf_params, + &nvbuf_surf->surfaceList[0], sizeof(NvBufSurfaceParams)); + ctx->cap_buffers[i].buf_fd = nvbuf_surf->surfaceList[0].bufferDesc; + + NvBufSurfaceMapEglImage(nvbuf_surf, 0); + EGLImageKHR eglImage = nvbuf_surf->surfaceList->mappedAddr.eglImage; + + if (eglImage == EGL_NO_IMAGE_KHR) + { + REL_PRINT("CAM_CTX(%p) Failed to create EGLImage from NvBuffer\n", ctx); + goto error_nomem; + } + iBufferSettings->setEGLImage(eglImage); + ctx->cap_buffers[i].argus_buffer = ctx->m_iStream->createBuffer(settings.get(), &buffer_status); + + /* The EGLImage may be destroyed once the Buffer has been created. */ + NvBufSurfaceUnMapEglImage(nvbuf_surf, 0); + + if (buffer_status != Argus::STATUS_OK || + (!Argus::interface_cast(ctx->cap_buffers[i].argus_buffer))) + { + REL_PRINT("CAM_CTX(%p) Failed to create Argus Buffer\n", ctx); + goto error_nomem; + } + + Argus::IBuffer *iBuffer = Argus::interface_cast(ctx->cap_buffers[i].argus_buffer); + + /* Map Argus::Buffer to NvBuffer */ + iBuffer->setClientData(&ctx->cap_buffers[i]); + ctx->cap_buffers[i].allocated = 1; + } + } + else + { + /* Destroy the allocated buffer if exceeds the capture_buffer_count. */ + if (ctx->cap_buffers[i].allocated) + { + FreeInputBuffers(&ctx->cap_buffers[i], i, ctx); + ctx->cap_buffers[i].allocated = 0; + } + } + } + + return 0; +error_nomem: + if (nvbuf_surf) + NvBufSurfaceDestroy(nvbuf_surf); + return ENOMEM; +} + +/* + * Allocates an argus buffer for the corresponding queried DMA buffer. + * The user interface is responsible for destroying the FD in case of error. + */ + +int32_t allocate_argus_buffers(v4l2_camera_context *ctx, cam_params *argus_params, + int32_t dmabuf_fd, uint32_t buffer_idx) +{ + int32_t ret = 0; + NvBufSurface *nvbuf_surf = 0; + Argus::Status buffer_status = Argus::STATUS_OK; + + /* Querybuf can be called multiple times. We just ignore argus allocation call at that time. */ + if (ctx->cap_buffers[buffer_idx].allocated && + !(v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped))) + { + DBG_PRINT("CAM_CTX(%p): Buffer %d already allocated\n", ctx, buffer_idx); + return 0; + } + + /* Get Buffer Setttings for BufferOutputStream */ + Argus::UniqueObj settings(ctx->m_iStream->createBufferSettings()); + Argus::IEGLImageBufferSettings *iBufferSettings = + Argus::interface_cast(settings); + if (!iBufferSettings) + { + REL_PRINT("CAM_CTX(%p) Failed to create Buffer Settings\n", ctx); + return EINVAL; + } + + if (!ctx->cap_buffers[buffer_idx].allocated) + { + ret = NvBufSurfaceFromFd(dmabuf_fd, (void **)(&nvbuf_surf)); + if (ret != 0) + { + REL_PRINT("CAM_CTX(%p) Failed to get buffer from FD\n", ctx); + return EINVAL; + } + memcpy(&ctx->cap_buffers[buffer_idx].surf_params, + &nvbuf_surf->surfaceList[0], sizeof(NvBufSurfaceParams)); + ctx->cap_buffers[buffer_idx].buf_fd = dmabuf_fd; + ctx->cap_buffers[buffer_idx].buffer_id = buffer_idx; + + NvBufSurfaceMapEglImage(nvbuf_surf, 0); + EGLImageKHR eglImage = nvbuf_surf->surfaceList->mappedAddr.eglImage; + if (eglImage == EGL_NO_IMAGE_KHR) + { + REL_PRINT("CAM_CTX(%p) Failed to create EGLImage from NvBuffer\n", ctx); + return EINVAL; + } + iBufferSettings->setEGLImage(eglImage); + ctx->cap_buffers[buffer_idx].argus_buffer = ctx->m_iStream->createBuffer(settings.get(), &buffer_status); + + // The EGLImage may be destroyed once the Buffer has been created. + NvBufSurfaceUnMapEglImage(nvbuf_surf, 0); + + if (buffer_status != Argus::STATUS_OK || + (!Argus::interface_cast(ctx->cap_buffers[buffer_idx].argus_buffer))) + { + REL_PRINT("CAM_CTX(%p) Failed to create Argus Buffer\n", ctx); + return EINVAL; + } + + Argus::IBuffer *iBuffer = + Argus::interface_cast(ctx->cap_buffers[buffer_idx].argus_buffer); + + /* Map Argus::Buffer to NvBuffer */ + iBuffer->setClientData(&ctx->cap_buffers[buffer_idx]); + ctx->cap_buffers[buffer_idx].allocated = 1; + } + + return 0; +} + +void free_cap_buffers(v4l2_camera_context *ctx) +{ + uint32_t i = 0; + REL_PRINT("CAM_CTX(%p) Releasing buffers of the camera ctx\n", ctx); + for (i = 0; i < ctx->capture_buffer_count; i++) + { + if (ctx->cap_buffers[i].allocated) + { + FreeInputBuffers(&ctx->cap_buffers[i], i, ctx); + ctx->cap_buffers[i].allocated = 0; + } + if (ctx->frame_metadata[i]) + { + free(ctx->frame_metadata[i]); + ctx->frame_metadata[i] = NULL; + } + } +} + +int32_t query_cam_buffers(v4l2_camera_context *ctx, cam_params *argus_params, + uint64_t *psizes) +{ + int32_t ret = 0; + NvBufSurface *nvbuf_surf = NULL; + NvBufSurfaceAllocateParams alloc_params = {{0}}; + + if (!argus_params->width || !argus_params->height) + { + ERROR_PRINT("CAM_CTX(%p) Invalid width= %d height= %d\n", + ctx, argus_params->width, argus_params->height); + return EINVAL; + } + + alloc_params.params.width = argus_params->width; + alloc_params.params.height = argus_params->height; + alloc_params.params.layout = NVBUF_LAYOUT_PITCH; + alloc_params.params.colorFormat = NVBUF_COLOR_FORMAT_NV12; + alloc_params.params.memType = NVBUF_MEM_SURFACE_ARRAY; + alloc_params.memtag = NvBufSurfaceTag_CAMERA; + + ret = NvBufSurfaceAllocate(&nvbuf_surf, 1, &alloc_params); + if (ret != 0) + { + ERROR_PRINT("CAM_CTX(%p) Unable to allocate HW buffer structure\n", ctx); + return ENOMEM; + } + nvbuf_surf->numFilled = 1; + NvBufSurfaceParams *surface_params = &nvbuf_surf->surfaceList[0]; + NvBufSurfacePlaneParams *pPlanes = &surface_params->planeParams; + + /* Determine the size of all planes. DMABUF require no mem_offsets information.*/ + for (uint32_t j = 0; j < pPlanes->num_planes; j++) + { + psizes[j] = pPlanes->psize[j]; + } + + NvBufSurfaceDestroy(nvbuf_surf); + nvbuf_surf = NULL; + + return 0; +} + +int32_t nvargus_enqueue_instream_buffers_from_capture_inQ(v4l2_camera_context *ctx, int32_t buffer_idx) +{ + DBG_PRINT("CAM_CTX(%p): %s : Enter\n", ctx, __func__); + + if (ctx->m_cameraStatus == Argus::STATUS_OK) + { + if (ReleaseStreamBuffer(ctx, buffer_idx) != 0) + { + REL_PRINT("CAM_CTX(%p) Error in releasing buffer Argus\n", ctx); + v4l2_cam_atomic_write_bool(ctx, &ctx->error_flag, true); + return EINVAL; + } + + ctx->num_queued_capture_buffers++; + } + else + { + REL_PRINT("CAM_CTX(%p) Argus in error : %s\n", ctx, getStatusString(ctx->m_cameraStatus)); + v4l2_cam_atomic_write_bool(ctx, &ctx->error_flag, true); + return EINVAL; + } + + DBG_PRINT("CAM_CTX(%p): %s : Buffer successfully enqueued\n", ctx, __func__); + return 0; +} + +static int32_t nvargus_copy_outstream_buffers_to_capplane_Q(v4l2_camera_context *ctx) +{ + q_entry cap_entry; + uint32_t index = 0; + argusframe_metadata *frame_metadata = NULL; + frame_metadata = (argusframe_metadata *)malloc(sizeof(argusframe_metadata)); + if (!frame_metadata) + { + REL_PRINT("Unable to allocate memory for Metadata\n"); + } + + DBG_PRINT("CAM_CTX(%p): %s: Enter\n", ctx, __func__); + + if (NULL == ctx) + return 0; + + while (ctx->m_cameraStatus == Argus::STATUS_OK) + { + memset((void *)frame_metadata, 0x0, sizeof(struct argusframe_metadata)); + memset(&cap_entry, 0, sizeof(q_entry)); + + /* Two seconds timeout to wait for an event. Core-SCF fix in Bug 200636333 */ + ctx->m_iEventProvider->waitForEvents(ctx->m_eventQueue.get(), TIMEOUT_FOUR_SECONDS); + if (ctx->m_iEventQueue->getSize() == 0) + { + if (ctx->stream_on) + { + REL_PRINT("CAM_CTX(%p) %s: Timeout. No events queued\n", ctx, __func__); + v4l2_cam_atomic_write_bool(ctx, &ctx->camera_state_stopped, true); + NvSemaphoreSignal(ctx->acquirebuf_sema); + } + if (frame_metadata) + free(frame_metadata); + return 0; + } + + Argus::Event *event = + (Argus::Event *)(ctx->m_iEventQueue->getEvent(ctx->m_iEventQueue->getSize() - 1)); + Argus::IEvent *iEvent = (Argus::IEvent *)Argus::interface_cast(event); + if (!iEvent) + { + v4l2_cam_atomic_write_bool(ctx, &ctx->error_flag, true); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + if (iEvent->getEventType() == Argus::EVENT_TYPE_ERROR) + { + if (v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped)) + { + if (frame_metadata) + free(frame_metadata); + return 0; + } + + const Argus::IEventError* iEventError = Argus::interface_cast(event); + ctx->m_cameraStatus = iEventError->getStatus(); + ERROR_PRINT("CAM_CTX(%p) ERROR Generated. ARGUS Status : %s\n", + ctx, getStatusString(ctx->m_cameraStatus)); + cap_entry.size = 0; + cap_entry.index = index; + cap_entry.fd = 0; + if (NvQueueEnQ(ctx->capplane_Q, &cap_entry) != 0) + { + REL_PRINT("CAM_CTX(%p) Error while enqueuing into captureplane_outQ\n", ctx); + v4l2_cam_atomic_write_bool(ctx, &ctx->error_flag, true); + } + v4l2_cam_atomic_write_bool(ctx, &ctx->camera_state_stopped, true); + v4l2_cam_atomic_write_bool(ctx, &ctx->error_flag, true); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + /* We only check for EVENT_TYPE_ERROR event and proceed with acquireBuffer*/ + Argus::Buffer *argus_buffer = AcquireStreamBuffer(ctx, TIMEOUT_TWO_SECONDS); + if (!argus_buffer) + { + REL_PRINT("CAM_CTX(%p) %s : NULL Stream Buffer\n", ctx, __func__); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + auto argus_iBuffer = Argus::interface_cast(argus_buffer); + const camerav4l2_buffer *data_buffer = + static_cast(argus_iBuffer->getClientData()); + if (!data_buffer) + { + REL_PRINT("CAM_CTX(%p) %s : NULL NvBuffer\n", ctx, __func__); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + index = data_buffer->buffer_id; + + if (!(ctx->cap_buffers[data_buffer->buffer_id].flags & + V4L2_BUF_FLAG_QUEUED)) + { + REL_PRINT("CAM_CTX(%p) Acquired buffer not queued in stream\n", ctx); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + if (index >= ctx->capture_buffer_count) + { + REL_PRINT("CAM_CTX(%p) Received wrong index in the capture_entry %d \n", ctx, index); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + /* Get Buffer metadata */ + ctx->m_captureMetadata = argus_iBuffer->getMetadata(); + const Argus::ICaptureMetadata *iMetadata = + Argus::interface_cast(ctx->m_captureMetadata); + + if (!iMetadata) + { + REL_PRINT("CAM_CTX(%p) Failed to get Capture Metadata for buffer idx %d\n", + ctx, data_buffer->buffer_id); + NvSemaphoreSignal(ctx->acquirebuf_sema); + if (frame_metadata) + free(frame_metadata); + return EINVAL; + } + + if (frame_metadata) + { + frame_metadata->id = iMetadata->getCaptureId(); + frame_metadata->aeLocked = iMetadata->getAeLocked(); + frame_metadata->aeState = iMetadata->getAeState(); + frame_metadata->focuserPosition = iMetadata->getFocuserPosition(); + frame_metadata->awbCct = iMetadata->getAwbCct(); + frame_metadata->awbState = iMetadata->getAwbState(); + frame_metadata->colorCorrectionMatrixEnable = iMetadata->getColorCorrectionMatrixEnable(); + frame_metadata->frameDuration = iMetadata->getFrameDuration(); + frame_metadata->ispDigitalGain = iMetadata->getIspDigitalGain(); + frame_metadata->frameReadoutTime = iMetadata->getFrameReadoutTime(); + frame_metadata->sceneLux = iMetadata->getSceneLux(); + frame_metadata->sensorAnalogGain = iMetadata->getSensorAnalogGain(); + frame_metadata->sensorExposureTime = iMetadata->getSensorExposureTime(); + frame_metadata->sensorSensitivity = iMetadata->getSensorSensitivity(); + + if (ctx->frame_metadata[index] == NULL) + { + ctx->frame_metadata[index] = (argusframe_metadata *)malloc(sizeof(argusframe_metadata)); + memset((void *)ctx->frame_metadata[index], 0x0, sizeof(struct argusframe_metadata)); + } + + *ctx->frame_metadata[index] = *frame_metadata; + + DBG_PRINT("CAM_CTX(%p): Metadata ID %d AeLock %d FrameDuration %ld " + "DigitalGainRange %f SceneLux %f SensorAnalogGain %f SensorExpTime %ld\n", + ctx, frame_metadata->id, frame_metadata->aeLocked, + frame_metadata->frameDuration, frame_metadata->ispDigitalGain, + frame_metadata->sceneLux, frame_metadata->sensorAnalogGain, + frame_metadata->sensorExposureTime); + } + cap_entry.size = data_buffer->surf_params.planeParams.psize[0]; + cap_entry.index = index; + cap_entry.fd = data_buffer->buf_fd; + static const uint64_t ground_clk = iMetadata->getSensorTimestamp(); + cap_entry.timestamp = iMetadata->getSensorTimestamp() - ground_clk; + + DBG_PRINT("CAM_CTX(%p): %s : capplane_Q index = %d Timestamp sec %ld usec %ld\n", + ctx, __func__, cap_entry.index, + (uint64_t)(cap_entry.timestamp / (1000000)), (uint64_t)(cap_entry.timestamp % (1000000))); + + if (NvQueueEnQ(ctx->capplane_Q, &cap_entry) != 0) + { + REL_PRINT("CAM_CTX(%p) Error while enqueuing into captureplane_outQ\n", ctx); + NvSemaphoreSignal(ctx->acquirebuf_sema); + free(frame_metadata); + return EINVAL; + } + NvSemaphoreSignal(ctx->acquirebuf_sema); + break; + + } + + if (frame_metadata) + free(frame_metadata); + return 0; +} + +void argus_capture_thread_func(void *args) +{ + v4l2_camera_context *ctx = (v4l2_camera_context *)args; + DBG_PRINT("CAM_CTX(%p): argus_capture_thread created\n", ctx); + + while (!v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped)) + { + NvMutexAcquire(ctx->context_mutex); + + if (v4l2_cam_atomic_read_bool(ctx, &ctx->error_flag)) + { + REL_PRINT("CAM_CTX(%p) Error encountered. Exiting capture\n", ctx); + NvSemaphoreSignal(ctx->acquirebuf_sema); + break; + } + if (ctx->stream_on) + { + nvargus_copy_outstream_buffers_to_capplane_Q(ctx); + // Check for polling + } + + NvMutexRelease(ctx->context_mutex); + } + DBG_PRINT("CAM_CTX(%p): Exiting from argus_capture_thread\n", ctx); +} diff --git a/libv4l2_nvargus/nvargusv4l2_context.cpp b/libv4l2_nvargus/nvargusv4l2_context.cpp new file mode 100644 index 0000000..ecbab8f --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2_context.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* This file implements the context related interface methods.*/ + +#include +#include +#include +#include +#include "linux/videodev2.h" +#include "nvargusv4l2_os.h" +#include "nvargusv4l2.h" +#include "nvargusv4l2_context.h" +#include "nvargusv4l2_ioctl.h" + +static v4l2_context *contexts[MAX_CONTEXTS]; +static int32_t next_free_index; +static int32_t total_open_instances; +extern NvMutexHandle global_mutex; + +Argus::CameraProvider *g_cameraProvider = NULL; +Argus::ICameraProvider *g_iCameraProvider = NULL; +std::vector g_cameraDevices; // All devices (sensors) + +v4l2_context* v4l2_get_context(int32_t fd) +{ + if (contexts[fd] == NULL) + { + REL_PRINT("NULL context on given FD\n"); + return NULL; + } + return contexts[fd]; +} + +int32_t initialize_camera_provider() +{ + if (!g_cameraProvider) + { + g_cameraProvider = Argus::CameraProvider::create(); + if (!g_cameraProvider) + { + REL_PRINT("Failed to create Argus CameraProvider\n"); + return -1; + } + } + /* Get the core interface. */ + g_iCameraProvider = Argus::interface_cast( + g_cameraProvider); + if (!g_iCameraProvider) + { + REL_PRINT("Argus does not support core camera interface\n"); + return -1; + } + + /* Get the list of CameraDevices to test valid execution. */ + if (g_iCameraProvider->getCameraDevices(&g_cameraDevices) != Argus::STATUS_OK) + { + REL_PRINT("Failed to get camera devices\n"); + return -1; + } + + if (g_cameraDevices.size() == 0) + return -1; + + return 0; +} + +void v4l2_argus_fill_sensor_modes(std::vector sensorModes, + std::vector *argusv4l2_sensormodes) +{ + printf("Available Sensor modes :\n"); + for (uint32_t idx = 0; idx < sensorModes.size(); ++idx) + { + Argus::ISensorMode *iSensorMode = NULL; + argusv4l2_sensormode argusv4l2_smidx; + iSensorMode = Argus::interface_cast(sensorModes[idx]); + argusv4l2_smidx.min_width = FRAMESIZE_MIN_WIDTH; + argusv4l2_smidx.min_height = FRAMESIZE_MIN_HEIGHT; + argusv4l2_smidx.max_width = iSensorMode->getResolution().width(); + argusv4l2_smidx.max_height = iSensorMode->getResolution().height(); + argusv4l2_smidx.frame_duration = iSensorMode->getFrameDurationRange().min(); + argusv4l2_smidx.bitdepth = iSensorMode->getInputBitDepth(); + argusv4l2_smidx.minGainRange = iSensorMode->getAnalogGainRange().min(); + argusv4l2_smidx.maxGainRange = iSensorMode->getAnalogGainRange().max(); + argusv4l2_smidx.minExposureTimeRange = iSensorMode->getExposureTimeRange().min(); + argusv4l2_smidx.maxExposureTimeRange = iSensorMode->getExposureTimeRange().max(); + argusv4l2_sensormodes->push_back(argusv4l2_smidx); + printf("Resolution: %d x %d ; Framerate = %f; Analog Gain Range Min %f, Max %f, " + "Exposure Range Min %lu, Max %lu\n\n", iSensorMode->getResolution().width(), + iSensorMode->getResolution().height(), (1e9/iSensorMode->getFrameDurationRange().min()), + iSensorMode->getAnalogGainRange().min(), iSensorMode->getAnalogGainRange().max(), + iSensorMode->getExposureTimeRange().min(), iSensorMode->getExposureTimeRange().max()); + } +} + +void v4l2_argus_set_defaults(cam_params *argus_defaults, cam_settings *argus_settings) +{ + /* Argus Defaults */ + argus_defaults->width = 640; + argus_defaults->height = 480; + argus_defaults->pixelformat = V4L2_PIX_FMT_NV12M; + argus_defaults->argus_format = Argus::PIXEL_FMT_YCbCr_420_888; + argus_defaults->bitdepth = 8; + argus_settings->sensorModeIdx = -1; + argus_settings->frameDuration = 1e9/30; + argus_settings->awbLock = false; + argus_settings->aeLock = false; + argus_settings->aeAntibandingMode = V4L2_ARGUS_AE_ANTIBANDING_MODE_AUTO; + argus_settings->awbMode = V4L2_ARGUS_AWB_MODE_AUTO; + argus_settings->edgeEnhanceMode = V4L2_ARGUS_EDGE_ENHANCE_MODE_FAST; + argus_settings->edgeEnhanceStrength = -1.0; + argus_settings->denoiseMode = V4L2_ARGUS_DENOISE_MODE_FAST; + argus_settings->denoiseStrength = -1.0; + argus_settings->exposureCompensation = 0.0; + argus_settings->ispDigitalGainRange = Argus::Range(1.0, 256.0); + argus_settings->enableColorSaturation = true; + argus_settings->colorSaturation = 1.0; + argus_settings->gainRange = Argus::Range(1.0, 16.0); +} + +// This call checks if we can register the plugin under Argus +int32_t initialize_arguscamera(v4l2_camera_context *ctx, uint32_t camera_index) +{ + /* Create the CameraProvider. */ + if (initialize_camera_provider() != 0) + { + REL_PRINT("Failed to initialize camera devices\n"); + goto error; + } + + ctx->m_captureSession = Argus::UniqueObj( + g_iCameraProvider->createCaptureSession(g_cameraDevices[camera_index])); + ctx->m_iCaptureSession = + Argus::interface_cast(ctx->m_captureSession); + + if (!ctx->m_iCaptureSession) + { + REL_PRINT("Failed to get ICaptureSession interface\n"); + goto error; + } + ctx->m_iEventProvider = + Argus::interface_cast(ctx->m_captureSession); + if (!ctx->m_iEventProvider) + { + REL_PRINT("Failed to get IEventProvider interface\n"); + goto error; + } + /* Argus drops EVENT_TYPE_ERROR if all 3 events are not subscribed. Setting all for now */ + ctx->m_eventTypes.push_back(Argus::EVENT_TYPE_CAPTURE_COMPLETE); + ctx->m_eventTypes.push_back(Argus::EVENT_TYPE_CAPTURE_STARTED); + ctx->m_eventTypes.push_back(Argus::EVENT_TYPE_ERROR); + + ctx->m_eventQueue = Argus::UniqueObj( + ctx->m_iEventProvider->createEventQueue(ctx->m_eventTypes)); + ctx->m_iEventQueue = Argus::interface_cast(ctx->m_eventQueue); + if (!ctx->m_iEventQueue) + { + REL_PRINT("Failed to get EventQueue interface\n"); + goto error; + } + + ctx->m_iCameraProperties = Argus::interface_cast( + g_cameraDevices[camera_index]); + if (!ctx->m_iCameraProperties) + { + REL_PRINT("Failed to create camera properties\n"); + goto error; + } + ctx->m_iCameraProperties->getAllSensorModes(&ctx->m_sensorModes); + if (ctx->m_sensorModes.size() == 0) + { + REL_PRINT("No Sensor Mode found associated with the camera node\n"); + goto error; + } + + v4l2_argus_fill_sensor_modes(ctx->m_sensorModes, &ctx->argusv4l2_sensormodes); + v4l2_argus_set_defaults(&ctx->argus_params, &ctx->argus_settings); + + return 0; +error: + if (g_cameraProvider) + g_cameraProvider->destroy(); + if (ctx->m_captureSession) + ctx->m_captureSession.reset(); + if (ctx->m_eventQueue) + ctx->m_eventQueue.reset(); + g_cameraDevices.clear(); + ctx->m_sensorModes.clear(); + ctx->m_eventTypes.clear(); + g_iCameraProvider = NULL; + ctx->m_iCaptureSession = NULL; + ctx->m_iEventProvider = NULL; + ctx->m_iEventQueue = NULL; + ctx->m_iCameraProperties = NULL; + return -1; +} + +int32_t v4l2_open_camera_context(uint32_t camera_index, int32_t flags) +{ + int32_t index = 0, loop_cnt = 0; + v4l2_context *ctx = NULL; + v4l2_camera_context *camera_ctx = NULL; + + v4l2_lock_global_mutex(); + index = next_free_index; + for (loop_cnt = 0; loop_cnt < MAX_CONTEXTS; ++loop_cnt) + { + if (contexts[index] == NULL) + { + contexts[index] = (v4l2_context *)malloc(sizeof(v4l2_context)); + ctx = contexts[index]; + memset(contexts[index], 0x0, sizeof(v4l2_context)); + REL_PRINT("Allocated ctx %p at idx %d\n", contexts[index], index); + + if (NvMutexCreate(&ctx->ioctl_mutex) != 0) + { + REL_PRINT("Error in creating ioctl_mutex\n"); + free(contexts[index]); + goto ctx_error; + } + + camera_ctx = (v4l2_camera_context *)malloc(sizeof(v4l2_camera_context)); + if (camera_ctx == NULL) + { + REL_PRINT("Error allocating argus context\n"); + free(contexts[index]); + goto ctx_error; + } + memset((void*)camera_ctx, 0x0, sizeof(v4l2_camera_context)); + + if (initialize_arguscamera(camera_ctx, camera_index) < 0) + { + REL_PRINT("Failed to initialize camera\n"); + free(camera_ctx); + free(contexts[index]); + goto ctx_error; + } + + ctx->actual_context = (void *)camera_ctx; + total_open_instances++; + + if (NvMutexCreate(&camera_ctx->context_mutex) != 0) + { + REL_PRINT("Error creating context mutex\n"); + free(camera_ctx); + free(contexts[index]); + goto ctx_error; + } + camera_ctx->blocking_mode = ! (flags & O_NONBLOCK); + next_free_index = index + 1; + if (next_free_index == 128) + next_free_index = 0; + v4l2_unlock_global_mutex(); + return index; + } + index++; + if (index == MAX_CONTEXTS) + index = 0; + } + REL_PRINT("No free index left , something is wrong \n"); +ctx_error: + v4l2_unlock_global_mutex(); + return -1; +} + +void v4l2_close_camera_context(int32_t fd) +{ + v4l2_context *ctx = v4l2_get_context(fd); + + REL_PRINT("CAM_CTX(%p) Closing the context %s\n", ctx->actual_context, __func__); + /* Close the camera context */ + NvMutexAcquire(ctx->ioctl_mutex); + + v4l2_lock_global_mutex(); + total_open_instances--; + v4l2_close_argus_context(ctx); + + NvMutexRelease(ctx->ioctl_mutex); + NvMutexDestroy(ctx->ioctl_mutex); + free(ctx); + contexts[fd] = NULL; + if (total_open_instances == 0) + { + if (g_cameraProvider) + g_cameraProvider->destroy(); + g_cameraDevices.clear(); + g_iCameraProvider = NULL; + } + REL_PRINT("Total Opened instances: %d\n", total_open_instances); + v4l2_unlock_global_mutex(); +} + +void v4l2_lock_global_mutex(void) +{ + assert(global_mutex != NULL); + NvMutexAcquire(global_mutex); +} + +void v4l2_unlock_global_mutex(void) +{ + assert(global_mutex != NULL); + NvMutexRelease(global_mutex); +} diff --git a/libv4l2_nvargus/nvargusv4l2_ioctl.cpp b/libv4l2_nvargus/nvargusv4l2_ioctl.cpp new file mode 100644 index 0000000..d3733f9 --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2_ioctl.cpp @@ -0,0 +1,1089 @@ +/* + * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "nvbufsurface.h" +#include "nvargusv4l2_ioctl.h" + +#define NVARGUS_CARD_TYPE "NvV4L2 Argus PLugin" +#define NVARGUS_BUS_INFO "NV-ARGUS" +#define NVARGUS_VERSION 1.0 + +static struct argusv4l2_fmt_struct capture_formats[] = { + { + "YUV 4:2:0", + Argus::PIXEL_FMT_YCbCr_420_888, + V4L2_PIX_FMT_NV12M, + 2, + 8, + }, + { + "", + Argus::PIXEL_FMT_UNKNOWN, + 0, + 0, + 0, + }, +}; + +/*To get the v4l2_camera context*/ +static v4l2_camera_context* get_v4l2_context_from_fd(int32_t fd) +{ + v4l2_context *v4l2_ctx = v4l2_get_context(fd); + if (v4l2_ctx) + { + v4l2_camera_context *ctx = (v4l2_camera_context *)v4l2_ctx->actual_context; + if (NULL == ctx) + return NULL; + else + return ctx; + } + else + return NULL; +} + +/* To start the argusv4l2 capture thread */ +static int32_t argus_capture_thread_start(v4l2_camera_context *ctx) +{ + int32_t error = 0; + if (!ctx->argus_capture_thread) + { + error = + NvThreadCreate((NvThreadFunction)argus_capture_thread_func, ctx, &ctx->argus_capture_thread); + if (error) + { + REL_PRINT("CAM_CTX(%p) Failed to create Argus capture thread\n", ctx); + return error; + } + NvThreadSetName(ctx->argus_capture_thread, "V4L2_CapThread"); + } + return error; +} + +/* To stop the argusv4l2 thread */ +static void argus_capture_thread_shutdown(v4l2_camera_context *ctx) +{ + if (ctx->argus_capture_thread) + { + REL_PRINT("CAM_CTX(%p) %s: Wait on join to thread argus_capture_thread_func\n", + ctx, __func__); + NvThreadJoin(ctx->argus_capture_thread); + ctx->argus_capture_thread = NULL; + } +} + +int32_t vidioc_cam_querycap(int32_t fd, struct v4l2_capability *caps) +{ + memset(caps, 0x0, sizeof(struct v4l2_capability)); + + caps->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; + caps->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; + caps->capabilities = caps->device_caps | V4L2_CAP_DEVICE_CAPS; + + memcpy((char*)caps->driver, g_iCameraProvider->getVersion().c_str(), + (strnlen(g_iCameraProvider->getVersion().c_str(), (sizeof(caps->driver) - 1)) + 1)); + + strncpy((char*)caps->card, NVARGUS_CARD_TYPE, sizeof(caps->card)); + snprintf((char*)caps->bus_info, sizeof(caps->bus_info), "platform:%s:%f", NVARGUS_BUS_INFO, NVARGUS_VERSION); + return 0; +} + +int32_t vidioc_cam_enum_fmt(int32_t fd, struct v4l2_fmtdesc *fmt) +{ + uint32_t i; + for (i = 0; ; ++i) + { + if (capture_formats[i].fourcc == 0) + return EINVAL; + if (i == fmt->index) + { + fmt->pixelformat = capture_formats[i].fourcc; + memcpy((char *)fmt->description, capture_formats[i].name, + (strnlen(capture_formats[i].name, (sizeof(fmt->description) - 1)) + 1)); + + return 0; + } + } + return 0; +} + +int32_t vidioc_cam_enum_framesizes(int32_t fd, struct v4l2_frmsizeenum *framesizes) +{ + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + std::vector argusv4l2_sensormode = ctx->argusv4l2_sensormodes; + + uint32_t sensormode_size = argusv4l2_sensormode.size(); + if (framesizes->index >= sensormode_size) + return EINVAL; + + for (uint32_t i = 0; ; ++i) + { + if (capture_formats[i].fourcc == 0) + return EINVAL; + if (capture_formats[i].fourcc == framesizes->pixel_format) + { + for (uint32_t j = 0; j < sensormode_size; ++j) + { + if (j == framesizes->index) + { + framesizes->type = V4L2_FRMSIZE_TYPE_STEPWISE; + framesizes->stepwise.min_width = argusv4l2_sensormode[j].min_width; + framesizes->stepwise.min_height = argusv4l2_sensormode[j].min_height; + framesizes->stepwise.max_width = argusv4l2_sensormode[j].max_width; + framesizes->stepwise.max_height = argusv4l2_sensormode[j].max_height; + framesizes->stepwise.step_width = 1; + framesizes->stepwise.step_height = 1; + return 0; + } + } + } + } + + return 0; +} + +int32_t vidioc_cam_enum_frameintervals(int32_t fd, struct v4l2_frmivalenum *frameival) +{ + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + std::vector argusv4l2_sensormode = ctx->argusv4l2_sensormodes; + + uint32_t sensormode_size = argusv4l2_sensormode.size(); + if (frameival->index >= sensormode_size) + return EINVAL; + + for (uint32_t i = 0; ; ++i) + { + if (capture_formats[i].fourcc == 0) + return EINVAL; + if (capture_formats[i].fourcc == frameival->pixel_format) + { + for (uint32_t j = 0; j < sensormode_size; ++j) + { + if (argusv4l2_sensormode[j].max_width == frameival->width && + argusv4l2_sensormode[j].max_height == frameival->height) + { + frameival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + frameival->stepwise.min.numerator = 1; + frameival->stepwise.min.denominator = 1; + frameival->stepwise.max.numerator = argusv4l2_sensormode[j].frame_duration; + frameival->stepwise.max.denominator = 1e9; + frameival->stepwise.step.numerator = 1; + frameival->stepwise.step.denominator = 1; + return 0; + } + } + } + } + return 0; +} + +int32_t vidioc_cam_g_fmt(int32_t fd, struct v4l2_format *format) +{ + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx || NULL == format) + return EINVAL; + + DBG_PRINT("CAM_CTX(%p): Getting format\n", ctx); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(format->type); + + NvMutexAcquire(ctx->context_mutex); + format->fmt.pix_mp.width = ctx->argus_params.width; + format->fmt.pix_mp.height = ctx->argus_params.height; + format->fmt.pix_mp.pixelformat = ctx->argus_params.pixelformat; + format->fmt.pix_mp.num_planes = 2; + format->fmt.pix_mp.field = V4L2_FIELD_NONE; + format->fmt.pix_mp.plane_fmt[0].sizeimage = + ctx->argus_params.width * ctx->argus_params.height; + format->fmt.pix_mp.plane_fmt[1].sizeimage = + (ctx->argus_params.width * ctx->argus_params.height) / 2; + format->fmt.pix_mp.plane_fmt[0].bytesperline = + format->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->argus_params.width; + + REL_PRINT("CAM_CTX(%p) VIDIOC_G_FMT returning width %d height %d\n", ctx, format->fmt.pix_mp.width, + format->fmt.pix_mp.height); + NvMutexRelease(ctx->context_mutex); + return 0; +} + +int32_t vidioc_cam_s_fmt(int32_t fd, struct v4l2_format *format) +{ + uint32_t i = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + DBG_PRINT("CAM_CTX(%p): Setting format\n", ctx); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(format->type); + + /* Check for allowed format */ + for (i = 0; ; i++) + { + if (capture_formats[i].fourcc == 0) + { + REL_PRINT("CAM_CTX(%p) Cannot set specified format. Using default\n", ctx); + i = 0; + break; + } + if (capture_formats[i].fourcc == format->fmt.pix_mp.pixelformat) + { + REL_PRINT("CAM_CTX(%p) Found allowed pixel format\n", ctx); + break; + } + } + format->fmt.pix_mp.pixelformat = capture_formats[i].fourcc; + format->fmt.pix_mp.num_planes = capture_formats[i].num_planes; + ctx->argus_params.width = format->fmt.pix_mp.width; + ctx->argus_params.height = format->fmt.pix_mp.height; + ctx->argus_params.pixelformat = format->fmt.pix_mp.pixelformat; + ctx->argus_params.argus_format = capture_formats[i].argus_format; + ctx->argus_params.bitdepth = capture_formats[i].bpp; + + if (initialize_outputstream(ctx, &ctx->argus_params) < 0) + { + REL_PRINT("CAM_CTX(%p) Error in initializing OutputStream\n", ctx); + return EINVAL; + } + + /*Set the fmt on capture plane here plane-wise */ + /* Plane 0 */ + switch (ctx->argus_params.pixelformat) + { + case V4L2_PIX_FMT_NV12M: + { + format->fmt.pix_mp.plane_fmt[0].sizeimage = (MEM_ALIGN_16(format->fmt.pix_mp.width)) * + (MEM_ALIGN_16(format->fmt.pix_mp.height)); + format->fmt.pix_mp.plane_fmt[0].bytesperline = (format->fmt.pix_mp.width + ALIGN_BYTES) & ~ALIGN_BYTES; + } + break; + default: + return EINVAL; + } + + /* Plane 1 */ + switch (ctx->argus_params.pixelformat) + { + case V4L2_PIX_FMT_NV12M: + { + format->fmt.pix_mp.plane_fmt[1].sizeimage = + ((MEM_ALIGN_16(format->fmt.pix_mp.width) >> 1) * (MEM_ALIGN_16(format->fmt.pix_mp.height) >> 1) << 1); + format->fmt.pix_mp.plane_fmt[1].bytesperline = (format->fmt.pix_mp.width/2 + ALIGN_BYTES) & ~ALIGN_BYTES; + } + break; + default: + break; + } + + format->fmt.pix_mp.width = MEM_ALIGN_16(format->fmt.pix_mp.width); + format->fmt.pix_mp.height = MEM_ALIGN_16(format->fmt.pix_mp.height); + + for (i = 0; i < format->fmt.pix_mp.num_planes; i++) + { + REL_PRINT("CAM_CTX(%p) Bytesperline for %d plane %d\n", ctx, + i, format->fmt.pix_mp.plane_fmt[i].bytesperline); + } + + return 0; +} + +int32_t vidioc_cam_reqbufs(int32_t fd, struct v4l2_requestbuffers *reqbufs) +{ + NvBufSurfacePlaneParams *pSurfParams = NULL; + uint32_t num_planes = 0; + int32_t retval = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + V4L2_MEMORY_TYPE_SUPPORTED_OR_ERROR(reqbufs->memory); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(reqbufs->type); + REL_PRINT("CAM_CTX(%p) Request %d buffers for capture plane\n", ctx, reqbufs->count); + + NvMutexAcquire(ctx->context_mutex); + if (reqbufs->count == 0) + { + if ((ctx->stream_on) && + !(v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped))) + { + REL_PRINT("CAM_CTX(%p) STREAMON and called REQBUFS\n", ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + REL_PRINT("CAM_CTX(%p) Releasing capture buffers\n", ctx); + free_cap_buffers(ctx); + NvMutexRelease(ctx->context_mutex); + return 0; + } + /*Reqbuffer count for the stream */ + if (reqbufs->count > MAX_OP_BUFFERS) + { + reqbufs->count = MAX_OP_BUFFERS; + } + if (reqbufs->count < MIN_OP_BUFFERS) + { + reqbufs->count = MIN_OP_BUFFERS; + } + ctx->capture_buffer_count = reqbufs->count; + ctx->capture_memory_type = reqbufs->memory; + + /* Right now, the supported format is PIX_FMT_NV12M */ + if (ctx->argus_params.pixelformat == V4L2_PIX_FMT_NV12M) + num_planes = 2; + + retval = allocate_all_capture_queues (ctx); + if (retval) + { + NvMutexRelease(ctx->context_mutex); + return retval; + } + + if (ctx->capture_memory_type != V4L2_MEMORY_DMABUF) + { + /* Allocate input buffers */ + retval = allocate_capture_buffers(ctx, &ctx->argus_params); + if (retval) + { + NvMutexRelease(ctx->context_mutex); + return retval; + } + for (uint32_t i = 0; i < reqbufs->count; i++) + { + pSurfParams = &ctx->cap_buffers[i].surf_params.planeParams; + for (uint32_t plane = 0; plane < num_planes; plane++) + { + NvBufSurface *nvbuf_surf = 0; + retval = NvBufSurfaceFromFd((int)ctx->cap_buffers[i].buf_fd, + (void **)(&nvbuf_surf)); + if (retval != 0) + REL_PRINT("CAM_CTX(%p) Failed to Get NvBufSurface from FD\n", ctx); + + retval = NvBufSurfaceMap(nvbuf_surf, 0, plane, NVBUF_MAP_READ_WRITE); + if (retval != 0) + REL_PRINT("CAM_CTX(%p) Failed to map NvBufSurface\n", ctx); + ctx->inbuf_mapped_address[i][plane] = (void *)nvbuf_surf->surfaceList[0].mappedAddr.addr[plane]; + + DBG_PRINT("CAM_CTX(%p): NvRmMemMapped bytes %d surface_number %d plane %d address %p \n", + ctx, (pSurfParams->pitch[plane] * pSurfParams->height[plane]), + i, plane, ctx->inbuf_mapped_address[i][plane]); + } + } + } + else + REL_PRINT("CAM_CTX(%p) Requested DMABUF memory, queues are created\n", ctx); + + NvMutexRelease(ctx->context_mutex); + return 0; +} + +int32_t vidioc_cam_streamon(int32_t fd, unsigned int *type) +{ + int32_t err_status = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + REL_PRINT("CAM_CTX(%p) STREAM ON\n", ctx); + + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(*type); + + NvMutexAcquire(ctx->context_mutex); + if (ctx->stream_on) + { + NvMutexRelease(ctx->context_mutex); + return 0; + } + + ctx->stream_on = true; + v4l2_cam_atomic_write_bool(ctx, &ctx->camera_state_stopped, false); + + err_status = argus_capture_thread_start(ctx); + if (err_status) + { + REL_PRINT("CAM_CTX(%p) Failed to create thread\n", ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + + /* Starting capture requests. Nothing happens since no buffers are available */ + if (ctx->m_iCaptureSession->repeat(ctx->m_request.get()) != Argus::STATUS_OK) + { + REL_PRINT("CAM_CTX(%p) Failed to start repeat capture request\n", ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + NvMutexRelease(ctx->context_mutex); + + return 0; +} + +int32_t vidioc_cam_streamoff(int32_t fd, unsigned int *type) +{ + q_entry entry = {0}; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + REL_PRINT("CAM_CTX(%p) STREAM OFF\n", ctx); + + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(*type); + + NvMutexAcquire(ctx->context_mutex); + if (!ctx->stream_on) + { + v4l2_cam_atomic_write_bool(ctx, &ctx->camera_state_stopped, true); + argus_capture_thread_shutdown(ctx); + NvMutexRelease(ctx->context_mutex); + return 0; + } + + ctx->stream_on = false; + v4l2_cam_atomic_write_bool(ctx, &ctx->camera_state_stopped, true); + NvSemaphoreSignal(ctx->acquirebuf_sema); + + /* Cancel all pending requests and signal eos to the BufferOutputStream */ + ctx->m_iCaptureSession->cancelRequests(); + ctx->m_iStream->endOfStream(); + + /* Try to release all buffers */ + while (NvQueueGetNumEntries(ctx->capplane_Q)) + { + if (NvQueueDeQ(ctx->capplane_Q, &entry) != 0) + { + REL_PRINT("CAM_CTX(%p) Error while dequeuing capplane_Q after stream off, entries %d\n", + ctx, NvQueueGetNumEntries(ctx->capplane_Q)); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + } + + for (uint32_t i = 0; i < ctx->capture_buffer_count; i++) + { + ctx->cap_buffers[i].flags = 0; + } + NvMutexRelease(ctx->context_mutex); + + argus_capture_thread_shutdown(ctx); + + return 0; +} + +int32_t vidioc_cam_qbuf(int32_t fd, struct v4l2_buffer *buffer) +{ + q_entry entry; + int32_t retval = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + V4L2_MEMORY_TYPE_SUPPORTED_OR_ERROR(buffer->memory); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(buffer->type); + + if (v4l2_cam_atomic_read_bool(ctx, &ctx->error_flag)) + return EINVAL; + + if (ctx->capture_buffer_count == 0 || ctx->capture_buffer_count <= buffer->index) + return EINVAL; + + if (ctx->cap_buffers[buffer->index].flags & (V4L2_BUF_FLAG_QUEUED)) + { + REL_PRINT("CAM_CTX(%p) Output Buffer at %d busy\n", ctx, buffer->index); + return EBUSY; + } + + memset(&entry, 0x0, sizeof (q_entry)); + entry.size = 0; // To indicate empty buffer being queued + entry.index = buffer->index; + entry.timestamp = buffer->timestamp.tv_sec * 1000 * 1000; + entry.timestamp += buffer->timestamp.tv_usec; + + if (buffer->memory == V4L2_MEMORY_DMABUF) + entry.fd = buffer->m.planes[0].m.fd; + + if (buffer->memory == V4L2_MEMORY_DMABUF) + { + NvBufSurface *nvbuf_surf = 0; + + if ((NvBufSurfaceFromFd(buffer->m.planes[0].m.fd, (void **)(&nvbuf_surf))) != 0) + { + REL_PRINT("CAM_CTX(%p) Unable to extract NvBufSurfaceFromFd\n", ctx); + return EINVAL; + } + memcpy(&ctx->cap_buffers[buffer->index].surf_params, + &nvbuf_surf->surfaceList[0], sizeof(NvBufSurfaceParams)); + } + + if (buffer->memory == V4L2_MEMORY_MMAP) + ctx->cap_buffers[buffer->index].flags |= V4L2_BUF_FLAG_MAPPED; + + retval = nvargus_enqueue_instream_buffers_from_capture_inQ(ctx, buffer->index); + if (retval != 0) + { + REL_PRINT("CAM_CTX(%p) Error in Queue\n", ctx); + return EINVAL; + } + + ctx->cap_buffers[buffer->index].flags |= V4L2_BUF_FLAG_QUEUED; + ctx->cap_buffers[buffer->index].flags |= (~V4L2_BUF_FLAG_DONE); + buffer->flags |= ctx->cap_buffers[buffer->index].flags; + ctx->cap_buffers[buffer->index].buffer_id = buffer->index; + + REL_PRINT("CAM_CTX(%p) Capturing Q index %d\n", ctx, buffer->index); + + return 0; +} + +int32_t vidioc_cam_dqbuf(int32_t fd, struct v4l2_buffer *buffer) +{ + int32_t eErr = 0; + q_entry entry; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + V4L2_MEMORY_TYPE_SUPPORTED_OR_ERROR(buffer->memory); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(buffer->type); + + if (v4l2_cam_atomic_read_bool(ctx, &ctx->error_flag)) + return EINVAL; + if (ctx->capture_buffer_count == 0) + return EINVAL; + if (!ctx->stream_on) + { + REL_PRINT("CAM_CTX(%p) DQBUF called during stream off\n", ctx); + return EPIPE; + } + + memset(&entry, 0x0, sizeof(q_entry)); + NvSemaphoreWait(ctx->acquirebuf_sema); + eErr = NvQueueDeQ(ctx->capplane_Q, &entry); + if (eErr != 0) + { + NvMutexAcquire(ctx->context_mutex); + if (v4l2_cam_atomic_read_bool(ctx, &ctx->error_flag)) + { + REL_PRINT("CAM_CTX(%p) Error occurred while DQ\n", ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + /* Check if camera stopped */ + if (v4l2_cam_atomic_read_bool(ctx, &ctx->camera_state_stopped)) + { + REL_PRINT("CAM_CTX(%p) Aborting Capture buffers\n", ctx); + free_cap_buffers(ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + NvMutexRelease(ctx->context_mutex); + return EAGAIN; + } + buffer->index = entry.index; + if (buffer->memory == V4L2_MEMORY_DMABUF) + buffer->m.planes[0].m.fd = entry.fd; + + ctx->cap_buffers[buffer->index].flags &= (~V4L2_BUF_FLAG_QUEUED); + ctx->cap_buffers[buffer->index].flags &= (V4L2_BUF_FLAG_DONE); + buffer->flags = ctx->cap_buffers[buffer->index].flags; + DBG_PRINT("CAM_CTX(%p): %s: DQed Buffer at index %d\n", ctx, __func__, entry.index); + + if (entry.size == 0) + { + buffer->bytesused = 0; + buffer->m.planes[0].bytesused = 0; + buffer->flags |= V4L2_BUF_FLAG_LAST; + if (ctx->m_cameraStatus != Argus::STATUS_OK) + { + argus_capture_thread_shutdown(ctx); + REL_PRINT("CAM_CTX(%p) Internal stream error\n", ctx); + return EIO; + } + } + else + { + /* As the buffers are already mapped, arbitrary values are set here */ + buffer->m.planes[0].bytesused = 1234; + buffer->m.planes[1].bytesused = 1234; + buffer->timestamp.tv_sec = entry.timestamp / (1000000); + buffer->timestamp.tv_usec = entry.timestamp % (1000000); + } + REL_PRINT("CAM_CTX(%p) CAPTURE_DQ index = %d Out Timestamp sec %ld usec %ld\n", + ctx, entry.index, + (uint64_t)buffer->timestamp.tv_sec, (uint64_t)buffer->timestamp.tv_usec); + + REL_PRINT("CAM_CTX(%p) Buffer index= %d Dequeue successful\n", ctx, buffer->index); + + return 0; +} + +int32_t vidioc_cam_querybuf(int32_t fd, v4l2_buffer *buffer) +{ + int32_t retval = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + DBG_PRINT("CAM_CTX(%p): Querybuf for capture\n", ctx); + V4L2_MEMORY_TYPE_SUPPORTED_OR_ERROR(buffer->memory); + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(buffer->type); + + if (ctx->capture_memory_type != V4L2_MEMORY_DMABUF) + { + NvBufSurfacePlaneParams *plane_ptr = + &ctx->cap_buffers[buffer->index].surf_params.planeParams; + if ((0 == ctx->capture_buffer_count || ctx->capture_buffer_count <= buffer->index)) + return EINVAL; + + buffer->m.planes[0].m.mem_offset = plane_ptr->offset[0]; + buffer->m.planes[0].length = plane_ptr->psize[0]; + buffer->m.planes[1].m.mem_offset = plane_ptr->offset[1]; + buffer->m.planes[1].length = plane_ptr->psize[1]; + REL_PRINT("CAM_CTX(%p) Querybuf for buffer index %d length[0] %d" + " mem_offset[0] %d length[1] %d mem_offset[1] %d\n", ctx, buffer->index, + buffer->m.planes[0].length, buffer->m.planes[0].m.mem_offset, + buffer->m.planes[1].length, buffer->m.planes[1].m.mem_offset); + } + else + { + uint64_t sizes[3] = {0}; + + /* Allocate the Argus buffer to map with the DMABUF fd, created in user-space */ + int32_t dmabuf_fd = buffer->m.planes[0].m.fd; + retval = allocate_argus_buffers(ctx, &ctx->argus_params, dmabuf_fd, buffer->index); + if (retval != 0) + return retval; + + retval = query_cam_buffers(ctx, &ctx->argus_params, &sizes[0]); + if (retval != 0) + return retval; + + buffer->m.planes[0].length = sizes[0]; + buffer->m.planes[1].length = sizes[1]; + REL_PRINT("CAM_CTX(%p) Querybuf for buffer index %d FD %d\n", ctx, buffer->index, + buffer->m.planes[0].m.fd); + } + + buffer->flags = ctx->cap_buffers[buffer->index].flags; + return 0; +} + +int32_t vidioc_cam_expbuf(int32_t fd, struct v4l2_exportbuffer *export_buffer) +{ + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + + if (NULL == ctx || NULL == export_buffer) + return EINVAL; + + export_buffer->fd = (__s32) ctx->cap_buffers[export_buffer->index].buf_fd; + + REL_PRINT("CAM_CTX(%p) EXP_BUF for capture plane with buffer_index %d plane %d fd returned %d\n", + ctx, export_buffer->index, export_buffer->plane, export_buffer->fd); + + return 0; +} + +int32_t vidioc_cam_s_extctrls(int32_t fd, struct v4l2_ext_controls *ctrl) +{ + int32_t retval = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + NvMutexAcquire(ctx->context_mutex); + cam_settings *argus_settings = &ctx->argus_settings; + if (ctrl->ctrl_class == V4L2_CTRL_CLASS_CAMERA) + { + for (uint32_t i = 0; i < ctrl->count; i++) + { + switch (ctrl->controls[i].id) + { + case V4L2_CID_ARGUS_SENSOR_MODE: + { + if (ctrl->controls[i].value < 0 || + static_cast(ctrl->controls[i].value) >= ctx->argusv4l2_sensormodes.size()) + { + REL_PRINT("CAM_CTX(%p) Invalid sensor-mode value. Keeping it default\n", ctx); + retval = EINVAL; + goto cleanup_sctl; + } + argus_settings->sensorModeIdx = ctrl->controls[i].value; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_SENSOR_MODE); + REL_PRINT("CAM_CTX(%p) Sensor mode set to %d\n", ctx, argus_settings->sensorModeIdx); + } + break; + case V4L2_CID_ARGUS_DENOISE_STRENGTH: + { + v4l2_argus_denoise_strength *settings = (v4l2_argus_denoise_strength *)ctrl->controls[i].string; + if (settings->DenoiseStrength > MAX_DENOISE_STRENGTH || + settings->DenoiseStrength < MIN_DENOISE_STRENGTH) + { + REL_PRINT("CAM_CTX(%p) Invalid Denoise Strength specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->denoiseStrength = settings->DenoiseStrength; + if (v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_DENOISE_STRENGTH) != 0) + { + retval = EINVAL; + goto cleanup_sctl; + } + REL_PRINT("CAM_CTX(%p) Denoise Strength set successfully to %f\n", + ctx, argus_settings->denoiseStrength); + } + break; + case V4L2_CID_ARGUS_DENOISE_MODE: + { + argus_settings->denoiseMode = ctrl->controls[i].value; + if (v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_DENOISE_MODE) != 0) + { + retval = EINVAL; + goto cleanup_sctl; + } + REL_PRINT("CAM_CTX(%p) Denoise Mode set successfully to %d\n", + ctx, argus_settings->denoiseMode); + } + break; + case V4L2_CID_ARGUS_EE_STRENGTH: + { + v4l2_argus_edge_enhance_strength *settings = + (v4l2_argus_edge_enhance_strength *)ctrl->controls[i].string; + if (settings->EdgeEnhanceStrength > MAX_EE_STRENGTH || + settings->EdgeEnhanceStrength < MIN_EE_STRENGTH) + { + REL_PRINT("CAM_CTX(%p) Invalid EE Strength specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->edgeEnhanceStrength = settings->EdgeEnhanceStrength; + if (v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_EE_STRENGTH) != 0) + { + retval = EINVAL; + goto cleanup_sctl; + } + REL_PRINT("CAM_CTX(%p) Edge Enhancement Strength set successfully to %f\n", + ctx, argus_settings->edgeEnhanceStrength); + } + break; + case V4L2_CID_ARGUS_EE_MODE: + { + argus_settings->edgeEnhanceMode = ctrl->controls[i].value; + if (v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_EE_MODE) != 0) + { + retval = EINVAL; + goto cleanup_sctl; + } + REL_PRINT("CAM_CTX(%p) Edge Enhancement Mode set successfully to %d\n", + ctx, argus_settings->edgeEnhanceMode); + } + break; + case V4L2_CID_ARGUS_AE_ANTIBANDING_MODE: + { + argus_settings->aeAntibandingMode = ctrl->controls[i].value; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_AE_ANTIBANDING_MODE); + REL_PRINT("CAM_CTX(%p) Setting AE AntiBanding mode to %d\n", + ctx, argus_settings->aeAntibandingMode); + } + break; + case V4L2_CID_ARGUS_AUTO_WHITE_BALANCE_MODE: + { + argus_settings->awbMode = ctrl->controls[i].value; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_AUTO_WHITE_BALANCE_MODE); + REL_PRINT("CAM_CTX(%p) Setting AWB mode to %d\n", + ctx, argus_settings->awbMode); + } + break; + case V4L2_CID_3A_LOCK: + { + argus_settings->aeLock = ctrl->controls[i].value & V4L2_LOCK_EXPOSURE; + argus_settings->awbLock = ctrl->controls[i].value & V4L2_LOCK_WHITE_BALANCE; + if (ctrl->controls[i].value & V4L2_LOCK_FOCUS) + { + REL_PRINT("CAM_CTX(%p) Auto Focus Lock not supported\n", ctx); + retval = EINVAL; + goto cleanup_sctl; + } + v4l2_cam_set_argus_settings(ctx, V4L2_CID_3A_LOCK); + REL_PRINT("CAM_CTX(%p) Setting Auto Exposure lock to %d\n", + ctx, argus_settings->aeLock); + REL_PRINT("CAM_CTX(%p) Setting Auto White Balance lock to %d\n", + ctx, argus_settings->awbLock); + } + break; + case V4L2_CID_ARGUS_EXPOSURE_COMPENSATION: + { + v4l2_argus_exposure_compensation *excomp = + (v4l2_argus_exposure_compensation *)ctrl->controls[i].string; + argus_settings->exposureCompensation = excomp->ExposureCompensation; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_EXPOSURE_COMPENSATION); + REL_PRINT("CAM_CTX(%p) Setting Exposure Compensation to %f\n", + ctx, argus_settings->exposureCompensation); + } + break; + case V4L2_CID_ARGUS_ISP_DIGITAL_GAIN_RANGE: + { + v4l2_argus_ispdigital_gainrange *digital_gainrange = + (v4l2_argus_ispdigital_gainrange *)ctrl->controls[i].string; + if (digital_gainrange->MinISPDigitalGainRange < MIN_DIGITAL_GAIN || + digital_gainrange->MaxISPDigitalGainRange > MAX_DIGITAL_GAIN) + { + REL_PRINT("CAM_CTX(%p) Invalid Digital Gain Range specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->ispDigitalGainRange.min() = digital_gainrange->MinISPDigitalGainRange; + argus_settings->ispDigitalGainRange.max() = digital_gainrange->MaxISPDigitalGainRange; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_ISP_DIGITAL_GAIN_RANGE); + REL_PRINT("CAM_CTX(%p) Set Digital Gain Range \"%f %f\"\n", ctx, + argus_settings->ispDigitalGainRange.min(), argus_settings->ispDigitalGainRange.max()); + } + break; + case V4L2_CID_ARGUS_COLOR_SATURATION: + { + v4l2_argus_color_saturation *color_sat = + (v4l2_argus_color_saturation *)ctrl->controls[i].string; + if (argus_settings->colorSaturation > MAX_COLOR_SATURATION || + argus_settings->colorSaturation < MIN_COLOR_SATURATION) + { + REL_PRINT("CAM_CTX(%p) Invalid Color Saturation specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->enableColorSaturation = color_sat->EnableSaturation; + argus_settings->colorSaturation = color_sat->ColorSaturation; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_COLOR_SATURATION); + REL_PRINT("CAM_CTX(%p) Set Color Saturation %f\n", + ctx, argus_settings->colorSaturation); + } + break; + case V4L2_CID_ARGUS_GAIN_RANGE: + { + v4l2_argus_gainrange *gainrange = + (v4l2_argus_gainrange *)ctrl->controls[i].string; + float min_gainrange = + ctx->argusv4l2_sensormodes[argus_settings->sensorModeIdx].minGainRange; + float max_gainrange = + ctx->argusv4l2_sensormodes[argus_settings->sensorModeIdx].maxGainRange; + if (gainrange->MinGainRange < min_gainrange || + gainrange->MaxGainRange > max_gainrange) + { + REL_PRINT("CAM_CTX(%p) Invalid Gain Range specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->gainRange.min() = gainrange->MinGainRange; + argus_settings->gainRange.max() = gainrange->MaxGainRange; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_GAIN_RANGE); + REL_PRINT("CAM_CTX(%p) Set Gain Range \"%f %f\"\n", ctx, + argus_settings->gainRange.min(), argus_settings->gainRange.max()); + } + break; + case V4L2_CID_ARGUS_EXPOSURE_TIME_RANGE: + { + v4l2_argus_exposure_timerange *exptimerange = + (v4l2_argus_exposure_timerange *)ctrl->controls[i].string; + float min_exptimerange = + ctx->argusv4l2_sensormodes[argus_settings->sensorModeIdx].minExposureTimeRange; + float max_exptimerange = + ctx->argusv4l2_sensormodes[argus_settings->sensorModeIdx].maxExposureTimeRange; + if (exptimerange->MinExposureTimeRange < min_exptimerange || + exptimerange->MaxExposureTimeRange > max_exptimerange) + { + REL_PRINT("CAM_CTX(%p) Invalid Exposure Time Range specified\n", ctx); + retval = ERANGE; + goto cleanup_sctl; + } + argus_settings->exposureTimeRange.min() = exptimerange->MinExposureTimeRange; + argus_settings->exposureTimeRange.max() = exptimerange->MaxExposureTimeRange; + v4l2_cam_set_argus_settings(ctx, V4L2_CID_ARGUS_EXPOSURE_TIME_RANGE); + REL_PRINT("CAM_CTX(%p) Set Exposure Time Range \"%lu %lu\" \n", ctx, + argus_settings->exposureTimeRange.min(), argus_settings->exposureTimeRange.max()); + } + break; + default: + REL_PRINT("CAM_CTX(%p) Invalid Control ID passed\n", ctx); + goto cleanup_sctl; + } + } + } +cleanup_sctl: + NvMutexRelease(ctx->context_mutex); + return retval; +} + +int32_t vidic_cam_g_extctrls(int32_t fd, struct v4l2_ext_controls *ctrl) +{ + int32_t retval = 0; + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + struct v4l2_ext_control *control = NULL; + for (uint32_t i = 0; i < ctrl->count; i++) + { + control = &(ctrl->controls[i]); + if (control->id == V4L2_CID_ARGUS_METADATA) + { + if (!ctx->stream_on) + { + return EBUSY; + } + argusframe_metadata *src; + v4l2_argus_ctrl_metadata *dest = + (v4l2_argus_ctrl_metadata *)(control->string); + if (ctx->frame_metadata[dest->BufferIndex] != NULL) + { + + src = ctx->frame_metadata[dest->BufferIndex]; + dest->AeLocked = src->aeLocked; + + if (src->aeState == Argus::AE_STATE_INACTIVE) + dest->AEState = V4L2_ARGUS_AE_STATE_INACTIVE; + else if (src->aeState == Argus::AE_STATE_SEARCHING) + dest->AEState = V4L2_ARGUS_AE_STATE_SEARCHING; + else if (src->aeState == Argus::AE_STATE_CONVERGED) + dest->AEState = V4L2_ARGUS_AE_STATE_CONVERGED; + else if (src->aeState == Argus::AE_STATE_FLASH_REQUIRED) + dest->AEState = V4L2_ARGUS_AE_STATE_FLASH_REQUIRED; + else if (src->aeState == Argus::AE_STATE_TIMEOUT) + dest->AEState = V4L2_ARGUS_AE_STATE_TIMEOUT; + else + dest->AEState = V4L2_ARGUS_AeState_Unknown; + + dest->FocuserPosition = src->focuserPosition; + dest->AwbCCT = src->awbCct; + + if (src->awbState == Argus::AWB_STATE_INACTIVE) + dest->AWBState = V4L2_ARGUS_AWB_STATE_INACTIVE; + else if (src->awbState == Argus::AWB_STATE_SEARCHING) + dest->AWBState = V4L2_ARGUS_AWB_STATE_SEARCHING; + else if (src->awbState == Argus::AWB_STATE_CONVERGED) + dest->AWBState = V4L2_ARGUS_AWB_STATE_CONVERGED; + else if (src->awbState == Argus::AWB_STATE_LOCKED) + dest->AWBState = V4L2_ARGUS_AWB_STATE_LOCKED; + else + dest->AWBState = V4L2_ARGUS_AwbState_Unknown; + + dest->FrameDuration = src->frameDuration; + dest->IspDigitalGain = src->ispDigitalGain; + dest->FrameReadoutTime = src->frameReadoutTime; + dest->SceneLux = src->sceneLux; + dest->SensorAnalogGain = src->sensorAnalogGain; + dest->SensorExposureTime = src->sensorExposureTime; + dest->SensorSensitivity = src->sensorSensitivity; + dest->ValidFrameStatus = true; + } + else + { + REL_PRINT("CAM_CTX(%p) No metadata recieved for given Buffer\n", ctx); + dest->ValidFrameStatus = false; + retval = EACCES; + break; + } + } + else + retval = EINVAL; + } + return retval; +} + +int32_t vidioc_cam_sparm(int32_t fd, struct v4l2_streamparm *streamparms) +{ + v4l2_camera_context *ctx = get_v4l2_context_from_fd(fd); + if (NULL == ctx) + return EINVAL; + + V4L2_BUFFER_TYPE_SUPPORTED_OR_ERROR(streamparms->type); + + NvMutexAcquire(ctx->context_mutex); + float frameduration = 1e9 * (streamparms->parm.capture.timeperframe.numerator)/ + (streamparms->parm.capture.timeperframe.denominator); + + if (set_framerate(ctx, frameduration) != 0) + { + REL_PRINT("CAM_CTX(%p) Failed to set the specified Frame rate\n", ctx); + NvMutexRelease(ctx->context_mutex); + return EINVAL; + } + + REL_PRINT("CAM_CTX(%p) Setting framerate as %d/%d\n", ctx, + streamparms->parm.capture.timeperframe.denominator, + streamparms->parm.capture.timeperframe.numerator); + NvMutexRelease(ctx->context_mutex); + return 0; +} + +int32_t nvargus_ioctl(int32_t fd, unsigned long cmd, void *arg) +{ + switch (cmd) + { + case VIDIOC_QUERYCAP: + return vidioc_cam_querycap(fd, (struct v4l2_capability *)arg); + case VIDIOC_ENUM_FMT: + return vidioc_cam_enum_fmt(fd, (struct v4l2_fmtdesc *)arg); + case VIDIOC_G_FMT: + return vidioc_cam_g_fmt(fd, (struct v4l2_format *)arg); + case VIDIOC_S_FMT: + return vidioc_cam_s_fmt(fd, (struct v4l2_format *)arg); + case VIDIOC_QBUF: + return vidioc_cam_qbuf(fd, (struct v4l2_buffer *)arg); + case VIDIOC_DQBUF: + return vidioc_cam_dqbuf(fd, (struct v4l2_buffer *)arg); + case VIDIOC_REQBUFS: + return vidioc_cam_reqbufs(fd, (struct v4l2_requestbuffers *)arg); + case VIDIOC_STREAMON: + return vidioc_cam_streamon(fd, (unsigned int*)arg); + case VIDIOC_STREAMOFF: + return vidioc_cam_streamoff(fd, (unsigned int*)arg); + case VIDIOC_QUERYBUF: + return vidioc_cam_querybuf(fd, (struct v4l2_buffer *)arg); + case VIDIOC_S_EXT_CTRLS: + return vidioc_cam_s_extctrls(fd, (struct v4l2_ext_controls *)arg); + case VIDIOC_TRY_FMT: + case VIDIOC_G_EXT_CTRLS: + return vidic_cam_g_extctrls(fd, (struct v4l2_ext_controls *)arg); + case VIDIOC_S_CTRL: + case VIDIOC_G_CTRL: + case VIDIOC_DQEVENT: + case VIDIOC_SUBSCRIBE_EVENT: + case VIDIOC_S_CROP: + case VIDIOC_G_CROP: + case VIDIOC_G_PARM: + printf("Not Implemented\n"); + return EINVAL; + case VIDIOC_S_PARM: + return vidioc_cam_sparm(fd, (struct v4l2_streamparm *)arg); + case VIDIOC_ENUM_FRAMESIZES: + return vidioc_cam_enum_framesizes(fd, (struct v4l2_frmsizeenum *)arg); + case VIDIOC_ENUM_FRAMEINTERVALS: + return vidioc_cam_enum_frameintervals(fd, (struct v4l2_frmivalenum *)arg); + case VIDIOC_EXPBUF: + return vidioc_cam_expbuf(fd, (struct v4l2_exportbuffer *)arg); + default: + printf("DEFAULT no IOCTL called\n"); + return EINVAL; + } + return 0; +} diff --git a/libv4l2_nvargus/nvargusv4l2_nvqueue.cpp b/libv4l2_nvargus/nvargusv4l2_nvqueue.cpp new file mode 100644 index 0000000..f722637 --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2_nvqueue.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "nvargusv4l2_nvqueue.h" + +int32_t NvQueueCreate(NvQueueHandle *phQueue, u_int32_t maxEntries, u_int32_t entrySize) +{ + NvQueue *pQueue; + int32_t retval = 0; + + if (maxEntries == 0) + return EINVAL; + + pQueue = (NvQueue *)malloc(sizeof(NvQueue)); + if (!pQueue) + return ENOMEM; + + memset(pQueue, 0x0, sizeof(NvQueue)); + pQueue->pEntryList = 0; + pQueue->pushIndex = 0; + pQueue->popIndex = 0; + pQueue->maxEntries = maxEntries + 1; + pQueue->entrySize = entrySize; + + retval = NvMutexCreate(&pQueue->mutexLock); + if (retval != 0) + goto nvqueue_exit; + + pQueue->pEntryList = (u_int8_t *)malloc(pQueue->maxEntries * entrySize); + if (!pQueue->pEntryList) + { + retval = ENOMEM; + goto nvqueue_exit; + } + + *phQueue = pQueue; + return 0; + +nvqueue_exit: + if (pQueue) + { + NvMutexDestroy(pQueue->mutexLock); + free(pQueue); + } + + *phQueue = 0; + return retval; +} + +int32_t NvQueueEnQ(NvQueueHandle hQueue, void *pElem) +{ + NvQueue *pQueue = hQueue; + int32_t retval = 0; + u_int32_t pushIdx, popIdx; + + NvMutexAcquire(pQueue->mutexLock); + + if (pElem == NULL) + { + retval = EINVAL; + goto nvqueue_exit; + } + + pushIdx = pQueue->pushIndex; + popIdx = pQueue->popIndex; + + /* Check if space available */ + if (pushIdx + 1 == popIdx || pushIdx + 1 == popIdx + pQueue->maxEntries) + { + retval = ENOMEM; + goto nvqueue_exit; + } + + memcpy(&pQueue->pEntryList[pushIdx * pQueue->entrySize], pElem, + pQueue->entrySize); + + if (++pushIdx >= pQueue->maxEntries) + pushIdx = 0; + pQueue->pushIndex = pushIdx; + +nvqueue_exit: + NvMutexRelease(pQueue->mutexLock); + return retval; +} + +int32_t NvQueueDeQ(NvQueueHandle hQueue, void *pElem) +{ + NvQueue *pQueue = hQueue; + int32_t retval = 0; + u_int32_t popIdx; + + NvMutexAcquire(pQueue->mutexLock); + + popIdx = pQueue->popIndex; + if (pQueue->pushIndex == popIdx) + { + retval = EINVAL; + goto nvqueue_exit; + } + + memcpy(pElem, &pQueue->pEntryList[popIdx * pQueue->entrySize], + pQueue->entrySize); + + if (++popIdx >= pQueue->maxEntries) + popIdx = 0; + pQueue->popIndex = popIdx; + +nvqueue_exit: + NvMutexRelease(pQueue->mutexLock); + return retval; +} + +int32_t NvQueuePeek(NvQueueHandle hQueue, void *pElem) +{ + NvQueue *pQueue = hQueue; + int32_t retval = 0; + u_int32_t popIdx; + + NvMutexAcquire(pQueue->mutexLock); + + popIdx = pQueue->popIndex; + if (pQueue->pushIndex == popIdx) + { + retval = EINVAL; + goto nvqueue_exit; + } + + memcpy(pElem, &pQueue->pEntryList[popIdx * pQueue->entrySize], + pQueue->entrySize); + +nvqueue_exit: + NvMutexRelease(pQueue->mutexLock); + return retval; +} + +int32_t NvQueuePeekEntry(NvQueueHandle hQueue, void *pElem, u_int32_t nEntry) +{ + NvQueue *pQueue = hQueue; + int32_t err = 0; + u_int32_t entry, pushIdx, popIdx, numEntries; + + NvMutexAcquire(pQueue->mutexLock); + + pushIdx = pQueue->pushIndex; + popIdx = pQueue->popIndex; + + numEntries = (pushIdx >= popIdx) ? pushIdx - popIdx : pQueue->maxEntries - popIdx + pushIdx; + + if ((numEntries == 0) || (numEntries <= nEntry)) + { + err = EINVAL; + goto nvqueue_exit; + } + + entry = popIdx + nEntry; + if (entry >= pQueue->maxEntries) + entry -= pQueue->maxEntries; + + memcpy(pElem, &pQueue->pEntryList[entry * pQueue->entrySize], + pQueue->entrySize); + +nvqueue_exit: + NvMutexRelease(pQueue->mutexLock); + return err; +} + +void NvQueueDestroy(NvQueueHandle *phQueue) +{ + NvQueue *pQueue = *phQueue; + if (!pQueue) + return; + NvMutexDestroy(pQueue->mutexLock); + free(pQueue->pEntryList); + free(pQueue); + *phQueue = NULL; +} + +u_int32_t NvQueueGetNumEntries(NvQueueHandle hQueue) +{ + NvQueue *pQueue = hQueue; + u_int32_t pushIdx = pQueue->pushIndex; + u_int32_t popIdx = pQueue->popIndex; + u_int32_t numEntries = (pushIdx >= popIdx) ? pushIdx - popIdx : pQueue->maxEntries - popIdx + pushIdx; + + return numEntries; +} diff --git a/libv4l2_nvargus/nvargusv4l2_os.cpp b/libv4l2_nvargus/nvargusv4l2_os.cpp new file mode 100644 index 0000000..2cdb1e6 --- /dev/null +++ b/libv4l2_nvargus/nvargusv4l2_os.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "nvargusv4l2_os.h" + +typedef struct NvMutex_ +{ + pthread_mutex_t mutex; + u_int32_t count; +} NvMutex; + +typedef struct NvSemaphore_ +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + u_int32_t count; /* Sema value*/ +} NvSemaphore; + +typedef struct NvThread_ +{ + pthread_t thread; +} NvThread; + +typedef struct +{ + NvThreadFunction function; + NvThread *thread; + pthread_mutex_t barrier; + void *thread_args; + NvSemaphoreHandle init; +} NvThreadArgs; + +int32_t NvMutexCreate(NvMutexHandle *mHandle) +{ + NvMutex *m; + pthread_mutexattr_t attr; + + m = (NvMutex *)malloc(sizeof(NvMutex)); + if (!m) + { + *mHandle = NULL; + return ENOMEM; + } + + /* Mutex initialization for local process */ + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m->mutex, &attr); + pthread_mutexattr_destroy(&attr); + + m->count = 0; + *mHandle = m; + return 0; +} + +int32_t NvMutexAcquire(NvMutexHandle mHandle) +{ + int32_t val = 0; + + if (!mHandle) + return EINVAL; + + val = pthread_mutex_lock(&mHandle->mutex); + mHandle->count++; + if (val) + return val; + + return 0; +} + +int32_t NvMutexRelease(NvMutexHandle mHandle) +{ + int32_t val = 0; + + if (!mHandle) + return EINVAL; + + mHandle->count--; + val = pthread_mutex_unlock(&mHandle->mutex); + if (val) + return val; + + return 0; +} + +int32_t NvMutexDestroy(NvMutexHandle mHandle) +{ + int32_t val = 0; + + if (!mHandle) + return EINVAL; + + val = pthread_mutex_destroy(&mHandle->mutex); + if (val) + return val; + + free(mHandle); + return 0; +} + +int32_t NvSemaphoreCreate(NvSemaphoreHandle *semaHandle, u_int32_t cnt) +{ + int32_t val = 0; + NvSemaphore *sema = (NvSemaphore *)malloc(sizeof(NvSemaphore)); + if (!sema) + return ENOMEM; + + val = pthread_mutex_init(&sema->mutex, 0); + if (val) + { + free(sema); + return val; + } + val = pthread_cond_init(&sema->cond, 0); + if (val) + { + pthread_mutex_destroy(&sema->mutex); + free(sema); + return val; + } + sema->count = cnt; + *semaHandle = sema; + + return 0; +} + +void NvSemaphoreWait(NvSemaphoreHandle semaHandle) +{ + if (!semaHandle) + return; + + pthread_mutex_lock(&semaHandle->mutex); + while (!semaHandle->count) + { + pthread_cond_wait(&semaHandle->cond, &semaHandle->mutex); + } + semaHandle->count--; + pthread_mutex_unlock(&semaHandle->mutex); +} + +void NvSemaphoreSignal(NvSemaphoreHandle semaHandle) +{ + if (!semaHandle) + return; + pthread_mutex_lock(&semaHandle->mutex); + semaHandle->count++; + pthread_cond_signal(&semaHandle->cond); + pthread_mutex_unlock(&semaHandle->mutex); +} + +int32_t NvSemaphoreDestroy(NvSemaphoreHandle semaHandle) +{ + int32_t val_m = 0, val_c = 0; + if (!semaHandle) + return EINVAL; + + val_m = pthread_mutex_destroy(&semaHandle->mutex); + val_c = pthread_cond_destroy(&semaHandle->cond); + + free(semaHandle); + + return (!val_m && !val_c) ? 0 : EINVAL; +} + +static void *thread_wrapper(void *pThread) +{ + NvThreadArgs *args = (NvThreadArgs *)pThread; + + if (!args) + return NULL; + NvSemaphoreSignal(args->init); + pthread_mutex_lock(&args->barrier); + pthread_mutex_unlock(&args->barrier); + + args->function(args->thread_args); + pthread_mutex_destroy(&args->barrier); + NvSemaphoreDestroy(args->init); + free(args); + return 0; +} + +int32_t NvThreadCreate( + NvThreadFunction function, + void *tArgs, + NvThreadHandle *tHandle) +{ + int32_t err; + NvThread *thrd = 0; + NvThreadArgs *args = 0; + + if (!function || !tHandle) + return EINVAL; + + /* create the thread struct */ + thrd = (NvThread *)malloc(sizeof(NvThread)); + if (thrd == NULL) + goto fail; + memset(thrd, 0x0, sizeof(NvThread)); + + /* setup the thread args */ + args = (NvThreadArgs *)malloc(sizeof(NvThreadArgs)); + if (args == NULL) + goto fail; + memset(args, 0x0, sizeof(NvThreadArgs)); + + args->function = function; + args->thread = thrd; + args->thread_args = tArgs; + (void)pthread_mutex_init(&args->barrier, 0); + + /* The thread is created with the mutex lock held, to prevent + * race conditions between thread assignment and + * thread function execution */ + NvSemaphoreCreate(&args->init, 0); + pthread_mutex_lock(&args->barrier); + + err = pthread_create(&thrd->thread, 0, thread_wrapper, args); + if (err) + goto fail; + + NvSemaphoreWait(args->init); + + *tHandle = thrd; + pthread_mutex_unlock(&args->barrier); + + return 0; + +fail: + if (args) + { + pthread_mutex_unlock(&args->barrier); + pthread_mutex_destroy(&args->barrier); + } + free(args); + free(thrd); + *tHandle = 0; + + return ENOMEM; +} + +int32_t NvThreadSetName(NvThreadHandle t, const char *name) +{ + int32_t val = pthread_setname_np(t->thread, name); + return val; +} + +void NvThreadJoin(NvThreadHandle tHandle) +{ + if (!tHandle) + return; + + int32_t val = pthread_join(tHandle->thread, 0); + if (val) + return; + + free(tHandle); +} + +u_int64_t NvGetCurrentThreadId(void) +{ + return pthread_self(); +} diff --git a/push_info.txt b/push_info.txt new file mode 100644 index 0000000..9c21818 --- /dev/null +++ b/push_info.txt @@ -0,0 +1 @@ +jetson_36.2