Hello,
Here is my source code :
/*
* Copyright (c) 2016 - 2018, 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.
*/
#include <Argus/Argus.h>
#include <Argus/Ext/BayerAverageMap.h>
#include <Argus/Ext/NonLinearHistogram.h>
#include <Argus/Ext/SensorPrivateMetadata.h>
#include "ArgusHelpers.h"
#include "CommonOptions.h"
#include <iomanip>
#include "EGLGlobal.h"
#include <EGLStream/EGLStream.h>
#include "GLContext.h"
#include "Error.h"
#include "Thread.h"
#include "Window.h"
namespace ArgusSamples
{
using namespace Argus;
using namespace EGLStream;
// Globals
EGLDisplayHolder g_display;
// Debug print macros
#define PRODUCER_PRINT(...) printf("PRODUCER: " __VA_ARGS__)
#define PREVIEW_CONSUMER_PRINT(...) printf("PREVIEW CONSUMER: " __VA_ARGS__)
// Convert a pixel number in embedded data line index
// This is valid for the first line only. For the second line an offset should be added to pixel (2 * line_length)
uint16_t embeddedDataIndex(uint16_t pixel) {
// Dummy bytes are inserted depending on the RAW format, see Software Reference Manual figures 4-11 to 4-13 on page 57
// RAW10 format:
return pixel - 1 + ((pixel - 1) / 4);
}
/*******************************************************************************
* Extended options class to add additional options specific to this sample.
******************************************************************************/
class MyAppOptions : public CommonOptions {
public:
MyAppOptions(const char *programName)
: CommonOptions(programName,
ArgusSamples::CommonOptions::Option_R_WindowRect |
ArgusSamples::CommonOptions::Option_F_FrameCount)
{
}
protected:
};
/*******************************************************************************
* Argus Consumer thread:
* Opens an on-screen GL window and renders a live camera preview
******************************************************************************/
class PreviewConsumerThread : public Thread {
public:
explicit PreviewConsumerThread(EGLDisplay display, EGLStreamKHR stream)
: m_display(display)
, m_stream(stream)
, m_texture(0)
, m_program(0)
, m_textureUniform(-1) {
}
~PreviewConsumerThread() {
}
private:
/** @name Thread methods */
/**@{*/
virtual bool threadInitialize();
virtual bool threadExecute();
virtual bool threadShutdown();
/**@}*/
EGLDisplay m_display;
GLContext m_context;
EGLStreamKHR m_stream;
GLuint m_texture;
GLuint m_program;
GLint m_textureUniform;
Size2D<uint32_t> m_windowSize;
};
bool PreviewConsumerThread::threadInitialize() {
// The Window class is defined in utils/gtk/Window.h if WINDOW_GUI_SUPPORT = WINDOW_GUI_GTK
// Window is derived from the WindowBase defined in utils/WindowBase.h
Window &window = Window::getInstance();
// Create the context and make it current.
PREVIEW_CONSUMER_PRINT("Creating OpenGL context.\n");
switch(WINDOW_GUI_SUPPORT) {
case 0 :
PREVIEW_CONSUMER_PRINT("WINDOW_GUI_SUPPORT = WINDOW_GUI_NONE\n");
break;
case 1 :
PREVIEW_CONSUMER_PRINT("WINDOW_GUI_SUPPORT = WINDOW_GUI_GTK\n");
break;
case 2 :
PREVIEW_CONSUMER_PRINT("WINDOW_GUI_SUPPORT = WINDOW_GUI_GLX\n");
break;
case 3 :
PREVIEW_CONSUMER_PRINT("WINDOW_GUI_SUPPORT = WINDOW_GUI_ANDROID\n");
break;
default :
PREVIEW_CONSUMER_PRINT("WINDOW_GUI_SUPPORT undefined\n");
}
PROPAGATE_ERROR(m_context.initialize(&window));
PROPAGATE_ERROR(m_context.makeCurrent());
// Get window size.
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
m_windowSize = Argus::Size2D<uint32_t>(viewport[2], viewport[3]);
// Create the shader program to render a texture.
static const GLfloat quadCoords[] = {1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
static const char vtxSrc[] =
"#version 300 es\n"
"in layout(location = 0) vec2 coord;\n"
"out vec2 texCoord;\n"
"void main() {\n"
" gl_Position = vec4((coord * 2.0) - 1.0, 0.0, 1.0);\n"
// Note: Argus frames use a top-left origin and need to be inverted for GL texture use.
" texCoord = vec2(coord.x, 1.0 - coord.y);\n"
"}\n";
static const char frgSrc[] =
"#version 300 es\n"
"#extension GL_OES_EGL_image_external : require\n"
"precision highp float;\n"
"uniform samplerExternalOES texSampler;\n"
"in vec2 texCoord;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = texture2D(texSampler, texCoord);\n"
"}\n";
PROPAGATE_ERROR(m_context.createProgram(vtxSrc, frgSrc, &m_program));
glUseProgram(m_program);
m_textureUniform = glGetUniformLocation(m_program, "texSampler");
glUniform1i(m_textureUniform, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, quadCoords);
// Create an external texture and connect it to the stream as the consumer.
PREVIEW_CONSUMER_PRINT("Connecting to EGLStream.\n");
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_texture);
if (!eglStreamConsumerGLTextureExternalKHR(m_display, m_stream))
ORIGINATE_ERROR("Unable to connect GL as consumer");
PREVIEW_CONSUMER_PRINT("Connected to stream.\n");
return true;
}
bool PreviewConsumerThread::threadExecute() {
EGLint state = EGL_STREAM_STATE_CONNECTING_KHR;
// Wait until the Argus producer is connected.
PREVIEW_CONSUMER_PRINT("Waiting until producer connect...\n");
while (true) {
if (!eglQueryStreamKHR(m_display, m_stream, EGL_STREAM_STATE_KHR, &state))
ORIGINATE_ERROR("Failed to query stream state (possible producer failure).");
if (state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR)
break;
}
PREVIEW_CONSUMER_PRINT("Producer connected; continuing.\n");
uint32_t frame = 0;
uint64_t sensorTimestampOffset = 0;
std::ostringstream awbModeStream;
// There are 2 ways of displaying the stream:
// - display new frames when they become available (this is what is used here);
// - display a stream at 60fps. If the producer stream comes at a smaller speed then the same frame can be displayed more than once.
// To use this method, loop with this condition "while (eglStreamConsumerAcquireKHR(m_display, m_stream))"
bool done = false;
while (!done && !m_doShutdown) {
if (m_doShutdown)
printf("m_doShutdown detected!\n");
bool newFrameAvailable = false;
if (!eglQueryStreamKHR(m_display, m_stream, EGL_STREAM_STATE_KHR, &state) || state == EGL_STREAM_STATE_DISCONNECTED_KHR) {
done = true;
break;
}
else if (state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) {
newFrameAvailable = true;
if (!eglStreamConsumerAcquireKHR(m_display, m_stream)) {
done = true;
break;
}
}
if (!done) {
if (newFrameAvailable) {
frame += 1;
// Render the image.
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Display frame counter
m_context.setTextSize(16.0f / m_windowSize.height(), (float)m_windowSize.height() / (float)m_windowSize.width());
float yPosition = 0.98;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream counterStream;
counterStream << "Frame: " << std::setw(3) << frame;
m_context.renderText(counterStream.str().c_str());
// Get the metadata from the current EGLStream frame.
// Note: This will likely fail for the last frame since the producer has
// already disconected from the EGLStream, so we need to handle
// failure gracefully.
UniqueObj<EGLStream::MetadataContainer> metadataContainer(EGLStream::MetadataContainer::create(m_display, m_stream));
EGLStream::IArgusCaptureMetadata *iArgusCaptureMetadata = interface_cast<EGLStream::IArgusCaptureMetadata>(metadataContainer);
if (iArgusCaptureMetadata)
{
CaptureMetadata *metadata = iArgusCaptureMetadata->getMetadata();
const ICaptureMetadata* iMetadata = interface_cast<const ICaptureMetadata>(metadata);
if (!iMetadata)
ORIGINATE_ERROR("Failed to get Argus metadata\n");
// Display timestamp
uint64_t sensorTimestamp = iMetadata->getSensorTimestamp();
if (sensorTimestampOffset == 0)
sensorTimestampOffset = sensorTimestamp;
uint64_t sensorTimestampMilliseconds = (sensorTimestamp - sensorTimestampOffset) / 1000000;
uint64_t sensorTimestampSeconds = sensorTimestampMilliseconds / 1000;
uint64_t sensorTimestampMinutes = sensorTimestampSeconds / 60;
sensorTimestampMilliseconds = sensorTimestampMilliseconds - (sensorTimestampSeconds * 1000);
sensorTimestampSeconds = sensorTimestampSeconds - (sensorTimestampMinutes * 60);
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream timestampStream;
timestampStream << "Timestamp: " << sensorTimestampMinutes << ":" << std::setfill('0') << std::setw(2) << sensorTimestampSeconds << ":" << std::setfill('0') << std::setw(3) << sensorTimestampMilliseconds;
m_context.renderText(timestampStream.str().c_str());
// Spacing
yPosition -= 0.02;
// Display framerate
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream framerateStream;
framerateStream << "Framerate (fps): " << (float)(1000000000 / iMetadata->getFrameDuration());
m_context.renderText(framerateStream.str().c_str());
// Display exposure time
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream exposureStream;
exposureStream << "Exposure time (us): " << (float)iMetadata->getSensorExposureTime() / 1000.0;
m_context.renderText(exposureStream.str().c_str());
// Get the sensor private metadata.
const Ext::ISensorPrivateMetadata *iSensorMetadata = interface_cast<const Ext::ISensorPrivateMetadata>(metadata);
if (!iSensorMetadata)
ORIGINATE_ERROR("Failed to get ISensorPrivateMetadata Interface");
size_t sensorMetadataSize = iSensorMetadata->getMetadataSize();
char* sensorMetadata = new char[sensorMetadataSize];
if (!sensorMetadata)
ORIGINATE_ERROR("Failed to allocate metadata buffer");
if (iSensorMetadata->getMetadata(sensorMetadata, sensorMetadataSize) != STATUS_OK)
ORIGINATE_ERROR("Failed to get sensor private metadata");
// Spacing
yPosition -= 0.02;
// Display sensor frame counter
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream sensorMetadataFrmCnt;
sensorMetadataFrmCnt << "FRM_CNT: " << std::setw(3) << (uint16_t)sensorMetadata[embeddedDataIndex(7)];
m_context.renderText(sensorMetadataFrmCnt.str().c_str());
// Display sensor coarse integration time and equivalent exposure time
// T sh = T line × (COARSE_INTEG_TIME [lines] + FINE_INTEG_TIME [pixels] / LINE_LENGTH_PCK [pixels/line])
uint32_t sensorCoarseIntegTime = (uint16_t)sensorMetadata[embeddedDataIndex(39)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(41)];
uint32_t sensorFineIntegTime = (uint16_t)sensorMetadata[embeddedDataIndex(35)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(37)];
uint32_t sensorLineLengthPck = (uint16_t)sensorMetadata[embeddedDataIndex(127)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(129)];
// T line = LINE_LENGTH_PCK [pixels/line] × IVTPXCK_period / 4 (Total number of image pipe lines)
float sensorIvtpxckPeriod = 1.0 / 209.925;
float sensorTline = sensorLineLengthPck * sensorIvtpxckPeriod / 4;
float sensorTsh = sensorTline * (sensorCoarseIntegTime + (sensorFineIntegTime / sensorLineLengthPck));
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream sensorCoarseIntegTimeStream;
sensorCoarseIntegTimeStream << "COARSE_INTEG_TIME: " << sensorCoarseIntegTime;
m_context.renderText(sensorCoarseIntegTimeStream.str().c_str());
yPosition -= 0.02;
m_context.setTextPosition(0.02f, yPosition);
std::ostringstream sensorExposureTime;
sensorExposureTime << "Exposure time (us): " << sensorTsh;
m_context.renderText(sensorExposureTime.str().c_str());
delete[] sensorMetadata;
}
PROPAGATE_ERROR(m_context.swapBuffers());
}
}
}
PREVIEW_CONSUMER_PRINT("No more frames. Cleaning up.\n");
PREVIEW_CONSUMER_PRINT("%s\n", awbModeStream.str().c_str());
PREVIEW_CONSUMER_PRINT("%d frames were acquired by the consumer.\n", frame);
PROPAGATE_ERROR(requestShutdown());
return true;
}
bool PreviewConsumerThread::threadShutdown() {
glDeleteProgram(m_program);
glDeleteTextures(1, &m_texture);
m_context.cleanup();
PREVIEW_CONSUMER_PRINT("Done.\n");
return true;
}
/*******************************************************************************
* Argus Producer thread:
* Opens the Argus camera driver, creates an OutputStream to be displayed
******************************************************************************/
static bool execute(const MyAppOptions& options) {
const uint32_t SelectedDevice = 0;
const uint32_t SelectedMode = 0;
// Initialize the window and EGL display
Window &window = Window::getInstance();
window.setWindowRect(options.windowRect());
PROPAGATE_ERROR(g_display.initialize(window.getEGLNativeDisplay()));
// Create the CameraProvider to establish libargus driver connection
UniqueObj<CameraProvider> cameraProvider(CameraProvider::create());
// Get the ICameraProvider interface from the global CameraProvider
// From the ICameraProvider we will get a camera device (1) and create a capture session (2)
ICameraProvider *iCameraProvider = interface_cast<ICameraProvider>(cameraProvider);
if (!iCameraProvider)
ORIGINATE_ERROR("Failed to get ICameraProvider interface");
printf("Argus Vendor: %s\n", iCameraProvider->getVendor().c_str());
printf("Argus Version: %s\n", iCameraProvider->getVersion().c_str());
/*
* 1. Get a camera device and set it to the correct mode
*/
// Query available CameraDevices from ICameraProvider interface
// Based on the number of devices connected, the camera device pointers will be returned by the 'getCameraDevices()' API
std::vector<CameraDevice*> cameraDevices;
iCameraProvider->getCameraDevices(&cameraDevices);
if (cameraDevices.size() == 0)
ORIGINATE_ERROR("No camera found");
printf("Number of cameras found: %lu\n", cameraDevices.size());
// Select a camera device
CameraDevice *cameraDevice = cameraDevices[SelectedDevice];
if (!cameraDevice)
ORIGINATE_ERROR("Selected camera device is not available");
// Make sure sensor private metadata is supported by the device
const Ext::ISensorPrivateMetadataCaps *iSensorMetadataCaps = interface_cast<const Ext::ISensorPrivateMetadataCaps>(cameraDevice);
if (!iSensorMetadataCaps)
ORIGINATE_ERROR("Failed to get ISensorPrivateMetadataCaps Interface");
// Get the device properties and capabilities
ICameraProperties *iCameraProperties = interface_cast<ICameraProperties>(cameraDevice);
if (!iCameraProperties)
ORIGINATE_ERROR("Failed to get ICameraProperties interface");
// Get available sensor modes
std::vector<SensorMode*> sensorModes;
Argus::Status status = iCameraProperties->getAllSensorModes(&sensorModes);
if (status != STATUS_OK)
ORIGINATE_ERROR("Failed to get sensor modes from device");
// Select a mode, the sensorMode will also be used when creating the request
SensorMode* sensorMode = sensorModes[SelectedMode];
ISensorMode *iSensorMode = interface_cast<ISensorMode>(sensorMode);
if (!iSensorMode)
ORIGINATE_ERROR("Selected sensor mode not available");
// Display sensor mode info
printf("Sensor mode %d:\n", SelectedMode);
ArgusSamples::ArgusHelpers::printSensorModeInfo(sensorMode, " ");
// Get the framerate and exposure ranges
Range<uint64_t> limitFrameDurationRange = iSensorMode->getFrameDurationRange();
printf("Sensor Frame Duration Range min %ju, max %ju\n", limitFrameDurationRange.min(), limitFrameDurationRange.max());
Range<uint64_t> limitExposureTimeRange = iSensorMode->getExposureTimeRange();
printf("Sensor Exposure Range min %ju, max %ju\n", limitExposureTimeRange.min(), limitExposureTimeRange.max());
/*
* 2. Create a capture session from the CameraProvider interface
* It will control all operations on the sensor
*/
// Create the capture session using the camera device.
// From there we will get an interface to the core CaptureSession methods (iCaptureSession) (2.1)
// and an interface for an object which generates Events (2.2)
UniqueObj<CaptureSession> captureSession(iCameraProvider->createCaptureSession(cameraDevice));
// Interface to the core CaptureSession methods. From there 3 objects will be created:
// - an OutputStreamSettings object that is used to configure the creation of the OutputStream (2.1.1)
// - an OutputStream object using the settings configured by the OutputStreamSettings object (2.1.2)
// - a request object that will be used with this CaptureSession (2.1.3)
ICaptureSession *iCaptureSession = interface_cast<ICaptureSession>(captureSession);
if (!iCaptureSession)
ORIGINATE_ERROR("Failed to create CaptureSession");
// 2.1.1 Creates the OutputStreamSettings object that will be used to configure the creation of an OutputStream.
// Check https://docs.nvidia.com/jetson/archives/l4t-multimedia-archived/l4t-multimedia-282/classArgus_1_1IOutputStreamSettings.html
// for the settings available.
UniqueObj<OutputStreamSettings> outputStreamSettings(iCaptureSession->createOutputStreamSettings(STREAM_TYPE_EGL));
IEGLOutputStreamSettings *iEGLStreamSettings = interface_cast<IEGLOutputStreamSettings>(outputStreamSettings);
if (!iEGLStreamSettings)
ORIGINATE_ERROR("Failed to create IEGLOutputStreamSettings");
iEGLStreamSettings->setPixelFormat(PIXEL_FMT_YCbCr_420_888);
iEGLStreamSettings->setResolution(Size2D<uint32_t>(options.windowRect().width(), options.windowRect().height()));
iEGLStreamSettings->setEGLDisplay(g_display.get());
// Sets the mode of the OutputStream.
// Available options are:
// - MAILBOX (default):
// In this mode, only the newest frame is made available to the consumer. When libargus completes a frame it empties the mailbox
// and inserts the new frame into the mailbox. The consumer then retrieves the frame from the mailbox and processes it; when finished,
// the frame is either placed back into the mailbox (if the mailbox is empty) or discarded (if the mailbox is not empty). This mode
// implies 2 things:
// - If the consumer consumes frames slower than libargus produces frames, then some frames may be lost (never seen by the consumer).
// - If the consumer consumes frames faster than libargus produces frames, then the consumer may see some frames more than once.
// - FIFO:
// When using this mode, every producer frame is made available to the consumer through the use of a fifo queue for the frames. When
// using this mode, the fifo queue length must be specified using setFifoLength. When libargus completes a frame it inserts it to the
// head of the fifo queue. If the fifo is full (already contains the number of frames equal to the fifo queue length), libargus will
// stall until the fifo is no longer full. The consumer consumes frames from the tail of the queue; however, if the consumer releases
// a frame while the queue is empty, the frame is set aside and will be returned again the next time the consumer requests a frame if
// another new frame has not been inserted into the fifo queue before then. Once a new frame is inserted into the fifo queue, any
// previously released frame will be permanently discarded. This mode implies:
// - Frames are never discarded until the consumer has processed them.
// - If the consumer consumes frames slower than libargus produces them, libargus will stall.
// - If the consumer consumes frames faster than libargus produces them, then the consumer may see some frames more than once.
// In this mode use setFifoLength() to set the FIFO queue length of the stream.
iEGLStreamSettings->setMode(EGL_STREAM_MODE_MAILBOX);
// Enables the sensor metadata: enabling this will allow an EGLStream::MetadataContainer to be created from frames acquired on the
// consumer side of the EGLStream that will expose the EGLStream::IArgusCaptureMetadata interface, which in turn provides access to
// the CaptureMetadata corresponding to that frame. This will also enable the IArgusCaptureMetadata interface directly on
// EGLStream::Frames acquired by an EGLStream::FrameConsumer.
iEGLStreamSettings->setMetadataEnable(true);
// 2.1.2 Create OutputStream that is consumed by the preview (OpenGL) consumer
printf("Creating preview output stream\n");
UniqueObj<OutputStream> previewStream(iCaptureSession->createOutputStream(outputStreamSettings.get()));
IEGLOutputStream *iPreviewStream = interface_cast<IEGLOutputStream>(previewStream);
if (!iPreviewStream)
ORIGINATE_ERROR("Failed to create Preview Stream");
// Launch the consumer thread to consume frames from the OutputStream's EGLStream
printf("Launching preview consumer thread\n");
PreviewConsumerThread previewConsumerThread(iPreviewStream->getEGLDisplay(), iPreviewStream->getEGLStream());
PROPAGATE_ERROR(previewConsumerThread.initialize());
PROPAGATE_ERROR(previewConsumerThread.waitRunning());
// 2.1.3 Create capture request
// The CaptureIntent instructs the driver to populate the request with recommended settings for that intent.
// For example, a PREVIEW intent may disable post-processing in order to reduce latency and resource usage while a STILL_CAPTURE
// intent will enable post-processing in order to optimize still image quality.
// Available options: CAPTURE_INTENT_MANUAL, CAPTURE_INTENT_PREVIEW, CAPTURE_INTENT_STILL_CAPTURE, CAPTURE_INTENT_VIDEO_RECORD, CAPTURE_INTENT_VIDEO_SNAPSHOT
// CAPTURE_INTENT_MANUAL: 16,491ms, 40fps
// CAPTURE_INTENT_PREVIEW: 16,491ms, 30fps
// CAPTURE_INTENT_STILL_CAPTURE: 16,491ms, 30fps
// CAPTURE_INTENT_VIDEO_RECORD: 16,491ms, 30fps
// CAPTURE_INTENT_VIDEO_SNAPSHOT: 16,491ms, 30fps
// none:
const CaptureIntent intent = CAPTURE_INTENT_PREVIEW;
UniqueObj<Request> request(iCaptureSession->createRequest(intent));
IRequest *iRequest = interface_cast<IRequest>(request);
if (!iRequest)
ORIGINATE_ERROR("Failed to get capture request interface");
// The internal post-processing pipeline is generated on a per-request basis, and is dependent on the full set of enabled output streams that have
// post-processing enabled. In order to prevent these pipeline changes, which may cause visual changes in the preview stream, post-processing
// is disabled for the periodic still capture.
IStreamSettings *streamSettings = interface_cast<IStreamSettings>(iRequest->getStreamSettings(previewStream.get()));
streamSettings->setPostProcessingEnable(false);
// Enable sensor private metadata output in the request
Ext::ISensorPrivateMetadataRequest *iSensorPrivateMetadataRequest = interface_cast<Ext::ISensorPrivateMetadataRequest>(request);
if (!iSensorPrivateMetadataRequest)
ORIGINATE_ERROR("Failed to get ISensorPrivateMetadata interface");
iSensorPrivateMetadataRequest->setMetadataEnable(true);
// Get the source settings for the request
// Check file:///home/bmichel/Downloads/nvidia/nvl4t_docs/classArgus_1_1ISourceSettings.html
printf("Get sensor settings\n");
ISourceSettings *iSourceSettings = interface_cast<ISourceSettings>(iRequest->getSourceSettings());
if (!iSourceSettings)
ORIGINATE_ERROR("Failed to get source settings interface");
// Get the auto control settings for the request
IAutoControlSettings* iAutoControlSettings = interface_cast<IAutoControlSettings>(iRequest->getAutoControlSettings());
if (!iAutoControlSettings)
ORIGINATE_ERROR("Failed to get AutoControlSettings interface");
// Enable the output stream: captures made with this request will produce output on the previewStream
status = iRequest->enableOutputStream(previewStream.get());
if (status != STATUS_OK)
ORIGINATE_ERROR("Failed to enable stream in capture request");
// Set frame rate to 30fps:
uint64_t userFramerate = 30;
uint64_t userFrameDuration = 1000000000 / userFramerate;
printf("Framerate = %ju fps (Frame Duration = %ju)\n", userFramerate, userFrameDuration);
status = iSourceSettings->setFrameDurationRange(userFrameDuration);
if (status != STATUS_OK)
ORIGINATE_ERROR("Unable to set the new framerate");
// Check the settings:
// Frame duration time range, in nanoseconds (determines frame rate)
Range<uint64_t> sensorFrameDurationRange = iSourceSettings->getFrameDurationRange();
printf("FrameDurationRange (ns): [%ju:%ju]\n", sensorFrameDurationRange.min(), sensorFrameDurationRange.max());
// Exposure time range, in nanoseconds
Range<uint64_t> sensorExposureTimeRange = iSourceSettings->getExposureTimeRange();
printf("ExposureTimeRange (ns): [%ju:%ju]\n", sensorExposureTimeRange.min(), sensorExposureTimeRange.max());
// Set the exposure time
uint64_t userExposureTime = 30000000;
printf("Exposure = %ju\n", userExposureTime);
status = iSourceSettings->setExposureTimeRange(Range<uint64_t>(userExposureTime));
if (status != STATUS_OK)
ORIGINATE_ERROR("Unable to set the new exposure time");
// Set the sensor gain
status = iSourceSettings->setGainRange(2);
if (status != STATUS_OK)
ORIGINATE_ERROR("Unable to set the gain");
// 2.2 Interface for an object which generates Events
IEventProvider *iEventProvider = interface_cast<IEventProvider>(captureSession);
if (!iEventProvider)
ORIGINATE_ERROR("iEventProvider is NULL");
std::vector<EventType> eventTypes;
eventTypes.push_back(EVENT_TYPE_CAPTURE_COMPLETE);
UniqueObj<EventQueue> queue(iEventProvider->createEventQueue(eventTypes));
IEventQueue *iQueue = interface_cast<IEventQueue>(queue);
if (!iQueue)
ORIGINATE_ERROR("event queue interface is NULL");
// Set up a repeating request: it will queue a request whenever the request queue is empty and the camera is ready to accept new requests
// There are 4 different types of requests in order to capture frame:
// - capture: single session single capture
// - captureBurst: multiple session single capture
// - repeat: single session continuous capture until stopRepeat() is called
// - repeatBurst: multiple session continuous capture until stopRepeat() is called
status = iCaptureSession->repeat(request.get());
if (status != STATUS_OK)
ORIGINATE_ERROR("Unable to submit repeat() request");
for (uint32_t frameCaptureLoop = 0; frameCaptureLoop < options.frameCount(); frameCaptureLoop++) {
// Keep PREVIEW display window serviced
window.pollEvents();
const uint64_t ONE_SECOND = 1000000000;
iEventProvider->waitForEvents(queue.get(), ONE_SECOND);
if(iQueue->getSize() == 0)
ORIGINATE_ERROR("No events in queue");
const Event* event = iQueue->getEvent(iQueue->getSize() - 1);
const IEventCaptureComplete *iEventCaptureComplete = interface_cast<const IEventCaptureComplete>(event);
if(!iEventCaptureComplete)
ORIGINATE_ERROR("Failed to get EventCaptureComplete Interface");
const CaptureMetadata *metaData = iEventCaptureComplete->getMetadata();
const ICaptureMetadata* iMetadata = interface_cast<const ICaptureMetadata>(metaData);
if (!iMetadata)
ORIGINATE_ERROR("Failed to get CaptureMetadata Interface");
uint64_t frameExposureTime = iMetadata->getSensorExposureTime();
float frameGain = iMetadata->getSensorAnalogGain();
// Get sensor private metadata
const Ext::ISensorPrivateMetadata *iSensorMetadata = interface_cast<const Ext::ISensorPrivateMetadata>(metaData);
if (!iSensorMetadata)
ORIGINATE_ERROR("Failed to get ISensorPrivateMetadata Interface");
size_t sensorMetadataSize = iSensorMetadata->getMetadataSize();
char* sensorMetadata = new char[sensorMetadataSize];
if (!sensorMetadata)
ORIGINATE_ERROR("Failed to allocate metadata buffer");
status = iSensorMetadata->getMetadata(sensorMetadata, sensorMetadataSize);
if (status != STATUS_OK)
ORIGINATE_ERROR("Failed to get sensor private metadata");
printf("Exposure Time: %.3f ms, Analog Gain: %f\n", frameExposureTime / 1000000.0, frameGain);
printf("FRM_CNT: %3d, ", (uint16_t)sensorMetadata[embeddedDataIndex(7)]);
printf("COARSE_INTEG_TIME: %4d, ", (uint16_t)sensorMetadata[embeddedDataIndex(39)] * 256 +
(uint16_t)sensorMetadata[embeddedDataIndex(41)]);
printf("ANA_GAIN_GLOBAL: %5d, ", (uint16_t)sensorMetadata[embeddedDataIndex(43)] * 256 +
(uint16_t)sensorMetadata[embeddedDataIndex(45)]);
printf("FINE_INTEG_TIME: %4d, ", (uint16_t)sensorMetadata[embeddedDataIndex(35)] * 256 +
(uint16_t)sensorMetadata[embeddedDataIndex(37)]);
printf("FRM_LENGTH_LINES: %4d, ", (uint16_t)sensorMetadata[embeddedDataIndex(123)] * 256 +
(uint16_t)sensorMetadata[embeddedDataIndex(125)]);
printf("LINE_LENGTH_PCK: %4d ", (uint16_t)sensorMetadata[embeddedDataIndex(127)] * 256 +
(uint16_t)sensorMetadata[embeddedDataIndex(129)]);
printf("\n");
// Exposure time calculation
// T sh = T line × (COARSE_INTEG_TIME [lines] + FINE_INTEG_TIME [pixels] / LINE_LENGTH_PCK [pixels/line])
uint32_t sensorCoarseIntegTime = (uint16_t)sensorMetadata[embeddedDataIndex(39)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(41)];
uint32_t sensorFineIntegTime = (uint16_t)sensorMetadata[embeddedDataIndex(35)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(37)];
uint32_t sensorLineLengthPck = (uint16_t)sensorMetadata[embeddedDataIndex(127)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(129)];
// T line = LINE_LENGTH_PCK [pixels/line] × IVTPXCK_period / 4 (Total number of image pipe lines)
float sensorIvtpxckPeriod = 1.0 / 209.925;
float sensorTline = sensorLineLengthPck * sensorIvtpxckPeriod / 4;
float sensorTsh = sensorTline * (sensorCoarseIntegTime + (sensorFineIntegTime / sensorLineLengthPck));
printf("Sensor Exposure Time: %.3f ms\n", sensorTsh / 1000.0);
// FPS calculation
// fps = pix_rate / pix_tot
// pix_rate = IVTPXCK * 4
// pix_tot = FRM_LENGTH_LINES * LINE_LENGTH_PCK
uint32_t sensorFrmLengthLines = (uint16_t)sensorMetadata[embeddedDataIndex(123)] * 256 + (uint16_t)sensorMetadata[embeddedDataIndex(125)];
uint64_t pixRate = 209925000 * 4;
uint64_t pixTot = sensorFrmLengthLines * sensorLineLengthPck;
float sensorFps = pixRate / pixTot;
printf("Sensor Frame Rate: %.3f\n", sensorFps);
delete[] sensorMetadata;
}
// Stop the repeating request and wait for idle
iCaptureSession->stopRepeat();
iCaptureSession->waitForIdle();
// Destroy the output stream (stops consumer threads)
previewStream.reset();
// Wait for the consumer thread to complete
PROPAGATE_ERROR(previewConsumerThread.shutdown());
// Shut down Argus
cameraProvider.reset();
// Shut down the window (destroys window's EGLSurface)
window.shutdown();
// Cleanup the EGL display
PROPAGATE_ERROR(g_display.cleanup());
PRODUCER_PRINT("Done -- exiting.\n");
return true;
}
}; // namespace ArgusSamples
int main(int argc, char** argv)
{
ArgusSamples::MyAppOptions options(basename(argv[0]));
if (!options.parse(argc, argv))
return EXIT_FAILURE;
if (options.requestedExit())
return EXIT_SUCCESS;
if (!ArgusSamples::execute(options))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
I did try argus_camera and I can set the frame rate to 30fps and the exposure time to 30ms however the exposure time stays at 16.491ms.
If I try an exposure of 25ms instead of 30ms, then I see the actual exposure time flickering between 20ms and 25ms. Both with my program and with argus_camera.
And at 20ms it is fine on argus_camera but stay at 16.491ms with my program.
Regards,
Ben