简体   繁体   English

FFmpeg - avcodec_receive_frame 不接收所有帧并在接收帧之前丢失帧

[英]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. vcodec_receive_frame函数没有收到其余的帧。 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 .我测试了视频总共有 132 帧,它只收到了 125 帧,在视频结束时丢失了 7 帧 How can I get the lost frames back?我怎样才能找回丢失的帧?

But something weird happened.但是奇怪的事情发生了。 As you can see the output from my MyDecode::receiveFrame() function.如您所见,我的MyDecode::receiveFrame()函数的输出。 The code inside the block if (ret != 0){} executed first, but the lost frames are at the end of the video. if (ret != 0){}块内的代码首先执行,但丢失的帧在视频的末尾。 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我的Demux.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.您必须向解码器发送 NULL pkts 以清除所有待处理的帧。

From avcodec.h来自 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).这些需要“刷新”(又名耗尽)编解码器,因为编解码器可能会在内部缓冲多个帧或数据包以提高性能或出于必要(考虑 B 帧)。
This is handled as follows:处理如下:
- Instead of valid input, send NULL to the avcodec_send_packet() (decoding) or avcodec_send_frame() (encoding) functions. - 将 NULL 发送到 avcodec_send_packet()(解码)或 avcodec_send_frame()(编码)函数,而不是有效输入。 This will enter draining mode.这将进入排水模式。
- Call avcodec_receive_frame() (decoding) or avcodec_receive_packet() (encoding) in a loop until AVERROR_EOF is returned. - 在循环中调用 avcodec_receive_frame()(解码)或 avcodec_receive_packet()(编码),直到返回 AVERROR_EOF。 The functions will not return AVERROR(EAGAIN), unless you forgot to enter draining mode.这些函数不会返回 AVERROR(EAGAIN),除非您忘记进入排水模式。 - Before decoding can be resumed again, the codec has to be reset with avcodec_flush_buffers(). - 在可以再次恢复解码之前,必须使用 avcodec_flush_buffers() 重置编解码器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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