- Main Page
- Modules
- Namespaces
- Classes
- Files

- Search for |

- File List
- File Members

# samples/09_camera_jpeg_capture/main.cpp

Go to the documentation of this file.

    00001 /*
    00002  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
    00003  *
    00004  * Redistribution and use in source and binary forms, with or without
    00005  * modification, are permitted provided that the following conditions
    00006  * are met:
    00007  *  * Redistributions of source code must retain the above copyright
    00008  *    notice, this list of conditions and the following disclaimer.
    00009  *  * Redistributions in binary form must reproduce the above copyright
    00010  *    notice, this list of conditions and the following disclaimer in the
    00011  *    documentation and/or other materials provided with the distribution.
    00012  *  * Neither the name of NVIDIA CORPORATION nor the names of its
    00013  *    contributors may be used to endorse or promote products derived
    00014  *    from this software without specific prior written permission.
    00015  *
    00016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
    00017  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    00018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    00019  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    00020  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    00021  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    00022  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    00023  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
    00024  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    00026  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    00027  */
    00028
    00029 #include "Error.h"
    00030 #include "Thread.h"
    00031
    00032 #include <Argus/Argus.h>
    00033 #include <EGLStream/EGLStream.h>
    00034 #include <EGLStream/NV/ImageNativeBuffer.h>
    00035
    00036 #include <NvEglRenderer.h>
    00037 #include <NvJpegEncoder.h>
    00038
    00039 #include <unistd.h>
    00040 #include <stdio.h>
    00041 #include <stdlib.h>
    00042 #include <getopt.h>
    00043 #include <iostream>
    00044 #include <fstream>
    00045
    00046 using namespace Argus;
    00047 using namespace EGLStream;
    00048
    00049 // Constant configuration.
    00050 static const uint32_t CAPTURE_TIME  = 1; // In seconds.
    00051 static const int      DEFAULT_FPS   = 30;
    00052
    00053 // Configurations which can be overrided by cmdline
    00054 static Size    PREVIEW_SIZE (640, 480);
    00055 static Size    CAPTURE_SIZE (1920, 1080);
    00056 static bool    DO_STAT = false;
    00057 static bool    VERBOSE_ENABLE = false;
    00058
    00059 #define JPEG_BUFFER_SIZE    (CAPTURE_SIZE.width * CAPTURE_SIZE.height * 3 / 2)
    00060
    00061 // Debug print macros.
    00062 #define PRODUCER_PRINT(...) printf("PRODUCER: " __VA_ARGS__)
    00063 #define CONSUMER_PRINT(...) printf("CONSUMER: " __VA_ARGS__)
    00064
    00065 namespace ArgusSamples
    00066 {
    00067
    00068 /*******************************************************************************
    00069  * Base Consumer thread:
    00070  *   Creates an EGLStream::FrameConsumer object to read frames from the
    00071  *   OutputStream, then creates/populates an NvBuffer (dmabuf) from the frames
    00072  *   to be processed by processV4L2Fd.
    00073  ******************************************************************************/
    00074 class ConsumerThread : public Thread
    00075 {
    00076 public:
    00077     explicit ConsumerThread(OutputStream* stream) :
    00078         m_stream(stream),
    00079         m_dmabuf(-1)
    00080     {
    00081     }
    00082     virtual ~ConsumerThread();
    00083
    00084 protected:
    00085     /** @name Thread methods */
    00086     /**@{*/
    00087     virtual bool threadInitialize();
    00088     virtual bool threadExecute();
    00089     virtual bool threadShutdown();
    00090     /**@}*/
    00091
    00092     virtual bool processV4L2Fd(int32_t fd, uint64_t frameNumber) = 0;
    00093
    00094     OutputStream* m_stream;
    00095     UniqueObj<FrameConsumer> m_consumer;
    00096     int m_dmabuf;
    00097 };
    00098
    00099 ConsumerThread::~ConsumerThread()
    00100 {
    00101     if (m_dmabuf != -1)
    00102         NvBufferDestroy(m_dmabuf);
    00103 }
    00104
    00105 bool ConsumerThread::threadInitialize()
    00106 {
    00107     // Create the FrameConsumer.
    00108     m_consumer = UniqueObj<FrameConsumer>(FrameConsumer::create(m_stream));
    00109     if (!m_consumer)
    00110         ORIGINATE_ERROR("Failed to create FrameConsumer");
    00111
    00112     return true;
    00113 }
    00114
    00115 bool ConsumerThread::threadExecute()
    00116 {
    00117     IStream *iStream = interface_cast<IStream>(m_stream);
    00118     IFrameConsumer *iFrameConsumer = interface_cast<IFrameConsumer>(m_consumer);
    00119
    00120     // Wait until the producer has connected to the stream.
    00121     CONSUMER_PRINT("Waiting until producer is connected...\n");
    00122     if (iStream->waitUntilConnected() != STATUS_OK)
    00123         ORIGINATE_ERROR("Stream failed to connect.");
    00124     CONSUMER_PRINT("Producer has connected; continuing.\n");
    00125
    00126     while (true)
    00127     {
    00128         // Acquire a frame.
    00129         UniqueObj<Frame> frame(iFrameConsumer->acquireFrame());
    00130         IFrame *iFrame = interface_cast<IFrame>(frame);
    00131         if (!iFrame)
    00132             break;
    00133
    00134         // Get the IImageNativeBuffer extension interface.
    00135         NV::IImageNativeBuffer *iNativeBuffer =
    00136             interface_cast<NV::IImageNativeBuffer>(iFrame->getImage());
    00137         if (!iNativeBuffer)
    00138             ORIGINATE_ERROR("IImageNativeBuffer not supported by Image.");
    00139
    00140         // If we don't already have a buffer, create one from this image.
    00141         // Otherwise, just blit to our buffer.
    00142         if (m_dmabuf == -1)
    00143         {
    00144             m_dmabuf = iNativeBuffer->createNvBuffer(iStream->getResolution(),
    00145                                                      NvBufferColorFormat_YUV420,
    00146                                                      NvBufferLayout_BlockLinear);
    00147             if (m_dmabuf == -1)
    00148                 CONSUMER_PRINT("\tFailed to create NvBuffer\n");
    00149         }
    00150         else if (iNativeBuffer->copyToNvBuffer(m_dmabuf) != STATUS_OK)
    00151         {
    00152             ORIGINATE_ERROR("Failed to copy frame to NvBuffer.");
    00153         }
    00154
    00155         // Process frame.
    00156         processV4L2Fd(m_dmabuf, iFrame->getNumber());
    00157     }
    00158
    00159     CONSUMER_PRINT("Done.\n");
    00160
    00161     requestShutdown();
    00162
    00163     return true;
    00164 }
    00165
    00166 bool ConsumerThread::threadShutdown()
    00167 {
    00168     return true;
    00169 }
    00170
    00171 /*******************************************************************************
    00172  * Preview Consumer thread:
    00173  *   Read frames from the OutputStream and render it on display.
    00174  ******************************************************************************/
    00175 class PreviewConsumerThread : public ConsumerThread
    00176 {
    00177 public:
    00178     PreviewConsumerThread(OutputStream *stream, NvEglRenderer *renderer);
    00179     ~PreviewConsumerThread();
    00180
    00181 private:
    00182     bool threadInitialize();
    00183     bool threadShutdown();
    00184     bool processV4L2Fd(int32_t fd, uint64_t frameNumber);
    00185
    00186     NvEglRenderer *m_renderer;
    00187 };
    00188
    00189 PreviewConsumerThread::PreviewConsumerThread(OutputStream *stream,
    00190                                              NvEglRenderer *renderer) :
    00191     ConsumerThread(stream),
    00192     m_renderer(renderer)
    00193 {
    00194 }
    00195
    00196 PreviewConsumerThread::~PreviewConsumerThread()
    00197 {
    00198 }
    00199
    00200 bool PreviewConsumerThread::threadInitialize()
    00201 {
    00202     if (!ConsumerThread::threadInitialize())
    00203         return false;
    00204
    00205     if (DO_STAT)
    00206         m_renderer->enableProfiling();
    00207
    00208     return true;
    00209 }
    00210
    00211 bool PreviewConsumerThread::threadShutdown()
    00212 {
    00213     if (DO_STAT)
    00214         m_renderer->printProfilingStats();
    00215
    00216     return ConsumerThread::threadShutdown();
    00217 }
    00218
    00219 bool PreviewConsumerThread::processV4L2Fd(int32_t fd, uint64_t frameNumber)
    00220 {
    00221     m_renderer->render(fd);
    00222     return true;
    00223 }
    00224
    00225 /*******************************************************************************
    00226  * Capture Consumer thread:
    00227  *   Read frames from the OutputStream and save it to JPEG file.
    00228  ******************************************************************************/
    00229 class CaptureConsumerThread : public ConsumerThread
    00230 {
    00231 public:
    00232     CaptureConsumerThread(OutputStream *stream);
    00233     ~CaptureConsumerThread();
    00234
    00235 private:
    00236     bool threadInitialize();
    00237     bool threadShutdown();
    00238     bool processV4L2Fd(int32_t fd, uint64_t frameNumber);
    00239
    00240     NvJPEGEncoder *m_JpegEncoder;
    00241     unsigned char *m_OutputBuffer;
    00242 };
    00243
    00244 CaptureConsumerThread::CaptureConsumerThread(OutputStream *stream) :
    00245     ConsumerThread(stream),
    00246     m_JpegEncoder(NULL),
    00247     m_OutputBuffer(NULL)
    00248 {
    00249 }
    00250
    00251 CaptureConsumerThread::~CaptureConsumerThread()
    00252 {
    00253     if (m_JpegEncoder)
    00254         delete m_JpegEncoder;
    00255
    00256     if (m_OutputBuffer)
    00257         delete [] m_OutputBuffer;
    00258 }
    00259
    00260 bool CaptureConsumerThread::threadInitialize()
    00261 {
    00262     if (!ConsumerThread::threadInitialize())
    00263         return false;
    00264
    00265     m_OutputBuffer = new unsigned char[JPEG_BUFFER_SIZE];
    00266     if (!m_OutputBuffer)
    00267         return false;
    00268
    00269     m_JpegEncoder = NvJPEGEncoder::createJPEGEncoder("jpenenc");
    00270     if (!m_JpegEncoder)
    00271         ORIGINATE_ERROR("Failed to create JPEGEncoder.");
    00272
    00273     if (DO_STAT)
    00274         m_JpegEncoder->enableProfiling();
    00275
    00276     return true;
    00277 }
    00278
    00279 bool CaptureConsumerThread::threadShutdown()
    00280 {
    00281     if (DO_STAT)
    00282         m_JpegEncoder->printProfilingStats();
    00283
    00284     return ConsumerThread::threadShutdown();
    00285 }
    00286
    00287 bool CaptureConsumerThread::processV4L2Fd(int32_t fd, uint64_t frameNumber)
    00288 {
    00289     char filename[FILENAME_MAX];
    00290     sprintf(filename, "output%03u.jpg", (unsigned) frameNumber);
    00291
    00292     std::ofstream *outputFile = new std::ofstream(filename);
    00293     if (outputFile)
    00294     {
    00295         unsigned long size = JPEG_BUFFER_SIZE;
    00296         unsigned char *buffer = m_OutputBuffer;
    00297         m_JpegEncoder->encodeFromFd(fd, JCS_YCbCr, &buffer, size);
    00298         outputFile->write((char *)buffer, size);
    00299         delete outputFile;
    00300     }
    00301
    00302     return true;
    00303 }
    00304
    00305 /*******************************************************************************
    00306  * Argus Producer thread:
    00307  *   Opens the Argus camera driver, creates two OutputStreams to output to
    00308  *   Preview Consumer and Capture Consumer respectively, then performs repeating
    00309  *   capture requests for CAPTURE_TIME seconds before closing the producer and
    00310  *   Argus driver.
    00311  ******************************************************************************/
    00312 static bool execute(NvEglRenderer *renderer)
    00313 {
    00314     // Create the CameraProvider object and get the core interface.
    00315     UniqueObj<CameraProvider> cameraProvider = UniqueObj<CameraProvider>(CameraProvider::create());
    00316     ICameraProvider *iCameraProvider = interface_cast<ICameraProvider>(cameraProvider);
    00317     if (!iCameraProvider)
    00318         ORIGINATE_ERROR("Failed to create CameraProvider");
    00319
    00320     // Get the camera devices.
    00321     std::vector<CameraDevice*> cameraDevices;
    00322     iCameraProvider->getCameraDevices(&cameraDevices);
    00323     if (cameraDevices.size() == 0)
    00324         ORIGINATE_ERROR("No cameras available");
    00325
    00326     // Create the capture session using the first device and get the core interface.
    00327     UniqueObj<CaptureSession> captureSession(
    00328             iCameraProvider->createCaptureSession(cameraDevices[0]));
    00329     ICaptureSession *iCaptureSession = interface_cast<ICaptureSession>(captureSession);
    00330     if (!iCaptureSession)
    00331         ORIGINATE_ERROR("Failed to get ICaptureSession interface");
    00332
    00333     // Create the OutputStream.
    00334     PRODUCER_PRINT("Creating output stream\n");
    00335     UniqueObj<OutputStreamSettings> streamSettings(iCaptureSession->createOutputStreamSettings());
    00336     IOutputStreamSettings *iStreamSettings = interface_cast<IOutputStreamSettings>(streamSettings);
    00337     if (!iStreamSettings)
    00338         ORIGINATE_ERROR("Failed to get IOutputStreamSettings interface");
    00339
    00340     iStreamSettings->setPixelFormat(PIXEL_FMT_YCbCr_420_888);
    00341     iStreamSettings->setEGLDisplay(renderer->getEGLDisplay());
    00342     iStreamSettings->setResolution(PREVIEW_SIZE);
    00343     UniqueObj<OutputStream> previewStream(iCaptureSession->createOutputStream(streamSettings.get()));
    00344     iStreamSettings->setResolution(CAPTURE_SIZE);
    00345     UniqueObj<OutputStream> captureStream(iCaptureSession->createOutputStream(streamSettings.get()));
    00346
    00347     // Launch the FrameConsumer thread to consume frames from the OutputStream.
    00348     PRODUCER_PRINT("Launching consumer thread\n");
    00349     PreviewConsumerThread previewConsumerThread(previewStream.get(), renderer);
    00350     PROPAGATE_ERROR(previewConsumerThread.initialize());
    00351     CaptureConsumerThread captureConsumerThread(captureStream.get());
    00352     PROPAGATE_ERROR(captureConsumerThread.initialize());
    00353
    00354
    00355     // Wait until the consumer is connected to the stream.
    00356     PROPAGATE_ERROR(previewConsumerThread.waitRunning());
    00357     PROPAGATE_ERROR(captureConsumerThread.waitRunning());
    00358
    00359     // Create capture request and enable output stream.
    00360     UniqueObj<Request> request(iCaptureSession->createRequest());
    00361     IRequest *iRequest = interface_cast<IRequest>(request);
    00362     if (!iRequest)
    00363         ORIGINATE_ERROR("Failed to create Request");
    00364     iRequest->enableOutputStream(previewStream.get());
    00365     iRequest->enableOutputStream(captureStream.get());
    00366
    00367     ISourceSettings *iSourceSettings = interface_cast<ISourceSettings>(iRequest->getSourceSettings());
    00368     if (!iSourceSettings)
    00369         ORIGINATE_ERROR("Failed to get ISourceSettings interface");
    00370     iSourceSettings->setFrameDurationRange(Range<uint64_t>(1e9/DEFAULT_FPS));
    00371
    00372     // Submit capture requests.
    00373     PRODUCER_PRINT("Starting repeat capture requests.\n");
    00374     if (iCaptureSession->repeat(request.get()) != STATUS_OK)
    00375         ORIGINATE_ERROR("Failed to start repeat capture request");
    00376
    00377     // Wait for CAPTURE_TIME seconds.
    00378     sleep(CAPTURE_TIME);
    00379
    00380     // Stop the repeating request and wait for idle.
    00381     iCaptureSession->stopRepeat();
    00382     iCaptureSession->waitForIdle();
    00383
    00384     // Destroy the output stream to end the consumer thread.
    00385     previewStream.reset();
    00386     captureStream.reset();
    00387
    00388     // Wait for the consumer thread to complete.
    00389     PROPAGATE_ERROR(previewConsumerThread.shutdown());
    00390     PROPAGATE_ERROR(captureConsumerThread.shutdown());
    00391
    00392     PRODUCER_PRINT("Done -- exiting.\n");
    00393
    00394     return true;
    00395 }
    00396
    00397 }; // namespace ArgusSamples
    00398
    00399 static void printHelp()
    00400 {
    00401     printf("Usage: camera_jpeg_capture [OPTIONS]\n"
    00402            "Options:\n"
    00403            "  --pre-res     Preview resolution WxH [Default 640x480]\n"
    00404            "  --img-res     Capture resolution WxH [Default 1920x1080]\n"
    00405            "  -s            Enable profiling\n"
    00406            "  -v            Enable verbose message\n"
    00407            "  -h            Print this help\n");
    00408 }
    00409
    00410 static bool parseCmdline(int argc, char * argv[])
    00411 {
    00412     enum
    00413     {
    00414         OPTION_PREVIEW_RESOLUTION = 0x100,
    00415         OPTION_CAPTURE_RESOLUTION,
    00416     };
    00417
    00418     static struct option longOptions[] =
    00419     {
    00420         { "pre-res", 1, NULL, OPTION_PREVIEW_RESOLUTION },
    00421         { "img-res", 1, NULL, OPTION_CAPTURE_RESOLUTION },
    00422         { 0 },
    00423     };
    00424
    00425     int c, w, h;
    00426     while ((c = getopt_long(argc, argv, "s::v::h", longOptions, NULL)) != -1)
    00427     {
    00428         switch (c)
    00429         {
    00430             case OPTION_PREVIEW_RESOLUTION:
    00431                 if (sscanf(optarg, "%dx%d", &w, &h) != 2)
    00432                     return false;
    00433                 PREVIEW_SIZE.width = w;
    00434                 PREVIEW_SIZE.height = h;
    00435                 break;
    00436             case OPTION_CAPTURE_RESOLUTION:
    00437                 if (sscanf(optarg, "%dx%d", &w, &h) != 2)
    00438                     return false;
    00439                 CAPTURE_SIZE.width = w;
    00440                 CAPTURE_SIZE.height = h;
    00441                 break;
    00442             case 's':
    00443                 DO_STAT = true;
    00444                 break;
    00445             case 'v':
    00446                 VERBOSE_ENABLE = true;
    00447                 break;
    00448             default:
    00449                 return false;
    00450         }
    00451     }
    00452     return true;
    00453 }
    00454
    00455 int main(int argc, char * argv[])
    00456 {
    00457     if (!parseCmdline(argc, argv))
    00458     {
    00459         printHelp();
    00460         return EXIT_FAILURE;
    00461     }
    00462
    00463     NvEglRenderer *renderer = NvEglRenderer::createEglRenderer("renderer0", PREVIEW_SIZE.width,
    00464                                             PREVIEW_SIZE.height, 0, 0);
    00465     if (!renderer)
    00466         ORIGINATE_ERROR("Failed to create EGLRenderer.");
    00467
    00468     if (!ArgusSamples::execute(renderer))
    00469         return EXIT_FAILURE;
    00470
    00471     delete renderer;
    00472
    00473     return EXIT_SUCCESS;
    00474 }