Skip to content

Commit 6131706

Browse files
committed
cudacodec - Add 10 bit YUV420 and YUV444 encoding functionality
1 parent 89529d7 commit 6131706

File tree

5 files changed

+110
-26
lines changed

5 files changed

+110
-26
lines changed

modules/cudacodec/include/opencv2/cudacodec.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ enum ColorFormat {
106106
NV_IYUV = 9, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. VideoWriter only.
107107
NV_YUV444 = 10, //!< Nvidia Buffer Format - Planar YUV [Y plane followed by U and V planes]. VideoWriter only.
108108
NV_AYUV = 11, //!< Nvidia Buffer Format - 8 bit Packed A8Y8U8V8. This is a word-ordered format where a pixel is represented by a 32-bit word with V in the lowest 8 bits, U in the next 8 bits, Y in the 8 bits after that and A in the highest 8 bits. VideoWriter only.
109+
NV_YUV420_10BIT = 12, //!< Nvidia Buffer Format - 10 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. VideoWriter only.
110+
NV_YUV444_10BIT = 13, //!< Nvidia Buffer Format - 10 bit Planar YUV444 [Y plane followed by U and V planes]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. VideoWriter only.
109111
#ifndef CV_DOXYGEN
110112
PROP_NOT_SUPPORTED
111113
#endif

modules/cudacodec/src/ffmpeg_video_source.cpp

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,6 @@ Codec FourccToCodec(int codec)
8989
CV_Error(Error::StsUnsupportedFormat, msg);
9090
}
9191

