I am using the Video Codec SDK V12.2.
Here is my setup:
- Main thread fetches input data into encoder which runs in async mode( I am on windows). The encode buffer in use pushed into encode queue.
- Download thread pull an encode buffer from the encode queue and locks on for bitstream download. Dowloads and saves the video frame into file.
I am running in CBR mode, I,P frames only. IDR frame inserted at GOP size intervals.
It all works wonderfully till the moment when I need to finish the session.
What i have been doing till today is to perform flushing by submitting one of the available encode buffers (same structure also contains input resource, completion event and output bitstream buffer pointer) for encoding but without populating all the fields of NV_ENC_PIC_PARAMS.
Here is an example:
NV_ENC_PIC_PARAMS picParams = { NV_ENC_PIC_PARAMS_VER };
picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
picParams.completionEvent = dummyEncodeData.completionEvent;
NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams));
Then, once the download thread has no more encode buffers in queue to process, it terminates and the encoder is destroyed. But then I noticed that sometime video length doesn’t always mach the expected length. So for example encoding 120 frames at 30 fps produces a length of 3.6999 or 3.967 and sometimes event 3.1…
I read the docs again and found this:
Notifying the End of Input Stream
To notify the end of input stream, the client must call
NvEncEncodePicture
with the flagNV_ENC_PIC_PARAMS:: encodePicFlags
set toNV_ENC_FLAGS_EOS
and all other members ofNV_ENC_PIC_PARAMS
set to 0. No input buffer is required while callingNvEncEncodePicture
for EOS notification.
But in nvidia encoding samples they do use completion events and other fields. The reuse the available encode buffers for flushing as well.
I tried to drain the encoder after signaling flush, something I haven’t done before. And I see it actually spits 2 or 3 more frames but those frames contain NV_ENC_LOCK_BITSTREAM::frameIdx and timestamps of already previously encoded frames. Though the data size differs. That frame is of type P and I receive it after all other 120 frames have been already downloaded with the frame 120th being IDR. So why encoder spits after flush that another frame that begins with P? Also why it contains the old frameIdx which is several frames behind the last frame?
Also, if I submit a flush command with no completion event set, the waiting object is waiting for the whole timeout I set ,which is 20 seconds. Should I omit the waiting after flush?
Should I repeatedly attempt downloading of packets after flush has been signaled until there is not data left? If I am getting extra 2-4 packets after flushing,when I already have got all 120 frames downloaded, is that expected? The video length becomes even shorter in this case than it was before getting those extra frames. So I have quite a few questions here none of which is addressed in the documentation.
Would be great if someone from Nvidia could provide a detailed pseudo code on how to finish an encoding session in async mode and getting all the data from the encoder before termination. The docs and code samples provide no clear answers in that area.
Thanks.
UPDATE:
Surprisingly, if I do no flushing, but just make sure my encode buffer queue has been drained on the download thread end, the resulting video has a correct length. So maybe I need no flushing at all if I use a queue to track the encode buffers lifetime?