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 "NvUtils.h"
00030 #include <fstream>
00031 #include <iostream>
00032 #include <linux/videodev2.h>
00033 #include <malloc.h>
00034 #include <sstream>
00035 #include <string.h>
00036
00037 #include "video_encode.h"
00038
00039 #define TEST_ERROR(cond, str, label) if(cond) { \
00040 cerr << str << endl; \
00041 error = 1; \
00042 goto label; }
00043
00044 #define TEST_PARSE_ERROR(cond, label) if(cond) { \
00045 cerr << "Error parsing runtime parameter changes string" << endl; \
00046 goto label; }
00047
00048 #define IS_DIGIT(c) (c >= '0' && c <= '9')
00049
00050 using namespace std;
00051
00052 static void
00053 abort(context_t *ctx)
00054 {
00055 ctx->got_error = true;
00056 ctx->enc->abort();
00057 }
00058
00059 static int
00060 write_encoder_output_frame(ofstream * stream, NvBuffer * buffer)
00061 {
00062 stream->write((char *) buffer->planes[0].data, buffer->planes[0].bytesused);
00063 return 0;
00064 }
00065
00066 static bool
00067 encoder_capture_plane_dq_callback(struct v4l2_buffer *v4l2_buf, NvBuffer * buffer,
00068 NvBuffer * shared_buffer, void *arg)
00069 {
00070 context_t *ctx = (context_t *) arg;
00071 NvVideoEncoder *enc = ctx->enc;
00072 uint32_t frame_num = ctx->enc->capture_plane.getTotalDequeuedBuffers() - 1;
00073
00074 if (v4l2_buf == NULL)
00075 {
00076 cout << "Error while dequeing buffer from output plane" << endl;
00077 abort(ctx);
00078 return false;
00079 }
00080
00081
00082 if (buffer->planes[0].bytesused == 0)
00083 {
00084 return false;
00085 }
00086
00087 write_encoder_output_frame(ctx->out_file, buffer);
00088
00089 if (ctx->report_metadata)
00090 {
00091 v4l2_ctrl_videoenc_outputbuf_metadata enc_metadata;
00092 if (ctx->enc->getMetadata(v4l2_buf->index, enc_metadata) == 0)
00093 {
00094 cout << "Frame " << frame_num <<
00095 ": isKeyFrame=" << (int) enc_metadata.KeyFrame <<
00096 " AvgQP=" << enc_metadata.AvgQP <<
00097 " MinQP=" << enc_metadata.FrameMinQP <<
00098 " MaxQP=" << enc_metadata.FrameMaxQP <<
00099 " EncodedBits=" << enc_metadata.EncodedFrameBits <<
00100 endl;
00101 }
00102 }
00103 if (ctx->dump_mv)
00104 {
00105 v4l2_ctrl_videoenc_outputbuf_metadata_MV enc_mv_metadata;
00106 if (ctx->enc->getMotionVectors(v4l2_buf->index, enc_mv_metadata) == 0)
00107 {
00108 uint32_t numMVs = enc_mv_metadata.bufSize / sizeof(MVInfo);
00109 MVInfo *pInfo = enc_mv_metadata.pMVInfo;
00110
00111 cout << "Frame " << frame_num << ": Num MVs=" << numMVs << endl;
00112
00113 for (uint32_t i = 0; i < numMVs; i++, pInfo++)
00114 {
00115 cout << i << ": mv_x=" << pInfo->mv_x <<
00116 " mv_y=" << pInfo->mv_y <<
00117 " weight=" << pInfo->weight <<
00118 endl;
00119 }
00120 }
00121 }
00122
00123 if (enc->capture_plane.qBuffer(*v4l2_buf, NULL) < 0)
00124 {
00125 cerr << "Error while Qing buffer at capture plane" << endl;
00126 abort(ctx);
00127 return false;
00128 }
00129
00130 return true;
00131 }
00132
00133 static int
00134 get_next_parsed_pair(context_t *ctx, char *id, uint32_t *value)
00135 {
00136 char charval;
00137
00138 *ctx->runtime_params_str >> *id;
00139 if (ctx->runtime_params_str->eof())
00140 {
00141 return -1;
00142 }
00143
00144 charval = ctx->runtime_params_str->peek();
00145 if (!IS_DIGIT(charval))
00146 {
00147 return -1;
00148 }
00149
00150 *ctx->runtime_params_str >> *value;
00151
00152 *ctx->runtime_params_str >> charval;
00153 if (ctx->runtime_params_str->eof())
00154 {
00155 return 0;
00156 }
00157
00158 return charval;
00159 }
00160
00161 static int
00162 set_runtime_params(context_t *ctx)
00163 {
00164 char charval;
00165 uint32_t intval;
00166 int ret;
00167
00168 cout << "Frame " << ctx->next_param_change_frame <<
00169 ": Changing parameters" << endl;
00170 while (!ctx->runtime_params_str->eof())
00171 {
00172 ret = get_next_parsed_pair(ctx, &charval, &intval);
00173 TEST_PARSE_ERROR(ret < 0, err);
00174 switch (charval)
00175 {
00176 case 'b':
00177 ctx->enc->setBitrate(intval);
00178 cout << "Bitrate = " << intval << endl;
00179 break;
00180 case 'r':
00181 {
00182 int fps_num = intval;
00183 TEST_PARSE_ERROR(ret != '/', err);
00184
00185 ctx->runtime_params_str->seekg(-1, ios::cur);
00186 ret = get_next_parsed_pair(ctx, &charval, &intval);
00187 TEST_PARSE_ERROR(ret < 0, err);
00188
00189 cout << "Framerate = " << fps_num << "/" << intval << endl;
00190
00191 ctx->enc->setFrameRate(fps_num, intval);
00192 break;
00193 }
00194 case 'i':
00195 if (intval > 0)
00196 {
00197 ctx->enc->forceIDR();
00198 cout << "Forcing IDR" << endl;
00199 }
00200 break;
00201 default:
00202 TEST_PARSE_ERROR(true, err);
00203 }
00204 switch (ret)
00205 {
00206 case 0:
00207 delete ctx->runtime_params_str;
00208 ctx->runtime_params_str = NULL;
00209 return 0;
00210 case ';':
00211 return 0;
00212 case ',':
00213 break;
00214 default:
00215 break;
00216 }
00217 }
00218 return 0;
00219 err:
00220 cerr << "Skipping further runtime parameter changes" <<endl;
00221 delete ctx->runtime_params_str;
00222 ctx->runtime_params_str = NULL;
00223 return -1;
00224 }
00225
00226 static int
00227 get_next_runtime_param_change_frame(context_t *ctx)
00228 {
00229 char charval;
00230 int ret;
00231
00232 ret = get_next_parsed_pair(ctx, &charval, &ctx->next_param_change_frame);
00233 if(ret == 0)
00234 {
00235 return 0;
00236 }
00237
00238 TEST_PARSE_ERROR((ret != ';' && ret != ',') || charval != 'f', err);
00239
00240 return 0;
00241
00242 err:
00243 cerr << "Skipping further runtime parameter changes" <<endl;
00244 delete ctx->runtime_params_str;
00245 ctx->runtime_params_str = NULL;
00246 return -1;
00247 }
00248
00249 static void
00250 set_defaults(context_t * ctx)
00251 {
00252 memset(ctx, 0, sizeof(context_t));
00253
00254 ctx->bitrate = 4 * 1024 * 1024;
00255 ctx->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
00256 ctx->ratecontrol = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
00257 ctx->iframe_interval = 30;
00258 ctx->idr_interval = 256;
00259 ctx->level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
00260 ctx->fps_n = 30;
00261 ctx->fps_d = 1;
00262 ctx->num_b_frames = (uint32_t) -1;
00263 ctx->nMinQpI = (uint32_t)QP_RETAIN_VAL;
00264 ctx->nMaxQpI = (uint32_t)QP_RETAIN_VAL;
00265 ctx->nMinQpP = (uint32_t)QP_RETAIN_VAL;
00266 ctx->nMaxQpP = (uint32_t)QP_RETAIN_VAL;
00267 ctx->nMinQpB = (uint32_t)QP_RETAIN_VAL;
00268 ctx->nMaxQpB = (uint32_t)QP_RETAIN_VAL;
00269 }
00270
00271 static void
00272 populate_roi_Param(std::ifstream * stream, v4l2_enc_frame_ROI_params *VEnc_ROI_params)
00273 {
00274 unsigned int ROIIndex = 0;
00275
00276 if (!stream->eof()) {
00277 *stream >> VEnc_ROI_params->num_ROI_regions;
00278 while (ROIIndex < VEnc_ROI_params->num_ROI_regions)
00279 {
00280 *stream >> VEnc_ROI_params->ROI_params[ROIIndex].QPdelta;
00281 *stream >> VEnc_ROI_params->ROI_params[ROIIndex].ROIRect.left;
00282 *stream >> VEnc_ROI_params->ROI_params[ROIIndex].ROIRect.top;
00283 *stream >> VEnc_ROI_params->ROI_params[ROIIndex].ROIRect.width;
00284 *stream >> VEnc_ROI_params->ROI_params[ROIIndex].ROIRect.height;
00285 ROIIndex++;
00286 }
00287 } else {
00288 cout << "EOF of ROI_param_file & rewind" << endl;
00289 stream->clear();
00290 stream->seekg(0);
00291 }
00292 }
00293
00294 int
00295 main(int argc, char *argv[])
00296 {
00297 context_t ctx;
00298 int ret = 0;
00299 int error = 0;
00300 bool eos = false;
00301
00302 set_defaults(&ctx);
00303
00304 ret = parse_csv_args(&ctx, argc, argv);
00305 TEST_ERROR(ret < 0, "Error parsing commandline arguments", cleanup);
00306
00307 if (ctx.runtime_params_str)
00308 {
00309 get_next_runtime_param_change_frame(&ctx);
00310 }
00311
00312 if (ctx.encoder_pixfmt == V4L2_PIX_FMT_H265)
00313 {
00314 TEST_ERROR(ctx.width < 144 || ctx.height < 144, "Height/Width should be"
00315 " > 144 for H.265", cleanup);
00316 }
00317
00318 ctx.in_file = new ifstream(ctx.in_file_path);
00319 TEST_ERROR(!ctx.in_file->is_open(), "Could not open input file", cleanup);
00320
00321 ctx.out_file = new ofstream(ctx.out_file_path);
00322 TEST_ERROR(!ctx.out_file->is_open(), "Could not open output file", cleanup);
00323
00324 if (ctx.ROI_Param_file_path) {
00325 ctx.roi_Param_file = new ifstream(ctx.ROI_Param_file_path);
00326 TEST_ERROR(!ctx.roi_Param_file->is_open(), "Could not open roi param file", cleanup);
00327 }
00328
00329 ctx.enc = NvVideoEncoder::createVideoEncoder("enc0");
00330 TEST_ERROR(!ctx.enc, "Could not create encoder", cleanup);
00331
00332
00333
00334
00335
00336 ret =
00337 ctx.enc->setCapturePlaneFormat(ctx.encoder_pixfmt, ctx.width,
00338 ctx.height, 2 * 1024 * 1024);
00339 TEST_ERROR(ret < 0, "Could not set output plane format", cleanup);
00340
00341
00342 ret =
00343 ctx.enc->setOutputPlaneFormat(V4L2_PIX_FMT_YUV420M, ctx.width,
00344 ctx.height);
00345 TEST_ERROR(ret < 0, "Could not set output plane format", cleanup);
00346
00347 ret = ctx.enc->setBitrate(ctx.bitrate);
00348 TEST_ERROR(ret < 0, "Could not set encoder bitrate", cleanup);
00349
00350 ret = ctx.enc->setProfile(ctx.profile);
00351 TEST_ERROR(ret < 0, "Could not set encoder profile", cleanup);
00352
00353 if (ctx.encoder_pixfmt == V4L2_PIX_FMT_H264)
00354 {
00355 ret = ctx.enc->setLevel(ctx.level);
00356 TEST_ERROR(ret < 0, "Could not set encoder level", cleanup);
00357 }
00358
00359 ret = ctx.enc->setRateControlMode(ctx.ratecontrol);
00360 TEST_ERROR(ret < 0, "Could not set encoder rate control mode", cleanup);
00361
00362 ret = ctx.enc->setIDRInterval(ctx.idr_interval);
00363 TEST_ERROR(ret < 0, "Could not set encoder IDR interval", cleanup);
00364
00365 ret = ctx.enc->setIFrameInterval(ctx.iframe_interval);
00366 TEST_ERROR(ret < 0, "Could not set encoder I-Frame interval", cleanup);
00367
00368 ret = ctx.enc->setFrameRate(ctx.fps_n, ctx.fps_d);
00369 TEST_ERROR(ret < 0, "Could not set framerate", cleanup);
00370
00371 if (ctx.temporal_tradeoff_level)
00372 {
00373 ret = ctx.enc->setTemporalTradeoff(ctx.temporal_tradeoff_level);
00374 TEST_ERROR(ret < 0, "Could not set temporal tradeoff level", cleanup);
00375 }
00376
00377 if (ctx.slice_length)
00378 {
00379 ret = ctx.enc->setSliceLength(ctx.slice_length_type,
00380 ctx.slice_length);
00381 TEST_ERROR(ret < 0, "Could not set slice length params", cleanup);
00382 }
00383
00384 if (ctx.virtual_buffer_size)
00385 {
00386 ret = ctx.enc->setVirtualBufferSize(ctx.virtual_buffer_size);
00387 TEST_ERROR(ret < 0, "Could not set virtual buffer size", cleanup);
00388 }
00389
00390 if (ctx.num_reference_frames)
00391 {
00392 ret = ctx.enc->setNumReferenceFrames(ctx.num_reference_frames);
00393 TEST_ERROR(ret < 0, "Could not set num reference frames", cleanup);
00394 }
00395
00396 if (ctx.slice_intrarefresh_interval)
00397 {
00398 ret = ctx.enc->setSliceIntrarefresh(ctx.slice_intrarefresh_interval);
00399 TEST_ERROR(ret < 0, "Could not set slice intrarefresh interval", cleanup);
00400 }
00401
00402 if (ctx.insert_sps_pps_at_idr)
00403 {
00404 ret = ctx.enc->setInsertSpsPpsAtIdrEnabled(true);
00405 TEST_ERROR(ret < 0, "Could not set insertSPSPPSAtIDR", cleanup);
00406 }
00407
00408 if (ctx.num_b_frames != (uint32_t) -1)
00409 {
00410 ret = ctx.enc->setNumBFrames(ctx.num_b_frames);
00411 TEST_ERROR(ret < 0, "Could not set number of B Frames", cleanup);
00412 }
00413
00414 if ((ctx.nMinQpI != (uint32_t)QP_RETAIN_VAL) ||
00415 (ctx.nMaxQpI != (uint32_t)QP_RETAIN_VAL) ||
00416 (ctx.nMinQpP != (uint32_t)QP_RETAIN_VAL) ||
00417 (ctx.nMaxQpP != (uint32_t)QP_RETAIN_VAL) ||
00418 (ctx.nMinQpB != (uint32_t)QP_RETAIN_VAL) ||
00419 (ctx.nMaxQpB != (uint32_t)QP_RETAIN_VAL))
00420 {
00421 ret = ctx.enc->setQpRange(ctx.nMinQpI, ctx.nMaxQpI, ctx.nMinQpP,
00422 ctx.nMaxQpP, ctx.nMinQpB, ctx.nMaxQpB);
00423 TEST_ERROR(ret < 0, "Could not set quantization parameters", cleanup);
00424 }
00425
00426 if (ctx.dump_mv)
00427 {
00428 ret = ctx.enc->enableMotionVectorReporting();
00429 TEST_ERROR(ret < 0, "Could not enable motion vector reporting", cleanup);
00430 }
00431
00432
00433
00434 ret = ctx.enc->output_plane.setupPlane(V4L2_MEMORY_MMAP, 10, true, false);
00435 TEST_ERROR(ret < 0, "Could not setup output plane", cleanup);
00436
00437
00438
00439 ret = ctx.enc->capture_plane.setupPlane(V4L2_MEMORY_MMAP, 10, true, false);
00440 TEST_ERROR(ret < 0, "Could not setup capture plane", cleanup);
00441
00442
00443 ret = ctx.enc->output_plane.setStreamStatus(true);
00444 TEST_ERROR(ret < 0, "Error in output plane streamon", cleanup);
00445
00446
00447 ret = ctx.enc->capture_plane.setStreamStatus(true);
00448 TEST_ERROR(ret < 0, "Error in capture plane streamon", cleanup);
00449
00450 ctx.enc->capture_plane.
00451 setDQThreadCallback(encoder_capture_plane_dq_callback);
00452
00453
00454
00455
00456 ctx.enc->capture_plane.startDQThread(&ctx);
00457
00458
00459 for (uint32_t i = 0; i < ctx.enc->capture_plane.getNumBuffers(); i++)
00460 {
00461 struct v4l2_buffer v4l2_buf;
00462 struct v4l2_plane planes[MAX_PLANES];
00463
00464 memset(&v4l2_buf, 0, sizeof(v4l2_buf));
00465 memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
00466
00467 v4l2_buf.index = i;
00468 v4l2_buf.m.planes = planes;
00469
00470 ret = ctx.enc->capture_plane.qBuffer(v4l2_buf, NULL);
00471 if (ret < 0)
00472 {
00473 cerr << "Error while queueing buffer at capture plane" << endl;
00474 abort(&ctx);
00475 goto cleanup;
00476 }
00477 }
00478
00479
00480 for (uint32_t i = 0; i < ctx.enc->output_plane.getNumBuffers(); i++)
00481 {
00482 struct v4l2_buffer v4l2_buf;
00483 struct v4l2_plane planes[MAX_PLANES];
00484 NvBuffer *buffer = ctx.enc->output_plane.getNthBuffer(i);
00485
00486 memset(&v4l2_buf, 0, sizeof(v4l2_buf));
00487 memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
00488
00489 v4l2_buf.index = i;
00490 v4l2_buf.m.planes = planes;
00491
00492 if (read_video_frame(ctx.in_file, *buffer) < 0)
00493 {
00494 cerr << "Could not read complete frame from input file" << endl;
00495 v4l2_buf.m.planes[0].bytesused = 0;
00496 }
00497
00498 if (ctx.runtime_params_str &&
00499 (ctx.enc->output_plane.getTotalQueuedBuffers() ==
00500 ctx.next_param_change_frame))
00501 {
00502 set_runtime_params(&ctx);
00503 if (ctx.runtime_params_str)
00504 get_next_runtime_param_change_frame(&ctx);
00505 }
00506
00507 if (ctx.ROI_Param_file_path)
00508 {
00509 v4l2_enc_frame_ROI_params VEnc_ROI_params;
00510
00511 populate_roi_Param(ctx.roi_Param_file, &VEnc_ROI_params);
00512
00513 ctx.enc->setROIParams(v4l2_buf.index, VEnc_ROI_params);
00514 v4l2_buf.reserved2 = v4l2_buf.index;
00515 }
00516
00517 ret = ctx.enc->output_plane.qBuffer(v4l2_buf, NULL);
00518 if (ret < 0)
00519 {
00520 cerr << "Error while queueing buffer at output plane" << endl;
00521 abort(&ctx);
00522 goto cleanup;
00523 }
00524
00525 if (v4l2_buf.m.planes[0].bytesused == 0)
00526 {
00527 cerr << "File read complete." << endl;
00528 eos = true;
00529 break;
00530 }
00531 }
00532
00533
00534 while (!ctx.got_error && !ctx.enc->isInError() && !eos)
00535 {
00536 struct v4l2_buffer v4l2_buf;
00537 struct v4l2_plane planes[MAX_PLANES];
00538 NvBuffer *buffer;
00539
00540 memset(&v4l2_buf, 0, sizeof(v4l2_buf));
00541 memset(planes, 0, sizeof(planes));
00542
00543 v4l2_buf.m.planes = planes;
00544
00545 if (ctx.enc->output_plane.dqBuffer(v4l2_buf, &buffer, NULL, 10) < 0)
00546 {
00547 cerr << "ERROR while DQing buffer at output plane" << endl;
00548 abort(&ctx);
00549 goto cleanup;
00550 }
00551
00552 if (ctx.runtime_params_str &&
00553 (ctx.enc->output_plane.getTotalQueuedBuffers() ==
00554 ctx.next_param_change_frame))
00555 {
00556 set_runtime_params(&ctx);
00557 if (ctx.runtime_params_str)
00558 get_next_runtime_param_change_frame(&ctx);
00559 }
00560 if (read_video_frame(ctx.in_file, *buffer) < 0)
00561 {
00562 cerr << "Could not read complete frame from input file" << endl;
00563 v4l2_buf.m.planes[0].bytesused = 0;
00564 }
00565
00566 if (ctx.ROI_Param_file_path)
00567 {
00568 v4l2_enc_frame_ROI_params VEnc_ROI_params;
00569
00570 populate_roi_Param(ctx.roi_Param_file, &VEnc_ROI_params);
00571
00572 ctx.enc->setROIParams(v4l2_buf.index, VEnc_ROI_params);
00573 v4l2_buf.reserved2 = v4l2_buf.index;
00574 }
00575
00576 ret = ctx.enc->output_plane.qBuffer(v4l2_buf, NULL);
00577 if (ret < 0)
00578 {
00579 cerr << "Error while queueing buffer at output plane" << endl;
00580 abort(&ctx);
00581 goto cleanup;
00582 }
00583
00584 if (v4l2_buf.m.planes[0].bytesused == 0)
00585 {
00586 cerr << "File read complete." << endl;
00587 eos = true;
00588 break;
00589 }
00590 }
00591
00592
00593
00594 ctx.enc->capture_plane.waitForDQThread(-1);
00595
00596 cleanup:
00597 if (ctx.enc && ctx.enc->isInError())
00598 {
00599 cerr << "Encoder is in error" << endl;
00600 error = 1;
00601 }
00602 if (ctx.got_error)
00603 {
00604 error = 1;
00605 }
00606
00607 delete ctx.enc;
00608 delete ctx.in_file;
00609 delete ctx.out_file;
00610 delete ctx.roi_Param_file;
00611
00612 free(ctx.in_file_path);
00613 free(ctx.out_file_path);
00614 free(ctx.ROI_Param_file_path);
00615 delete ctx.runtime_params_str;
00616
00617 if (error)
00618 {
00619 cout << "App run failed" << endl;
00620 }
00621 else
00622 {
00623 cout << "App run was successful" << endl;
00624 }
00625 return -error;
00626 }