/* * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of NVIDIA CORPORATION nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Sample pipeline * * gst-launch-1.0 * nvarguscamerasrc ! * "video/x-raw(memory:NVMM), width=640, height=480, format=NV12, framerate=30/1" ! * nvoverlaysink -e -v */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include // for useconds_t #include "Ordered.h" #include "gstnvarguscamerasrc.hpp" #include "Error.h" #define CAPTURE_CAPS \ "video/x-raw(memory:NVMM), " \ "width = (int) [ 1, MAX ], " \ "height = (int) [ 1, MAX ], " \ "format = (string) { NV12 }, " \ "framerate = (fraction) [ 0, MAX ];" #define MIN_BUFFERS 6 #define MAX_BUFFERS 8 #define MIN_GAIN 1 #define MAX_GAIN 16 #define MIN_EXPOSURE_TIME 34000 #define MAX_EXPOSURE_TIME 358733000 #define MIN_DIGITAL_GAIN 1 #define MAX_DIGITAL_GAIN 256 #define GST_NVARGUS_MEMORY_TYPE "nvarguscam" static const int DEFAULT_FPS = 30; static const uint64_t TIMEOUT_FIVE_SECONDS = 5000000000; static const uint64_t ACQUIRE_FRAME_TIMEOUT = 1000000000; #ifdef __cplusplus extern "C" { #endif using namespace std; using namespace Argus; using namespace EGLStream; namespace ArgusSamples { ThreadArgus::ThreadArgus() : m_doShutdown(false) , m_threadID(0) , m_threadState(THREAD_INACTIVE) { } ThreadArgus::~ThreadArgus() { (void)shutdown(); } bool ThreadArgus::initialize(GstNvArgusCameraSrc *src) { if (m_threadID) return true; this->src = src; if (pthread_create(&m_threadID, NULL, threadFunctionStub, this) != 0) ORIGINATE_ERROR("Failed to create thread."); // wait for the thread to start up while (m_threadState == THREAD_INACTIVE) usleep(100); return true; } bool ThreadArgus::shutdown() { if (m_threadID) { m_doShutdown = true; if (pthread_join(m_threadID, NULL) != 0) ORIGINATE_ERROR("Failed to join thread"); m_threadID = 0; m_doShutdown = false; m_threadState = THREAD_INACTIVE; } return true; } bool ThreadArgus::waitRunning(useconds_t timeoutUs) { // Can only wait for a thread which is initializing or already running if ((m_threadState != THREAD_INITIALIZING) && (m_threadState != THREAD_RUNNING)) ORIGINATE_ERROR("Invalid thread state %d", m_threadState.get()); // wait for the thread to run const useconds_t sleepTimeUs = 100; while (m_threadState != THREAD_RUNNING) { usleep(sleepTimeUs); #ifdef DEBUG // in debug mode wait indefinitely #else if (timeoutUs < sleepTimeUs) return false; timeoutUs -= sleepTimeUs; #endif } return true; } /** * Thread function stub, calls the real thread function. * * @param [in] dataPtr Pointer to user data */ /* static */ void *ThreadArgus::threadFunctionStub(void *dataPtr) { ThreadArgus *thread = static_cast(dataPtr); if (!thread->threadFunction(thread->src)) thread->m_threadState = ThreadArgus::THREAD_FAILED; else thread->m_threadState = ThreadArgus::THREAD_DONE; return NULL; } /** * Thread function */ bool ThreadArgus::threadFunction(GstNvArgusCameraSrc *src) { m_threadState = THREAD_INITIALIZING; PROPAGATE_ERROR(threadInitialize(src)); m_threadState = THREAD_RUNNING; while (!m_doShutdown) { PROPAGATE_ERROR(threadExecute(src)); } PROPAGATE_ERROR(threadShutdown(src)); return true; } }; // namespace ArgusSamples namespace ArgusCamera { // Constants #define GST_ARGUS_PRINT(...) printf("GST_ARGUS: " __VA_ARGS__) #define CONSUMER_PRINT(...) printf("CONSUMER: " __VA_ARGS__) #define GST_ARGUS_ERROR(...) printf("ARGUS_ERROR: Error generated. %s, %s: %d %s", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) const char* getStatusString(Argus::Status status); /******************************************************************************* * StreamConsumer thread: * Creates a StreamConsumer object to read frames from the OutputStream just tests * for sanity. ******************************************************************************/ class StreamConsumer : public ArgusSamples::ThreadArgus { public: explicit StreamConsumer(OutputStream* stream) : m_stream(stream) { } ~StreamConsumer() { } private: /** @name Thread methods */ /**@{*/ virtual bool threadInitialize(GstNvArgusCameraSrc *); virtual bool threadExecute(GstNvArgusCameraSrc *); virtual bool threadShutdown(GstNvArgusCameraSrc *); /**@}*/ OutputStream* m_stream; //GstNvArgusCameraSrc *argus_src; UniqueObj m_consumer; }; 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 StreamConsumer::threadInitialize(GstNvArgusCameraSrc *src) { // Create the FrameConsumer. m_consumer = UniqueObj(FrameConsumer::create(m_stream)); if (!m_consumer) ORIGINATE_ERROR("Failed to create FrameConsumer"); return true; } bool StreamConsumer::threadExecute(GstNvArgusCameraSrc *src) { IEGLOutputStream *iStream = interface_cast(m_stream); Size2D streamSize (src->width, src->height); IFrameConsumer *iFrameConsumer = interface_cast(m_consumer); // Wait until the producer has connected to the stream. CONSUMER_PRINT("Waiting until producer is connected...\n"); if (iStream->waitUntilConnected() != STATUS_OK) ORIGINATE_ERROR("Stream failed to connect."); CONSUMER_PRINT("Producer has connected; continuing.\n"); IAutoControlSettings* l_iAutoControlSettings_ptr = (IAutoControlSettings *)src->iAutoControlSettings_ptr; ICaptureSession* l_iCaptureSession = (ICaptureSession *)src->iCaptureSession_ptr; IDenoiseSettings* l_iDenoiseSettings_ptr = (IDenoiseSettings *)src->iDenoiseSettings_ptr; IEdgeEnhanceSettings* l_iEeSettings_ptr = (IEdgeEnhanceSettings *)src->iEeSettings_ptr; ISourceSettings* l_iRequestSourceSettings_ptr = (ISourceSettings *)src->iRequestSourceSettings_ptr; Request* l_captureRequest = (Request*)src->request_ptr; IEventProvider *iEventProvider_ptr = (IEventProvider*)src->iEventProvider_ptr; IEventQueue *iEventQueue_ptr = (IEventQueue*)src->iEventQueue_ptr; Range sensorModeAnalogGainRange; Range ispDigitalGainRange; Range limitExposureTimeRange; l_iCaptureSession->repeat(l_captureRequest); src->frameInfo = g_slice_new(NvArgusFrameInfo); src->frameInfo->fd = -1; while (true) { Argus::Status frame_status; GError *error = NULL; Event* event = NULL; IEvent* iEvent = NULL; static GQuark domain = g_quark_from_static_string ("NvArgusCameraSrc"); iEventProvider_ptr->waitForEvents(src->queue.get(), TIMEOUT_FIVE_SECONDS); if (iEventQueue_ptr->getSize() == 0) { g_mutex_lock (&src->argus_buffers_queue_lock); src->stop_requested = TRUE; g_mutex_unlock (&src->argus_buffers_queue_lock); break; } int event_index = iEventQueue_ptr->getSize() - 1; process_event: event = (Event* )iEventQueue_ptr->getEvent(event_index); iEvent = (IEvent*)interface_cast(event); if (!iEvent) { src->argus_in_error = true; ORIGINATE_ERROR("Failed to get IEvent interface"); } if (iEvent->getEventType() == EVENT_TYPE_ERROR) { if (src->stop_requested == TRUE) break; src->argus_in_error = TRUE; const IEventError* iEventError = interface_cast(event); Argus::Status argusStatus = iEventError->getStatus(); error = g_error_new (domain, argusStatus, getStatusString(argusStatus)); GstMessage *message = gst_message_new_error (GST_OBJECT(src), error, "Argus Error Status"); gst_element_post_message (GST_ELEMENT_CAST(src), message); g_mutex_lock (&src->argus_buffers_queue_lock); src->stop_requested = TRUE; g_mutex_unlock (&src->argus_buffers_queue_lock); break; } { UniqueObj frame(iFrameConsumer->acquireFrame(ACQUIRE_FRAME_TIMEOUT, &frame_status)); if (src->stop_requested == TRUE) { break; } if (frame_status != STATUS_OK) { if (!src->timeout_complete) { src->argus_in_error = true; error = g_error_new (domain, frame_status, getStatusString(frame_status)); GstMessage *message = gst_message_new_error (GST_OBJECT(src), error, "Argus Error Status"); gst_element_post_message (GST_ELEMENT_CAST(src), message); g_mutex_lock (&src->argus_buffers_queue_lock); src->stop_requested = TRUE; g_mutex_unlock (&src->argus_buffers_queue_lock); break; } } if (!frame) { g_mutex_lock (&src->argus_buffers_queue_lock); src->stop_requested = TRUE; g_mutex_unlock (&src->argus_buffers_queue_lock); break; } if (src->wbPropSet) { switch (src->controls.wbmode) { case NvArgusCamAwbMode_Off: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_OFF); break; case NvArgusCamAwbMode_Auto: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_AUTO); break; case NvArgusCamAwbMode_Incandescent: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_INCANDESCENT); break; case NvArgusCamAwbMode_Fluorescent: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_FLUORESCENT); break; case NvArgusCamAwbMode_WarmFluorescent: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_WARM_FLUORESCENT); break; case NvArgusCamAwbMode_Daylight: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_DAYLIGHT); break; case NvArgusCamAwbMode_CloudyDaylight: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_CLOUDY_DAYLIGHT); break; case NvArgusCamAwbMode_Twilight: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_TWILIGHT); break; case NvArgusCamAwbMode_Shade: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_SHADE); break; case NvArgusCamAwbMode_Manual: l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_MANUAL); break; default : l_iAutoControlSettings_ptr->setAwbMode(AWB_MODE_OFF); break; } src->wbPropSet = FALSE; l_iCaptureSession->repeat(l_captureRequest); } if (src->saturationPropSet) { l_iAutoControlSettings_ptr->setColorSaturationEnable(TRUE); l_iAutoControlSettings_ptr->setColorSaturation(src->controls.saturation); l_iCaptureSession->repeat(l_captureRequest); src->saturationPropSet = FALSE; } if (src->exposureCompensationPropSet) { l_iAutoControlSettings_ptr->setExposureCompensation(src->controls.ExposureCompensation); l_iCaptureSession->repeat(l_captureRequest); src->exposureCompensationPropSet = FALSE; } if(src->aeLockPropSet) { if(src->controls.AeLock) l_iAutoControlSettings_ptr->setAeLock(true); else l_iAutoControlSettings_ptr->setAeLock(false); l_iCaptureSession->repeat(l_captureRequest); src->aeLockPropSet = FALSE; } if(src->awbLockPropSet) { if(src->controls.AwbLock) l_iAutoControlSettings_ptr->setAwbLock(true); else l_iAutoControlSettings_ptr->setAwbLock(false); l_iCaptureSession->repeat(l_captureRequest); src->awbLockPropSet = FALSE; } if(src->tnrModePropSet) { switch (src->controls.NoiseReductionMode) { case NvArgusCamNoiseReductionMode_Off: l_iDenoiseSettings_ptr->setDenoiseMode(DENOISE_MODE_OFF); break; case NvArgusCamNoiseReductionMode_Fast: l_iDenoiseSettings_ptr->setDenoiseMode(DENOISE_MODE_FAST); break; case NvArgusCamNoiseReductionMode_HighQuality: l_iDenoiseSettings_ptr->setDenoiseMode(DENOISE_MODE_HIGH_QUALITY); break; default : l_iDenoiseSettings_ptr->setDenoiseMode(DENOISE_MODE_OFF); break; } l_iCaptureSession->repeat(l_captureRequest); src->tnrModePropSet = FALSE; } if(src->tnrStrengthPropSet) { l_iDenoiseSettings_ptr->setDenoiseStrength(src->controls.NoiseReductionStrength); l_iCaptureSession->repeat(l_captureRequest); src->tnrStrengthPropSet = FALSE; } if(src->edgeEnhancementModePropSet) { switch (src->controls.EdgeEnhancementMode) { case NvArgusCamEdgeEnhancementMode_Off: l_iEeSettings_ptr->setEdgeEnhanceMode(EDGE_ENHANCE_MODE_OFF); break; case NvArgusCamEdgeEnhancementMode_Fast: l_iEeSettings_ptr->setEdgeEnhanceMode(EDGE_ENHANCE_MODE_FAST); break; case NvArgusCamEdgeEnhancementMode_HighQuality: l_iEeSettings_ptr->setEdgeEnhanceMode(EDGE_ENHANCE_MODE_HIGH_QUALITY); break; default : l_iEeSettings_ptr->setEdgeEnhanceMode(EDGE_ENHANCE_MODE_OFF); break; } l_iCaptureSession->repeat(l_captureRequest); src->edgeEnhancementModePropSet = FALSE; } if(src->edgeEnhancementStrengthPropSet) { l_iEeSettings_ptr->setEdgeEnhanceStrength(src->controls.EdgeEnhancementStrength); l_iCaptureSession->repeat(l_captureRequest); src->edgeEnhancementStrengthPropSet = FALSE; } if(src->aeAntibandingPropSet) { switch (src->controls.AeAntibandingMode) { case NvArgusCamAeAntibandingMode_Off: l_iAutoControlSettings_ptr->setAeAntibandingMode(AE_ANTIBANDING_MODE_OFF); break; case NvArgusCamAeAntibandingMode_Auto: l_iAutoControlSettings_ptr->setAeAntibandingMode(AE_ANTIBANDING_MODE_AUTO); break; case NvArgusCamAeAntibandingMode_50HZ: l_iAutoControlSettings_ptr->setAeAntibandingMode(AE_ANTIBANDING_MODE_50HZ); break; case NvArgusCamAeAntibandingMode_60HZ: l_iAutoControlSettings_ptr->setAeAntibandingMode(AE_ANTIBANDING_MODE_60HZ); break; default : l_iAutoControlSettings_ptr->setAeAntibandingMode(AE_ANTIBANDING_MODE_OFF); break; } l_iCaptureSession->repeat(l_captureRequest); src->aeAntibandingPropSet = FALSE; } if(src->gainRangePropSet == TRUE) { sensorModeAnalogGainRange.min() = src->controls.gainRange.low; sensorModeAnalogGainRange.max() = src->controls.gainRange.high; l_iRequestSourceSettings_ptr->setGainRange(sensorModeAnalogGainRange); l_iCaptureSession->repeat(l_captureRequest); src->gainRangePropSet = FALSE; } if(src->ispDigitalGainRangePropSet == TRUE) { ispDigitalGainRange.min() = src->controls.ispDigitalGainRange.low; ispDigitalGainRange.max() = src->controls.ispDigitalGainRange.high; l_iAutoControlSettings_ptr->setIspDigitalGainRange(ispDigitalGainRange); l_iCaptureSession->repeat(l_captureRequest); src->ispDigitalGainRangePropSet = FALSE; } if(src->exposureTimePropSet == TRUE) { limitExposureTimeRange.min() = src->controls.exposureTimeRange.low; limitExposureTimeRange.max() = src->controls.exposureTimeRange.high; l_iRequestSourceSettings_ptr->setExposureTimeRange(limitExposureTimeRange); l_iCaptureSession->repeat(l_captureRequest); src->exposureTimePropSet = FALSE; } // Use the IFrame interface to print out the frame number/timestamp, and // to provide access to the Image in the Frame. IFrame *iFrame = interface_cast