- Main Page
- Modules
- Namespaces
- Classes
- Files

- Search for |

- File List
- File Members

# samples/10_camera_recording/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 <NvVideoEncoder.h>
    00037 #include <NvApplicationProfiler.h>
    00038
    00039 #include <unistd.h>
    00040 #include <stdio.h>
    00041 #include <stdlib.h>
    00042 #include <iostream>
    00043 #include <fstream>
    00044
    00045 using namespace Argus;
    00046 using namespace EGLStream;
    00047
    00048 // Constant configuration.
    00049 static const int    MAX_ENCODER_FRAMES = 5;
    00050 static const int    DEFAULT_FPS        = 30;
    00051
    00052 // Configurations which can be overrided by cmdline
    00053 static int          CAPTURE_TIME = 1; // In seconds.
    00054 static Size         STREAM_SIZE (640, 480);
    00055 static std::string  OUTPUT_FILENAME ("output.h264");
    00056 static uint32_t     ENCODER_PIXFMT = V4L2_PIX_FMT_H264;
    00057 static bool         DO_STAT = false;
    00058 static bool         VERBOSE_ENABLE = false;
    00059
    00060 // Debug print macros.
    00061 #define PRODUCER_PRINT(...) printf("PRODUCER: " __VA_ARGS__)
    00062 #define CONSUMER_PRINT(...) printf("CONSUMER: " __VA_ARGS__)
    00063 #define CHECK_ERROR(expr) \
    00064     do { \
    00065         if ((expr) < 0) { \
    00066             abort(); \
    00067             ORIGINATE_ERROR(#expr " failed"); \
    00068         } \
    00069     } while (0);
    00070
    00071 namespace ArgusSamples
    00072 {
    00073
    00074 /*******************************************************************************
    00075  * FrameConsumer thread:
    00076  *   Creates an EGLStream::FrameConsumer object to read frames from the stream
    00077  *   and create NvBuffers (dmabufs) from acquired frames before providing the
    00078  *   buffers to V4L2 for video encoding. The encoder will save the encoded
    00079  *   stream to disk.
    00080  ******************************************************************************/
    00081 class ConsumerThread : public Thread
    00082 {
    00083 public:
    00084     explicit ConsumerThread(OutputStream* stream);
    00085     ~ConsumerThread();
    00086
    00087     bool isInError()
    00088     {
    00089         return m_gotError;
    00090     }
    00091
    00092 private:
    00093     /** @name Thread methods */
    00094     /**@{*/
    00095     virtual bool threadInitialize();
    00096     virtual bool threadExecute();
    00097     virtual bool threadShutdown();
    00098     /**@}*/
    00099
    00100     bool createVideoEncoder();
    00101     void abort();
    00102
    00103     static bool encoderCapturePlaneDqCallback(
    00104             struct v4l2_buffer *v4l2_buf,
    00105             NvBuffer *buffer,
    00106             NvBuffer *shared_buffer,
    00107             void *arg);
    00108
    00109     OutputStream* m_stream;
    00110     UniqueObj<FrameConsumer> m_consumer;
    00111     NvVideoEncoder *m_VideoEncoder;
    00112     std::ofstream *m_outputFile;
    00113     bool m_gotError;
    00114 };
    00115
    00116 ConsumerThread::ConsumerThread(OutputStream* stream) :
    00117         m_stream(stream),
    00118         m_VideoEncoder(NULL),
    00119         m_outputFile(NULL),
    00120         m_gotError(false)
    00121 {
    00122 }
    00123
    00124 ConsumerThread::~ConsumerThread()
    00125 {
    00126     if (m_VideoEncoder)
    00127     {
    00128         if (DO_STAT)
    00129              m_VideoEncoder->printProfilingStats(std::cout);
    00130         delete m_VideoEncoder;
    00131     }
    00132
    00133     if (m_outputFile)
    00134         delete m_outputFile;
    00135 }
    00136
    00137 bool ConsumerThread::threadInitialize()
    00138 {
    00139     // Create the FrameConsumer.
    00140     m_consumer = UniqueObj<FrameConsumer>(FrameConsumer::create(m_stream));
    00141     if (!m_consumer)
    00142         ORIGINATE_ERROR("Failed to create FrameConsumer");
    00143
    00144     // Create Video Encoder
    00145     if (!createVideoEncoder())
    00146         ORIGINATE_ERROR("Failed to create video m_VideoEncoderoder");
    00147
    00148     // Create output file
    00149     m_outputFile = new std::ofstream(OUTPUT_FILENAME.c_str());
    00150     if (!m_outputFile)
    00151         ORIGINATE_ERROR("Failed to open output file.");
    00152
    00153     // Stream on
    00154     int e = m_VideoEncoder->output_plane.setStreamStatus(true);
    00155     if (e < 0)
    00156         ORIGINATE_ERROR("Failed to stream on output plane");
    00157     e = m_VideoEncoder->capture_plane.setStreamStatus(true);
    00158     if (e < 0)
    00159         ORIGINATE_ERROR("Failed to stream on capture plane");
    00160
    00161     // Set video encoder callback
    00162     m_VideoEncoder->capture_plane.setDQThreadCallback(encoderCapturePlaneDqCallback);
    00163
    00164     // startDQThread starts a thread internally which calls the
    00165     // encoderCapturePlaneDqCallback whenever a buffer is dequeued
    00166     // on the plane
    00167     m_VideoEncoder->capture_plane.startDQThread(this);
    00168
    00169     // Enqueue all the empty capture plane buffers
    00170     for (uint32_t i = 0; i < m_VideoEncoder->capture_plane.getNumBuffers(); i++)
    00171     {
    00172         struct v4l2_buffer v4l2_buf;
    00173         struct v4l2_plane planes[MAX_PLANES];
    00174
    00175         memset(&v4l2_buf, 0, sizeof(v4l2_buf));
    00176         memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
    00177
    00178         v4l2_buf.index = i;
    00179         v4l2_buf.m.planes = planes;
    00180
    00181         CHECK_ERROR(m_VideoEncoder->capture_plane.qBuffer(v4l2_buf, NULL));
    00182     }
    00183
    00184     return true;
    00185 }
    00186
    00187 bool ConsumerThread::threadExecute()
    00188 {
    00189     IStream *iStream = interface_cast<IStream>(m_stream);
    00190     IFrameConsumer *iFrameConsumer = interface_cast<IFrameConsumer>(m_consumer);
    00191
    00192     // Wait until the producer has connected to the stream.
    00193     CONSUMER_PRINT("Waiting until producer is connected...\n");
    00194     if (iStream->waitUntilConnected() != STATUS_OK)
    00195         ORIGINATE_ERROR("Stream failed to connect.");
    00196     CONSUMER_PRINT("Producer has connected; continuing.\n");
    00197
    00198     int bufferIndex;
    00199
    00200     bufferIndex = 0;
    00201
    00202     // Keep acquire frames and queue into encoder
    00203     while (!m_gotError)
    00204     {
    00205         NvBuffer *buffer;
    00206         int fd = -1;
    00207
    00208         struct v4l2_buffer v4l2_buf;
    00209         struct v4l2_plane planes[MAX_PLANES];
    00210
    00211         memset(&v4l2_buf, 0, sizeof(v4l2_buf));
    00212         memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
    00213
    00214         v4l2_buf.m.planes = planes;
    00215
    00216         // Check if we need dqBuffer first
    00217         if (bufferIndex < MAX_ENCODER_FRAMES &&
    00218             m_VideoEncoder->output_plane.getNumQueuedBuffers() <
    00219             m_VideoEncoder->output_plane.getNumBuffers())
    00220         {
    00221             // The queue is not full, no need to dqBuffer
    00222             // Prepare buffer index for the following qBuffer
    00223             v4l2_buf.index = bufferIndex++;
    00224         }
    00225         else
    00226         {
    00227             // Output plane full or max outstanding number reached
    00228             CHECK_ERROR(m_VideoEncoder->output_plane.dqBuffer(v4l2_buf, &buffer,
    00229                                                               NULL, 10));
    00230             // Release the frame.
    00231             fd = v4l2_buf.m.planes[0].m.fd;
    00232             NvBufferDestroy(fd);
    00233             if (VERBOSE_ENABLE)
    00234                 CONSUMER_PRINT("Released frame. %d\n", fd);
    00235         }
    00236
    00237         // Acquire a frame.
    00238         UniqueObj<Frame> frame(iFrameConsumer->acquireFrame());
    00239         IFrame *iFrame = interface_cast<IFrame>(frame);
    00240         if (!iFrame)
    00241         {
    00242             // Send EOS
    00243             v4l2_buf.m.planes[0].m.fd = fd;
    00244             v4l2_buf.m.planes[0].bytesused = 0;
    00245             CHECK_ERROR(m_VideoEncoder->output_plane.qBuffer(v4l2_buf, NULL));
    00246             break;
    00247         }
    00248
    00249         // Get the IImageNativeBuffer extension interface and create the fd.
    00250         NV::IImageNativeBuffer *iNativeBuffer =
    00251             interface_cast<NV::IImageNativeBuffer>(iFrame->getImage());
    00252         if (!iNativeBuffer)
    00253             ORIGINATE_ERROR("IImageNativeBuffer not supported by Image.");
    00254         fd = iNativeBuffer->createNvBuffer(STREAM_SIZE,
    00255                                            NvBufferColorFormat_YUV420,
    00256                                            NvBufferLayout_BlockLinear);
    00257         if (VERBOSE_ENABLE)
    00258             CONSUMER_PRINT("Acquired Frame. %d\n", fd);
    00259
    00260         // Push the frame into V4L2.
    00261         v4l2_buf.m.planes[0].m.fd = fd;
    00262         v4l2_buf.m.planes[0].bytesused = 1; // byteused must be non-zero
    00263         CHECK_ERROR(m_VideoEncoder->output_plane.qBuffer(v4l2_buf, NULL));
    00264     }
    00265
    00266     // Wait till capture plane DQ Thread finishes
    00267     // i.e. all the capture plane buffers are dequeued
    00268     m_VideoEncoder->capture_plane.waitForDQThread(2000);
    00269
    00270     CONSUMER_PRINT("Done.\n");
    00271
    00272     requestShutdown();
    00273
    00274     return true;
    00275 }
    00276
    00277 bool ConsumerThread::threadShutdown()
    00278 {
    00279     return true;
    00280 }
    00281
    00282 bool ConsumerThread::createVideoEncoder()
    00283 {
    00284     int ret = 0;
    00285
    00286     m_VideoEncoder = NvVideoEncoder::createVideoEncoder("enc0");
    00287     if (!m_VideoEncoder)
    00288         ORIGINATE_ERROR("Could not create m_VideoEncoderoder");
    00289
    00290     if (DO_STAT)
    00291         m_VideoEncoder->enableProfiling();
    00292
    00293     ret = m_VideoEncoder->setCapturePlaneFormat(ENCODER_PIXFMT, STREAM_SIZE.width,
    00294                                     STREAM_SIZE.height, 2 * 1024 * 1024);
    00295     if (ret < 0)
    00296         ORIGINATE_ERROR("Could not set capture plane format");
    00297
    00298     ret = m_VideoEncoder->setOutputPlaneFormat(V4L2_PIX_FMT_YUV420M, STREAM_SIZE.width,
    00299                                     STREAM_SIZE.height);
    00300     if (ret < 0)
    00301         ORIGINATE_ERROR("Could not set output plane format");
    00302
    00303     ret = m_VideoEncoder->setBitrate(4 * 1024 * 1024);
    00304     if (ret < 0)
    00305         ORIGINATE_ERROR("Could not set bitrate");
    00306
    00307     if (ENCODER_PIXFMT == V4L2_PIX_FMT_H264)
    00308     {
    00309         ret = m_VideoEncoder->setProfile(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);
    00310     }
    00311     else
    00312     {
    00313         ret = m_VideoEncoder->setProfile(V4L2_MPEG_VIDEO_H265_PROFILE_MAIN);
    00314     }
    00315     if (ret < 0)
    00316         ORIGINATE_ERROR("Could not set m_VideoEncoderoder profile");
    00317
    00318     if (ENCODER_PIXFMT == V4L2_PIX_FMT_H264)
    00319     {
    00320         ret = m_VideoEncoder->setLevel(V4L2_MPEG_VIDEO_H264_LEVEL_5_0);
    00321         if (ret < 0)
    00322             ORIGINATE_ERROR("Could not set m_VideoEncoderoder level");
    00323     }
    00324
    00325     ret = m_VideoEncoder->setRateControlMode(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
    00326     if (ret < 0)
    00327         ORIGINATE_ERROR("Could not set rate control mode");
    00328
    00329     ret = m_VideoEncoder->setIFrameInterval(30);
    00330     if (ret < 0)
    00331         ORIGINATE_ERROR("Could not set I-frame interval");
    00332
    00333     ret = m_VideoEncoder->setFrameRate(30, 1);
    00334     if (ret < 0)
    00335         ORIGINATE_ERROR("Could not set m_VideoEncoderoder framerate");
    00336
    00337     // Query, Export and Map the output plane buffers so that we can read
    00338     // raw data into the buffers
    00339     ret = m_VideoEncoder->output_plane.setupPlane(V4L2_MEMORY_DMABUF, 10, true, false);
    00340     if (ret < 0)
    00341         ORIGINATE_ERROR("Could not setup output plane");
    00342
    00343     // Query, Export and Map the output plane buffers so that we can write
    00344     // m_VideoEncoderoded data from the buffers
    00345     ret = m_VideoEncoder->capture_plane.setupPlane(V4L2_MEMORY_MMAP, 10, true, false);
    00346     if (ret < 0)
    00347         ORIGINATE_ERROR("Could not setup capture plane");
    00348
    00349     printf("create vidoe encoder return true\n");
    00350     return true;
    00351 }
    00352
    00353 void ConsumerThread::abort()
    00354 {
    00355     m_VideoEncoder->abort();
    00356     m_gotError = true;
    00357 }
    00358
    00359 bool ConsumerThread::encoderCapturePlaneDqCallback(struct v4l2_buffer *v4l2_buf,
    00360                                                    NvBuffer * buffer,
    00361                                                    NvBuffer * shared_buffer,
    00362                                                    void *arg)
    00363 {
    00364     ConsumerThread *thiz = (ConsumerThread*)arg;
    00365
    00366     if (!v4l2_buf)
    00367     {
    00368         thiz->abort();
    00369         ORIGINATE_ERROR("Failed to dequeue buffer from encoder capture plane");
    00370     }
    00371
    00372     thiz->m_outputFile->write((char *) buffer->planes[0].data,
    00373                               buffer->planes[0].bytesused);
    00374
    00375     thiz->m_VideoEncoder->capture_plane.qBuffer(*v4l2_buf, NULL);
    00376
    00377     // GOT EOS from m_VideoEncoderoder. Stop dqthread.
    00378     if (buffer->planes[0].bytesused == 0)
    00379     {
    00380         CONSUMER_PRINT("Got EOS, exiting...\n");
    00381         return false;
    00382     }
    00383
    00384     return true;
    00385 }
    00386
    00387 /*******************************************************************************
    00388  * Argus Producer thread:
    00389  *   Opens the Argus camera driver, creates an OutputStream to output to a
    00390  *   FrameConsumer, then performs repeating capture requests for CAPTURE_TIME
    00391  *   seconds before closing the producer and Argus driver.
    00392  ******************************************************************************/
    00393 static bool execute()
    00394 {
    00395     // Create the CameraProvider object and get the core interface.
    00396     UniqueObj<CameraProvider> cameraProvider = UniqueObj<CameraProvider>(CameraProvider::create());
    00397     ICameraProvider *iCameraProvider = interface_cast<ICameraProvider>(cameraProvider);
    00398     if (!iCameraProvider)
    00399         ORIGINATE_ERROR("Failed to create CameraProvider");
    00400
    00401     // Get the camera devices.
    00402     std::vector<CameraDevice*> cameraDevices;
    00403     iCameraProvider->getCameraDevices(&cameraDevices);
    00404     if (cameraDevices.size() == 0)
    00405         ORIGINATE_ERROR("No cameras available");
    00406
    00407     // Create the capture session using the first device and get the core interface.
    00408     UniqueObj<CaptureSession> captureSession(
    00409             iCameraProvider->createCaptureSession(cameraDevices[0]));
    00410     ICaptureSession *iCaptureSession = interface_cast<ICaptureSession>(captureSession);
    00411     if (!iCaptureSession)
    00412         ORIGINATE_ERROR("Failed to get ICaptureSession interface");
    00413
    00414     // Create the OutputStream.
    00415     PRODUCER_PRINT("Creating output stream\n");
    00416     UniqueObj<OutputStreamSettings> streamSettings(iCaptureSession->createOutputStreamSettings());
    00417     IOutputStreamSettings *iStreamSettings = interface_cast<IOutputStreamSettings>(streamSettings);
    00418     if (!iStreamSettings)
    00419         ORIGINATE_ERROR("Failed to get IOutputStreamSettings interface");
    00420
    00421     iStreamSettings->setPixelFormat(PIXEL_FMT_YCbCr_420_888);
    00422     iStreamSettings->setResolution(STREAM_SIZE);
    00423     UniqueObj<OutputStream> outputStream(iCaptureSession->createOutputStream(streamSettings.get()));
    00424
    00425     // Launch the FrameConsumer thread to consume frames from the OutputStream.
    00426     PRODUCER_PRINT("Launching consumer thread\n");
    00427     ConsumerThread frameConsumerThread(outputStream.get());
    00428     PROPAGATE_ERROR(frameConsumerThread.initialize());
    00429
    00430     // Wait until the consumer is connected to the stream.
    00431     PROPAGATE_ERROR(frameConsumerThread.waitRunning());
    00432
    00433     // Create capture request and enable output stream.
    00434     UniqueObj<Request> request(iCaptureSession->createRequest());
    00435     IRequest *iRequest = interface_cast<IRequest>(request);
    00436     if (!iRequest)
    00437         ORIGINATE_ERROR("Failed to create Request");
    00438     iRequest->enableOutputStream(outputStream.get());
    00439
    00440     ISourceSettings *iSourceSettings = interface_cast<ISourceSettings>(iRequest->getSourceSettings());
    00441     if (!iSourceSettings)
    00442         ORIGINATE_ERROR("Failed to get ISourceSettings interface");
    00443     iSourceSettings->setFrameDurationRange(Range<uint64_t>(1e9/DEFAULT_FPS));
    00444
    00445     // Submit capture requests.
    00446     PRODUCER_PRINT("Starting repeat capture requests.\n");
    00447     if (iCaptureSession->repeat(request.get()) != STATUS_OK)
    00448         ORIGINATE_ERROR("Failed to start repeat capture request");
    00449
    00450     // Wait for CAPTURE_TIME seconds.
    00451     for (int i = 0; i < CAPTURE_TIME && !frameConsumerThread.isInError(); i++)
    00452         sleep(1);
    00453
    00454     // Stop the repeating request and wait for idle.
    00455     iCaptureSession->stopRepeat();
    00456     iCaptureSession->waitForIdle();
    00457
    00458     // Destroy the output stream to end the consumer thread.
    00459     outputStream.reset();
    00460
    00461     // Wait for the consumer thread to complete.
    00462     PROPAGATE_ERROR(frameConsumerThread.shutdown());
    00463
    00464     PRODUCER_PRINT("Done -- exiting.\n");
    00465
    00466     return true;
    00467 }
    00468
    00469 }; // namespace ArgusSamples
    00470
    00471
    00472 static void printHelp()
    00473 {
    00474     printf("Usage: camera_recording [OPTIONS]\n"
    00475            "Options:\n"
    00476            "  -r        Set output resolution WxH [Default 640x480]\n"
    00477            "  -f        Set output filename [Default output.h264]\n"
    00478            "  -t        Set encoder type H264 or H265 [Default H264]\n"
    00479            "  -d        Set capture duration [Default 5 seconds]\n"
    00480            "  -s        Enable profiling\n"
    00481            "  -v        Enable verbose message\n"
    00482            "  -h        Print this help\n");
    00483 }
    00484
    00485 static bool parseCmdline(int argc, char **argv)
    00486 {
    00487     int c, w, h;
    00488     bool haveFilename = false;
    00489     while ((c = getopt(argc, argv, "r:f:t:d:s::v::h")) != -1)
    00490     {
    00491         switch (c)
    00492         {
    00493             case 'r':
    00494                 if (sscanf(optarg, "%dx%d", &w, &h) != 2)
    00495                     return false;
    00496                 STREAM_SIZE.width = w;
    00497                 STREAM_SIZE.height = h;
    00498                 break;
    00499             case 'f':
    00500                 OUTPUT_FILENAME = optarg;
    00501                 haveFilename = true;
    00502                 break;
    00503             case 't':
    00504                 if (strcmp(optarg, "H264") == 0)
    00505                     ENCODER_PIXFMT = V4L2_PIX_FMT_H264;
    00506                 else if (strcmp(optarg, "H265") == 0)
    00507                 {
    00508                     ENCODER_PIXFMT = V4L2_PIX_FMT_H265;
    00509                     if (!haveFilename)
    00510                         OUTPUT_FILENAME = "output.h265";
    00511                 }
    00512                 else
    00513                     return false;
    00514                 break;
    00515             case 'd':
    00516                 CAPTURE_TIME = atoi(optarg);
    00517                 break;
    00518             case 's':
    00519                 DO_STAT = true;
    00520                 break;
    00521             case 'v':
    00522                 VERBOSE_ENABLE = true;
    00523                 break;
    00524             default:
    00525                 return false;
    00526         }
    00527     }
    00528     return true;
    00529 }
    00530
    00531 int main(int argc, char *argv[])
    00532 {
    00533     if (!parseCmdline(argc, argv))
    00534     {
    00535         printHelp();
    00536         return EXIT_FAILURE;
    00537     }
    00538
    00539     NvApplicationProfiler &profiler = NvApplicationProfiler::getProfilerInstance();
    00540
    00541     if (!ArgusSamples::execute())
    00542         return EXIT_FAILURE;
    00543
    00544     profiler.stop();
    00545     profiler.printProfilerData(std::cout);
    00546
    00547     return EXIT_SUCCESS;
    00548 }