在解码H264流时如何跳过帧?

时间:2018-09-13 04:51:44

标签: c++ ffmpeg decode h.264

我正在使用FFMPEG解码H264(或H265)RTSP流。

我的系统有2个软件:服务器和客户端

Server: Read frames from RTSP stream --> Forward frames to Client    
Client: Receive frames from Server --> Decode --> Render

我已经实现了并且工作正常,但是有一种情况使我的系统无法正常工作。即从服务器-客户端的Internet速度较慢时,帧无法实时传输到客户端。

目前,我通过在达到队列数限制时跳过一些帧(不发送给客户端)来解决此问题。以下是我的摘要代码

//At Server Software (include 2 threads A and B)
//Thread A: Read AVPacket and forward to Client
while(true)
{
    AVPacket packet;
    av_init_packet(&packet);
    packet.size = 0;
    packet.data = NULL;
    int ret = AVERROR(EAGAIN);
    while (AVERROR(EAGAIN) == ret)
        ret = av_read_frame(pFormatCtx, &packet);
    if(packet.size > 0)
    {
        if(mySendQueue.count < 120) //limit 120 packet in queue
            mySendQueue.Enqueue(packet); ////Thread B will read from this queue, to send packets to Client via TCP socket
        else
            ;//SkipThisFrame ***: No send
    }
}
//Thread B: Send To Client via TCP Socket
While(true)
{
    AVPacket packet;
    if(mySendQueue.Dequeue(packet))
    {
        SendPacketToClient(packet);
    }
}

//At Server Software : Receive AVPacket from Server --> Decode --> Render
While(true)
{
    AVPacket packet;
    AVFrame frame;
    ReadPacketFromServer(packet);
    if (av_decode_asyn(pCodecCtx, &frame, &frameFinished, &packet) == RS_OK)
    {
        if (frameFinished)
        {
            RenderFrame(frame);
        }
    }           
}
UINT32 __clrcall av_decode_asyn(AVCodecContext *pCodecCtx, AVFrame *frame, int *frameFinished, AVPacket *packet)
{
    int ret = -1;
    *frameFinished = 0;
    if (packet) 
    {
        ret = avcodec_send_packet(pCodecCtx, packet);
        // In particular, we don't expect AVERROR(EAGAIN), because we read all
        // decoded frames with avcodec_receive_frame() until done.
        if (ret < 0 && ret != AVERROR_EOF)
            return RS_NOT_OK;
    }

    ret = avcodec_receive_frame(pCodecCtx, frame);
    if (ret < 0 && ret != AVERROR(EAGAIN))
    {
        return RS_NOT_OK;
    }
    if (ret >= 0)
        *frameFinished = 1;

    return RS_OK;
}

我的问题是集中在代码SkipThisFrame ***的行中,此算法连续跳过帧,因此它可能会使Client上的解码器意外发生错误或崩溃?

然后像这样跳过帧时,使客户端渲染帧不正常吗?

有人打电话给我,说明在我的情况下跳过帧的正确算法?

非常感谢您!

2 个答案:

答案 0 :(得分:2)

我对AVPacket的文档进行了简短的阅读,内容为:

  

对于视频,通常应包含一个压缩帧。

理论上,您不能跳过压缩视频流的帧,因为大多数帧不包含有关该帧图像的完整信息,而仅包含与某些先前帧相比的更改。因此,如果您跳过一个帧,则很多尾随解码帧可能不会包含正确的结果(直到下一个关键帧刷新整个图像)。

答案 1 :(得分:1)

  

“”我的问题是代码SkipThisFrame ***的这一行重点   连续跳过帧,因此可能会使客户端上的解码器出现   意外的错误或崩溃?”

我发现一件事是错误的...
您的While(true)语句也需要break;才能停止,否则它们将永远运行,从而阻塞了其他功能并导致系统崩溃。想一想,您说“虽然循环确实是XYZ指令,但确实如此” ,但您从不说何时停止( eg: break做下一个指示)。计算机卡住了,只能执行第一个While循环,并且还要重复执行无限循环...

尝试这样设置:

//At Server Software (include 2 threads A and B)
//Thread A: Read AVPacket and forward to Client
while(true)
{
    AVPacket packet;
    av_init_packet(&packet);
    packet.size = 0;
    packet.data = NULL;
    int ret = AVERROR(EAGAIN);

    while (AVERROR(EAGAIN) == ret) { ret = av_read_frame(pFormatCtx, &packet); }

    if(packet.size > 0)
    {
        if(mySendQueue.count < 120) //limit 120 packet in queue
        {
            mySendQueue.Enqueue(packet); ////Thread B will read from this queue, to send packets to Client via TCP socket
        }
        //else {  } //no need for ELSE if doing nothing... //SkipThisFrame ***: No send
    }

    break; //stop this part and move to "Thead B"
}

//Thread B: Send To Client via TCP Socket
While(true)
{
    AVPacket packet;
    if( mySendQueue.Dequeue(packet) )
    { SendPacketToClient(packet); break; }
}

//At Server Software : Receive AVPacket from Server --> Decode --> Render
While(true)
{
    AVPacket packet; AVFrame frame;
    ReadPacketFromServer(packet);
    if (av_decode_asyn(pCodecCtx, &frame, &frameFinished, &packet) == RS_OK)
    { 
        if (frameFinished) { RenderFrame(frame);  break; }
    }           
}

UINT32 __clrcall av_decode_asyn(AVCodecContext *pCodecCtx, AVFrame *frame, int *frameFinished, AVPacket *packet)
{
    int ret = -1;
    *frameFinished = 0;
    if (packet) 
    {
        ret = avcodec_send_packet(pCodecCtx, packet);
        // In particular, we don't expect AVERROR(EAGAIN), because we read all
        // decoded frames with avcodec_receive_frame() until done.
        if (ret < 0 && ret != AVERROR_EOF)
            return RS_NOT_OK;
    }

    ret = avcodec_receive_frame(pCodecCtx, frame);
    if (ret < 0 && ret != AVERROR(EAGAIN))
    {
        return RS_NOT_OK;
    }
    if (ret >= 0)
        *frameFinished = 1;

    return RS_OK;
}

希望有帮助。让我知道结果/错误。