Merge pull request #3569 from cudawarped:videoio_ffmpeg_add_stream_encapsulation

`cudacodec::VideoWriter` add container output
pull/3586/head
Alexander Smorkalov 2 years ago committed by GitHub
commit 13588000d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      modules/cudacodec/include/opencv2/cudacodec.hpp
  2. 10
      modules/cudacodec/src/NvEncoder.cpp
  3. 11
      modules/cudacodec/src/NvEncoder.h
  4. 82
      modules/cudacodec/src/video_writer.cpp
  5. 60
      modules/cudacodec/test/test_video.cpp

@ -184,18 +184,18 @@ struct CV_EXPORTS_W_SIMPLE EncoderParams
public:
CV_WRAP EncoderParams() : nvPreset(ENC_PRESET_P3), tuningInfo(ENC_TUNING_INFO_HIGH_QUALITY), encodingProfile(ENC_CODEC_PROFILE_AUTOSELECT),
rateControlMode(ENC_PARAMS_RC_VBR), multiPassEncoding(ENC_MULTI_PASS_DISABLED), constQp({ 0,0,0 }), averageBitRate(0), maxBitRate(0),
targetQuality(30), gopLength(0) {};
targetQuality(30), gopLength(250), idrPeriod(250) {};
CV_PROP_RW EncodePreset nvPreset;
CV_PROP_RW EncodeTuningInfo tuningInfo;
CV_PROP_RW EncodeProfile encodingProfile;
CV_PROP_RW EncodeParamsRcMode rateControlMode;
CV_PROP_RW EncodeMultiPass multiPassEncoding;
CV_PROP_RW EncodeQp constQp; //!< QP's for ENC_PARAMS_RC_CONSTQP.
CV_PROP_RW int averageBitRate; //!< target bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CBR.
CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for ENC_PARAMS_RC_VBR and ENC_PARAMS_RC_CONSTQP.
CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with ENC_PARAMS_RC_VBR.
CV_PROP_RW int gopLength;
CV_PROP_RW EncodeQp constQp; //!< QP's for \ref ENC_PARAMS_RC_CONSTQP.
CV_PROP_RW int averageBitRate; //!< target bitrate for \ref ENC_PARAMS_RC_VBR and \ref ENC_PARAMS_RC_CBR.
CV_PROP_RW int maxBitRate; //!< upper bound on bitrate for \ref ENC_PARAMS_RC_VBR and \ref ENC_PARAMS_RC_CONSTQP.
CV_PROP_RW uint8_t targetQuality; //!< value 0 - 51 where video quality decreases as targetQuality increases, used with \ref ENC_PARAMS_RC_VBR.
CV_PROP_RW int gopLength; //!< the number of pictures in one GOP, ensuring \ref idrPeriod >= \ref gopLength.
CV_PROP_RW int idrPeriod; //!< IDR interval, ensuring \ref idrPeriod >= \ref gopLength.
};
CV_EXPORTS bool operator==(const EncoderParams& lhs, const EncoderParams& rhs);
@ -209,7 +209,7 @@ public:
@param vPacket The raw bitstream for one or more frames.
*/
virtual void onEncoded(std::vector<std::vector<uint8_t>> vPacket) = 0;
virtual void onEncoded(const std::vector<std::vector<uint8_t>>& vPacket) = 0;
/** @brief Callback function to that the encoding has finished.
* */
@ -218,14 +218,14 @@ public:
virtual ~EncoderCallback() {}
};
/** @brief Video writer interface.
/** @brief Video writer interface, see createVideoWriter().
Available when built with WITH_NVCUVENC=ON while Nvidia's Video Codec SDK is installed.
Available if Nvidia's Video Codec SDK is installed.
Encoding support is dependent on the GPU, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details.
Only Codec::H264 and Codec::HEVC are supported with encoding support dependent on the GPU, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details.
@note
- An example on how to use the videoWriter class can be found at
- An example on how to use the VideoWriter class can be found at
opencv_source_code/samples/gpu/video_writer.cpp
*/
class CV_EXPORTS_W VideoWriter
@ -253,9 +253,9 @@ public:
/** @brief Creates video writer.
@param fileName Name of the output video file. Only raw h264 or hevc files are supported.
@param fileName Name of the output video file.
@param frameSize Size of the input video frames.
@param codec Codec.
@param codec Supports Codec::H264 and Codec::HEVC.
@param fps Framerate of the created video stream.
@param colorFormat OpenCv color format of the frames to be encoded.
@param encoderCallback Callbacks for video encoder. See cudacodec::EncoderCallback. Required for working with the encoded video stream.
@ -266,9 +266,9 @@ CV_EXPORTS_W Ptr<cudacodec::VideoWriter> createVideoWriter(const String& fileNam
/** @brief Creates video writer.
@param fileName Name of the output video file. Only raw h264 or hevc files are supported.
@param fileName Name of the output video file.
@param frameSize Size of the input video frames.
@param codec Codec.
@param codec Supports Codec::H264 and Codec::HEVC.
@param fps Framerate of the created video stream.
@param colorFormat OpenCv color format of the frames to be encoded.
@param params Additional encoding parameters.
@ -361,14 +361,14 @@ enum class VideoReaderProps {
#endif
};
/** @brief Video reader interface.
/** @brief Video reader interface, see createVideoReader().
Available when built with WITH_NVCUVID=ON while Nvidia's Video Codec SDK is installed.
Available if Nvidia's Video Codec SDK is installed.
Decoding support is dependent on the GPU, refer to the Nvidia Video Codec SDK Video Encode and Decode GPU Support Matrix for details.
@note
- An example on how to use the videoReader class can be found at
- An example on how to use the VideoReader interface can be found at
opencv_source_code/samples/gpu/video_reader.cpp
*/
class CV_EXPORTS_W VideoReader

