00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
00049 static const int MAX_ENCODER_FRAMES = 5;
00050 static const int DEFAULT_FPS = 30;
00051
00052
00053 static int CAPTURE_TIME = 1;
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
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
00076
00077
00078
00079
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
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
00140 m_consumer = UniqueObj<FrameConsumer>(FrameConsumer::create(m_stream));
00141 if (!m_consumer)
00142 ORIGINATE_ERROR("Failed to create FrameConsumer");
00143
00144
00145 if (!createVideoEncoder())
00146 ORIGINATE_ERROR("Failed to create video m_VideoEncoderoder");
00147
00148
00149 m_outputFile = new std::ofstream(OUTPUT_FILENAME.c_str());
00150 if (!m_outputFile)
00151 ORIGINATE_ERROR("Failed to open output file.");
00152
00153
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
00162 m_VideoEncoder->capture_plane.setDQThreadCallback(encoderCapturePlaneDqCallback);
00163
00164
00165
00166
00167 m_VideoEncoder->capture_plane.startDQThread(this);
00168
00169
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
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
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
00217 if (bufferIndex < MAX_ENCODER_FRAMES &&
00218 m_VideoEncoder->output_plane.getNumQueuedBuffers() <
00219 m_VideoEncoder->output_plane.getNumBuffers())
00220 {
00221
00222
00223 v4l2_buf.index = bufferIndex++;
00224 }
00225 else
00226 {
00227
00228 CHECK_ERROR(m_VideoEncoder->output_plane.dqBuffer(v4l2_buf, &buffer,
00229 NULL, 10));
00230
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
00238 UniqueObj<Frame> frame(iFrameConsumer->acquireFrame());
00239 IFrame *iFrame = interface_cast<IFrame>(frame);
00240 if (!iFrame)
00241 {
00242
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
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
00261 v4l2_buf.m.planes[0].m.fd = fd;
00262 v4l2_buf.m.planes[0].bytesused = 1;
00263 CHECK_ERROR(m_VideoEncoder->output_plane.qBuffer(v4l2_buf, NULL));
00264 }
00265
00266
00267
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
00338
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
00344
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
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
00389
00390
00391
00392
00393 static bool execute()
00394 {
00395
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
00402 std::vector<CameraDevice*> cameraDevices;
00403 iCameraProvider->getCameraDevices(&cameraDevices);
00404 if (cameraDevices.size() == 0)
00405 ORIGINATE_ERROR("No cameras available");
00406
00407
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
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
00426 PRODUCER_PRINT("Launching consumer thread\n");
00427 ConsumerThread frameConsumerThread(outputStream.get());
00428 PROPAGATE_ERROR(frameConsumerThread.initialize());
00429
00430
00431 PROPAGATE_ERROR(frameConsumerThread.waitRunning());
00432
00433
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
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
00451 for (int i = 0; i < CAPTURE_TIME && !frameConsumerThread.isInError(); i++)
00452 sleep(1);
00453
00454
00455 iCaptureSession->stopRepeat();
00456 iCaptureSession->waitForIdle();
00457
00458
00459 outputStream.reset();
00460
00461
00462 PROPAGATE_ERROR(frameConsumerThread.shutdown());
00463
00464 PRODUCER_PRINT("Done -- exiting.\n");
00465
00466 return true;
00467 }
00468
00469 };
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 }