Skip to content

Commit 57f18a9

Browse files
committed
cudacodec: VideoWriter add container output
1 parent 6d78a0b commit 57f18a9

File tree

5 files changed

+118
-52
lines changed

5 files changed

+118
-52
lines changed

modules/cudacodec/include/opencv2/cudacodec.hpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,18 +184,18 @@ struct CV_EXPORTS_W_SIMPLE EncoderParams
184184
public:
185185
CV_WRAP EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT),
186186
rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0),
187-
targetQuality(30), gopLength(0) {};
188-
187+
targetQuality(30), gopLength(250), idrPeriod(250) {};
189188
CV_PROP_RW EncodePreset nvPreset;
190189
CV_PROP_RW EncodeTuningInfo tuningInfo;
191190
CV_PROP_RW EncodeProfile encodingProfile;
192191
CV_PROP_RW EncodeParamsRcMode rateControlMode;
193192
CV_PROP_RW EncodeMultiPass multiPassEncoding;
194-
CV_PROP_RW EncodeQp constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP.
195-
CV_PROP_RW int averageBitRate; //!< target bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CBR.
196-
CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CONSTQP.
197-
CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR.
198-
CV_PROP_RW int gopLength;
193+
CV_PROP_RW EncodeQp constQp; //!< QP's for \ref ENC_PARAMS_RC_CONSTQP.
194+
CV_PROP_RW int averageBitRate; //!< target bitrate for \ref ENC_PARAMS_RC_VBR and \ref ENC_PARAMS_RC_CBR.
195+
CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for \ref ENC_PARAMS_RC_VBR and \ref ENC_PARAMS_RC_CONSTQP.
196+
CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with \ref ENC_PARAMS_RC_VBR.
197+
CV_PROP_RW int gopLength; //!< the number of pictures in one GOP, ensuring \ref idrPeriod >= \ref gopLength.
198+
CV_PROP_RW int idrPeriod; //!< IDR interval, ensuring \ref idrPeriod >= \ref gopLength.
199199
};
200200
CV_EXPORTS bool operator==(const EncoderParams& lhs, const EncoderParams& rhs);
201201

modules/cudacodec/src/NvEncoder.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@
77
#include "NvEncoder.h"
88

