From 8ac1580bd0544fac25b63a814e6e74d6dd1dd38d Mon Sep 17 00:00:00 2001 From: Hiroshi Yamamoto Date: Mon, 6 Sep 2021 20:46:21 +0900 Subject: [PATCH] Support frame rate specification by float. If no matching value is found, select the closest value. --- camera/gstCamera.cpp | 46 +++++++++++++++++++++++++++++------------- camera/gstCamera.h | 2 +- video/videoOptions.cpp | 9 ++++++++- video/videoOptions.h | 2 ++ 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/camera/gstCamera.cpp b/camera/gstCamera.cpp index e63714ba..821e7c1f 100644 --- a/camera/gstCamera.cpp +++ b/camera/gstCamera.cpp @@ -141,7 +141,7 @@ bool gstCamera::buildLaunchStr() else if( mOptions.flipMethod == videoOptions::FLIP_ROTATE_180 ) mOptions.flipMethod = videoOptions::FLIP_NONE; - ss << "nvarguscamerasrc sensor-id=" << mOptions.resource.port << " ! video/x-raw(memory:NVMM), width=(int)" << GetWidth() << ", height=(int)" << GetHeight() << ", framerate=" << (int)mOptions.frameRate << "/1, format=(string)NV12 ! nvvidconv flip-method=" << mOptions.flipMethod << " ! "; + ss << "nvarguscamerasrc sensor-id=" << mOptions.resource.port << " ! video/x-raw(memory:NVMM), width=(int)" << GetWidth() << ", height=(int)" << GetHeight() << ", framerate=" << mOptions.frameRateNum << "/" << mOptions.frameRateDenom << ", format=(string)NV12 ! nvvidconv flip-method=" << mOptions.flipMethod << " ! "; #else // older JetPack versions use nvcamerasrc element instead of nvarguscamerasrc ss << "nvcamerasrc fpsRange=\"" << (int)mOptions.frameRate << " " << (int)mOptions.frameRate << "\" ! video/x-raw(memory:NVMM), width=(int)" << GetWidth() << ", height=(int)" << GetHeight() << ", format=(string)NV12 ! nvvidconv flip-method=" << mOptions.flipMethod << " ! "; //'video/x-raw(memory:NVMM), width=(int)1920, height=(int)1080, format=(string)I420, framerate=(fraction)30/1' ! "; @@ -160,7 +160,7 @@ bool gstCamera::buildLaunchStr() if( mOptions.codec == videoOptions::CODEC_RAW ) ss << "format=(string)" << gst_format_to_string(mFormatYUV) << ", "; - ss << "width=(int)" << GetWidth() << ", height=(int)" << GetHeight() << " ! "; + ss << "width=(int)" << GetWidth() << ", height=(int)" << GetHeight() << ", framerate=" << mOptions.frameRateNum << "/" << mOptions.frameRateDenom << " ! "; } //ss << "queue max-size-buffers=16 ! "; @@ -216,7 +216,7 @@ bool gstCamera::printCaps( GstCaps* device_caps ) // parseCaps -bool gstCamera::parseCaps( GstStructure* caps, videoOptions::Codec* _codec, imageFormat* _format, uint32_t* _width, uint32_t* _height, float* _frameRate ) +bool gstCamera::parseCaps( GstStructure* caps, videoOptions::Codec* _codec, imageFormat* _format, uint32_t* _width, uint32_t* _height, float* _frameRate, int* _frameRateNum, int* _frameRateDenom ) { // parse codec/format const videoOptions::Codec codec = gst_parse_codec(caps); @@ -242,7 +242,8 @@ bool gstCamera::parseCaps( GstStructure* caps, videoOptions::Codec* _codec, imag return false; } - // get highest framerate + // get nearest framerate + const float rate_target = mOptions.frameRate; float frameRate = 0; int frameRateNum = 0; int frameRateDenom = 0; @@ -264,15 +265,20 @@ bool gstCamera::parseCaps( GstStructure* caps, videoOptions::Codec* _codec, imag if( GST_VALUE_HOLDS_FRACTION(value) ) { - frameRateNum = gst_value_get_fraction_numerator(value); - frameRateDenom = gst_value_get_fraction_denominator(value); + auto rate_num = gst_value_get_fraction_numerator(value); + auto rate_denom = gst_value_get_fraction_denominator(value); - if( frameRateNum > 0 && frameRateDenom > 0 ) + if( rate_num > 0 && rate_denom > 0 ) { - const float rate = float(frameRateNum) / float(frameRateDenom); - - if( rate > frameRate ) + const float rate = float(rate_num) / float(rate_denom); + const float diff_now = abs(rate - rate_target); + const float diff_pre = abs(frameRate - rate_target); + + if (frameRate == 0.0f || diff_now < diff_pre) { frameRate = rate; + frameRateNum = rate_num; + frameRateDenom = rate_denom; + } } } } @@ -287,6 +293,8 @@ bool gstCamera::parseCaps( GstStructure* caps, videoOptions::Codec* _codec, imag *_width = width; *_height = height; *_frameRate = frameRate; + *_frameRateNum = frameRateNum; + *_frameRateDenom = frameRateDenom; return true; } @@ -300,6 +308,7 @@ bool gstCamera::matchCaps( GstCaps* device_caps ) int bestResolution = 1000000; float bestFrameRate = 0.0f; + float rate_target = mOptions.frameRate; videoOptions::Codec bestCodec = videoOptions::CODEC_UNKNOWN; for( uint32_t n=0; n < numCaps; n++ ) @@ -313,15 +322,21 @@ bool gstCamera::matchCaps( GstCaps* device_caps ) imageFormat format; uint32_t width, height; float frameRate; + int frameRateNum; + int frameRateDenom; - if( !parseCaps(caps, &codec, &format, &width, &height, &frameRate) ) + if( !parseCaps(caps, &codec, &format, &width, &height, &frameRate, &frameRateNum, &frameRateDenom) ) continue; const int resolutionDiff = abs(int(mOptions.width) - int(width)) + abs(int(mOptions.height) - int(height)); + const float rate_diff_now = abs(frameRate - rate_target); + const float rate_diff_pre = abs(bestFrameRate - rate_target); + const bool is_best_rate = (bestFrameRate == 0.0f || rate_diff_now < rate_diff_pre); // pick this one if the resolution is closer, or if the resolution is the same but the framerate is better // (or if the framerate is the same and previous codec was MJPEG, pick the new one because MJPEG isn't preferred) - if( resolutionDiff < bestResolution || (resolutionDiff == bestResolution && (frameRate > bestFrameRate || bestCodec == videoOptions::CODEC_MJPEG)) ) + // In the case of I420, if you get an error image, uncomment the Boolean expression of I420. + if( /* format != imageFormat::IMAGE_I420 && */ (resolutionDiff < bestResolution || (resolutionDiff == bestResolution && (is_best_rate || bestCodec == videoOptions::CODEC_MJPEG))) ) { bestResolution = resolutionDiff; bestFrameRate = frameRate; @@ -339,7 +354,7 @@ bool gstCamera::matchCaps( GstCaps* device_caps ) return false; } - if( !parseCaps(bestCaps, &mOptions.codec, &mFormatYUV, &mOptions.width, &mOptions.height, &mOptions.frameRate) ) + if( !parseCaps(bestCaps, &mOptions.codec, &mFormatYUV, &mOptions.width, &mOptions.height, &mOptions.frameRate, &mOptions.frameRateNum, &mOptions.frameRateDenom) ) return false; return true; @@ -356,8 +371,11 @@ bool gstCamera::discover() if( mOptions.height == 0 ) mOptions.height = DefaultHeight; - if( mOptions.frameRate <= 0 ) + if( mOptions.frameRate <= 0 ) { mOptions.frameRate = 30; + mOptions.frameRateDenom = 100; + mOptions.frameRateNum = int(mOptions.frameRate * mOptions.frameRateDenom); + } // MIPI CSI cameras aren't enumerated if( mOptions.resource.protocol != "v4l2" ) diff --git a/camera/gstCamera.h b/camera/gstCamera.h index fc37c711..eda98dff 100644 --- a/camera/gstCamera.h +++ b/camera/gstCamera.h @@ -216,7 +216,7 @@ class gstCamera : public videoSource bool matchCaps( GstCaps* caps ); bool printCaps( GstCaps* caps ); - bool parseCaps( GstStructure* caps, videoOptions::Codec* codec, imageFormat* format, uint32_t* width, uint32_t* height, float* frameRate ); + bool parseCaps( GstStructure* caps, videoOptions::Codec* codec, imageFormat* format, uint32_t* width, uint32_t* height, float* frameRate, int* frameRateNum, int* frameRateDenom ); _GstBus* mBus; _GstAppSink* mAppSink; diff --git a/video/videoOptions.cpp b/video/videoOptions.cpp index d3af0469..19e6a0e4 100644 --- a/video/videoOptions.cpp +++ b/video/videoOptions.cpp @@ -32,6 +32,8 @@ videoOptions::videoOptions() width = 0; height = 0; frameRate = 0; + frameRateNum = 0; + frameRateDenom = 0; bitRate = 0; numBuffers = 4; loop = 0; @@ -62,7 +64,7 @@ void videoOptions::Print( const char* prefix ) const LogInfo(" -- codec: %s\n", CodecToStr(codec)); LogInfo(" -- width: %u\n", width); LogInfo(" -- height: %u\n", height); - LogInfo(" -- frameRate: %f\n", frameRate); + LogInfo(" -- frameRate: %.2f (%d/%d)\n", frameRate, frameRateNum, frameRateDenom); LogInfo(" -- bitRate: %u\n", bitRate); LogInfo(" -- numBuffers: %u\n", numBuffers); LogInfo(" -- zeroCopy: %s\n", zeroCopy ? "true" : "false"); @@ -126,6 +128,11 @@ bool videoOptions::Parse( const char* URI, const commandLine& cmdLine, videoOpti if( frameRate == 0 ) frameRate = cmdLine.GetFloat("framerate"); + if (frameRate != 0) { + frameRateDenom = 100; + frameRateNum = int(frameRate * frameRateDenom); + } + // flip-method const char* flipStr = (type == INPUT) ? cmdLine.GetString("input-flip") : cmdLine.GetString("output-flip"); diff --git a/video/videoOptions.h b/video/videoOptions.h index 2e9934cc..22d482a0 100644 --- a/video/videoOptions.h +++ b/video/videoOptions.h @@ -68,6 +68,8 @@ struct videoOptions * for input and output streams, respectively. The `--framerate=N` option sets it for both. */ float frameRate; + int frameRateNum; + int frameRateDenom; /** * The encoding bitrate for compressed streams (only applies to video codecs like H264/H265).