Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question]: Smaller IDR interval results in x2 increase in file size #493

Open
MichaelLudeo opened this issue Aug 19, 2024 · 9 comments
Open
Labels

Comments

@MichaelLudeo
Copy link

Hello.
I changed the IDR frame insertion rate so that instead of every 30th frame,I insert every 5th. The output video video size jumped from 7mb to 17 mb. I am comparing the results to the Nvidia encoder, same IDR interval, same bitrate and same resolution. Nvidia encoded file size increased maybe 0.05%.

Here are the parameters I work with:

//idrInterval = 5
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, idrInterval);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEADER_INSERTION_SPACING, idrInterval);

My question is - how to reduce the file size when IDR frames are inserted more frequently? Should I use a different quality preset?

@MikhailAMD
Copy link
Collaborator

What is whole set of AMF parameters you use?

@MichaelLudeo
Copy link
Author

Here:

#define DEFAULT_VIDEO_BITRATE 1000000
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_USAGE, AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE, AMF_VIDEO_ENCODER_PROFILE_MAIN);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE_LEVEL, AMF_H264_LEVEL__5);

AMFRate frameRate = { fps, 1u };
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, frameRate);
Result = m_amfEncoder->SetProperty<AMFSize>(AMF_VIDEO_ENCODER_FRAMESIZE, { (amf_int32)m_Width, (amf_int32)m_Height });
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR);

if (rcMode == RateControlMode::CBR)
{
	m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FILLER_DATA_ENABLE, true);
}
//bitRate  = 5Mbps in this case
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, bitRate > 0u ? bitRate : 10 * DEFAULT_VIDEO_BITRATE);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, bitRate > 0u ? bitRate : DEFAULT_VIDEO_BITRATE);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED);
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, 0); // no B frames
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_MIN_QP, 0);//must be in range   0 <-> 51
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_MAX_QP, 0);//must be in range   0 <-> 51
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT, 16); 

if (codecUsageMode == AMF_VIDEO_ENCODER_USAGE_HIGH_QUALITY)
{
	Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, false);
}

if (codecUsageMode == AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY ||
	codecUsageMode == AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY)
{
	Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, true);
}
else
{
	Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, false);
}

if (idrInterval == 0)
{
	idrInterval = fps;
}
 //idrInterval = 5 in this case
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, idrInterval);
//inserts SPS/PPS with each IDR
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEADER_INSERTION_SPACING, idrInterval);
		 

@MikhailAMD
Copy link
Collaborator

Few things to start with:

  • Setting MAX_QP to 0 is not a good idea. I suggest to leave default for both MIN_QP and MAX_QP.
  • Setting PEAK_BITRATE to the same value as TARGET_BITRATE doesn't allow room for fluctuations which is needed for I-frames. Setting PEAK_BITRATE to 10xTARGET_BITRATE is is too much. Try PEAK_BITRATE = 1.3 TARGET_BITRATE, or 1.5.

@DimkaTsv
Copy link

Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_MIN_QP, 0);//must be in range 0 <-> 51
Result = m_amfEncoder->SetProperty(AMF_VIDEO_ENCODER_MAX_QP, 0);//must be in range 0 <-> 51

