GDR in H265 encoding not working as expected

Hi everyone,

I’m using Jetpack 4.5.1 on an Xavier AGX and I would like to enable GDR for H265 encoding. I’m testing the 01_video_encode sample with the following configuration:

./video_encode input.yuv 1920 1080 H265 output.h265 --egdr -gdrf gdr_params.txt --input-metadata

where gdr_params.txt has “1 5”, however I saw in this post that for h265 the gdr_num_frames parameter seems to be ignored, and that’s what is happening to me, it always splits the I frame into 4 P frames.

Then I saw in the latest answer of that same post that you had a fix on Jetpack 4.6 so I tried the same configuration and this time it splits the I frame into the 5 P frames as the configuration file says. However, it only works during one cycle, the first 5 P frames have the refresh wave of the I frame in each of them but then the following frames don’t. I noticed that in JP 4.5.1 it used to ignore the gdr_num_frames but at least the refresh wave repeated periodically every 4 frames.

Here’s a video that shows the behavior (left JP 4.5.1, right JP 4.6)

Test in 4.6:

I made a slight modification on the sample source code to set the GDR settings to the encoder every time the cycle ends, and it works as expected (at least with 5), however I’m not sure if this is the proper way to configure it since in JP 4.5.1 it was done automatically.

This is the modification:

diff --git a/video_encode_main_org.cpp b/video_encode_main_mod.cpp
index a74831b..c7bd953 100644
--- a/video_encode_main_org.cpp
+++ b/video_encode_main_mod.cpp
@@ -1258,7 +1258,8 @@ static int encoder_proc_blocking(context_t &ctx, bool eos)
                     if (ctx.gdr_start_frame_number == 0xFFFFFFFF)
                         populate_gdr_Param(ctx.gdr_Param_file, &ctx.gdr_start_frame_number,
                                     &ctx.gdr_num_frames);
-                    if (ctx.input_frames_queued_count == ctx.gdr_start_frame_number)
+                   // if (ctx.input_frames_queued_count == ctx.gdr_start_frame_number)
+                   if ((ctx.input_frames_queued_count % ctx.gdr_num_frames) - ctx.gdr_start_frame_number == 0)
                     {
                         ctx.gdr_out_frame_number = ctx.gdr_start_frame_number;
                         VEnc_gdr_params.nGDRFrames = ctx.gdr_num_frames;
@@ -2028,7 +2029,8 @@ encode_proc(context_t& ctx, int argc, char *argv[])
                     if (ctx.gdr_start_frame_number == 0xFFFFFFFF)
                         populate_gdr_Param(ctx.gdr_Param_file, &ctx.gdr_start_frame_number,
                                     &ctx.gdr_num_frames);
-                    if (ctx.input_frames_queued_count == ctx.gdr_start_frame_number)
+                    // if (ctx.input_frames_queued_count == ctx.gdr_start_frame_number)
+                    if ((ctx.input_frames_queued_count % ctx.gdr_num_frames) - ctx.gdr_start_frame_number == 0)
                     {
                         ctx.gdr_out_frame_number = ctx.gdr_start_frame_number;
                         VEnc_gdr_params.nGDRFrames = ctx.gdr_num_frames;

So, my questions are:

  1. Is this change between Jetpack versions in the cycle repeating expected?
  2. Is the modification I made in the sample app the proper way to enable the repeating? If not, could you point to the correct configuration.
  3. Would it be possible to make it work completely on Jetpack 4.5.1?

Thanks in advance!

Hi,
Please check
Jetson Nano multimedia API example 01_encoder with gdr - #16 by DaneLLL

The issue is fixed in Jetpack 4.6. Could you upgrade and try?

Hi @DaneLLL thanks for your reply.

However, I already tested it in JP 4.6 and still it does not work as expected. It configures the Correct gdr_num_frames but it just works the first gdr_num_frames after the start_frame, then it does not repeat again.

Please reread my question, there are the details of my test in JP 4.6.

Hi,
It looks to be a new issue. So on Jetpack 4.6, you only see first I frame is split into multiple P frames. The rest I frames are encoded only inside the frame. Is this correct?

Yes, you can see on the video I posted that on the right side which is with JP 4.6 the blue bar represents the piece of I frame in every P frame, it looks well until the frame 5 where it shows the I part on the end and after that it does not repeat anymore the cycle (which it does on the left side with JP 4.5)

Hi,
Please help try this:

  1. Apply the if condition to 01_video_encode:
//if (ctx.input_frames_queued_count == ctx.gdr_start_frame_number)
if ((ctx.input_frames_queued_count % ctx.gdr_start_frame_number) == 0 && ctx.input_frames_queued_count > 0)
  1. Run
01_video_encode$ cat gdr_params.txt
15 6

01_video_encode$ ./video_encode 720.yuv 1280 720 H265 test.h265 --egdr -gdrf gdr_params.txt -idri 1000 -ifi 1000 --input-metadata

We can see I frames are generated periodically(every 15 frames). Would need your help to check if the bitstream is expected.

Hi,

I applied your changes and your example behaves as expected. At every 15 frames it starts distributing the I frame into the following 6 frames. Here’s a video:

However, if I set the gdr_params.txt to “15 14” after every 15 frames it only distributes the I frame onto the next 12 frames, not 14, and then restarts until the next multiple of 15. Here’s another video that shows this behavior:

Do you know if there’s a limitation regarding the gdr_num_frames that can be configured?

Hi,
Please check the explanation from our team:

We do GDR intra refresh for complete CTB row at a time.

If any client specifies GDR num frames, then we update it internally based on HW restrictions as it does intra refresh for full CTB row.
Our encoder is using CTB size of 32x32. In case of 1280x720, we have 23 CTB rows.

If we divide, 23 CTB rows by 15 then we get around ~1.7 and that is set to it’s ceiling value. i.e 2.

So, we again calculate GDR frame number = 23/2 = 12.

That’s why GDR refresh is extend to 12 frames only.

Thanks @DaneLLL , your answer really helped me to understand what was going on.

Hi,
an little bit off-topic question:
The GDR capability, splits the IDR frames as well as it splits I frames? or one should send IDR frames once in a while (in order for the decoder to start playing)?
thanks