Skip to content

Commit 353e37e

Browse files
authored
Merge pull request #3453 from cudawarped:cudacodec_add_reconfigure_decoder
`cudacodec::VideoReader` add reconfigure decoder functionality
2 parents ea988b1 + 045a9b0 commit 353e37e

File tree

8 files changed

+269
-81
lines changed

8 files changed

+269
-81
lines changed

modules/cudacodec/include/opencv2/cudacodec.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,13 @@ enum DeinterlaceMode
309309
*/
310310
struct CV_EXPORTS_W_SIMPLE FormatInfo
311311
{
312-
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
312+
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), nBitDepthChromaMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
313313
fps(0), ulNumDecodeSurfaces(0), videoFullRangeFlag(false) {};
314314

315315
CV_PROP_RW Codec codec;
316316
CV_PROP_RW ChromaFormat chromaFormat;
317317
CV_PROP_RW int nBitDepthMinus8;
318+
CV_PROP_RW int nBitDepthChromaMinus8;
318319
CV_PROP_RW int ulWidth;//!< Coded sequence width in pixels.
319320
CV_PROP_RW int ulHeight;//!< Coded sequence height in pixels.
320321
CV_PROP_RW int width;//!< Width of the decoded frame returned by nextFrame(frame).

modules/cudacodec/src/frame_queue.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,29 @@ cv::cudacodec::detail::FrameQueue::~FrameQueue() {
5555

5656
void cv::cudacodec::detail::FrameQueue::init(const int _maxSz) {
5757
AutoLock autoLock(mtx_);
58-
if (isFrameInUse_)
59-
return;
58+
if (isFrameInUse_) return;
6059
maxSz = _maxSz;
6160
displayQueue_ = std::vector<CUVIDPARSERDISPINFO>(maxSz, CUVIDPARSERDISPINFO());
6261
isFrameInUse_ = new volatile int[maxSz];
6362
std::memset((void*)isFrameInUse_, 0, sizeof(*isFrameInUse_) * maxSz);
6463
}
6564

65+
void cv::cudacodec::detail::FrameQueue::resize(const int newSz) {
66+
if (newSz == maxSz) return;
67+
if (!isFrameInUse_) return init(newSz);
68+
AutoLock autoLock(mtx_);
69+
const int maxSzOld = maxSz; maxSz = newSz;
70+
const auto displayQueueOld = displayQueue_;
71+
displayQueue_ = std::vector<CUVIDPARSERDISPINFO>(maxSz, CUVIDPARSERDISPINFO());
72+
for (int i = readPosition_; i < readPosition_ + framesInQueue_; i++)
73+
displayQueue_.at(i % displayQueue_.size()) = displayQueueOld.at(i % displayQueueOld.size());
74+
const volatile int* const isFrameInUseOld = isFrameInUse_;
75+
isFrameInUse_ = new volatile int[maxSz];
76+
std::memset((void*)isFrameInUse_, 0, sizeof(*isFrameInUse_) * maxSz);
77+
std::memcpy((void*)isFrameInUse_, (void*)isFrameInUseOld, sizeof(*isFrameInUseOld) * min(maxSz,maxSzOld));
78+
delete[] isFrameInUseOld;
79+
}
80+
6681
bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex, const bool allowFrameDrop)
6782
{
6883
while (isInUse(pictureIndex))
@@ -79,6 +94,15 @@ bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex
7994
return true;
8095
}
8196

