diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 9a90b28ce7c..cf3502b2972 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -457,16 +457,24 @@ The `params` parameter allows to specify extra parameters encoded as pairs `(par See cv::VideoCaptureProperties e.g. when streaming from an RTSP source CAP_PROP_OPEN_TIMEOUT_MSEC may need to be set. @param rawMode Allow the raw encoded data which has been read up until the last call to grab() to be retrieved by calling retrieve(rawData,RAW_DATA_IDX). +@param minNumDecodeSurfaces Minimum number of internal decode surfaces used by the hardware decoder. NVDEC will automatically determine the minimum number of +surfaces it requires for correct functionality and optimal video memory usage but not necessarily for best performance, which depends on the design of the +overall application. The optimal number of decode surfaces (in terms of performance and memory utilization) should be decided by experimentation for each application, +but it cannot go below the number determined by NVDEC. FFMPEG is used to read videos. User can implement own demultiplexing with cudacodec::RawVideoSource */ -CV_EXPORTS_W Ptr createVideoReader(const String& filename, const std::vector& params = {}, const bool rawMode = false); +CV_EXPORTS_W Ptr createVideoReader(const String& filename, const std::vector& params = {}, const bool rawMode = false, const int minNumDecodeSurfaces = 0); /** @overload @param source RAW video source implemented by user. @param rawMode Allow the raw encoded data which has been read up until the last call to grab() to be retrieved by calling retrieve(rawData,RAW_DATA_IDX). +@param minNumDecodeSurfaces Minimum number of internal decode surfaces used by the hardware decoder. NVDEC will automatically determine the minimum number of +surfaces it requires for correct functionality and optimal video memory usage but not necessarily for best performance, which depends on the design of the +overall application. The optimal number of decode surfaces (in terms of performance and memory utilization) should be decided by experimentation for each application, +but it cannot go below the number determined by NVDEC. */ -CV_EXPORTS_W Ptr createVideoReader(const Ptr& source, const bool rawMode = false); +CV_EXPORTS_W Ptr createVideoReader(const Ptr& source, const bool rawMode = false, const int minNumDecodeSurfaces = 0); //! @} diff --git a/modules/cudacodec/src/video_decoder.hpp b/modules/cudacodec/src/video_decoder.hpp index 3f59ed0b19a..98d8e652542 100644 --- a/modules/cudacodec/src/video_decoder.hpp +++ b/modules/cudacodec/src/video_decoder.hpp @@ -49,9 +49,10 @@ namespace cv { namespace cudacodec { namespace detail { class VideoDecoder { public: - VideoDecoder(const Codec& codec, CUcontext ctx, CUvideoctxlock lock) : ctx_(ctx), lock_(lock), decoder_(0) + VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, CUcontext ctx, CUvideoctxlock lock) : ctx_(ctx), lock_(lock), decoder_(0) { videoFormat_.codec = codec; + videoFormat_.ulNumDecodeSurfaces = minNumDecodeSurfaces; } ~VideoDecoder() @@ -64,7 +65,7 @@ class VideoDecoder // Get the code-type currently used. cudaVideoCodec codec() const { return static_cast(videoFormat_.codec); } - unsigned long maxDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; } + int nDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; } unsigned long frameWidth() const { return videoFormat_.ulWidth; } unsigned long frameHeight() const { return videoFormat_.ulHeight; } diff --git a/modules/cudacodec/src/video_parser.cpp b/modules/cudacodec/src/video_parser.cpp index 7ed656625b2..d178ea3e791 100644 --- a/modules/cudacodec/src/video_parser.cpp +++ b/modules/cudacodec/src/video_parser.cpp @@ -110,7 +110,7 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa format->coded_height != thiz->videoDecoder_->frameHeight() || format->chroma_format != thiz->videoDecoder_->chromaFormat()|| format->bit_depth_luma_minus8 != thiz->videoDecoder_->nBitDepthMinus8() || - format->min_num_decode_surfaces != thiz->videoDecoder_->maxDecodeSurfaces()) + format->min_num_decode_surfaces != thiz->videoDecoder_->nDecodeSurfaces()) { FormatInfo newFormat; newFormat.codec = static_cast(format->codec); @@ -122,7 +122,7 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa newFormat.height = format->coded_height; newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom)); newFormat.fps = format->frame_rate.numerator / static_cast(format->frame_rate.denominator); - newFormat.ulNumDecodeSurfaces = format->min_num_decode_surfaces; + newFormat.ulNumDecodeSurfaces = max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast(format->min_num_decode_surfaces)); if (format->progressive_sequence) newFormat.deinterlaceMode = Weave; else @@ -154,7 +154,7 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa } } - return thiz->videoDecoder_->maxDecodeSurfaces(); + return thiz->videoDecoder_->nDecodeSurfaces(); } int CUDAAPI cv::cudacodec::detail::VideoParser::HandlePictureDecode(void* userData, CUVIDPICPARAMS* picParams) diff --git a/modules/cudacodec/src/video_reader.cpp b/modules/cudacodec/src/video_reader.cpp index b1cbe70f4ce..963bddf6c70 100644 --- a/modules/cudacodec/src/video_reader.cpp +++ b/modules/cudacodec/src/video_reader.cpp @@ -48,8 +48,8 @@ using namespace cv::cudacodec; #ifndef HAVE_NVCUVID -Ptr cv::cudacodec::createVideoReader(const String&, const std::vector&, const bool) { throw_no_cuda(); return Ptr(); } -Ptr cv::cudacodec::createVideoReader(const Ptr&, const bool) { throw_no_cuda(); return Ptr(); } +Ptr cv::cudacodec::createVideoReader(const String&, const std::vector&, const bool, const int) { throw_no_cuda(); return Ptr(); } +Ptr cv::cudacodec::createVideoReader(const Ptr&, const bool, const int) { throw_no_cuda(); return Ptr(); } #else // HAVE_NVCUVID @@ -62,7 +62,7 @@ namespace class VideoReaderImpl : public VideoReader { public: - explicit VideoReaderImpl(const Ptr& source); + explicit VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces); ~VideoReaderImpl(); bool nextFrame(GpuMat& frame, Stream& stream) CV_OVERRIDE; @@ -103,7 +103,7 @@ namespace return videoSource_->format(); } - VideoReaderImpl::VideoReaderImpl(const Ptr& source) : + VideoReaderImpl::VideoReaderImpl(const Ptr& source, const int minNumDecodeSurfaces) : videoSource_(source), lock_(0) { @@ -115,7 +115,7 @@ namespace cuSafeCall( cuCtxGetCurrent(&ctx) ); cuSafeCall( cuvidCtxLockCreate(&lock_, ctx) ); frameQueue_.reset(new FrameQueue()); - videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, ctx, lock_)); + videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, minNumDecodeSurfaces, ctx, lock_)); videoParser_.reset(new VideoParser(videoDecoder_, frameQueue_)); videoSource_->setVideoParser(videoParser_); videoSource_->start(); @@ -291,7 +291,7 @@ namespace } } -Ptr cv::cudacodec::createVideoReader(const String& filename, const std::vector& params, const bool rawMode) +Ptr cv::cudacodec::createVideoReader(const String& filename, const std::vector& params, const bool rawMode, const int minNumDecodeSurfaces) { CV_Assert(!filename.empty()); @@ -309,13 +309,13 @@ Ptr cv::cudacodec::createVideoReader(const String& filename, const videoSource.reset(new CuvidVideoSource(filename)); } - return makePtr(videoSource); + return makePtr(videoSource, minNumDecodeSurfaces); } -Ptr cv::cudacodec::createVideoReader(const Ptr& source, const bool rawMode) +Ptr cv::cudacodec::createVideoReader(const Ptr& source, const bool rawMode, const int minNumDecodeSurfaces) { Ptr videoSource(new RawVideoSourceWrapper(source, rawMode)); - return makePtr(videoSource); + return makePtr(videoSource, minNumDecodeSurfaces); } #endif // HAVE_NVCUVID diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 8dca0841255..6d8cf2170ed 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -66,6 +66,10 @@ PARAM_TEST_CASE(CheckKeyFrame, cv::cuda::DeviceInfo, std::string) { }; +PARAM_TEST_CASE(CheckDecodeSurfaces, cv::cuda::DeviceInfo, std::string) +{ +}; + struct CheckParams : testing::TestWithParam { cv::cuda::DeviceInfo devInfo; @@ -281,7 +285,7 @@ CUDA_TEST_P(CheckParams, Reader) cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, { cv::VideoCaptureProperties::CAP_PROP_FORMAT, capPropFormats.at(i) }); } - catch (cv::Exception ex) { + catch (cv::Exception &ex) { if (ex.code == Error::StsUnsupportedFormat) exceptionThrown = true; } @@ -289,6 +293,47 @@ CUDA_TEST_P(CheckParams, Reader) } } } + +CUDA_TEST_P(CheckDecodeSurfaces, Reader) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + GET_PARAM(1); + int ulNumDecodeSurfaces = 0; + { + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); + cv::cudacodec::FormatInfo fmt = reader->format(); + if (!fmt.valid) { + reader->grab(); + fmt = reader->format(); + ASSERT_TRUE(fmt.valid); + } + ulNumDecodeSurfaces = fmt.ulNumDecodeSurfaces; + } + + { + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, false, ulNumDecodeSurfaces - 1); + cv::cudacodec::FormatInfo fmt = reader->format(); + if (!fmt.valid) { + reader->grab(); + fmt = reader->format(); + ASSERT_TRUE(fmt.valid); + } + ASSERT_TRUE(fmt.ulNumDecodeSurfaces == ulNumDecodeSurfaces); + for (int i = 0; i < 100; i++) ASSERT_TRUE(reader->grab()); + } + + { + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, false, ulNumDecodeSurfaces + 1); + cv::cudacodec::FormatInfo fmt = reader->format(); + if (!fmt.valid) { + reader->grab(); + fmt = reader->format(); + ASSERT_TRUE(fmt.valid); + } + ASSERT_TRUE(fmt.ulNumDecodeSurfaces == ulNumDecodeSurfaces + 1); + for (int i = 0; i < 100; i++) ASSERT_TRUE(reader->grab()); + } +} #endif // HAVE_NVCUVID #if defined(_WIN32) && defined(HAVE_NVCUVENC) @@ -372,5 +417,9 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckKeyFrame, testing::Combine( INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckParams, ALL_DEVICES); +INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckDecodeSurfaces, testing::Combine( + ALL_DEVICES, + testing::Values("highgui/video/big_buck_bunny.mp4"))); + #endif // HAVE_NVCUVID || HAVE_NVCUVENC }} // namespace