Как пропустить кадры при декодировании потока H264? - PullRequest
0 голосов
/ 13 сентября 2018

Я использую FFMPEG для декодирования потока RTSP H264 (или H265).

В моей системе 2 программы: Сервер и Клиент

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

Я реализовал, и все работало нормально,но есть случай, чтобы моя система работала плохо.То есть, когда интернет с сервера-клиента работает медленно, кадры не могут передавать клиенту в режиме реального времени.

В настоящее время я имею дело с этой проблемой, пропуская некоторые кадры (не отправляемые клиенту), когда в очереди достигнут предел количества.Ниже приведен мой сводный код

//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 Render кадров не нормально?

А кто-нибудь позвонит и покажет мне правильный алгоритм пропуска кадров в моем случае?

Спасибо большое!

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

"Мой вопрос заключается в фокусировке в строке кода SkipThisFrame ***, этот алгоритм непрерывно пропускает кадр , так что это может привести к тому, что декодер на клиенте неожиданно произойдет ошибка или сбой?"

Одна вещь, которую я заметил, неверна ...
Вашим операторам While(true) также требуется break; для остановки, в противном случае они будут выполняться вечно, блокируя другие функции и вызывая системуврезатьсяПодумайте об этом, вы говорите «Пока цикл верен, выполняйте инструкции XYZ» , но вы никогда не говорите, когда нужно остановиться ( например: break из этого цикла while, чтобы выполнить следующие инструкции).Компьютер зависает, выполняя только первую 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;
}

Надеюсь, это поможет.Сообщите мне о результатах / ошибках.

0 голосов
/ 13 сентября 2018

У меня есть краткое чтение документа AVPacket, там написано:

Для видео оно обычно должно содержать один сжатый кадр.

Теоретически вы не можетепропускайте кадры для сжатого видеопотока, так как большинство кадров не содержат полной информации об изображении этого кадра, а содержат только изменения по сравнению с некоторыми предыдущими кадрами.Поэтому, если вы пропустите кадр, вероятно, что многие конечные декодированные кадры не будут содержать правильный результат (пока следующий ключевой кадр не очистит все изображение).

...