diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index b910fe951a..7b8678df81 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -231,7 +231,7 @@ CV_EXPORTS_W Ptr createVideoWriter(const Ptr(VideoReaderProps::PROP_DECODED_FRAME_IDX)) const = 0; + + /** @brief Sets a property in the VideoReader. + + @param property Property identifier from cv::cudacodec::VideoReaderProps (eg. cv::cudacodec::PROP_DECODED_FRAME_IDX, cv::cudacodec::PROP_EXTRA_DATA_INDEX, ...) + @param propertyVal Value of the property. + @return `true` if the property has been set. + */ + CV_WRAP virtual bool set(const VideoReaderProps property, const double propertyVal) = 0; + + /** @brief Returns the specified VideoReader property + + @param property Property identifier from cv::cudacodec::VideoReaderProps (eg. cv::cudacodec::PROP_DECODED_FRAME_IDX, cv::cudacodec::PROP_EXTRA_DATA_INDEX, ...) + @param propertyVal Optional value for the property. + @return Value for the specified property. Value -1 is returned when querying a property that is not supported. + */ + CV_WRAP virtual int get(const VideoReaderProps property, const int propertyVal = -1) const = 0; }; /** @brief Interface for video demultiplexing. : @@ -328,26 +400,39 @@ class CV_EXPORTS_W RawVideoSource */ virtual bool getNextPacket(unsigned char** data, size_t* size) = 0; + /** @brief Returns true if the last packet contained a key frame. + */ + virtual bool lastPacketContainsKeyFrame() const { return false; } + /** @brief Returns information about video file format. */ virtual FormatInfo format() const = 0; /** @brief Updates the coded width and height inside format. */ - virtual void updateFormat(const int codedWidth, const int codedHeight) = 0; + virtual void updateFormat(const FormatInfo& videoFormat) = 0; + + /** @brief Returns any extra data associated with the video source. + + @param extraData 1D cv::Mat containing the extra data if it exists. + */ + virtual void getExtraData(cv::Mat& extraData) const = 0; }; /** @brief Creates video reader. @param filename Name of the input video file. +@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). FFMPEG is used to read videos. User can implement own demultiplexing with cudacodec::RawVideoSource */ -CV_EXPORTS_W Ptr createVideoReader(const String& filename); +CV_EXPORTS_W Ptr createVideoReader(const String& filename, const bool rawMode = false); + /** @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). */ -CV_EXPORTS_W Ptr createVideoReader(const Ptr& source); +CV_EXPORTS_W Ptr createVideoReader(const Ptr& source, const bool rawMode = false); //! @} diff --git a/modules/cudacodec/src/cuvid_video_source.cpp b/modules/cudacodec/src/cuvid_video_source.cpp index aab7889352..3d7bbbb8c0 100644 --- a/modules/cudacodec/src/cuvid_video_source.cpp +++ b/modules/cudacodec/src/cuvid_video_source.cpp @@ -76,6 +76,8 @@ cv::cudacodec::detail::CuvidVideoSource::CuvidVideoSource(const String& fname) format_.height = vidfmt.coded_height; format_.displayArea = Rect(Point(vidfmt.display_area.left, vidfmt.display_area.top), Point(vidfmt.display_area.right, vidfmt.display_area.bottom)); format_.valid = true; + if (vidfmt.frame_rate.numerator != 0 && vidfmt.frame_rate.denominator != 0) + format_.fps = vidfmt.frame_rate.numerator / (double)vidfmt.frame_rate.denominator; } cv::cudacodec::detail::CuvidVideoSource::~CuvidVideoSource() @@ -88,10 +90,9 @@ FormatInfo cv::cudacodec::detail::CuvidVideoSource::format() const return format_; } -void cv::cudacodec::detail::CuvidVideoSource::updateFormat(const int codedWidth, const int codedHeight) +void cv::cudacodec::detail::CuvidVideoSource::updateFormat(const FormatInfo& videoFormat) { - format_.width = codedWidth; - format_.height = codedHeight; + format_ = videoFormat; format_.valid = true; } @@ -119,7 +120,7 @@ int CUDAAPI cv::cudacodec::detail::CuvidVideoSource::HandleVideoData(void* userD { CuvidVideoSource* thiz = static_cast(userData); - return thiz->parseVideoData(packet->payload, packet->payload_size, (packet->flags & CUVID_PKT_ENDOFSTREAM) != 0); + return thiz->parseVideoData(packet->payload, packet->payload_size, thiz->RawModeEnabled(), false, (packet->flags & CUVID_PKT_ENDOFSTREAM) != 0); } #endif // HAVE_NVCUVID diff --git a/modules/cudacodec/src/cuvid_video_source.hpp b/modules/cudacodec/src/cuvid_video_source.hpp index 314f3104cc..21b28980a2 100644 --- a/modules/cudacodec/src/cuvid_video_source.hpp +++ b/modules/cudacodec/src/cuvid_video_source.hpp @@ -55,7 +55,7 @@ class CuvidVideoSource : public VideoSource ~CuvidVideoSource(); FormatInfo format() const CV_OVERRIDE; - void updateFormat(const int codedWidth, const int codedHeight); + void updateFormat(const FormatInfo& videoFormat) CV_OVERRIDE; void start() CV_OVERRIDE; void stop() CV_OVERRIDE; bool isStarted() const CV_OVERRIDE; diff --git a/modules/cudacodec/src/ffmpeg_video_source.cpp b/modules/cudacodec/src/ffmpeg_video_source.cpp index 019ac84b17..4efbe775a0 100644 --- a/modules/cudacodec/src/ffmpeg_video_source.cpp +++ b/modules/cudacodec/src/ffmpeg_video_source.cpp @@ -75,6 +75,7 @@ Codec FourccToCodec(int codec) case CV_FOURCC_MACRO('M', 'P', 'G', '1'): return MPEG1; case CV_FOURCC_MACRO('M', 'P', 'G', '2'): return MPEG2; case CV_FOURCC_MACRO('X', 'V', 'I', 'D'): // fallthru + case CV_FOURCC_MACRO('m', 'p', '4', 'v'): // fallthru case CV_FOURCC_MACRO('D', 'I', 'V', 'X'): return MPEG4; case CV_FOURCC_MACRO('W', 'V', 'C', '1'): return VC1; case CV_FOURCC_MACRO('H', '2', '6', '4'): // fallthru @@ -112,6 +113,22 @@ void FourccToChromaFormat(const int pixelFormat, ChromaFormat &chromaFormat, int } } +int StartCodeLen(unsigned char* data, const int sz) { + if (sz >= 3 && data[0] == 0 && data[1] == 0 && data[2] == 1) + return 3; + else if (sz >= 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) + return 4; + else + return 0; +} + +bool ParamSetsExist(unsigned char* parameterSets, const int szParameterSets, unsigned char* data, const int szData) { + const int paramSetStartCodeLen = StartCodeLen(parameterSets, szParameterSets); + const int packetStartCodeLen = StartCodeLen(data, szData); + // weak test to see if the parameter set has already been included in the RTP stream + return paramSetStartCodeLen != 0 && packetStartCodeLen != 0 && parameterSets[paramSetStartCodeLen] == data[packetStartCodeLen]; +} + cv::cudacodec::detail::FFmpegVideoSource::FFmpegVideoSource(const String& fname) { if (!videoio_registry::hasBackend(CAP_FFMPEG)) @@ -125,6 +142,11 @@ cv::cudacodec::detail::FFmpegVideoSource::FFmpegVideoSource(const String& fname) CV_Error(Error::StsUnsupportedFormat, "Fetching of RAW video streams is not supported"); CV_Assert(cap.get(CAP_PROP_FORMAT) == -1); + const int codecExtradataIndex = static_cast(cap.get(CAP_PROP_CODEC_EXTRADATA_INDEX)); + Mat tmpExtraData; + if (cap.retrieve(tmpExtraData, codecExtradataIndex) && tmpExtraData.total()) + extraData = tmpExtraData.clone(); + int codec = (int)cap.get(CAP_PROP_FOURCC); int pixelFormat = (int)cap.get(CAP_PROP_CODEC_PIXEL_FORMAT); @@ -133,6 +155,7 @@ cv::cudacodec::detail::FFmpegVideoSource::FFmpegVideoSource(const String& fname) format_.width = cap.get(CAP_PROP_FRAME_WIDTH); format_.displayArea = Rect(0, 0, format_.width, format_.height); format_.valid = false; + format_.fps = cap.get(CAP_PROP_FPS); FourccToChromaFormat(pixelFormat, format_.chromaFormat, format_.nBitDepthMinus8); } @@ -147,10 +170,9 @@ FormatInfo cv::cudacodec::detail::FFmpegVideoSource::format() const return format_; } -void cv::cudacodec::detail::FFmpegVideoSource::updateFormat(const int codedWidth, const int codedHeight) +void cv::cudacodec::detail::FFmpegVideoSource::updateFormat(const FormatInfo& videoFormat) { - format_.width = codedWidth; - format_.height = codedHeight; + format_ = videoFormat; format_.valid = true; } @@ -159,7 +181,26 @@ bool cv::cudacodec::detail::FFmpegVideoSource::getNextPacket(unsigned char** dat cap >> rawFrame; *data = rawFrame.data; *size = rawFrame.total(); + if (iFrame++ == 0 && extraData.total()) { + if (format_.codec == Codec::MPEG4 || + ((format_.codec == Codec::H264 || format_.codec == Codec::HEVC) && !ParamSetsExist(extraData.data, extraData.total(), *data, *size))) + { + const size_t nBytesToTrimFromData = format_.codec == Codec::MPEG4 ? 3 : 0; + const size_t newSz = extraData.total() + *size - nBytesToTrimFromData; + dataWithHeader = Mat(1, newSz, CV_8UC1); + memcpy(dataWithHeader.data, extraData.data, extraData.total()); + memcpy(dataWithHeader.data + extraData.total(), (*data) + nBytesToTrimFromData, *size - nBytesToTrimFromData); + *data = dataWithHeader.data; + *size = newSz; + } + } + return *size != 0; } +bool cv::cudacodec::detail::FFmpegVideoSource::lastPacketContainsKeyFrame() const +{ + return cap.get(CAP_PROP_LRF_HAS_KEY_FRAME); +} + #endif // HAVE_CUDA diff --git a/modules/cudacodec/src/ffmpeg_video_source.hpp b/modules/cudacodec/src/ffmpeg_video_source.hpp index 0c802d80f0..e8346331e4 100644 --- a/modules/cudacodec/src/ffmpeg_video_source.hpp +++ b/modules/cudacodec/src/ffmpeg_video_source.hpp @@ -56,15 +56,19 @@ class FFmpegVideoSource : public RawVideoSource bool getNextPacket(unsigned char** data, size_t* size) CV_OVERRIDE; + bool lastPacketContainsKeyFrame() const; + FormatInfo format() const CV_OVERRIDE; - void updateFormat(const int codedWidth, const int codedHeight); + void updateFormat(const FormatInfo& videoFormat) CV_OVERRIDE; + void getExtraData(cv::Mat& _extraData) const CV_OVERRIDE { _extraData = extraData; } private: FormatInfo format_; VideoCapture cap; - Mat rawFrame; + Mat rawFrame, extraData, dataWithHeader; + int iFrame = 0; }; }}} diff --git a/modules/cudacodec/src/frame_queue.cpp b/modules/cudacodec/src/frame_queue.cpp index d3c42c902c..f5d8eb9191 100644 --- a/modules/cudacodec/src/frame_queue.cpp +++ b/modules/cudacodec/src/frame_queue.cpp @@ -45,13 +45,22 @@ #ifdef HAVE_NVCUVID -cv::cudacodec::detail::FrameQueue::FrameQueue() : - endOfDecode_(0), - framesInQueue_(0), - readPosition_(0) -{ - std::memset(displayQueue_, 0, sizeof(displayQueue_)); - std::memset((void*) isFrameInUse_, 0, sizeof(isFrameInUse_)); +RawPacket::RawPacket(const unsigned char* _data, const size_t _size, const bool _containsKeyFrame) : size(_size), containsKeyFrame(_containsKeyFrame) { + data = cv::makePtr(new unsigned char[size]); + memcpy(*data, _data, size); +}; + +cv::cudacodec::detail::FrameQueue::~FrameQueue() { + if (isFrameInUse_) + delete[] isFrameInUse_; +} + +void cv::cudacodec::detail::FrameQueue::init(const int _maxSz) { + AutoLock autoLock(mtx_); + maxSz = _maxSz; + displayQueue_ = std::vector(maxSz, CUVIDPARSERDISPINFO()); + isFrameInUse_ = new volatile int[maxSz]; + std::memset((void*)isFrameInUse_, 0, sizeof(*isFrameInUse_) * maxSz); } bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex) @@ -68,7 +77,7 @@ bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex return true; } -void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picParams) +void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector rawPackets) { // Mark the frame as 'in-use' so we don't re-use it for decoding until it is no longer needed // for display @@ -82,10 +91,12 @@ void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picPa { AutoLock autoLock(mtx_); - if (framesInQueue_ < MaximumSize) + if (framesInQueue_ < maxSz) { - int writePosition = (readPosition_ + framesInQueue_) % MaximumSize; - displayQueue_[writePosition] = *picParams; + const int writePosition = (readPosition_ + framesInQueue_) % maxSz; + displayQueue_.at(writePosition) = *picParams; + for (const auto& rawPacket : rawPackets) + rawPacketQueue.push(rawPacket); framesInQueue_++; isFramePlaced = true; } @@ -99,15 +110,19 @@ void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picPa } while (!isEndOfDecode()); } -bool cv::cudacodec::detail::FrameQueue::dequeue(CUVIDPARSERDISPINFO& displayInfo) +bool cv::cudacodec::detail::FrameQueue::dequeue(CUVIDPARSERDISPINFO& displayInfo, std::vector& rawPackets) { AutoLock autoLock(mtx_); if (framesInQueue_ > 0) { int entry = readPosition_; - displayInfo = displayQueue_[entry]; - readPosition_ = (entry + 1) % MaximumSize; + displayInfo = displayQueue_.at(entry); + while (!rawPacketQueue.empty()) { + rawPackets.push_back(rawPacketQueue.front()); + rawPacketQueue.pop(); + } + readPosition_ = (entry + 1) % maxSz; framesInQueue_--; return true; } diff --git a/modules/cudacodec/src/frame_queue.hpp b/modules/cudacodec/src/frame_queue.hpp index 3051a1e450..f5a9b34373 100644 --- a/modules/cudacodec/src/frame_queue.hpp +++ b/modules/cudacodec/src/frame_queue.hpp @@ -43,17 +43,27 @@ #ifndef __FRAME_QUEUE_HPP__ #define __FRAME_QUEUE_HPP__ +#include #include "opencv2/core/utility.hpp" +class RawPacket { +public: + RawPacket(const unsigned char* _data, const size_t _size = 0, const bool _containsKeyFrame = false); + unsigned char* Data() const { return *data; } + size_t size; + bool containsKeyFrame; +private: + cv::Ptr data = 0; +}; + namespace cv { namespace cudacodec { namespace detail { class FrameQueue { public: - static const int MaximumSize = 20; // MAX_FRM_CNT; - - FrameQueue(); + ~FrameQueue(); + void init(const int _maxSz); void endDecode() { endOfDecode_ = true; } bool isEndOfDecode() const { return endOfDecode_ != 0;} @@ -64,7 +74,7 @@ class FrameQueue // available, the method returns false. bool waitUntilFrameAvailable(int pictureIndex); - void enqueue(const CUVIDPARSERDISPINFO* picParams); + void enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector rawPackets); // Deque the next frame. // Parameters: @@ -72,7 +82,7 @@ class FrameQueue // Returns: // true, if a new frame was returned, // false, if the queue was empty and no new frame could be returned. - bool dequeue(CUVIDPARSERDISPINFO& displayInfo); + bool dequeue(CUVIDPARSERDISPINFO& displayInfo, std::vector& rawPackets); void releaseFrame(const CUVIDPARSERDISPINFO& picParams) { isFrameInUse_[picParams.picture_index] = false; } @@ -80,13 +90,13 @@ class FrameQueue bool isInUse(int pictureIndex) const { return isFrameInUse_[pictureIndex] != 0; } Mutex mtx_; - - volatile int isFrameInUse_[MaximumSize]; - volatile int endOfDecode_; - - int framesInQueue_; - int readPosition_; - CUVIDPARSERDISPINFO displayQueue_[MaximumSize]; + volatile int* isFrameInUse_ = 0; + volatile int endOfDecode_ = 0; + int framesInQueue_ = 0; + int readPosition_ = 0; + std::vector< CUVIDPARSERDISPINFO> displayQueue_; + int maxSz = 0; + std::queue rawPacketQueue; }; }}} diff --git a/modules/cudacodec/src/video_decoder.cpp b/modules/cudacodec/src/video_decoder.cpp index 1293055bff..e5e9353e4f 100644 --- a/modules/cudacodec/src/video_decoder.cpp +++ b/modules/cudacodec/src/video_decoder.cpp @@ -47,13 +47,13 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat) { - if (videoFormat.nBitDepthMinus8 > 0 || videoFormat.chromaFormat != YUV420) - CV_Error(Error::StsUnsupportedFormat, "NV12 output requires 8 bit YUV420"); + if (videoFormat.nBitDepthMinus8 > 0 || videoFormat.chromaFormat == YUV444) + CV_Error(Error::StsUnsupportedFormat, "NV12 output currently supported for 8 bit YUV420, YUV422 and Monochrome inputs."); - cudaVideoCodec _codec = static_cast(videoFormat.codec); - cudaVideoChromaFormat _chromaFormat = static_cast(videoFormat.chromaFormat); - - cudaVideoCreateFlags videoCreateFlags = (_codec == cudaVideoCodec_JPEG || _codec == cudaVideoCodec_MPEG2) ? + videoFormat_ = videoFormat; + const cudaVideoCodec _codec = static_cast(videoFormat.codec); + const cudaVideoChromaFormat _chromaFormat = static_cast(videoFormat.chromaFormat); + const cudaVideoCreateFlags videoCreateFlags = (_codec == cudaVideoCodec_JPEG || _codec == cudaVideoCodec_MPEG2) ? cudaVideoCreate_PreferCUDA : cudaVideoCreate_PreferCUVID; @@ -101,35 +101,29 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat) if (!decodeCaps.bIsSupported) CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder"); - CV_Assert(videoFormat.width >= decodeCaps.nMinWidth && - videoFormat.height >= decodeCaps.nMinHeight && - videoFormat.width <= decodeCaps.nMaxWidth && - videoFormat.height <= decodeCaps.nMaxHeight); + CV_Assert(videoFormat.ulWidth >= decodeCaps.nMinWidth && + videoFormat.ulHeight >= decodeCaps.nMinHeight && + videoFormat.ulWidth <= decodeCaps.nMaxWidth && + videoFormat.ulHeight <= decodeCaps.nMaxHeight); CV_Assert((videoFormat.width >> 4)* (videoFormat.height >> 4) <= decodeCaps.nMaxMBCount); #endif - - // Fill the decoder-create-info struct from the given video-format struct. - std::memset(&createInfo_, 0, sizeof(CUVIDDECODECREATEINFO)); - // Create video decoder + CUVIDDECODECREATEINFO createInfo_ = {}; createInfo_.CodecType = _codec; - createInfo_.ulWidth = videoFormat.width; - createInfo_.ulHeight = videoFormat.height; - createInfo_.ulNumDecodeSurfaces = FrameQueue::MaximumSize; + createInfo_.ulWidth = videoFormat.ulWidth; + createInfo_.ulHeight = videoFormat.ulHeight; + createInfo_.ulNumDecodeSurfaces = videoFormat.ulNumDecodeSurfaces; createInfo_.ChromaFormat = _chromaFormat; createInfo_.OutputFormat = cudaVideoSurfaceFormat_NV12; - createInfo_.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive; - - // No scaling - static const int MAX_FRAME_COUNT = 2; - - createInfo_.ulTargetWidth = createInfo_.ulWidth; - createInfo_.ulTargetHeight = createInfo_.ulHeight; - createInfo_.ulNumOutputSurfaces = MAX_FRAME_COUNT; // We won't simultaneously map more than 8 surfaces + createInfo_.DeinterlaceMode = static_cast(videoFormat.deinterlaceMode); + createInfo_.ulTargetWidth = videoFormat.width; + createInfo_.ulTargetHeight = videoFormat.height; + createInfo_.ulMaxWidth = videoFormat.ulMaxWidth; + createInfo_.ulMaxHeight = videoFormat.ulMaxHeight; + createInfo_.ulNumOutputSurfaces = 2; createInfo_.ulCreationFlags = videoCreateFlags; createInfo_.vidLock = lock_; - cuSafeCall(cuCtxPushCurrent(ctx_)); cuSafeCall(cuvidCreateDecoder(&decoder_, &createInfo_)); cuSafeCall(cuCtxPopCurrent(NULL)); diff --git a/modules/cudacodec/src/video_decoder.hpp b/modules/cudacodec/src/video_decoder.hpp index 4e1da2472d..3f59ed0b19 100644 --- a/modules/cudacodec/src/video_decoder.hpp +++ b/modules/cudacodec/src/video_decoder.hpp @@ -49,9 +49,9 @@ namespace cv { namespace cudacodec { namespace detail { class VideoDecoder { public: - VideoDecoder(const FormatInfo& videoFormat, CUcontext ctx, CUvideoctxlock lock) : ctx_(ctx), lock_(lock), decoder_(0) + VideoDecoder(const Codec& codec, CUcontext ctx, CUvideoctxlock lock) : ctx_(ctx), lock_(lock), decoder_(0) { - create(videoFormat); + videoFormat_.codec = codec; } ~VideoDecoder() @@ -63,17 +63,18 @@ class VideoDecoder void release(); // Get the code-type currently used. - cudaVideoCodec codec() const { return createInfo_.CodecType; } - unsigned long maxDecodeSurfaces() const { return createInfo_.ulNumDecodeSurfaces; } + cudaVideoCodec codec() const { return static_cast(videoFormat_.codec); } + unsigned long maxDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; } - unsigned long frameWidth() const { return createInfo_.ulWidth; } - unsigned long frameHeight() const { return createInfo_.ulHeight; } + unsigned long frameWidth() const { return videoFormat_.ulWidth; } + unsigned long frameHeight() const { return videoFormat_.ulHeight; } + FormatInfo format() { AutoLock autoLock(mtx_); return videoFormat_;} - unsigned long targetWidth() const { return createInfo_.ulTargetWidth; } - unsigned long targetHeight() const { return createInfo_.ulTargetHeight; } + unsigned long targetWidth() { return videoFormat_.width; } + unsigned long targetHeight() { return videoFormat_.height; } - cudaVideoChromaFormat chromaFormat() const { return createInfo_.ChromaFormat; } - int nBitDepthMinus8() const { return createInfo_.bitDepthMinus8; } + cudaVideoChromaFormat chromaFormat() const { return static_cast(videoFormat_.chromaFormat); } + int nBitDepthMinus8() const { return videoFormat_.nBitDepthMinus8; } bool decodePicture(CUVIDPICPARAMS* picParams) { @@ -87,8 +88,7 @@ class VideoDecoder cuSafeCall( cuvidMapVideoFrame(decoder_, picIdx, &ptr, &pitch, &videoProcParams) ); - - return cuda::GpuMat(targetHeight() * 3 / 2, targetWidth(), CV_8UC1, (void*) ptr, pitch); + return cuda::GpuMat(frameHeight() * 3 / 2, frameWidth(), CV_8UC1, (void*) ptr, pitch); } void unmapFrame(cuda::GpuMat& frame) @@ -98,10 +98,11 @@ class VideoDecoder } private: + CUcontext ctx_ = 0; CUvideoctxlock lock_; - CUcontext ctx_; - CUVIDDECODECREATEINFO createInfo_; - CUvideodecoder decoder_; + CUvideodecoder decoder_ = 0; + FormatInfo videoFormat_ = {}; + Mutex mtx_; }; }}} diff --git a/modules/cudacodec/src/video_parser.cpp b/modules/cudacodec/src/video_parser.cpp index 5327f22a93..7ed656625b 100644 --- a/modules/cudacodec/src/video_parser.cpp +++ b/modules/cudacodec/src/video_parser.cpp @@ -52,7 +52,7 @@ cv::cudacodec::detail::VideoParser::VideoParser(VideoDecoder* videoDecoder, Fram std::memset(¶ms, 0, sizeof(CUVIDPARSERPARAMS)); params.CodecType = videoDecoder->codec(); - params.ulMaxNumDecodeSurfaces = videoDecoder->maxDecodeSurfaces(); + params.ulMaxNumDecodeSurfaces = 1; params.ulMaxDisplayDelay = 1; // this flag is needed so the parser will push frames out to the decoder as quickly as it can params.pUserData = this; params.pfnSequenceCallback = HandleVideoSequence; // Called before decoding frames and/or whenever there is a format change @@ -62,7 +62,7 @@ cv::cudacodec::detail::VideoParser::VideoParser(VideoDecoder* videoDecoder, Fram cuSafeCall( cuvidCreateVideoParser(&parser_, ¶ms) ); } -bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* data, size_t size, bool endOfStream) +bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* data, size_t size, const bool rawMode, const bool containsKeyFrame, bool endOfStream) { CUVIDSOURCEDATAPACKET packet; std::memset(&packet, 0, sizeof(CUVIDSOURCEDATAPACKET)); @@ -73,6 +73,9 @@ bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* dat packet.payload_size = static_cast(size); packet.payload = data; + if (rawMode) + currentFramePackets.push_back(RawPacket(data, size, containsKeyFrame)); + if (cuvidParseVideoData(parser_, &packet) != CUDA_SUCCESS) { hasError_ = true; @@ -80,7 +83,7 @@ bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* dat return false; } - const int maxUnparsedPackets = 20; + constexpr int maxUnparsedPackets = 20; ++unparsedPackets_; if (unparsedPackets_ > maxUnparsedPackets) @@ -106,17 +109,39 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa format->coded_width != thiz->videoDecoder_->frameWidth() || format->coded_height != thiz->videoDecoder_->frameHeight() || format->chroma_format != thiz->videoDecoder_->chromaFormat()|| - format->bit_depth_luma_minus8 != thiz->videoDecoder_->nBitDepthMinus8()) + format->bit_depth_luma_minus8 != thiz->videoDecoder_->nBitDepthMinus8() || + format->min_num_decode_surfaces != thiz->videoDecoder_->maxDecodeSurfaces()) { FormatInfo newFormat; - newFormat.codec = static_cast(format->codec); newFormat.chromaFormat = static_cast(format->chroma_format); + newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8; + newFormat.ulWidth = format->coded_width; + newFormat.ulHeight = format->coded_height; newFormat.width = format->coded_width; 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.nBitDepthMinus8 = format->bit_depth_luma_minus8; - + newFormat.fps = format->frame_rate.numerator / static_cast(format->frame_rate.denominator); + newFormat.ulNumDecodeSurfaces = format->min_num_decode_surfaces; + if (format->progressive_sequence) + newFormat.deinterlaceMode = Weave; + else + newFormat.deinterlaceMode = Adaptive; + int maxW = 0, maxH = 0; + // AV1 has max width/height of sequence in sequence header + if (format->codec == cudaVideoCodec_AV1 && format->seqhdr_data_length > 0) + { + CUVIDEOFORMATEX* vidFormatEx = (CUVIDEOFORMATEX*)format; + maxW = vidFormatEx->av1.max_width; + maxH = vidFormatEx->av1.max_height; + } + if (maxW < (int)format->coded_width) + maxW = format->coded_width; + if (maxH < (int)format->coded_height) + maxH = format->coded_height; + newFormat.ulMaxWidth = maxW; + newFormat.ulMaxHeight = maxH; + thiz->frameQueue_->init(newFormat.ulNumDecodeSurfaces); try { thiz->videoDecoder_->release(); @@ -129,7 +154,7 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa } } - return true; + return thiz->videoDecoder_->maxDecodeSurfaces(); } int CUDAAPI cv::cudacodec::detail::VideoParser::HandlePictureDecode(void* userData, CUVIDPICPARAMS* picParams) @@ -158,8 +183,8 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandlePictureDisplay(void* userD thiz->unparsedPackets_ = 0; - thiz->frameQueue_->enqueue(picParams); - + thiz->frameQueue_->enqueue(picParams, thiz->currentFramePackets); + thiz->currentFramePackets.clear(); return true; } diff --git a/modules/cudacodec/src/video_parser.hpp b/modules/cudacodec/src/video_parser.hpp index 91e50b3e2b..870a2105a8 100644 --- a/modules/cudacodec/src/video_parser.hpp +++ b/modules/cudacodec/src/video_parser.hpp @@ -59,7 +59,7 @@ class VideoParser cuvidDestroyVideoParser(parser_); } - bool parseVideoData(const unsigned char* data, size_t size, bool endOfStream); + bool parseVideoData(const unsigned char* data, size_t size, const bool rawMode, const bool containsKeyFrame, bool endOfStream); bool hasError() const { return hasError_; } @@ -68,6 +68,7 @@ class VideoParser FrameQueue* frameQueue_; CUvideoparser parser_; int unparsedPackets_; + std::vector currentFramePackets; volatile bool hasError_; // Called when the decoder encounters a video format change (or initial sequence header) diff --git a/modules/cudacodec/src/video_reader.cpp b/modules/cudacodec/src/video_reader.cpp index 51f49225c2..49884f17b2 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&) { throw_no_cuda(); return Ptr(); } -Ptr cv::cudacodec::createVideoReader(const Ptr&) { throw_no_cuda(); return Ptr(); } +Ptr cv::cudacodec::createVideoReader(const String&, const bool) { throw_no_cuda(); return Ptr(); } +Ptr cv::cudacodec::createVideoReader(const Ptr&, const bool) { throw_no_cuda(); return Ptr(); } #else // HAVE_NVCUVID @@ -69,16 +69,31 @@ namespace FormatInfo format() const CV_OVERRIDE; + bool grab(Stream& stream) CV_OVERRIDE; + + bool retrieve(OutputArray frame, const size_t idx) const CV_OVERRIDE; + + bool set(const VideoReaderProps property, const double propertyVal) CV_OVERRIDE; + + int get(const VideoReaderProps property, const int propertyVal) const CV_OVERRIDE; + private: + bool internalGrab(GpuMat& frame, Stream& stream); + Ptr videoSource_; - Ptr frameQueue_; - Ptr videoDecoder_; - Ptr videoParser_; + Ptr frameQueue_ = 0; + Ptr videoDecoder_ = 0; + Ptr videoParser_ = 0; CUvideoctxlock lock_; std::deque< std::pair > frames_; + std::vector rawPackets; + GpuMat lastFrame; + static const int decodedFrameIdx = 0; + static const int extraDataIdx = 1; + static const int rawPacketsBaseIdx = 2; }; FormatInfo VideoReaderImpl::format() const @@ -97,11 +112,9 @@ namespace CUcontext ctx; cuSafeCall( cuCtxGetCurrent(&ctx) ); cuSafeCall( cuvidCtxLockCreate(&lock_, ctx) ); - - frameQueue_.reset(new FrameQueue); - videoDecoder_.reset(new VideoDecoder(videoSource_->format(), ctx, lock_)); + frameQueue_.reset(new FrameQueue()); + videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, ctx, lock_)); videoParser_.reset(new VideoParser(videoDecoder_, frameQueue_)); - videoSource_->setVideoParser(videoParser_); videoSource_->start(); } @@ -122,18 +135,17 @@ namespace CUvideoctxlock m_lock; }; - bool VideoReaderImpl::nextFrame(GpuMat& frame, Stream& stream) - { + bool VideoReaderImpl::internalGrab(GpuMat& frame, Stream& stream) { if (videoSource_->hasError() || videoParser_->hasError()) CV_Error(Error::StsUnsupportedFormat, "Unsupported video source"); if (frames_.empty()) { CUVIDPARSERDISPINFO displayInfo; - + rawPackets.clear(); for (;;) { - if (frameQueue_->dequeue(displayInfo)) + if (frameQueue_->dequeue(displayInfo, rawPackets)) break; if (videoSource_->hasError() || videoParser_->hasError()) @@ -148,7 +160,7 @@ namespace bool isProgressive = displayInfo.progressive_frame != 0; const int num_fields = isProgressive ? 1 : 2 + displayInfo.repeat_first_field; - videoSource_->updateFormat(videoDecoder_->targetWidth(), videoDecoder_->targetHeight()); + videoSource_->updateFormat(videoDecoder_->format()); for (int active_field = 0; active_field < num_fields; ++active_field) { @@ -192,11 +204,82 @@ namespace return true; } + + bool VideoReaderImpl::grab(Stream& stream) { + return internalGrab(lastFrame, stream); + } + + bool VideoReaderImpl::retrieve(OutputArray frame, const size_t idx) const { + if (idx == decodedFrameIdx) { + if (!frame.isGpuMat()) + CV_Error(Error::StsUnsupportedFormat, "Decoded frame is stored on the device and must be retrieved using a cv::cuda::GpuMat"); + frame.getGpuMatRef() = lastFrame; + } + else if (idx == extraDataIdx) { + if (!frame.isMat()) + CV_Error(Error::StsUnsupportedFormat, "Extra data is stored on the host and must be retrueved using a cv::Mat"); + videoSource_->getExtraData(frame.getMatRef()); + } + else{ + if (idx >= rawPacketsBaseIdx && idx < rawPacketsBaseIdx + rawPackets.size()) { + if (!frame.isMat()) + CV_Error(Error::StsUnsupportedFormat, "Raw data is stored on the host and must retrievd using a cv::Mat"); + Mat tmp(1, rawPackets.at(idx - rawPacketsBaseIdx).size, CV_8UC1, rawPackets.at(idx - rawPacketsBaseIdx).Data(), rawPackets.at(idx - rawPacketsBaseIdx).size); + frame.getMatRef() = tmp; + } + } + return !frame.empty(); + } + + bool VideoReaderImpl::set(const VideoReaderProps property, const double propertyVal) { + switch (property) { + case VideoReaderProps::PROP_RAW_MODE : + videoSource_->SetRawMode(static_cast(propertyVal)); + break; + } + return true; + } + + int VideoReaderImpl::get(const VideoReaderProps property, const int propertyVal) const { + switch (property) + { + case VideoReaderProps::PROP_DECODED_FRAME_IDX: + return decodedFrameIdx; + case VideoReaderProps::PROP_EXTRA_DATA_INDEX: + return extraDataIdx; + case VideoReaderProps::PROP_RAW_PACKAGES_BASE_INDEX: + if (videoSource_->RawModeEnabled()) + return rawPacketsBaseIdx; + else + break; + case VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB: + return rawPackets.size(); + case::VideoReaderProps::PROP_RAW_MODE: + return videoSource_->RawModeEnabled(); + case::VideoReaderProps::PROP_LRF_HAS_KEY_FRAME: { + const int iPacket = propertyVal - rawPacketsBaseIdx; + if (videoSource_->RawModeEnabled() && iPacket >= 0 && iPacket < rawPackets.size()) + return rawPackets.at(iPacket).containsKeyFrame; + else + break; + } + default: + break; + } + return -1; + } + + bool VideoReaderImpl::nextFrame(GpuMat& frame, Stream& stream) + { + if (!internalGrab(frame, stream)) + return false; + return true; + } } -Ptr cv::cudacodec::createVideoReader(const String& filename) +Ptr cv::cudacodec::createVideoReader(const String& filename, const bool rawMode) { - CV_Assert( !filename.empty() ); + CV_Assert(!filename.empty()); Ptr videoSource; @@ -204,7 +287,7 @@ Ptr cv::cudacodec::createVideoReader(const String& filename) { // prefer ffmpeg to cuvidGetSourceVideoFormat() which doesn't always return the corrct raw pixel format Ptr source(new FFmpegVideoSource(filename)); - videoSource.reset(new RawVideoSourceWrapper(source)); + videoSource.reset(new RawVideoSourceWrapper(source, rawMode)); } catch (...) { @@ -214,9 +297,9 @@ Ptr cv::cudacodec::createVideoReader(const String& filename) return makePtr(videoSource); } -Ptr cv::cudacodec::createVideoReader(const Ptr& source) +Ptr cv::cudacodec::createVideoReader(const Ptr& source, const bool rawMode) { - Ptr videoSource(new RawVideoSourceWrapper(source)); + Ptr videoSource(new RawVideoSourceWrapper(source, rawMode)); return makePtr(videoSource); } diff --git a/modules/cudacodec/src/video_source.cpp b/modules/cudacodec/src/video_source.cpp index 3a233b9629..ab549fbd8e 100644 --- a/modules/cudacodec/src/video_source.cpp +++ b/modules/cudacodec/src/video_source.cpp @@ -49,14 +49,15 @@ using namespace cv; using namespace cv::cudacodec; using namespace cv::cudacodec::detail; -bool cv::cudacodec::detail::VideoSource::parseVideoData(const unsigned char* data, size_t size, bool endOfStream) +bool cv::cudacodec::detail::VideoSource::parseVideoData(const unsigned char* data, size_t size, const bool rawMode, const bool containsKeyFrame, bool endOfStream) { - return videoParser_->parseVideoData(data, size, endOfStream); + return videoParser_->parseVideoData(data, size, rawMode, containsKeyFrame, endOfStream); } -cv::cudacodec::detail::RawVideoSourceWrapper::RawVideoSourceWrapper(const Ptr& source) : +cv::cudacodec::detail::RawVideoSourceWrapper::RawVideoSourceWrapper(const Ptr& source, const bool rawMode) : source_(source) { + SetRawMode(rawMode); CV_Assert( !source_.empty() ); } @@ -65,9 +66,9 @@ cv::cudacodec::FormatInfo cv::cudacodec::detail::RawVideoSourceWrapper::format() return source_->format(); } -void cv::cudacodec::detail::RawVideoSourceWrapper::updateFormat(const int codedWidth, const int codedHeight) +void cv::cudacodec::detail::RawVideoSourceWrapper::updateFormat(const FormatInfo& videoFormat) { - source_->updateFormat(codedWidth,codedHeight); + source_->updateFormat(videoFormat); } void cv::cudacodec::detail::RawVideoSourceWrapper::start() @@ -109,7 +110,19 @@ void cv::cudacodec::detail::RawVideoSourceWrapper::readLoop(void* userData) break; } - if (!thiz->parseVideoData(data, size)) + bool containsKeyFrame = false; + if (thiz->RawModeEnabled()) { + containsKeyFrame = thiz->source_->lastPacketContainsKeyFrame(); + if (!thiz->extraDataQueried) { + thiz->extraDataQueried = true; + Mat extraData; + thiz->source_->getExtraData(extraData); + if(!extraData.empty()) + thiz->setExtraData(extraData); + } + } + + if (!thiz->parseVideoData(data, size, thiz->RawModeEnabled(), containsKeyFrame)) { thiz->hasError_ = true; break; @@ -119,7 +132,7 @@ void cv::cudacodec::detail::RawVideoSourceWrapper::readLoop(void* userData) break; } - thiz->parseVideoData(0, 0, true); + thiz->parseVideoData(0, 0, false, false, true); } #endif // HAVE_NVCUVID diff --git a/modules/cudacodec/src/video_source.hpp b/modules/cudacodec/src/video_source.hpp index f17d4ab080..ebaa3ba301 100644 --- a/modules/cudacodec/src/video_source.hpp +++ b/modules/cudacodec/src/video_source.hpp @@ -56,41 +56,49 @@ class VideoSource virtual ~VideoSource() {} virtual FormatInfo format() const = 0; - virtual void updateFormat(const int codedWidth, const int codedHeight) = 0; + virtual void updateFormat(const FormatInfo& videoFormat) = 0; virtual void start() = 0; virtual void stop() = 0; virtual bool isStarted() const = 0; virtual bool hasError() const = 0; - void setVideoParser(detail::VideoParser* videoParser) { videoParser_ = videoParser; } - + void setExtraData(const cv::Mat _extraData) { + AutoLock autoLock(mtx_); + extraData = _extraData.clone(); + } + void getExtraData(cv::Mat& _extraData) { + AutoLock autoLock(mtx_); + _extraData = extraData.clone(); + } + void SetRawMode(const bool enabled) { rawMode_ = enabled; } + bool RawModeEnabled() const { return rawMode_; } protected: - bool parseVideoData(const uchar* data, size_t size, bool endOfStream = false); - + bool parseVideoData(const uchar* data, size_t size, const bool rawMode, const bool containsKeyFrame, bool endOfStream = false); + bool extraDataQueried = false; private: - detail::VideoParser* videoParser_; + detail::VideoParser* videoParser_ = 0; + cv::Mat extraData; + bool rawMode_ = false; + Mutex mtx_; }; class RawVideoSourceWrapper : public VideoSource { public: - RawVideoSourceWrapper(const Ptr& source); + RawVideoSourceWrapper(const Ptr& source, const bool rawMode); FormatInfo format() const CV_OVERRIDE; - void updateFormat(const int codedWidth, const int codedHeight) CV_OVERRIDE; + void updateFormat(const FormatInfo& videoFormat) CV_OVERRIDE; void start() CV_OVERRIDE; void stop() CV_OVERRIDE; bool isStarted() const CV_OVERRIDE; bool hasError() const CV_OVERRIDE; - private: - Ptr source_; - - Ptr thread_; + static void readLoop(void* userData); + Ptr source_ = 0; + Ptr thread_ = 0; volatile bool stop_; volatile bool hasError_; - - static void readLoop(void* userData); }; }}} diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index 31a001f9d1..f23360dceb 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -45,14 +45,106 @@ namespace opencv_test { namespace { #if defined(HAVE_NVCUVID) || defined(HAVE_NVCUVENC) +PARAM_TEST_CASE(CheckSet, cv::cuda::DeviceInfo, std::string) +{ +}; + PARAM_TEST_CASE(Video, cv::cuda::DeviceInfo, std::string) { }; +PARAM_TEST_CASE(VideoReadRaw, cv::cuda::DeviceInfo, std::string) +{ +}; + +PARAM_TEST_CASE(CheckKeyFrame, cv::cuda::DeviceInfo, std::string) +{ +}; + #if defined(HAVE_NVCUVID) ////////////////////////////////////////////////////// // VideoReader +//========================================================================== + +CUDA_TEST_P(CheckSet, Reader) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend was not found"); + + std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + +"../" + GET_PARAM(1); + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); + ASSERT_FALSE(reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE)); + ASSERT_TRUE(reader->set(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE,true)); + ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE)); + bool rawPacketsAvailable = false; + while (reader->grab()) { + if (reader->get(cv::cudacodec::VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB) > 0) { + rawPacketsAvailable = true; + break; + } + } + ASSERT_TRUE(rawPacketsAvailable); +} + +typedef tuple check_extra_data_params_t; +typedef testing::TestWithParam< check_extra_data_params_t > CheckExtraData; + +CUDA_TEST_P(CheckExtraData, Reader) +{ + // RTSP streaming is only supported by the FFmpeg back end + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend not found"); + + const std::vector devices = cvtest::DeviceManager::instance().values(); + for (const auto& device : devices) { + cv::cuda::setDevice(device.deviceID()); + const string path = GET_PARAM(0); + const int sz = GET_PARAM(1); + std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + path; + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, true); + ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE)); + const int extraDataIdx = reader->get(cv::cudacodec::VideoReaderProps::PROP_EXTRA_DATA_INDEX); + ASSERT_EQ(extraDataIdx, 1 ); + ASSERT_TRUE(reader->grab()); + cv::Mat extraData; + const bool newData = reader->retrieve(extraData, extraDataIdx); + ASSERT_TRUE(newData && sz || !newData && !sz); + ASSERT_EQ(extraData.total(), sz); + } +} + +CUDA_TEST_P(CheckKeyFrame, Reader) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + + // RTSP streaming is only supported by the FFmpeg back end + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend not found"); + + const string path = GET_PARAM(1); + std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + path; + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, true); + ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE)); + const int rawIdxBase = reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_PACKAGES_BASE_INDEX); + ASSERT_EQ(rawIdxBase, 2); + constexpr int maxNPackagesToCheck = 2; + int nPackages = 0; + while (nPackages < maxNPackagesToCheck) { + ASSERT_TRUE(reader->grab()); + const int N = reader->get(cv::cudacodec::VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB); + for (int i = rawIdxBase; i < N + rawIdxBase; i++) { + nPackages++; + const bool containsKeyFrame = reader->get(cv::cudacodec::VideoReaderProps::PROP_LRF_HAS_KEY_FRAME, i); + ASSERT_TRUE(nPackages == 1 && containsKeyFrame || nPackages == 2 && !containsKeyFrame) << "nPackage: " << i; + if (nPackages >= maxNPackagesToCheck) + break; + } + } +} + CUDA_TEST_P(Video, Reader) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); @@ -74,6 +166,62 @@ CUDA_TEST_P(Video, Reader) ASSERT_FALSE(frame.empty()); } } + +CUDA_TEST_P(VideoReadRaw, Reader) +{ + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + + // RTSP streaming is only supported by the FFmpeg back end + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend not found"); + + std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../" + GET_PARAM(1); + const string fileNameOut = tempfile("test_container_stream"); + { + std::ofstream file(fileNameOut, std::ios::binary); + ASSERT_TRUE(file.is_open()); + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile,true); + ASSERT_TRUE(reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_MODE)); + const int rawIdxBase = reader->get(cv::cudacodec::VideoReaderProps::PROP_RAW_PACKAGES_BASE_INDEX); + ASSERT_EQ(rawIdxBase, 2); + cv::cuda::GpuMat frame; + for (int i = 0; i < 100; i++) + { + ASSERT_TRUE(reader->grab()); + ASSERT_TRUE(reader->retrieve(frame)); + ASSERT_FALSE(frame.empty()); + const int N = reader->get(cv::cudacodec::VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB); + ASSERT_TRUE(N >= 0) << N << " < 0"; + for (int i = rawIdxBase; i <= N + rawIdxBase; i++) { + Mat rawPackets; + reader->retrieve(rawPackets, i); + file.write((char*)rawPackets.data, rawPackets.total()); + } + } + } + + std::cout << "Checking written video stream: " << fileNameOut << std::endl; + + { + cv::Ptr readerReference = cv::cudacodec::createVideoReader(inputFile); + cv::Ptr readerActual = cv::cudacodec::createVideoReader(fileNameOut,true); + const int decodedFrameIdx = readerActual->get(cv::cudacodec::VideoReaderProps::PROP_DECODED_FRAME_IDX); + ASSERT_EQ(decodedFrameIdx, 0); + cv::cuda::GpuMat reference, actual; + cv::Mat referenceHost, actualHost; + for (int i = 0; i < 100; i++) + { + ASSERT_TRUE(readerReference->nextFrame(reference)); + ASSERT_TRUE(readerActual->grab()); + ASSERT_TRUE(readerActual->retrieve(actual, decodedFrameIdx)); + actual.download(actualHost); + reference.download(referenceHost); + ASSERT_TRUE(cvtest::norm(actualHost, referenceHost, NORM_INF) == 0); + } + } + + ASSERT_EQ(0, remove(fileNameOut.c_str())); +} #endif // HAVE_NVCUVID #if defined(_WIN32) && defined(HAVE_NVCUVENC) @@ -125,11 +273,40 @@ CUDA_TEST_P(Video, Writer) #endif // _WIN32, HAVE_NVCUVENC -#define VIDEO_SRC "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ +INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckSet, testing::Combine( + ALL_DEVICES, + testing::Values("highgui/video/big_buck_bunny.mp4"))); + +#define VIDEO_SRC_R "highgui/video/big_buck_bunny.mp4", "cv/video/768x576.avi", "cv/video/1920x1080.avi", "highgui/video/big_buck_bunny.avi", \ "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265", "highgui/video/big_buck_bunny.mpg" INSTANTIATE_TEST_CASE_P(CUDA_Codec, Video, testing::Combine( ALL_DEVICES, - testing::Values(VIDEO_SRC))); + testing::Values(VIDEO_SRC_R))); + +#define VIDEO_SRC_RW "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265" +INSTANTIATE_TEST_CASE_P(CUDA_Codec, VideoReadRaw, testing::Combine( + ALL_DEVICES, + testing::Values(VIDEO_SRC_RW))); + +const check_extra_data_params_t check_extra_data_params[] = +{ + check_extra_data_params_t("cv/video/768x576.avi", 44), + check_extra_data_params_t("cv/video/1920x1080.avi", 47), + check_extra_data_params_t("highgui/video/big_buck_bunny.h264", 38), + check_extra_data_params_t("highgui/video/big_buck_bunny.h265", 84), + check_extra_data_params_t("highgui/video/big_buck_bunny.mp4", 45), + check_extra_data_params_t("highgui/video/big_buck_bunny.avi", 58), + check_extra_data_params_t("highgui/video/big_buck_bunny.mpg", 12), + check_extra_data_params_t("highgui/video/big_buck_bunny.mov", 45), + check_extra_data_params_t("highgui/video/big_buck_bunny.mjpg.avi", 0) +}; + +INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckExtraData, testing::ValuesIn(check_extra_data_params)); + +INSTANTIATE_TEST_CASE_P(CUDA_Codec, CheckKeyFrame, testing::Combine( + ALL_DEVICES, + testing::Values(VIDEO_SRC_R))); + #endif // HAVE_NVCUVID || HAVE_NVCUVENC }} // namespace