@ -7,16 +7,6 @@
#include "NvEncoder.h"
namespace cv { namespace cudacodec {
#ifndef _WIN32
#include <cstring>
static inline bool operator==(const GUID& guid1, const GUID& guid2) {
return !memcmp(&guid1, &guid2, sizeof(GUID));
}
static inline bool operator!=(const GUID& guid1, const GUID& guid2) {
return !(guid1 == guid2);
}
#endif
NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat,
uint32_t nExtraOutputDelay) :

@ -15,6 +15,17 @@
namespace cv { namespace cudacodec {
#ifndef _WIN32
#include <cstring>
static inline bool operator==(const GUID& guid1, const GUID& guid2) {
return !memcmp(&guid1, &guid2, sizeof(GUID));
}
static inline bool operator!=(const GUID& guid1, const GUID& guid2) {
return !(guid1 == guid2);
}
#endif
#define NVENC_THROW_ERROR( errorStr, errorCode ) \
do \
{ \

@ -59,7 +59,6 @@ GUID CodecGuid(const Codec codec);
void FrameRate(const double fps, uint32_t& frameRateNum, uint32_t& frameRateDen);
GUID EncodingProfileGuid(const EncodeProfile encodingProfile);
GUID EncodingPresetGuid(const EncodePreset nvPreset);
bool Equal(const GUID& g1, const GUID& g2);
bool operator==(const EncoderParams& lhs, const EncoderParams& rhs)
{
@ -68,12 +67,48 @@ bool operator==(const EncoderParams& lhs, const EncoderParams& rhs)
rhs.averageBitRate, rhs.maxBitRate, rhs.targetQuality, rhs.gopLength);
};
class FFmpegVideoWriter : public EncoderCallback
{
public:
FFmpegVideoWriter(const String& fileName, const Codec codec, const int fps, const Size sz, const int idrPeriod);
~FFmpegVideoWriter();
void onEncoded(const std::vector<std::vector<uint8_t>>& vPacket);
void onEncodingFinished();
private:
cv::VideoWriter writer;
};
FFmpegVideoWriter::FFmpegVideoWriter(const String& fileName, const Codec codec, const int fps, const Size sz, const int idrPeriod) {
if (!videoio_registry::hasBackend(CAP_FFMPEG))
CV_Error(Error::StsNotImplemented, "FFmpeg backend not found");
const int fourcc = codec == Codec::H264 ? cv::VideoWriter::fourcc('a', 'v', 'c', '1') : cv::VideoWriter::fourcc('h', 'e', 'v', '1');
writer.open(fileName, fourcc, fps, sz, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1, VideoWriterProperties::VIDEOWRITER_PROP_KEY_INTERVAL, idrPeriod });
if (!writer.isOpened())
CV_Error(Error::StsUnsupportedFormat, "Unsupported video sink");
}
void FFmpegVideoWriter::onEncodingFinished() {
writer.release();
}
FFmpegVideoWriter::~FFmpegVideoWriter() {
onEncodingFinished();
}
void FFmpegVideoWriter::onEncoded(const std::vector<std::vector<uint8_t>>& vPacket) {
for (auto& packet : vPacket) {
Mat wrappedPacket(1, packet.size(), CV_8UC1, (void*)packet.data());
writer.write(wrappedPacket);
}
}
class RawVideoWriter : public EncoderCallback
{
public:
RawVideoWriter(String fileName);
RawVideoWriter(const String fileName);
~RawVideoWriter();
void onEncoded(std::vector<std::vector<uint8_t>> vPacket);
void onEncoded(const std::vector<std::vector<uint8_t>>& vPacket);
void onEncodingFinished();
private:
std::ofstream fpOut;
@ -93,9 +128,9 @@ RawVideoWriter::~RawVideoWriter() {
onEncodingFinished();
}
void RawVideoWriter::onEncoded(std::vector<std::vector<uint8_t>> vPacket) {
void RawVideoWriter::onEncoded(const std::vector<std::vector<uint8_t>>& vPacket) {
for (auto& packet : vPacket)
fpOut.write(reinterpret_cast<char*>(packet.data()), packet.size());
fpOut.write(reinterpret_cast<const char*>(packet.data()), packet.size());
}
class VideoWriterImpl : public VideoWriter
@ -172,12 +207,6 @@ VideoWriterImpl::VideoWriterImpl(const Ptr<EncoderCallback>& encoderCallBack_, c
Init(codec, fps, frameSz);
}
VideoWriterImpl::VideoWriterImpl(const Ptr<EncoderCallback>& encoderCallback, const Size frameSz, const Codec codec, const double fps,
const ColorFormat colorFormat, const Stream& stream) :
VideoWriterImpl(encoderCallback, frameSz, codec, fps, colorFormat, EncoderParams(), stream)
{
}
void VideoWriterImpl::release() {
pEnc->EndEncode(vPacket);
encoderCallback->onEncoded(vPacket);
@ -271,12 +300,6 @@ GUID EncodingPresetGuid(const EncodePreset nvPreset) {
CV_Error(Error::StsUnsupportedFormat, msg);
}
bool Equal(const GUID& g1, const GUID& g2) {
if (std::tie(g1.Data1, g1.Data2, g1.Data3, g1.Data4) == std::tie(g2.Data1, g2.Data2, g2.Data3, g2.Data4))
return true;
return false;
}
void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps)
{
NV_ENC_INITIALIZE_PARAMS initializeParams = {};
@ -293,10 +316,10 @@ void VideoWriterImpl::InitializeEncoder(const GUID codec, const double fps)
initializeParams.encodeConfig->rcParams.maxBitRate = encoderParams.maxBitRate;
initializeParams.encodeConfig->rcParams.targetQuality = encoderParams.targetQuality;
initializeParams.encodeConfig->gopLength = encoderParams.gopLength;
if (Equal(codec, NV_ENC_CODEC_H264_GUID))
initializeParams.encodeConfig->encodeCodecConfig.h264Config.idrPeriod = encoderParams.gopLength;
else if (Equal(codec, NV_ENC_CODEC_HEVC_GUID))
initializeParams.encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = encoderParams.gopLength;
if (codec == NV_ENC_CODEC_H264_GUID)
initializeParams.encodeConfig->encodeCodecConfig.h264Config.idrPeriod = encoderParams.idrPeriod;
else if (codec == NV_ENC_CODEC_HEVC_GUID)
initializeParams.encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = encoderParams.idrPeriod;
pEnc->CreateEncoder(&initializeParams);
}
@ -371,14 +394,25 @@ EncoderParams VideoWriterImpl::getEncoderParams() const {
Ptr<VideoWriter> createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat,
Ptr<EncoderCallback> encoderCallback, const Stream& stream)
{
encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName);
return makePtr<VideoWriterImpl>(encoderCallback, frameSize, codec, fps, colorFormat, stream);
return createVideoWriter(fileName, frameSize, codec, fps, colorFormat, EncoderParams(), encoderCallback, stream);
}
Ptr<VideoWriter> createVideoWriter(const String& fileName, const Size frameSize, const Codec codec, const double fps, const ColorFormat colorFormat,
const EncoderParams& params, Ptr<EncoderCallback> encoderCallback, const Stream& stream)
{
encoderCallback = encoderCallback ? encoderCallback : new RawVideoWriter(fileName);
CV_Assert(params.idrPeriod >= params.gopLength);
if (!encoderCallback) {
// required until PR for raw video encapsulation is merged and windows dll is updated
#ifndef WIN32 // remove #define and keep code once merged
try {
encoderCallback = new FFmpegVideoWriter(fileName, codec, fps, frameSize, params.idrPeriod);
}
catch (...)
#endif
{
encoderCallback = new RawVideoWriter(fileName);
}
}
return makePtr<VideoWriterImpl>(encoderCallback, frameSize, codec, fps, colorFormat, params, stream);
}

@ -639,7 +639,13 @@ CUDA_TEST_P(TransCode, H264ToH265)
constexpr cv::cudacodec::ColorFormat colorFormat = cv::cudacodec::ColorFormat::NV_NV12;
constexpr double fps = 25;
const cudacodec::Codec codec = cudacodec::Codec::HEVC;
const std::string ext = ".h265";
// required until PR for raw video encapsulation is merged and windows dll is updated
#ifdef WIN32
const std::string ext = ".hevc";
#else
// use this after update
const std::string ext = ".mp4";
#endif
const std::string outputFile = cv::tempfile(ext.c_str());
constexpr int nFrames = 5;
Size frameSz;
@ -716,7 +722,13 @@ CUDA_TEST_P(Write, Writer)
const cudacodec::Codec codec = GET_PARAM(2);
const double fps = GET_PARAM(3);
const cv::cudacodec::ColorFormat colorFormat = GET_PARAM(4);
// required until PR for raw video encapsulation is merged and windows dll is updated
#ifdef WIN32
const std::string ext = codec == cudacodec::Codec::H264 ? ".h264" : ".hevc";
#else
// use this after update
const std::string ext = ".mp4";
#endif
const std::string outputFile = cv::tempfile(ext.c_str());
constexpr int nFrames = 5;
Size frameSz;
@ -750,7 +762,7 @@ CUDA_TEST_P(Write, Writer)
const int width = static_cast<int>(cap.get(CAP_PROP_FRAME_WIDTH));
const int height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));
ASSERT_EQ(frameSz, Size(width, height));
ASSERT_TRUE(abs(fps - cap.get(CAP_PROP_FPS)) < 0.5);
ASSERT_EQ(fps, cap.get(CAP_PROP_FPS));
Mat frame;
for (int i = 0; i < nFrames; ++i) {
cap >> frame;
@ -761,24 +773,22 @@ CUDA_TEST_P(Write, Writer)
}
#define DEVICE_SRC true, false
#define FPS 10, 29.7
#define FPS 10, 29
#define CODEC cv::cudacodec::Codec::H264, cv::cudacodec::Codec::HEVC
#define COLOR_FORMAT cv::cudacodec::ColorFormat::BGR, cv::cudacodec::ColorFormat::RGB, cv::cudacodec::ColorFormat::BGRA, \
cv::cudacodec::ColorFormat::RGBA, cv::cudacodec::ColorFormat::GRAY
INSTANTIATE_TEST_CASE_P(CUDA_Codec, Write, testing::Combine(ALL_DEVICES, testing::Values(DEVICE_SRC), testing::Values(CODEC), testing::Values(FPS),
testing::Values(COLOR_FORMAT)));
struct EncoderParams : testing::TestWithParam<cv::cuda::DeviceInfo>
PARAM_TEST_CASE(EncoderParams, cv::cuda::DeviceInfo, int)
{
cv::cuda::DeviceInfo devInfo;
cv::cudacodec::EncoderParams params;
virtual void SetUp()
{
devInfo = GetParam();
devInfo = GET_PARAM(0);
cv::cuda::setDevice(devInfo.deviceID());
// Fixed params for CBR test
params.nvPreset = cv::cudacodec::EncodePreset::ENC_PRESET_P7;
params.tuningInfo = cv::cudacodec::EncodeTuningInfo::ENC_TUNING_INFO_HIGH_QUALITY;
params.encodingProfile = cv::cudacodec::EncodeProfile::ENC_H264_PROFILE_MAIN;
params.rateControlMode = cv::cudacodec::EncodeParamsRcMode::ENC_PARAMS_RC_CBR;
@ -787,19 +797,25 @@ struct EncoderParams : testing::TestWithParam<cv::cuda::DeviceInfo>
params.maxBitRate = 0;
params.targetQuality = 0;
params.gopLength = 5;
params.idrPeriod = GET_PARAM(1);
}
};
CUDA_TEST_P(EncoderParams, Writer)
{
const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny.mp4";
constexpr double fps = 25.0;
constexpr cudacodec::Codec codec = cudacodec::Codec::H264;
// required until PR for raw video encapsulation is merged and windows dll is updated
#ifdef WIN32
const std::string ext = ".h264";
#else
// use this after update
const std::string ext = ".mp4";
#endif
const std::string outputFile = cv::tempfile(ext.c_str());
Size frameSz;
constexpr int nFrames = 5;
const int nFrames = max(params.gopLength, params.idrPeriod) + 1;
{
cv::VideoCapture reader(inputFile);
ASSERT_TRUE(reader.isOpened());
@ -829,20 +845,36 @@ CUDA_TEST_P(EncoderParams, Writer)
const int height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));
ASSERT_EQ(frameSz, Size(width, height));
ASSERT_EQ(fps, cap.get(CAP_PROP_FPS));
const bool checkGop = videoio_registry::hasBackend(CAP_FFMPEG);
Mat frame;
const bool checkFrameType = videoio_registry::hasBackend(CAP_FFMPEG);
VideoCapture capRaw;
int idrPeriod = 0;
if (checkFrameType) {
capRaw.open(outputFile, CAP_FFMPEG, { CAP_PROP_FORMAT, -1 });
ASSERT_TRUE(capRaw.isOpened());
idrPeriod = params.idrPeriod == 0 ? params.gopLength : params.idrPeriod;
}
const double frameTypeIAsciiCode = 73.0; // see CAP_PROP_FRAME_TYPE
Mat frame, frameRaw;
for (int i = 0; i < nFrames; ++i) {
cap >> frame;
ASSERT_FALSE(frame.empty());
if (checkGop && (cap.get(CAP_PROP_FRAME_TYPE) == 73)) {
ASSERT_TRUE(i % params.gopLength == 0);
if (checkFrameType) {
capRaw >> frameRaw;
ASSERT_FALSE(frameRaw.empty());
const bool intraFrameReference = cap.get(CAP_PROP_FRAME_TYPE) == frameTypeIAsciiCode;
const bool intraFrameActual = i % params.gopLength == 0;
ASSERT_EQ(intraFrameActual, intraFrameReference);
const bool keyFrameActual = capRaw.get(CAP_PROP_LRF_HAS_KEY_FRAME) == 1.0;
const bool keyFrameReference = i % idrPeriod == 0;
ASSERT_EQ(keyFrameActual, keyFrameReference);
}
}
}
ASSERT_EQ(0, remove(outputFile.c_str()));
}
INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, ALL_DEVICES);
#define IDR_PERIOD testing::Values(5,10)
INSTANTIATE_TEST_CASE_P(CUDA_Codec, EncoderParams, testing::Combine(ALL_DEVICES, IDR_PERIOD));
#endif // HAVE_NVCUVENC

Loading…
Cancel
Save