ffmpeg solves the frame loss problem: there are still a few frames of data cached in the decoder and need to be taken out

foreword
       

Recently, I am learning how to use ffmpeg to analyze video. The method of searching on the Internet is similar. I directly talk about the decoding functions avcodec_send_packet and avcodec_receive_frame. These two are together and must appear at the same time. I read video files through sub-threads and keep reading Put the extracted packets into videoQueue or audioQueue, and then use SDL to create videoThread to continuously take out packets from the video queue and send them to the decoder, that is, use avcodec_send_packet to send the packets to the decoder, and use avcodec_receive_frame to parse the Frame from ffmpeg Take it out, but look at the time of the current frame through the progress bar or directly print the decoded Frame->pts, there will always be a few frames less at the end, very depressing

1. Reasons for frame loss
There is a buffer queue inside the decoder. At the beginning, it will store a few frames of data first, and then start to output valid frames. So in the end, there are still a few frames of data in the decoder. Depending on the size of the video, there are still remaining frames in the decoder. The number of packets is not the same, so there are still a few frames that are not output at the end, and frame loss occurs.

2. Solution
The first idea is to search online how to solve frame loss, but there are few, and there is no source code, especially the frame loss solutions of ffmpeg's avcodec_send_packet and avcodec_receive_frame, so I turned to the official document and finally found A little clue, the following figure is found in avcode.h, which is also the header file declared by these two functions

The above shows that out of need or to improve performance, the codec may cache a few frames of data at the end, and the codec needs to be refreshed, and it also provides ideas

1. Send an empty packet into avcodec_send_packet.
As the idea provided by the official document above, you need to send an empty packet into the decoder. The code is as follows

packet->data = nullptr;
packet->size = 0;
avcodec_send_packet(pVideoCodecCtx, packet);


2. After calling avcodec_receive_frame
to send an empty packet, call avcodec_receive_frame to take out the decoded frame until avcodec_receive_frame returns AVERROR_EOF. The code is as follows

/**队列里取数据**/
        if (packet_queue_get(&pVideosState->videoQueue, packet, 0) <= 0)
        {
 
            if (pVideosState->readFinished)
            {
                /**队列里面没有数据了且读取完毕了**/
                while(1)
                {
                    packet->data = nullptr;
                    packet->size = 0;
                    avcodec_send_packet(pVideoCodecCtx, packet);
                    
                    ret = avcodec_receive_frame(pVideoCodecCtx, pFrame);
                    if(ret == AVERROR_EOF)
                        break;
                    
                    //进行你所需的一系列操作
                }
                //这个循环跳出后就全部取出解码器缓存的数据了
            }

Guess you like

Origin blog.csdn.net/xionglifei2014/article/details/124951833