简体   繁体   中英

FFmpeg - avcodec_receive_frame doesn't receive all frames and lose frames before receving frames

The vcodec_receive_frame function didn't receive the rest of the frames. I tested that there were totally 132 frames of the video and it only received 125 frames losing 7 frames at the END of the video . How can I get the lost frames back?

But something weird happened. As you can see the output from my MyDecode::receiveFrame() function. The code inside the block if (ret != 0){} executed first, but the lost frames are at the end of the video. So how could they come out first? What caused this happen?

MyDecode.cpp

AVFrame* MyDecode::receiveFrame()
{
    mux.lock();
    if (!codecCtx) {
        mux.unlock();
        return 0;
    }
    AVFrame* frame = av_frame_alloc();
    int ret = avcodec_receive_frame(codecCtx, frame);
    mux.unlock();
    if (ret != 0)
    {
        static int lost_frames = 1;
        std::cout << "Lost frames: " << lost_frames << std::endl;
        lost_frames += 1;
        av_frame_free(&frame);
        return nullptr;
    }
    std::cout << "Received frames: " << received_frame_num << std::endl;
    received_frame_num += 1;
    return frame;
}

bool MyDecode::sendPacket(AVPacket* packet)
{
    if (!packet || !packet->data || packet->size == 0)
        return false;
    mux.lock();
    if (!codecCtx) {
        mux.unlock();
        return false;
    }
    int ret = avcodec_send_packet(codecCtx, packet);
    mux.unlock();
    av_packet_free(&packet);
    if (ret != 0) {
        return false;
    }
    return true;
}

Console output

Total frames: 132
Lost frames: 1
Lost frames: 2
Lost frames: 3
Lost frames: 4
Lost frames: 5
Lost frames: 6
Lost frames: 7
Received frames: 1
Received frames: 2
Received frames: 3
................
Received frames: 125

UPDATE:

MyDemux.cpp

AVPacket* MyDemux::readFrame()
{
    mux.lock();
    if (!formatCtx) {
        std::cout << "formaetCtx is null" << std::endl;
        mux.unlock();
        return nullptr;
    }
    AVPacket* packet = av_packet_alloc();
    if (!packet) {
        std::cout << "packet is null" << std::endl;
        mux.unlock();
        return nullptr;
    }

    int ret = av_read_frame(formatCtx, packet);
    if (ret != 0) {
        while (true) {
            av_read_frame(formatCtx, nullptr);
        }
        mux.unlock();
        av_packet_free(&packet);
        av_packet_unref(packet);
        return nullptr;
    }
    media_type = packet->stream_index;
    mux.unlock();
    return packet;
}

main.cpp

while (true) {
            AVPacket* pkt = demux.readFrame();
            if (demux.get_media_type() == 0) {
                AVFrame* frame = video_decode.receiveFrame();
                videoWidget->paintFrame(frame);
            }
            else if (demux.get_media_type() == 1) {
            }
            if (!pkt) {
                std::cout << "to break" << std::endl;
                break;
            }
        }

You have to send NULL pkts to the decoder to drain all pending frames.

From avcodec.h

End of stream situations. These require "flushing" (aka draining) the codec, as the codec might buffer multiple frames or packets internally for performance or out of necessity (consider B-frames).
This is handled as follows:
- Instead of valid input, send NULL to the avcodec_send_packet() (decoding) or avcodec_send_frame() (encoding) functions. This will enter draining mode.
- Call avcodec_receive_frame() (decoding) or avcodec_receive_packet() (encoding) in a loop until AVERROR_EOF is returned. The functions will not return AVERROR(EAGAIN), unless you forgot to enter draining mode. - Before decoding can be resumed again, the codec has to be reset with avcodec_flush_buffers().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM