Change Constant QP value without generating IDR

Hi,

I’m trying to encode video using NvEnc by controlling constQP value (quality) for each frame.
To force constQP value, I must reconfigure the encoder. But after reconfiguring it, the current picture is always encoded as an IDR picture. So the video stream only contains IDR frames.

The encoder parameters are NV_ENC_PARAMS_RC_CONSTQP, enablePTD = 1 and idrPeriod = 10.

If I set the value of NV_ENC_RECONFIGURE_PARAMS::forceIDR to 0, the behavior does not change.

Can we change the constQP value without generating IDR ?

Thank you.

Hi Wudusu,
Does NV_ENC_RECONFIGURE_PARAMS::resetEncoder value change the behavior you see?
Can you provide a standalone reproducer to check what could be going wrong?

Thanks.

Hi mgodse,

I must set the value of NV_ENC_RECONFIGURE_PARAMS::resetEncoder to 1 to apply the new constQP value.

The documentation says:

NV_ENC_RECONFIGURE_PARAMS::resetEncoder
“This resets the rate control states and other internal encoder states.
This should be used only with an IDR frame. If NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1, encoder will force the frame type to IDR.”

So, this is not a bug, I get the expected behavior.

However, I do not figure out why after changing constQP value, NvEnc must force the frame type to be IDR. This is not the case with others encoders (x264 and openh264).

Regards.

Hi wudusu,
If not already tried, can you check once with NV_ENC_RECONFIGURE_PARAMS::resetEncoder set to 0
and let me know the behavior?

Alternative approach is to use QP delta map for every frame.
Apply below patch to extracted Video Codec SDK 9.0 and check AppEncCuda sample application.
Let me know if that helps.

--- a/Samples/AppEncode/AppEncCuda/AppEncCuda.cpp
+++ b/Samples/AppEncode/AppEncCuda/AppEncCuda.cpp
@@ -46,6 +46,8 @@
     initializeParams.encodeConfig = &encodeConfig;
     enc.CreateDefaultEncoderParams(&initializeParams, pEncodeCLIOptions->GetEncodeGUID(), pEncodeCLIOptions->GetPresetGUID());
 
+    initializeParams.encodeConfig->rcParams.qpMapMode = NV_ENC_QP_MAP_DELTA;
+
     pEncodeCLIOptions->SetInitParams(&initializeParams, eFormat);
 
     enc.CreateEncoder(&initializeParams);
@@ -53,9 +55,24 @@
     int nFrameSize = enc.GetFrameSize();
 
     std::unique_ptr<uint8_t[]> pHostFrame(new uint8_t[nFrameSize]);
+
+    int sizeInMbs = ((nWidth + 15) / 16) * ((nHeight + 15) / 16);
+
+    std::vector<int8_t> qpDeltaMap1(sizeInMbs);
+    std::fill(qpDeltaMap1.begin(), qpDeltaMap1.end(), 1);
+
+    std::vector<int8_t> qpDeltaMap2(sizeInMbs);
+    std::fill(qpDeltaMap2.begin(), qpDeltaMap2.end(), -1);
+
     int nFrame = 0;
+    int count = 0;
     while (true)
     {
+        NV_ENC_PIC_PARAMS picParams = { 0 };
+
+        picParams.qpDeltaMap = (count % 2 == 0 ? qpDeltaMap1 : qpDeltaMap2).data();
+        picParams.qpDeltaMapSize = sizeInMbs;
+
         // Load the next frame from disk
         std::streamsize nRead = fpIn.read(reinterpret_cast<char*>(pHostFrame.get()), nFrameSize).gcount();
         // For receiving encoded packets
@@ -72,7 +89,7 @@
                 encoderInputFrame->chromaOffsets,
                 encoderInputFrame->numChromaPlanes);
 
-            enc.EncodeFrame(vPacket);
+            enc.EncodeFrame(vPacket, &picParams);
         }
         else
         {
@@ -86,6 +103,8 @@
         }
 
         if (nRead != nFrameSize) break;
+
+        count++;
     }
 
     enc.DestroyEncoder();

Perfect, that works.

Many thanks.