97+
bool cv::cudacodec::detail::FrameQueue::waitUntilEmpty() {
98+
while (framesInQueue_) {
99+
Thread::sleep(1);
100+
if (isEndOfDecode())
101+
return false;
102+
}
103+
return true;
104+
}
105+
82106
void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector<RawPacket> rawPackets)
83107
{
84108
// Mark the frame as 'in-use' so we don't re-use it for decoding until it is no longer needed

modules/cudacodec/src/frame_queue.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ class FrameQueue
6666
~FrameQueue();
6767
void init(const int _maxSz);
6868

69+
// Resize the current frame queue keeping any existing queued values - must only
70+
// be called in the same thread as enqueue.
71+
// Parameters:
72+
// newSz - new size of the frame queue.
73+
void resize(const int newSz);
74+
6975
void endDecode() { endOfDecode_ = true; }
7076
bool isEndOfDecode() const { return endOfDecode_ != 0;}
7177

@@ -77,6 +83,8 @@ class FrameQueue
7783
// to ensure a frame is available.
7884
bool waitUntilFrameAvailable(int pictureIndex, const bool allowFrameDrop = false);
7985

86+
bool waitUntilEmpty();
87+
8088
void enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector<RawPacket> rawPackets);
8189

8290
// Deque the next frame.
@@ -97,6 +105,7 @@ class FrameQueue
97105
bool dequeueUntil(const int pictureIndex);
98106

99107
void releaseFrame(const CUVIDPARSERDISPINFO& picParams) { isFrameInUse_[picParams.picture_index] = 0; }
108+
int getMaxSz() { return maxSz; }
100109
private:
101110
bool isInUse(int pictureIndex) const { return isFrameInUse_[pictureIndex] != 0; }
102111

modules/cudacodec/src/video_decoder.cpp

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
124124
cuSafeCall(cuvidGetDecoderCaps(&decodeCaps));
125125
cuSafeCall(cuCtxPopCurrent(NULL));
126126
if (!(decodeCaps.bIsSupported && (decodeCaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)))){
127-
CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder");
128127
CV_LOG_ERROR(NULL, "Video source is not supported by hardware video decoder.");
128+
CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder");
129129
}
130130
CV_Assert(videoFormat.ulWidth >= decodeCaps.nMinWidth &&
131131
videoFormat.ulHeight >= decodeCaps.nMinHeight &&
@@ -162,6 +162,61 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
162162
cuSafeCall(cuCtxPushCurrent(ctx_));
163163
cuSafeCall(cuvidCreateDecoder(&decoder_, &createInfo_));
164164
cuSafeCall(cuCtxPopCurrent(NULL));
165+
inited_ = true;
166+
}
167+
168+
int cv::cudacodec::detail::VideoDecoder::reconfigure(const FormatInfo& videoFormat) {
169+
if (videoFormat.nBitDepthMinus8 != videoFormat_.nBitDepthMinus8 || videoFormat.nBitDepthChromaMinus8 != videoFormat_.nBitDepthChromaMinus8) {
170+
CV_LOG_ERROR(NULL, "Reconfigure Not supported for bit depth change");
171+
CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported for bit depth change");
172+
}
173+
174+
if (videoFormat.chromaFormat != videoFormat_.chromaFormat) {
175+
CV_LOG_ERROR(NULL, "Reconfigure Not supported for chroma format change");
176+
CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported for chroma format change");
177+
}
178+
179+
const bool decodeResChange = !(videoFormat.ulWidth == videoFormat_.ulWidth && videoFormat.ulHeight == videoFormat_.ulHeight);
180+
181+
if ((videoFormat.ulWidth > videoFormat_.ulMaxWidth) || (videoFormat.ulHeight > videoFormat_.ulMaxHeight)) {
182+
// For VP9, let driver handle the change if new width/height > maxwidth/maxheight
183+
if (videoFormat.codec != Codec::VP9) {
184+
CV_LOG_ERROR(NULL, "Reconfigure Not supported when width/height > maxwidth/maxheight");
185+
CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported when width/height > maxwidth/maxheight");
186+
}
187+
}
188+
189+
if (!decodeResChange)
190+
return 1;
191+
192+
{
193+
AutoLock autoLock(mtx_);
194+
videoFormat_.ulNumDecodeSurfaces = videoFormat.ulNumDecodeSurfaces;
195+
videoFormat_.ulWidth = videoFormat.ulWidth;
196+
videoFormat_.ulHeight = videoFormat.ulHeight;
197+
videoFormat_.targetRoi = videoFormat.targetRoi;
198+
}
199+
200+
CUVIDRECONFIGUREDECODERINFO reconfigParams = { 0 };
201+
reconfigParams.ulWidth = videoFormat_.ulWidth;
202+
reconfigParams.ulHeight = videoFormat_.ulHeight;
203+
reconfigParams.display_area.left = videoFormat_.displayArea.x;
204+
reconfigParams.display_area.right = videoFormat_.displayArea.x + videoFormat_.displayArea.width;
205+
reconfigParams.display_area.top = videoFormat_.displayArea.y;
206+
reconfigParams.display_area.bottom = videoFormat_.displayArea.y + videoFormat_.displayArea.height;
207+
reconfigParams.ulTargetWidth = videoFormat_.width;
208+
reconfigParams.ulTargetHeight = videoFormat_.height;
209+
reconfigParams.target_rect.left = videoFormat_.targetRoi.x;
210+
reconfigParams.target_rect.right = videoFormat_.targetRoi.x + videoFormat_.targetRoi.width;
211+
reconfigParams.target_rect.top = videoFormat_.targetRoi.y;
212+
reconfigParams.target_rect.bottom = videoFormat_.targetRoi.y + videoFormat_.targetRoi.height;
213+
reconfigParams.ulNumDecodeSurfaces = videoFormat_.ulNumDecodeSurfaces;
214+
215+
cuSafeCall(cuCtxPushCurrent(ctx_));
216+
cuSafeCall(cuvidReconfigureDecoder(decoder_, &reconfigParams));
217+
cuSafeCall(cuCtxPopCurrent(NULL));
218+
CV_LOG_INFO(NULL, "Reconfiguring Decoder");
219+
return videoFormat_.ulNumDecodeSurfaces;
165220
}
166221