99
namespace cv { namespace cudacodec {
10-
#ifndef _WIN32
11-
#include <cstring>
12-
static inline bool operator==(const GUID& guid1, const GUID& guid2) {
13-
return !memcmp(&guid1, &guid2, sizeof(GUID));
14-
}
15-
16-
static inline bool operator!=(const GUID& guid1, const GUID& guid2) {
17-
return !(guid1 == guid2);
18-
}
19-
#endif
2010

2111
NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat,
2212
uint32_t nExtraOutputDelay) :

modules/cudacodec/src/NvEncoder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@
1515

1616
namespace cv { namespace cudacodec {
1717

18+
#ifndef _WIN32
19+
#include <cstring>
20+
static inline bool operator==(const GUID& guid1, const GUID& guid2) {
21+
return !memcmp(&guid1, &guid2, sizeof(GUID));
22+
}
23+
24+
static inline bool operator!=(const GUID& guid1, const GUID& guid2) {
25+
return !(guid1 == guid2);
26+
}
27+
#endif
28+
1829
#define NVENC_THROW_ERROR( errorStr, errorCode ) \
1930
do \
2031
{ \

modules/cudacodec/src/video_writer.cpp

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ GUID CodecGuid(const Codec codec);
5959
void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen);
6060
GUID EncodingProfileGuid(const EncodeProfile encodingProfile);
6161
GUID EncodingPresetGuid(const EncodePreset nvPreset);
62-
bool Equal(const GUID& g1, const GUID& g2);
6362

6463
bool operator==(const EncoderParams& lhs, const EncoderParams& rhs)
6564
{
@@ -68,10 +67,46 @@ bool operator==(const EncoderParams& lhs, const EncoderParams& rhs)
6867
rhs.averageBitRate, rhs.maxBitRate, rhs.targetQuality, rhs.gopLength);
6968
};
7069

70+
class FFmpegVideoWriter : public EncoderCallback
71+
{
72+
public:
73+
FFmpegVideoWriter(const String fileName, const Codec codec, const int fps, const Size sz, const int idrPeriod);
74+
~FFmpegVideoWriter();
75+
void onEncoded(std::vector<std::vector<uint8_t>> vPacket);
76+
void onEncodingFinished();
77+
private:
78+
cv::VideoWriter writer;
79+
};
80+
81+
FFmpegVideoWriter::FFmpegVideoWriter(const String fileName, const Codec codec, const int fps, const Size sz, const int idrPeriod) {
82+
if (!videoio_registry::hasBackend(CAP_FFMPEG))
83+
CV_Error(Error::StsNotImplemented, "FFmpeg backend not found");
84+
const int fourcc = codec == Codec::H264 ? cv::VideoWriter::fourcc('a', 'v', 'c', '1') : cv::VideoWriter::fourcc('h', 'e', 'v', '1');
85+
writer.open(fileName, fourcc, fps, sz, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1, VideoWriterProperties::VIDEOWRITER_PROP_KEY_INTERVAL, idrPeriod });
86+
if (!writer.isOpened())
87+
CV_Error(Error::StsUnsupportedFormat, "Unsupported video sink");
88+
}
89+
90+
void FFmpegVideoWriter::onEncodingFinished() {
91+
writer.release();
92+
}
93+
94+
FFmpegVideoWriter::~FFmpegVideoWriter() {
95+
onEncodingFinished();
96+
}
97+
98+
void FFmpegVideoWriter::onEncoded(std::vector<std::vector<uint8_t>> vPacket) {
99+
for (auto& packet : vPacket) {
100+
Mat wrappedPacket(1, packet.size(), CV_8UC1, packet.data());
101+
writer.write(wrappedPacket);
102+
}
103+
}
104+
105+
71106
class RawVideoWriter : public EncoderCallback
72107
{
73108
public:
74-
RawVideoWriter(String fileName);
109+
RawVideoWriter(const String fileName);
75110
~RawVideoWriter();
76111
void onEncoded(std::vector<std::vector<uint8_t>> vPacket);
77112
void onEncodingFinished();
@@ -172,12 +207,6 @@ VideoWriterImpl::VideoWriterImpl(const Ptr<EncoderCallback>& encoderCallBack_, c
172207
Init(codec, fps, frameSz);
173208
}
174209

175-
VideoWriterImpl::VideoWriterImpl(const Ptr<EncoderCallback>& encoderCallback, const Size frameSz, const Codec codec, const double fps,
176-
const ColorFormat colorFormat, const Stream& stream) :
177-
VideoWriterImpl(encoderCallback, frameSz, codec, fps, colorFormat, EncoderParams(), stream)
178-
{
179-
}
180-
181210
void VideoWriterImpl::release() {
182211
pEnc->EndEncode(vPacket);
183212
encoderCallback->onEncoded(vPacket);
@@ -271,12 +300,6 @@ GUID EncodingPresetGuid(const EncodePreset nvPreset) {
271300
CV_Error(Error::StsUnsupportedFormat, msg);
272301
}
273302

274-
bool Equal(const GUID& g1, const GUID& g2) {
275-
if (std::tie(g1.Data1, g1.Data2, g1.Data3, g1.Data4) == std::tie(g2.Data1, g2.Data2, g2.Data3, g2.Data4))
276-
return true;
277-
return false;
278-
}
279-
280303
void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps)
281304
{
282305
NV_ENC_INITIALIZE_PARAMS initializeParams = {};
@@ -293,10 +316,10 @@ void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps)
293316
initializeParams.encodeConfig->rcParams.maxBitRate = encoderParams.maxBitRate;
294317
initializeParams.encodeConfig->rcParams.targetQuality = encoderParams.targetQuality;
295318
initializeParams.encodeConfig->gopLength = encoderParams.gopLength;
296-
if (Equal(codec, NV_ENC_CODEC_H264_GUID))
297-
initializeParams.encodeConfig->encodeCodecConfig.h264Config.idrPeriod = encoderParams.gopLength;
298-
else if (Equal(codec, NV_ENC_CODEC_HEVC_GUID))
299-
initializeParams.encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = encoderParams.gopLength;
319+
if (codec == NV_ENC_CODEC_H264_GUID)
320+
initializeParams.encodeConfig->encodeCodecConfig.h264Config.idrPeriod = encoderParams.idrPeriod;
321+
else if (codec == NV_ENC_CODEC_HEVC_GUID)
322+
initializeParams.encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = encoderParams.idrPeriod;
300323
pEnc->CreateEncoder(&initializeParams);
301324
}
302325

@@ -371,14 +394,25 @@ EncoderParams VideoWriterImpl::getEncoderParams() const {
371394
Ptr<VideoWriter> createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat,
372395
Ptr<EncoderCallback> encoderCallback, const Stream& stream)
373396
{
374-
encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName);
375-
return makePtr<VideoWriterImpl>(encoderCallback, frameSize, codec, fps, colorFormat, stream);
397+
return createVideoWriter(fileName, frameSize, codec, fps, colorFormat, EncoderParams(), encoderCallback, stream);
376398
}
377399

