Skip to content

Commit f0fa5bb

Browse files
committed
cudacodec: add capacity to reconfigure decoder on resolution change
1 parent ed1873b commit f0fa5bb

File tree

9 files changed

+315
-76
lines changed

9 files changed

+315
-76
lines changed

modules/cudacodec/include/opencv2/cudacodec.hpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,27 @@ enum DeinterlaceMode
305305
Adaptive = 2
306306
};
307307

308+
/** @brief Output format for a decoded frame when the resolution of the source is reduced by the encoder. In all cases the size of the output frame remains the same.
309+
* * @param Default Use the approach adopted by cv::VideoCapture, i.e. maintain the same frame size by placing the smaller output in the top left corner.
310+
* @param Qos Maintain the same frame resolution by upscaling to the original resolution to seamlessly process streams produced by servers that are adhering to Quality of Service constraints.
311+
* */
312+
enum ResolutionChangeMode
313+
{
314+
Default = 0,
315+
Qos = 1
316+
};
317+
308318
/** @brief Struct providing information about video file format. :
309319
*/
310320
struct CV_EXPORTS_W_SIMPLE FormatInfo
311321
{
312-
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
322+
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), nBitDepthChromaMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
313323
fps(0), ulNumDecodeSurfaces(0) {};
314324

315325
CV_PROP_RW Codec codec;
316326
CV_PROP_RW ChromaFormat chromaFormat;
317327
CV_PROP_RW int nBitDepthMinus8;
328+
CV_PROP_RW int nBitDepthChromaMinus8;
318329
CV_PROP_RW int ulWidth;//!< Coded sequence width in pixels.
319330
CV_PROP_RW int ulHeight;//!< Coded sequence height in pixels.
320331
CV_PROP_RW int width;//!< Width of the decoded frame returned by nextFrame(frame).
@@ -329,6 +340,7 @@ struct CV_EXPORTS_W_SIMPLE FormatInfo
329340
CV_PROP_RW cv::Size targetSz;//!< Post-processed size of the output frame.
330341
CV_PROP_RW cv::Rect srcRoi;//!< Region of interest decoded from video source.
331342
CV_PROP_RW cv::Rect targetRoi;//!< Region of interest in the output frame containing the decoded frame.
343+
CV_PROP_RW ResolutionChangeMode resChangeMode;//!< Output format for a decoded frame when the resolution of the source is reduced by the encoder.
332344
};
333345

334346
/** @brief cv::cudacodec::VideoReader generic properties identifier.
@@ -533,16 +545,18 @@ but it cannot go below the number determined by NVDEC.
533545
@param srcRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) decoded from video source, defaults to the full frame.
534546
@param targetRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) within the output frame to copy and resize the decoded frame to,
535547
defaults to the full frame.
548+
@param resChangeMode Output mode to use when the resolution of the source is changed by the encoder, ignored when targetRoi is specified.
536549
*/
537550
struct CV_EXPORTS_W_SIMPLE VideoReaderInitParams {
538-
CV_WRAP VideoReaderInitParams() : udpSource(false), allowFrameDrop(false), minNumDecodeSurfaces(0), rawMode(0) {};
551+
CV_WRAP VideoReaderInitParams() : udpSource(false), allowFrameDrop(false), minNumDecodeSurfaces(0), rawMode(0), resChangeMode(ResolutionChangeMode::Default) {};
539552
CV_PROP_RW bool udpSource;
540553
CV_PROP_RW bool allowFrameDrop;
541554
CV_PROP_RW int minNumDecodeSurfaces;
542555
CV_PROP_RW bool rawMode;
543556
CV_PROP_RW cv::Size targetSz;
544557
CV_PROP_RW cv::Rect srcRoi;
545558
CV_PROP_RW cv::Rect targetRoi;
559+
CV_PROP_RW ResolutionChangeMode resChangeMode;
546560
};
547561

548562
/** @brief Creates video reader.

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+
{
190+
AutoLock autoLock(mtx_);
191+
videoFormat_.ulNumDecodeSurfaces = videoFormat.ulNumDecodeSurfaces;
192+
videoFormat_.ulWidth = videoFormat.ulWidth;
193+
videoFormat_.ulHeight = videoFormat.ulHeight;
194+
videoFormat_.targetRoi = videoFormat.targetRoi;
195+
}
196+
197+
if (!decodeResChange)
198+
return 1;
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: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ namespace cv { namespace cudacodec { namespace detail {
4949
class VideoDecoder
5050
{
5151
public:
52-
VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, cv::Size targetSz, cv::Rect srcRoi, cv::Rect targetRoi, CUcontext ctx, CUvideoctxlock lock) :
52+
VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, cv::Size targetSz, cv::Rect srcRoi, cv::Rect targetRoi, const ResolutionChangeMode resChangeMode, CUcontext ctx, CUvideoctxlock lock) :
5353
ctx_(ctx), lock_(lock), decoder_(0)
5454
{
5555
videoFormat_.codec = codec;
5656
videoFormat_.ulNumDecodeSurfaces = minNumDecodeSurfaces;
57+
videoFormat_.resChangeMode = resChangeMode;
5758
// alignment enforced by nvcuvid, likely due to chroma subsampling
5859
videoFormat_.targetSz.width = targetSz.width - targetSz.width % 2; videoFormat_.targetSz.height = targetSz.height - targetSz.height % 2;
5960
videoFormat_.srcRoi.x = srcRoi.x - srcRoi.x % 4; videoFormat_.srcRoi.width = srcRoi.width - srcRoi.width % 4;
@@ -68,14 +69,16 @@ class VideoDecoder
6869
}
6970

7071
void create(const FormatInfo& videoFormat);
72+
int reconfigure(const FormatInfo& videoFormat);
7173
void release();
7274

73-
// Get the code-type currently used.
75+
// Get the codec-type currently used.
7476
cudaVideoCodec codec() const { return static_cast<cudaVideoCodec>(videoFormat_.codec); }
7577
int nDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; }
7678
cv::Size getTargetSz() const { return videoFormat_.targetSz; }
7779
cv::Rect getSrcRoi() const { return videoFormat_.srcRoi; }
7880
cv::Rect getTargetRoi() const { return videoFormat_.targetRoi; }
81+
ResolutionChangeMode getResChangeMode() const { return videoFormat_.resChangeMode; }
7982

8083
unsigned long frameWidth() const { return videoFormat_.ulWidth; }
8184
unsigned long frameHeight() const { return videoFormat_.ulHeight; }
@@ -84,6 +87,8 @@ class VideoDecoder
8487
unsigned long targetWidth() { return videoFormat_.width; }
8588
unsigned long targetHeight() { return videoFormat_.height; }
8689

90+
bool inited() { return inited_; }
91+
8792
cudaVideoChromaFormat chromaFormat() const { return static_cast<cudaVideoChromaFormat>(videoFormat_.chromaFormat); }
8893
int nBitDepthMinus8() const { return videoFormat_.nBitDepthMinus8; }
8994

@@ -114,6 +119,7 @@ class VideoDecoder
114119
CUvideodecoder decoder_ = 0;
115120
FormatInfo videoFormat_ = {};
116121
Mutex mtx_;
122+
bool inited_ = false;
117123
};
118124

119125
}}}

0 commit comments

Comments
 (0)