繁体   English   中英

替换av_read_frame()以减少延迟

[英]Replacing av_read_frame() to reduce delay

我正在使用ffmpeg实现(非常)低延迟视频流C ++应用程序。 客户端接收使用x264的zerolatency预设编码的视频,因此无需缓冲。 如上所述这里 ,如果你使用av_read_frame()来读取编码视频流的数据包,你将永远有,因为在做的ffmpeg内部缓冲的至少一个帧的延迟。 因此,当我将第n + 1帧发送到客户端后调用av_read_frame ()时,该函数将返回第n帧。

通过设置AVFormatContext标志AVFMT_FLAG_NOPARSE |来摆脱这种缓冲 如所建议的AVFMT_FLAG_NOFILLIN 禁用分组解析,并因此中断解码,如在所提到的

因此,我正在编写自己的数据包接收器和解析器。 首先,以下是使用av_read_frame ()的工作解决方案的相关步骤(包括一个帧延迟):

AVFormatContext *fctx;
AVCodecContext *cctx;
AVPacket *pkt;
AVFrame *frm;

//Initialization of AV structures
//…

//Main Loop
while(true){

    //Receive packet
    av_read_frame(fctx, pkt);

    //Decode:
    avcodec_send_packet(cctx, pkt);
    avcodec_receive_frame(cctx, frm);

    //Display frame
    //…
}

以下是我的解决方案,它模仿av_read_frame ()的行为,只要我可以重现它。 我能够跟踪av_read_frame ()的源代码到ff_read_packet (),但我找不到AVInputformat.read_packet ()的源代码。

int tcpsocket;
AVCodecContext *cctx;
AVPacket *pkt;
AVFrame *frm;
uint8_t recvbuf[(int)10e5];
memset(recvbuf,0,10e5);
int pos = 0;

AVCodecParserContext * parser = av_parser_init(AV_CODEC_ID_H264);
parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
parser->flags |= PARSER_FLAG_USE_CODEC_TS;

//Initialization of AV structures and the tcpsocket
//…

//Main Loop
while(true){

    //Receive packet
    int length = read(tcpsocket, recvbuf, 10e5);
    if (length >= 0) {

        //Creating temporary packet
        AVPacket * tempPacket = new AVPacket;
        av_init_packet(tempPacket);
        av_new_packet(tempPacket, length);
        memcpy(tempPacket->data, recvbuf, length);
        tempPacket->pos = pos;
        pos += length;
        memset(recvbuf,0,length);

        //Parsing temporary packet into pkt
        av_init_packet(pkt);
        av_parser_parse2(parser, cctx,
            &(pkt->data), &(pkt->size),
            tempPacket->data, tempPacket->size,
            tempPacket->pts, tempPacket->dts, tempPacket->pos
            );

        pkt->pts = parser->pts;
        pkt->dts = parser->dts;
        pkt->pos = parser->pos;

        //Set keyframe flag
        if (parser->key_frame == 1 ||
            (parser->key_frame == -1 &&
            parser->pict_type == AV_PICTURE_TYPE_I))
            pkt->flags |= AV_PKT_FLAG_KEY;
        if (parser->key_frame == -1 && parser->pict_type == AV_PICTURE_TYPE_NONE && (pkt->flags & AV_PKT_FLAG_KEY))
            pkt->flags |= AV_PKT_FLAG_KEY;
        pkt->duration = 96000; //Same result as in av_read_frame()

        //Decode:
        avcodec_send_packet(cctx, pkt);
        avcodec_receive_frame(cctx, frm);
        //Display frame
        //…
    }
}

我在两个解决方案中的avcodec_send_packet ()之前检查了生成的数据包( pkt )的字段 就我所说的而言,它们完全相同。 唯一的区别可能是pkt-> data的实际内容。 我的解决方案很好地解码了I帧,但是P帧中的引用似乎被破坏,导致严重的伪像和错误消息,例如“无效级别前缀”,“解码MB xx时出错”等。

我会非常感谢任何提示。

PS:我暂时开发了一种解决方法:在视频服务器中,在发送包含帧的编码数据的数据包之后,我发送一个虚拟数据包,其中只包含标记数据包开头和结尾的分隔符。 这样,我通过av_read_frame ()推送实际的视频数据帧。 我在av_frame_read ()之后立即丢弃虚拟数据包。

av_parser_parse2 ()不会一次性消耗你的tempPacket 您必须在另一个循环中调用它并检查其返回值,就像在API文档中一样

暂无
暂无

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

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