167222
void cv::cudacodec::detail::VideoDecoder::release()

modules/cudacodec/src/video_decoder.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ class VideoDecoder
6868
}
6969

7070
void create(const FormatInfo& videoFormat);
71+
int reconfigure(const FormatInfo& videoFormat);
7172
void release();
7273

73-
// Get the code-type currently used.
74+
// Get the codec-type currently used.
7475
cudaVideoCodec codec() const { return static_cast<cudaVideoCodec>(videoFormat_.codec); }
7576
int nDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; }
7677
cv::Size getTargetSz() const { return videoFormat_.targetSz; }
@@ -84,6 +85,8 @@ class VideoDecoder
8485
unsigned long targetWidth() { return videoFormat_.width; }
8586
unsigned long targetHeight() { return videoFormat_.height; }
8687

88+
bool inited() { return inited_; }
89+
8790
cudaVideoChromaFormat chromaFormat() const { return static_cast<cudaVideoChromaFormat>(videoFormat_.chromaFormat); }
8891
int nBitDepthMinus8() const { return videoFormat_.nBitDepthMinus8; }
8992

@@ -114,6 +117,7 @@ class VideoDecoder
114117
CUvideodecoder decoder_ = 0;
115118
FormatInfo videoFormat_ = {};
116119
Mutex mtx_;
120+
bool inited_ = false;
117121
};
118122

119123
}}}

modules/cudacodec/src/video_parser.cpp

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* dat
6868
CUVIDSOURCEDATAPACKET packet;
6969
std::memset(&packet, 0, sizeof(CUVIDSOURCEDATAPACKET));
7070

71+
packet.flags = CUVID_PKT_TIMESTAMP;
7172
if (endOfStream)
7273
packet.flags |= CUVID_PKT_ENDOFSTREAM;
7374

@@ -107,68 +108,69 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa
107108

108109
thiz->unparsedPackets_ = 0;
109110