92-
static
93-
void FourccToChromaFormat(const int pixelFormat, ChromaFormat &chromaFormat, int & nBitDepthMinus8)
94-
{
95-
switch (pixelFormat)
96-
{
97-
case CV_FOURCC_MACRO('I', '4', '2', '0'):
98-
chromaFormat = YUV420;
99-
nBitDepthMinus8 = 0;
100-
break;
101-
default:
102-
CV_LOG_WARNING(NULL, cv::format("ChromaFormat not recognized: 0x%08X (%s). Assuming I420", pixelFormat, fourccToString(pixelFormat).c_str()));
103-
chromaFormat = YUV420;
104-
nBitDepthMinus8 = 0;
105-
break;
106-
}
107-
}
108-
10992
static
11093
int StartCodeLen(unsigned char* data, const int sz) {
11194
if (sz >= 3 && data[0] == 0 && data[1] == 0 && data[2] == 1)
@@ -145,14 +128,12 @@ cv::cudacodec::detail::FFmpegVideoSource::FFmpegVideoSource(const String& fname,
145128
extraData = tmpExtraData.clone();
146129

147130
int codec = (int)cap.get(CAP_PROP_FOURCC);
148-
int pixelFormat = (int)cap.get(CAP_PROP_CODEC_PIXEL_FORMAT);
149131
format_.codec = FourccToCodec(codec);
150132
format_.height = cap.get(CAP_PROP_FRAME_HEIGHT);
151133
format_.width = cap.get(CAP_PROP_FRAME_WIDTH);
152134
format_.displayArea = Rect(0, 0, format_.width, format_.height);
153135
format_.valid = false;
154136
format_.fps = cap.get(CAP_PROP_FPS);
155-
FourccToChromaFormat(pixelFormat, format_.chromaFormat, format_.nBitDepthMinus8);
156137
}
157138

158139
cv::cudacodec::detail::FFmpegVideoSource::~FFmpegVideoSource()

modules/cudacodec/src/video_decoder.cpp

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,44 @@
4545

4646
#ifdef HAVE_NVCUVID
4747

48-
#if (CUDART_VERSION < 9000)
48+
#if (CUDART_VERSION >= 9000)
49+
static const char* GetVideoCodecString(cudaVideoCodec eCodec) {
50+
static struct {
51+
cudaVideoCodec eCodec;
52+
const char* name;
53+
} aCodecName[] = {
54+
{ cudaVideoCodec_MPEG1, "MPEG-1" },
55+
{ cudaVideoCodec_MPEG2, "MPEG-2" },
56+
{ cudaVideoCodec_MPEG4, "MPEG-4 (ASP)" },
57+
{ cudaVideoCodec_VC1, "VC-1/WMV" },
58+
{ cudaVideoCodec_H264, "AVC/H.264" },
59+
{ cudaVideoCodec_JPEG, "M-JPEG" },
60+
{ cudaVideoCodec_H264_SVC, "H.264/SVC" },
61+
{ cudaVideoCodec_H264_MVC, "H.264/MVC" },
62+
{ cudaVideoCodec_HEVC, "H.265/HEVC" },
63+
{ cudaVideoCodec_VP8, "VP8" },
64+
{ cudaVideoCodec_VP9, "VP9" },
65+
{ cudaVideoCodec_AV1, "AV1" },
66+
{ cudaVideoCodec_NumCodecs, "Invalid" },
67+
{ cudaVideoCodec_YUV420, "YUV 4:2:0" },
68+
{ cudaVideoCodec_YV12, "YV12 4:2:0" },
69+
{ cudaVideoCodec_NV12, "NV12 4:2:0" },
70+
{ cudaVideoCodec_YUYV, "YUYV 4:2:2" },
71+
{ cudaVideoCodec_UYVY, "UYVY 4:2:2" },
72+
};
73+
74+
if (eCodec >= 0 && eCodec <= cudaVideoCodec_NumCodecs) {
75+
return aCodecName[eCodec].name;
76+
}
77+
for (int i = cudaVideoCodec_NumCodecs + 1; i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) {
78+
if (eCodec == aCodecName[i].eCodec) {
79+
return aCodecName[eCodec].name;
80+
}
81+
}
82+
return "Unknown";
83+
}
84+
#endif
85+
4986
static const char* GetVideoChromaFormatString(cudaVideoChromaFormat eChromaFormat) {
5087
static struct {
5188
cudaVideoChromaFormat eChromaFormat;
@@ -62,7 +99,6 @@ static const char* GetVideoChromaFormatString(cudaVideoChromaFormat eChromaForma
6299
}
63100
return "Unknown";
64101
}
65-
#endif
66102

67103
void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
68104
{
@@ -141,7 +177,7 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
141177
cuSafeCall(cuCtxPopCurrent(NULL));
142178

143179
if (!decodeCaps.bIsSupported) {
144-
CV_Error(Error::StsUnsupportedFormat, "Video codec is not supported by this GPU hardware video decoder refer to Nvidia's GPU Support Matrix to confirm your GPU supports hardware decoding of the video source's codec.");
180+
CV_Error(Error::StsUnsupportedFormat, std::to_string(decodeCaps.nBitDepthMinus8 + 8) + " bit " + GetVideoCodecString(_codec) + " with " + GetVideoChromaFormatString(_chromaFormat) + " chroma format is not supported by this GPU hardware video decoder. Please refer to Nvidia's GPU Support Matrix to confirm your GPU supports hardware decoding of this video source.");
145181
}
146182

147183
if (!(decodeCaps.nOutputFormatMask & (1 << surfaceFormat)))

modules/cudacodec/src/video_writer.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,22 +188,26 @@ NV_ENC_BUFFER_FORMAT EncBufferFormat(const ColorFormat colorFormat) {
188188
case ColorFormat::NV_IYUV: return NV_ENC_BUFFER_FORMAT_IYUV;
189189
case ColorFormat::NV_YUV444: return NV_ENC_BUFFER_FORMAT_YUV444;
190190
case ColorFormat::NV_AYUV: return NV_ENC_BUFFER_FORMAT_AYUV;
191+
case ColorFormat::NV_YUV420_10BIT: return NV_ENC_BUFFER_FORMAT_YUV420_10BIT;
192+
case ColorFormat::NV_YUV444_10BIT: return NV_ENC_BUFFER_FORMAT_YUV444_10BIT;
191193
default: return NV_ENC_BUFFER_FORMAT_UNDEFINED;
192194
}
193195
}
194196

195197
int NChannels(const ColorFormat colorFormat) {
196198
switch (colorFormat) {
197199
case ColorFormat::BGR:
198-
case ColorFormat::RGB:
199-
case ColorFormat::NV_IYUV:
200-
case ColorFormat::NV_YUV444: return 3;
200+
case ColorFormat::RGB: return 3;
201201
case ColorFormat::RGBA:
202202
case ColorFormat::BGRA:
203203
case ColorFormat::NV_AYUV: return 4;
204204
case ColorFormat::GRAY:
205205
case ColorFormat::NV_NV12:
206-
case ColorFormat::NV_YV12: return 1;
206+
case ColorFormat::NV_IYUV:
207+
case ColorFormat::NV_YV12:
208+
case ColorFormat::NV_YUV420_10BIT:
209+
case ColorFormat::NV_YUV444:
210+
case ColorFormat::NV_YUV444_10BIT: return 1;
207211
default: return 0;
208212
}
209213
}

modules/cudacodec/test/test_video.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,67 @@ CUDA_TEST_P(H264ToH265, Transcode)
10651065

10661066
INSTANTIATE_TEST_CASE_P(CUDA_Codec, H264ToH265, ALL_DEVICES);
10671067

1068+
CV_ENUM(YuvColorFormats, cudacodec::ColorFormat::NV_YUV444, cudacodec::ColorFormat::NV_YUV420_10BIT, cudacodec::ColorFormat::NV_YUV444_10BIT)
1069+
PARAM_TEST_CASE(YUVFormats, cv::cuda::DeviceInfo, YuvColorFormats)
1070+
{
1071+
};
1072+
1073+
CUDA_TEST_P(YUVFormats, Transcode)
1074+
{
1075+
cv::cuda::setDevice(GET_PARAM(0).deviceID());
1076+
const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.h265";
1077+
const cv::cudacodec::ColorFormat writerColorFormat = static_cast<cudacodec::ColorFormat>(static_cast<int>(GET_PARAM(1)));
1078+
constexpr double fps = 25;
1079+
const cudacodec::Codec codec = cudacodec::Codec::HEVC;
1080+
const std::string ext = ".mp4";
1081+
const std::string outputFile = cv::tempfile(ext.c_str());
1082+
constexpr int nFrames = 5;
1083+
vector<Mat> bgrGs;
1084+
{
1085+
VideoCapture cap(inputFile);
1086+
cv::Ptr<cv::cudacodec::VideoWriter> writer;
1087+
Mat frame, yuv, bgr;
1088+
cv::cudacodec::EncoderParams params;
1089+
params.tuningInfo = cv::cudacodec::EncodeTuningInfo::ENC_TUNING_INFO_LOSSLESS;
1090+
params.rateControlMode = cv::cudacodec::EncodeParamsRcMode::ENC_PARAMS_RC_CONSTQP;
1091+
for (int i = 0; i < nFrames; ++i) {
1092+
ASSERT_TRUE(cap.read(frame));
1093+
ASSERT_FALSE(frame.empty());
1094+
cudacodec::SurfaceFormat yuvFormat = cudacodec::SurfaceFormat::SF_YUV444;
1095+
cudacodec::BitDepth bitDepth = cudacodec::BitDepth::EIGHT;
1096+
if (writerColorFormat == cudacodec::ColorFormat::NV_YUV444_10BIT) {
1097+
yuvFormat = cudacodec::SurfaceFormat::SF_YUV444_16Bit;
1098+
bitDepth = cudacodec::BitDepth::SIXTEEN;
1099+
}
1100+
else if (writerColorFormat == cudacodec::ColorFormat::NV_YUV420_10BIT){
1101+
yuvFormat = cudacodec::SurfaceFormat::SF_P016;
1102+
bitDepth = cudacodec::BitDepth::SIXTEEN;
1103+
}
1104+
generateTestImages(frame, yuv, bgr, yuvFormat, cudacodec::ColorFormat::BGR, bitDepth, false);
1105+
bgrGs.push_back(bgr.clone());
1106+
if (writer.empty())
1107+
writer = cv::cudacodec::createVideoWriter(outputFile, frame.size(), codec, fps, writerColorFormat, params);
1108+
writer->write(yuv);
1109+
}
1110+
}
1111+
1112+
{
1113+
cv::Ptr<cv::cudacodec::VideoReader> reader = cv::cudacodec::createVideoReader(outputFile);
1114+
reader->set(cudacodec::ColorFormat::BGR);
1115+
cv::cuda::GpuMat frame, frameGs;
1116+
Mat frameHost, frameGsHost;
1117+
for (int i = 0; i < nFrames; ++i) {
1118+
ASSERT_TRUE(reader->nextFrame(frame));
1119+
frame.download(frameHost);
1120+
frameGsHost = bgrGs[i];
1121+
const int diff = writerColorFormat == cudacodec::ColorFormat::NV_YUV420_10BIT || writerColorFormat == cudacodec::ColorFormat::NV_YUV444_10BIT ? 512 : 1;
1122+
EXPECT_MAT_NEAR(frameHost, frameGsHost, diff);
1123+
}
1124+
}
1125+
ASSERT_EQ(0, remove(outputFile.c_str()));
1126+
}
1127+
1128+
INSTANTIATE_TEST_CASE_P(CUDA_Codec, YUVFormats, testing::Combine(ALL_DEVICES, YuvColorFormats::all()));
10681129
#endif
10691130

10701131
#if defined(HAVE_NVCUVENC)

0 commit comments

Comments
 (0)