Ehm... Did you just set both min and max qp to 0 with this? Meaning it is forced to not do compression neither below, nor above CQP 0.
Also meaning that instead of doing encode with 1000000 bps (1000 kbps? Doesn't look like 5 mbps as well) it basically does encode with CQP 0 as it has "MAX_QP 0" flag enforced? I am surprised that your file size hadn't become completely monstruous.

Also... Why would you even decided to constrain QP for CBR encode? Isn't it directly opposite of what CBR supposed to represent? QP constraint has priority over bitrate management. Meaning that with min/max QP set your CBR encode will actually not be CBR.

@MichaelLudeo
Copy link
Author

MichaelLudeo commented Aug 21, 2024

I set QP to zero because I am not intending to use QP mode. I am using CBR. So these params are not supposed to impact the encoder at all. At least that's how it works in NVENC. That's also why I set the pick bitrate same as the target as I need it to be constant no matter what.
@DimkaTsv please read my code. I am not using QP mode so these params are not supposed to have any effect. The encoder produces quite reasonable size as long as I keep IDR period of 30 frames. But it becomes way to big when I insert IDR every 5th frame. Here I am working also on two other encoders with the same parameters: x264 and Nvidia based. Both produce slightly bigger file size.

Also... Why would you even decided to constrain QP for CBR encode? Isn't it directly opposite of what CBR supposed to represent? QP constraint has priority over bitrate management. Meaning that with min/max QP set your CBR encode will actually not be CBR.

This assumption is wrong. I decide to use CBR mode because I want the quality to be dictated by the bitrate. If I wanted it to be based on the QP I would use QP mode.
If AMF takes QP into consideration in CBR mode, then this is wrong. It is not supposed to happen.

@DimkaTsv
Copy link

DimkaTsv commented Aug 21, 2024

@DimkaTsv please read my code. I am not using QP mode so these params are not supposed to have any effect.
At least that's how it works in NVENC.

Sorry, Github was down, so i was forced to wait a bit.

It seems like you have some misunderstanding, AMF is not NVENC and that is not how it works with AMF. Frankly speaking NVENC also should work this way, and if it doesn't, then i have questions myself. QP is just "compression level" with extra steps. It doesn't matter if you use CQP or CBR, in the end compression is represented via QP. And in AMF min/max QP are not guidelines for bitrate that tell encoder to not use QP, but instead enforcing limiters that tell encoder what range of compression levels it can choose from. For example, those can be used in cases when you want quality to be at least on X level no matter what and how much bitrate it will take. By default MIN_QP is 0 and MAX_QP is 51.

CQP uses it's own parameter set, which is basically either single QP value for IPB frames, or 3 different QP values for I:P:B (or 2 for I:P, depends on codec). And CQP mode actually disables min/max QP parameters, so it could potentially use different QP values for I:P:B frames.

What do you think these parameters are meant for if they are "supposed" to be used with CQP? CQP is constant by description, and there is no place for min/max range. And CQP is only QP rate control besides QVBR (which, coincidentally is also controlled by QP option, even if separate from CQP, inverted and also ignores target bitrate). QVBR, on other hand does not ignore limits enforced by min/max QP.


Easy proof that QP is hard limiter, rather than guideline (also don't mind this --max-bitrate 800000, it was done just for purity of test):

Command (CBR with restricted QP): ./VCEEncC64.exe -i "C:\Users\-----\Videos\test sample\Big_Buck_Bunny_1080_10s_10MB.mp4" -o "Big_Buck_Bunny_1080_10s_10MB_processed.mp4" --codec avc --profile main --preset slow --vbr 10000 --max-bitrate 800000 --qp-min 0 --qp-max 0 --audio-copy
Output:

encoded 300 frames, 228.83 fps, 151989.05 kbps, 181.19 MB
encode time 0:00:01, CPU: 1.0%, GPU: 1.0%, VE: 89.0%
frame type IDR   1
frame type I     1,  avgQP  4.00,  total size    1.25 MB
frame type P   299,  avgQP  4.00,  total size  179.94 MB

As you can see, encoder just cannot compress anything below specific QP mark.

Command (Set QP_MAX to 8): ./VCEEncC64.exe -i "C:\Users\-----\Videos\test sample\Big_Buck_Bunny_1080_10s_10MB.mp4" -o "Big_Buck_Bunny_1080_10s_10MB_processed.mp4" --codec avc --profile main --preset slow --vbr 10000 --max-bitrate 800000 --qp-min 0 --qp-max 8 --audio-copy

Output:

encoded 300 frames, 278.29 fps, 102306.17 kbps, 121.96 MB
encode time 0:00:01, CPU: 0.0%, GPU: 1.0%, VE: 83.0%
frame type IDR   1
frame type I     1,  avgQP  8.00,  total size    0.99 MB
frame type P   299,  avgQP  8.00,  total size  120.97 MB

From here you can see that encoder tries to follow set bitrate, but that MAX_QP 8 hard limits it from doing so forcing it to encode everything at QP 8 as it is highest encoder allowed to do.

Command (Just CBR. I only removed min/max QP): ./VCEEncC64.exe -i "C:\Users\-----\Videos\test sample\Big_Buck_Bunny_1080_10s_10MB.mp4" -o "Big_Buck_Bunny_1080_10s_10MB_processed.mp4" --codec avc --profile main --preset slow --vbr 10000 --max-bitrate 800000 --audio-copy

Output:

encoded 300 frames, 310.88 fps, 10181.00 kbps, 12.14 MB
encode time 0:00:00, CPULoad: 0.0%
frame type IDR   1
frame type I     1,  avgQP  24.00,  total size   0.32 MB
frame type P   299,  avgQP  25.95,  total size  11.82 MB

Hmm... I also now know that there is another intenal limiter to how shallow QP can be for bitrate constraining methods, it seems. (QP 4 is limit, for CQP limit is QP 1). Never experimented with this beforehand. TIL.


The encoder produces quite reasonable size as long as I keep IDR period of 30 frames. But it becomes way to big when I insert IDR every 5th frame.

Will say though this is one is weird. Can provide samples of said source and encoded video?
IDR frames do contain significantly more data though, as IDR supposed to encode complete image and not relative difference. So it is probable reason. Is it 10 MB worth of data going from 30 frame GOP to 5 frame one? Maybe.
What is an issue in your case though is that your parameters make it so CBR does not follow bitrate. It should follow target even if you set GOP to 1 frame (aka every frame is IDR frame)!

@MichaelLudeo
Copy link
Author

What do you mean by "Command (Just CBR. I only removed min/max QP)" ? Remove - means set to defaults?
Because this looks like it is exactly what I need.

I grabbed Libx264 to verify your statement regarding the QP params impact in CBR mode and yes, you're right, I am getting even worse compression than on AMF with min/max QP set to 0. Default QP - 1500kb , Zero QP - 9433Kb. I will try to set the Max QP to 51 and see what we get.
I checked again my NVENC code and now I see that the reason why QP has no effect at all is because it is disabled. The API has separate params for enabling min and max QPs.

And thanks for the clarification on the QP purpose.

@DimkaTsv
Copy link

DimkaTsv commented Aug 21, 2024

What do you mean by "Command (Just CBR. I only removed min/max QP)" ? Remove - means set to defaults?
Because this looks like it is exactly what I need.

Yes, i just removed those arguments from command, meaning they weren't passed, so they were automatically set to defaults.

I grabbed Libx264 to verify your statement regarding the QP params impact in CBR mode and yes, you're right, I am getting even worse compression than on AMF with min/max QP set to 0.

And that's because either VCEEnc or AMF has additional inernal limit on MIN_QP option which does not allow you to do less than QP 4. Unless you do CQP, then QP 1 is very much possible.

@MichaelLudeo
Copy link
Author

Changed maxQP to 51, the file size is back to normal. Thanks for the assistance guys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants