gpt4 book ai didi

c++ - 替换 av_read_frame() 以减少延迟

转载 作者:太空狗 更新时间:2023-10-29 22:55:40 35 4
gpt4 key购买 nike

我正在使用 ffmpeg 实现一个(非常)低延迟的视频流 C++ 应用程序。客户端收到使用 x264 零延迟预设编码的视频,因此无需缓冲。如所述here ,如果您使用 av_read_frame() 读取编码视频流的数据包,由于 ffmpeg 中的内部缓冲,您将始终至少有一帧延迟。因此,当我在第 n+1 帧发送到客户端后调用 av_read_frame() 时,该函数将返回第 n 帧。

通过设置 AVFormatContext 标志 AVFMT_FLAG_NOPARSE 摆脱这种缓冲 | source 中建议的 AVFMT_FLAG_NOFILLIN禁用数据包解析并因此中断解码,如 source 中所述.

因此,我正在编写自己的数据包接收器和解析器。首先,这里是使用 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
//…
}
}

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

如有任何提示,我将不胜感激。

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

编辑 2: 已解决 here由 rom1v,如他对这个问题的评论中所写。

最佳答案

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

关于c++ - 替换 av_read_frame() 以减少延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50682518/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com