简体   繁体   English

解码H264流时如何跳过帧?

[英]How to Skip frames while decoding H264 stream?

I'm using FFMPEG to decode H264 (or H265) RTSP Stream. 我正在使用FFMPEG解码H264(或H265)RTSP流。

My system have 2 software: Server and Client 我的系统有2个软件:服务器和客户端

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

I have implemented and it worked ok, but there is a case make my system work not good. 我已经实现了并且工作正常,但是有一种情况使我的系统无法正常工作。 That is when internet from Server - Client is slow, frames can not transfer real-time to Client. 即从服务器-客户端的Internet速度较慢时,帧无法实时传输到客户端。

In present, I deal with this issue by Skip some frames (not send to Client) when the Queue is reached limit of count. 目前,当队列达到计数限制时,我会跳过一些帧(不发送给客户端)来解决此问题。 The following is my summary code 以下是我的摘要代码

//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;
}

My question is focus in line of code SkipThisFrame *** , this algorithm skip frame continuously, so it maybe make the decoder on Client occur unexpectedly error or Crash? 我的问题是集中在代码SkipThisFrame ***行中,此算法连续跳过帧,因此它可能会使Client上的解码器意外发生错误或崩溃?

And when skip frame like that, make Client Render frames is not normally? 当像这样跳过帧时,使客户端渲染帧不正常吗?

And someone call show me the proper algorithm to skip frames in my case? 有人打电话给我看看在我的情况下跳过帧的正确算法?

Thank you very much! 非常感谢你!

I have a brief read on doc of AVPacket , it says: 我简要阅读了AVPacket文档,其中说:

For video, it should typically contain one compressed frame. 对于视频,通常应包含一个压缩帧。

Theoretically you cannot skip frames for a compressed video stream, as most frames do not contain complete information about that frame's image, but only contain changes compared with some previous frames. 从理论上讲,您不能跳过压缩视频流的帧,因为大多数帧不包含有关该帧图像的完整信息,而仅包含与某些先前帧相比的更改。 So if you skip a frame, it is probable that many trailing decoded frames won't contain correct result (until next key frame flushes whole image). 因此,如果跳过一帧,则许多尾随解码帧可能不会包含正确的结果(直到下一个关键帧刷新整个图像)。

"My question is focus in line of code SkipThisFrame *** , this algorithm skip frame continuously , so it maybe make the decoder on Client occur unexpectedly error or Crash?" “我的问题是集中在代码SkipThisFrame ***行中,此算法连续跳过帧 ,因此它可能会使Client上的解码器意外发生错误或崩溃?”

One thing I notice is wrong... 我注意到的一件事是错误的...
Your While(true) statements also need a break; 您的While(true)语句也需要break; to stop, otherwise they will run forever, blocking other functions and causing the system to crash. 停止,否则它们将永远运行,从而阻塞其他功能并导致系统崩溃。 Think about it, you say "While the loop is true do XYZ instructions" but you never say when to stop ( eg: break out of this While loop to do next instructions). 想一想,您说“虽然循环是正确的,但要执行XYZ指令”,但您从不说何时停止( 例如: break此While循环以执行下一条指令)。 Computer is stuck doing first While loop only and also repeating that to infinity... 计算机被困在仅首先执行While循环的过程中,并且也将其重复到无穷大……

Try setting up like this: 尝试像这样设置:

//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;
}

Hope it helps. 希望能帮助到你。 Let me know of results / errors. 让我知道结果/错误。

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

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