378400
Ptr<VideoWriter> createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat,
379401
const EncoderParams& params, Ptr<EncoderCallback> encoderCallback, const Stream& stream)
380402
{
381-
encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName);
403+
CV_Assert(params.idrPeriod >= params.gopLength);
404+
if (!encoderCallback) {
405+
// required until PR for raw video encapsulation is merged and windows dll is updated
406+
#ifndef WIN32 // remove #define and keep code once merged
407+
try {
408+
encoderCallback = new FFmpegVideoWriter(fileName, codec, fps, frameSize, params.idrPeriod);
409+
}
410+
catch (...)
411+
#endif
412+
{
413+
encoderCallback = new RawVideoWriter(fileName);
414+
}
415+
}
382416
return makePtr<VideoWriterImpl>(encoderCallback, frameSize, codec, fps, colorFormat, params, stream);
383417
}
384418

modules/cudacodec/test/test_video.cpp

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,13 @@ CUDA_TEST_P(TransCode, H264ToH265)
639639
constexpr cv::cudacodec::ColorFormat colorFormat = cv::cudacodec::ColorFormat::NV_NV12;
640640
constexpr double fps = 25;
641641
const cudacodec::Codec codec = cudacodec::Codec::HEVC;
642-
const std::string ext = ".h265";
642+
// required until PR for raw video encapsulation is merged and windows dll is updated
643+
#ifdef WIN32
644+
const std::string ext = ".hevc";
645+
#else
646+
// use this after update
647+
const std::string ext = ".mp4";
648+
#endif
643649
const std::string outputFile = cv::tempfile(ext.c_str());
644650
constexpr int nFrames = 5;
645651
Size frameSz;
@@ -716,7 +722,13 @@ CUDA_TEST_P(Write, Writer)
716722
const cudacodec::Codec codec = GET_PARAM(2);
717723
const double fps = GET_PARAM(3);
718724
const cv::cudacodec::ColorFormat colorFormat = GET_PARAM(4);
725+
// required until PR for raw video encapsulation is merged and windows dll is updated
726+
#ifdef WIN32
719727
const std::string ext = codec == cudacodec::Codec::H264 ? ".h264" : ".hevc";
728+
#else
729+
// use this after update
730+
const std::string ext = ".mp4";
731+
#endif
720732
const std::string outputFile = cv::tempfile(ext.c_str());
721733
constexpr int nFrames = 5;
722734
Size frameSz;
@@ -750,7 +762,7 @@ CUDA_TEST_P(Write, Writer)
750762
const int width = static_cast<int>(cap.get(CAP_PROP_FRAME_WIDTH));
751763
const int height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));
752764
ASSERT_EQ(frameSz, Size(width, height));
753-
ASSERT_TRUE(abs(fps - cap.get(CAP_PROP_FPS)) < 0.5);
765+
ASSERT_EQ(fps, cap.get(CAP_PROP_FPS));
754766
Mat frame;
755767
for (int i = 0; i < nFrames; ++i) {
756768
cap >> frame;
@@ -761,24 +773,22 @@ CUDA_TEST_P(Write, Writer)
761773
}
762774

763775
#define DEVICE_SRC true, false
764-
#define FPS 10, 29.7
776+
#define FPS 10, 29
765777
#define CODEC cv::cudacodec::Codec::H264, cv::cudacodec::Codec::HEVC
766778
#define COLOR_FORMAT cv::cudacodec::ColorFormat::BGR, cv::cudacodec::ColorFormat::RGB, cv::cudacodec::ColorFormat::BGRA, \
767779
cv::cudacodec::ColorFormat::RGBA, cv::cudacodec::ColorFormat::GRAY
768780
INSTANTIATE_TEST_CASE_P(CUDA_Codec, Write, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS),
769781
testing::Values(COLOR_FORMAT)));
770782