110-
if (format->codec != thiz->videoDecoder_->codec() ||
111-
format->coded_width != thiz->videoDecoder_->frameWidth() ||
112-
format->coded_height != thiz->videoDecoder_->frameHeight() ||
113-
format->chroma_format != thiz->videoDecoder_->chromaFormat()||
114-
format->bit_depth_luma_minus8 != thiz->videoDecoder_->nBitDepthMinus8() ||
115-
format->min_num_decode_surfaces != thiz->videoDecoder_->nDecodeSurfaces())
111+
FormatInfo newFormat;
112+
newFormat.videoFullRangeFlag = format->video_signal_description.video_full_range_flag;
113+
newFormat.codec = static_cast<Codec>(format->codec);
114+
newFormat.chromaFormat = static_cast<ChromaFormat>(format->chroma_format);
115+
newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8;
116+
newFormat.nBitDepthChromaMinus8 = format->bit_depth_chroma_minus8;
117+
newFormat.ulWidth = format->coded_width;
118+
newFormat.ulHeight = format->coded_height;
119+
newFormat.fps = format->frame_rate.numerator / static_cast<float>(format->frame_rate.denominator);
120+
newFormat.targetSz = thiz->videoDecoder_->getTargetSz();
121+
newFormat.srcRoi = thiz->videoDecoder_->getSrcRoi();
122+
if (newFormat.srcRoi.empty()) {
123+
newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom));
124+
if (newFormat.targetSz.empty())
125+
newFormat.targetSz = Size((format->display_area.right - format->display_area.left), (format->display_area.bottom - format->display_area.top));
126+
}
127+
else
128+
newFormat.displayArea = newFormat.srcRoi;
129+
newFormat.width = newFormat.targetSz.width ? newFormat.targetSz.width : format->coded_width;
130+
newFormat.height = newFormat.targetSz.height ? newFormat.targetSz.height : format->coded_height;
131+
newFormat.targetRoi = thiz->videoDecoder_->getTargetRoi();
132+
newFormat.ulNumDecodeSurfaces = min(!thiz->allowFrameDrop_ ? max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast<int>(format->min_num_decode_surfaces)) :
133+
format->min_num_decode_surfaces * 2, 32);
134+
if (format->progressive_sequence)
135+
newFormat.deinterlaceMode = Weave;
136+
else
137+
newFormat.deinterlaceMode = Adaptive;
138+
int maxW = 0, maxH = 0;
139+
// AV1 has max width/height of sequence in sequence header
140+
if (format->codec == cudaVideoCodec_AV1 && format->seqhdr_data_length > 0)
116141
{
117-
FormatInfo newFormat;
118-
newFormat.videoFullRangeFlag = format->video_signal_description.video_full_range_flag;
119-
newFormat.codec = static_cast<Codec>(format->codec);
120-
newFormat.chromaFormat = static_cast<ChromaFormat>(format->chroma_format);
121-
newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8;
122-
newFormat.ulWidth = format->coded_width;
123-
newFormat.ulHeight = format->coded_height;
124-
newFormat.fps = format->frame_rate.numerator / static_cast<float>(format->frame_rate.denominator);
125-
newFormat.targetSz = thiz->videoDecoder_->getTargetSz();
126-
newFormat.srcRoi = thiz->videoDecoder_->getSrcRoi();
127-
if (newFormat.srcRoi.empty()) {
128-
newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom));
129-
if (newFormat.targetSz.empty())
130-
newFormat.targetSz = Size((format->display_area.right - format->display_area.left), (format->display_area.bottom - format->display_area.top));
131-
}
132-
else
133-
newFormat.displayArea = newFormat.srcRoi;
134-
newFormat.width = newFormat.targetSz.width ? newFormat.targetSz.width : format->coded_width;
135-
newFormat.height = newFormat.targetSz.height ? newFormat.targetSz.height : format->coded_height;
136-
newFormat.targetRoi = thiz->videoDecoder_->getTargetRoi();
137-
newFormat.ulNumDecodeSurfaces = min(!thiz->allowFrameDrop_ ? max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast<int>(format->min_num_decode_surfaces)) :
138-
format->min_num_decode_surfaces * 2, 32);
139-
if (format->progressive_sequence)
140-
newFormat.deinterlaceMode = Weave;
141-
else
142-
newFormat.deinterlaceMode = Adaptive;
143-
int maxW = 0, maxH = 0;
144-
// AV1 has max width/height of sequence in sequence header
145-
if (format->codec == cudaVideoCodec_AV1 && format->seqhdr_data_length > 0)
146-
{
147-
CUVIDEOFORMATEX* vidFormatEx = (CUVIDEOFORMATEX*)format;
148-
maxW = vidFormatEx->av1.max_width;
149-
maxH = vidFormatEx->av1.max_height;
142+
CUVIDEOFORMATEX* vidFormatEx = (CUVIDEOFORMATEX*)format;
143+
maxW = vidFormatEx->av1.max_width;
144+
maxH = vidFormatEx->av1.max_height;
145+
}
146+
if (maxW < (int)format->coded_width)
147+
maxW = format->coded_width;
148+
if (maxH < (int)format->coded_height)
149+
maxH = format->coded_height;
150+
newFormat.ulMaxWidth = maxW;
151+
newFormat.ulMaxHeight = maxH;
152+
153+
thiz->frameQueue_->waitUntilEmpty();
154+
int retVal = newFormat.ulNumDecodeSurfaces;
155+
try
156+
{
157+
if (thiz->videoDecoder_->inited()) {
158+
retVal = thiz->videoDecoder_->reconfigure(newFormat);
159+
if (retVal > 1 && newFormat.ulNumDecodeSurfaces != thiz->frameQueue_->getMaxSz())
160+
thiz->frameQueue_->resize(newFormat.ulNumDecodeSurfaces);
150161
}
151-
if (maxW < (int)format->coded_width)
152-
maxW = format->coded_width;
153-
if (maxH < (int)format->coded_height)
154-
maxH = format->coded_height;
155-
newFormat.ulMaxWidth = maxW;
156-
newFormat.ulMaxHeight = maxH;
157-
thiz->frameQueue_->init(newFormat.ulNumDecodeSurfaces);
158-
try
159-
{
160-
thiz->videoDecoder_->release();
162+
else {
163+
thiz->frameQueue_->init(newFormat.ulNumDecodeSurfaces);
161164
thiz->videoDecoder_->create(newFormat);
162165
}
163-
catch (const cv::Exception&)
164-
{
165-
CV_LOG_ERROR(NULL, "Attempt to reconfigure Nvidia decoder failed!");
166-
thiz->hasError_ = true;
167-
return false;
168-
}
169166
}
170-
171-
return thiz->videoDecoder_->nDecodeSurfaces();
167+
catch (const cv::Exception&)
168+
{
169+
CV_LOG_ERROR(NULL, "Attempt to configure Nvidia decoder failed!");
170+
thiz->hasError_ = true;
171+
retVal = 0;
172+
}
173+
return retVal;
172174
}
173175

174176
int CUDAAPI cv::cudacodec::detail::VideoParser::HandlePictureDecode(void* userData, CUVIDPICPARAMS* picParams)

modules/cudacodec/src/video_parser.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ class VideoParser
7878
bool allowFrameDrop_ = false;
7979

8080
// Called when the decoder encounters a video format change (or initial sequence header)
81-
// This particular implementation of the callback returns 0 in case the video format changes
82-
// to something different than the original format. Returning 0 causes a stop of the app.
8381
static int CUDAAPI HandleVideoSequence(void* pUserData, CUVIDEOFORMAT* pFormat);
8482

8583
// Called by the video parser to decode a single picture

0 commit comments

Comments
 (0)