771-
772-
struct EncoderParams : testing::TestWithParam<cv::cuda::DeviceInfo>
783+
PARAM_TEST_CASE(EncoderParams, cv::cuda::DeviceInfo, int)
773784
{
774785
cv::cuda::DeviceInfo devInfo;
775786
cv::cudacodec::EncoderParams params;
776787
virtual void SetUp()
777788
{
778-
devInfo = GetParam();
789+
devInfo = GET_PARAM(0);
779790
cv::cuda::setDevice(devInfo.deviceID());
780791
// Fixed params for CBR test
781-
params.nvPreset = cv::cudacodec::EncodePreset::ENC_PRESET_P7;
782792
params.tuningInfo = cv::cudacodec::EncodeTuningInfo::ENC_TUNING_INFO_HIGH_QUALITY;
783793
params.encodingProfile = cv::cudacodec::EncodeProfile::ENC_H264_PROFILE_MAIN;
784794
params.rateControlMode = cv::cudacodec::EncodeParamsRcMode::ENC_PARAMS_RC_CBR;
@@ -787,19 +797,25 @@ struct EncoderParams : testing::TestWithParam<cv::cuda::DeviceInfo>
787797
params.maxBitRate = 0;
788798
params.targetQuality = 0;
789799
params.gopLength = 5;
800+
params.idrPeriod = GET_PARAM(1);
790801
}
791802
};
792803

793-
794804
CUDA_TEST_P(EncoderParams, Writer)
795805
{
796806
const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4";
797807
constexpr double fps = 25.0;
798808
constexpr cudacodec::Codec codec = cudacodec::Codec::H264;
809+
// required until PR for raw video encapsulation is merged and windows dll is updated
810+
#ifdef WIN32
799811
const std::string ext = ".h264";
812+
#else
813+
// use this after update
814+
const std::string ext = ".mp4";
815+
#endif
800816
const std::string outputFile = cv::tempfile(ext.c_str());
801817
Size frameSz;
802-
constexpr int nFrames = 5;
818+
const int nFrames = max(params.gopLength, params.idrPeriod) + 1;
803819
{
804820
cv::VideoCapture reader(inputFile);
805821
ASSERT_TRUE(reader.isOpened());
@@ -829,20 +845,35 @@ CUDA_TEST_P(EncoderParams, Writer)
829845
const int height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));
830846
ASSERT_EQ(frameSz, Size(width, height));
831847
ASSERT_EQ(fps, cap.get(CAP_PROP_FPS));
832-
const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG);
833-
Mat frame;
848+
const bool checkFrameType = videoio_registry::hasBackend(CAP_FFMPEG);
849+
VideoCapture capRaw;
850+
int idrPeriod = 0;
851+
if (checkFrameType) {
852+
capRaw.open(outputFile, CAP_FFMPEG, { CAP_PROP_FORMAT, -1 });
853+
ASSERT_TRUE(capRaw.isOpened());
854+
idrPeriod = params.idrPeriod == 0 ? params.gopLength : params.idrPeriod;
855+
}
856+
Mat frame, frameRaw;
834857
for (int i = 0; i < nFrames; ++i) {
835858
cap >> frame;
836859
ASSERT_FALSE(frame.empty());
837-
if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) {
838-
ASSERT_TRUE(i % params.gopLength == 0);
860+
if (checkFrameType) {
861+
capRaw >> frameRaw;
862+
ASSERT_FALSE(frameRaw.empty());
863+
const bool intraFrameReference = cap.get(CAP_PROP_FRAME_TYPE) == 73.0;
864+
const bool intraFrameActual = i % params.gopLength == 0;
865+
ASSERT_EQ(intraFrameActual, intraFrameReference);
866+
const bool keyFrameActual = capRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) == 1.0;
867+
const bool keyFrameReference = i % idrPeriod == 0;
868+
ASSERT_EQ(keyFrameActual, keyFrameReference);
839869
}
840870
}
841871
}
842872
ASSERT_EQ(0, remove(outputFile.c_str()));
843873
}
844874

845-
INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, ALL_DEVICES);
875+
#define IDR_PERIOD testing::Values(5,10)
876+
INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, testing::Combine(ALL_DEVICES, IDR_PERIOD));
846877

847878
#endif // HAVE_NVCUVENC
848879

0 commit comments

Comments
 (0)