当前位置:首页 » 《随便一记》 » 正文

ffmpeg花屏解决(修改源码,丢弃不完整帧)

24 人参与  2022年07月22日 11:54  分类 : 《随便一记》  评论

点击全文阅读


linux下模拟丢帧的命令,因为帧之间的参考关系,实测如果是1%几乎没有完好的帧。

sudo tc qdisc add dev enp0s31f6 root netem loss 0.1%

删除上面的设置

sudo tc qdisc del dev enp0s31f6 root

在头文件libavformat/avformat.h中av_read_frame函数后添加av_deviser_flag函数:

//extern int deviser_flag;int av_read_frame(AVFormatContext *s, AVPacket *pkt);int av_deviser_flag();

在头文件libavformat/utils.c ff_read_packet函数前添加int deviser_flag = 666666;

int deviser_flag = 666666;int av_deviser_flag(){    return deviser_flag;}int ff_read_packet(AVFormatContext *s, AVPacket *pkt)

同样在这个头文件中,ff_read_packet函数内添加deviser_flag = 666666;,这个函数是被av_read_frame函数调用的,可以看出这个函数内循环读取pkt,并对pkt处理。

int ff_read_packet(AVFormatContext *s, AVPacket *pkt){    int ret, i, err;    AVStream *st;    pkt->data = NULL;    pkt->size = 0;    av_init_packet(pkt);    for (;;) {        deviser_flag = 666666;        AVPacketList *pktl = s->internal->raw_packet_buffer;        const AVPacket *pkt1;

同样在这个头文件中,ff_read_packet函数内添加deviser_flag = 111111;
注意看这段函数检测包是否完整,也就是AV_PKT_FLAG_CORRUPT这个标记。还有一个标记可以了解一下AVFMT_FLAG_DISCARD_CORRUPT,这个标记是在av_dict_set(&options, “fflags”, “discardcorrupt”, 0);时添加到流的,这段代码的意思如果丢包了,并且添加了discardcorrupt的fflags那么这个包直接丢弃,不会被av_read_frame函数取出来。实测下面的pkt只包含一帧视频帧。用命令行设置的话,就是-fflags discardcorrupt。

if (pkt->flags & AV_PKT_FLAG_CORRUPT) {            av_log(s, AV_LOG_WARNING,                   "Packet corrupt (stream = %d, dts = %s)",                   pkt->stream_index, av_ts2str(pkt->dts));                deviser_flag = 111111;            if (s->flags & AVFMT_FLAG_DISCARD_CORRUPT) {                av_log(s, AV_LOG_WARNING, ", dropping it.\n");                av_packet_unref(pkt);                continue;            }            av_log(s, AV_LOG_WARNING, ".\n");        }

因此在我们的代码中在av_read_frame函数后:
if ((av_deviser_flag()) == 111111)
{
//丢弃这帧视频,如果是参考帧(I和P帧),后续的gop帧都要丢掉,直到下个I帧到来。如果是B帧的话,直接丢弃就好。
}

这样再解码测试,还有花屏,看到ffmpeg报的错误是解码错误,推测这是因为不是所有的不完整帧ffmpeg都能检测出来,导致一些不完整帧解码时出错导致。
根据出错的地方打印日志:
在AVFrame结构体中有这个一个标志,专门记录这一帧在解码时出的错误,并不是就解码出错这帧就解不出来了,和其正常帧一样出来,只是做了记录。

/** * decode error flags of the frame, set to a combination of * FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there * were errors during the decoding. * - encoding: unused * - decoding: set by libavcodec, read by user. */int decode_error_flags;#define FF_DECODE_ERROR_INVALID_BITSTREAM   1#define FF_DECODE_ERROR_MISSING_REFERENCE   2#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE  4#define FF_DECODE_ERROR_DECODE_SLICES       8

经过实测,不仅这几个,decode_error_flags为12时也是解码出错,正确时decode_error_flags为0。
这些记录标志的代码在下面文件中。
libavcodec/h264_slice.c
libavcodec/h264_parse.c
libavcodec/error_resilience.c
libavcodec/h264_cabac.c

打印日志发现每次花屏都是I帧解码出错,并且这时decode_error_flags为4或12,因此这个I帧和后面的gop都需要丢掉。

if(ic->streams[video_index]->codec->codec_id == AV_CODEC_ID_H264 && this->frame_v->key_frame && (this->frame_v->decode_error_flags == 12 || this->frame_v->decode_error_flags == 4)){ //丢帧,直到下个无错的I帧到,这里是h264解码。}

重新编译ffmpeg,到这里测试发现视频虽然卡顿但不会花屏了。

decode_error_flags是ffmepg已经记录的错误,如果你想在源码中做标记的话,可以使用下面的方法。
在libavcodec/avcodec.h头文件中添加:

extern int deviser_1;int av_deviser_1();int av_set_deviser_1(int temp_pram);

在libavcodec/h264_parse.c //定义全局变量av_set_deviser_1,和函数

int av_deviser_1(){return deviser_1;}int deviser_1 = 666666;int av_set_deviser_1(int temp_pram){deviser_1 = temp_pram;return deviser_1;}int ff_h264_check_intra4x4_pred_mode(int8_t *pred_mode_cache, void *logctx,                                     int top_samples_available, int left_samples_available)

同样的文件里,在ff_h264_check_intra4x4_pred_mode函数中,也有一个花屏常见的错误,left block unavailable for requested intra4x4 mode,例如你想在这里做个标记,添加deviser_1 = 222222;

if (status < 0) {                    av_log(logctx, AV_LOG_ERROR,                           "left block unavailable for requested intra4x4 mode %d\n",                           status);                         deviser_1 = 222222;                    return AVERROR_INVALIDDATA;                }

那么在avcodec_receive_frame函数得到解码数据后检查:
if (av_deviser_1() == 555555)
{
//处理
}
同时处理后应该在下次解码前复位标志,因为这个函数是在解码时调用的:

av_set_deviser_1(666666);

如果在libavcodec文件价内其他文件标记,只需要包含avcodec.h,然后直接使用deviser_1变量即可。注意不可非libavcodec模块内使用全局变量因为编译时不再同一个.so文件中,访问不到。

ffmpeg是音视频必备,但即使从业数年,它似乎依然有无穷的秘密,感兴趣添加笔者,加入ffmpeg微信群讨论。但记得备注:ffmpeg爱好者

附录用ffplay播放花屏时常见报错: libavcodec/h264_parse.c [h264 @ 0x7f8af0045780] left block unavailable for requested intra mode 111111 libavcodec/h264_parse.c left block unavailable for requested intra4x4 mode 222222 //libavcodec/h264_slice.c [h264 @ 0x7f8af0045780] error while decoding MB 0 30, bytestream 80303 333333 libavcodec/error_resilience.c [h264 @ 0x7f8af0045780] concealing 4609 DC, 4609 AC, 4609 MV errors in I frame 444444 libavcodec/h264_cabac.c [h264 @ 0x7fa298c462c0] cabac decode of qscale diff failed at 62 59 555555 这个目前没见到

[hevc @ 0x7fcc0cb82b40] The cu_qp_delta 52 is outside the valid range [-26, 25]. 这个解码出来花屏
[mpegts @ 0x7fcc0c000940] Packet corrupt (stream = 0, dts = 47665500).
[mpegts @ 0x7fcc0c000940] PES packet size mismatch
[hevc @ 0x7fcc0c59dc00] Could not find ref with POC 14052
[hevc @ 0x7fcc0c57cc40] CABAC_MAX_BIN : 7
send_error

参考:[
链路模拟工具
花屏解决1
花屏解决2


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/43691.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

最新文章

  • 产检报告一切正常,全家却都盼着我去死结局+番外_师成安茵茵明白精校文本_小说后续在线阅读_无删减免费完结_
  • 沈毓灵踹掉言情男主,勾搭男频帝王小说(踹掉言情男主,勾搭男频帝王)前传+全书阅读新作预览
  • 改娶抓阄选中的残疾女将军后,郡主悔疯了看点十足_太后叶眠雪裴知晏宝藏文_小说后续在线阅读_无删减免费完结_
  • 高考还没开始,爸妈开始惩罚我了惊天黑幕_青青董梦竹许诺全新_小说后续在线阅读_无删减免费完结_
  • 沈时宴林初眠附加完整在线阅读(踹掉渣男后,我被闺蜜小叔宠上天)最近更新列表
  • 娇妾善撩又能生,男主为她折腰(顾熙宁)结局+番外新上热文_(顾熙宁)娇妾善撩又能生,男主为她折腰小说全文免费阅读最新章节列表笔趣阁
  • ***宝藏文_芝芝玲玲贺云章最新阅读_小说后续在线阅读_无删减免费完结_
  • 靠弹幕知道父母的兄弟穷养计划后,我成功摆脱结局_小说后续在线阅读_无删减免费完结_
  • (傅砚臣盛书意)傅砚臣盛书意(盛书意傅砚臣)无套路无弹窗全部章节列表
  • 救命!我穿到修仙界当催生员了:结局+番外(程诺诺)小说免费更新在线阅读_救命!我穿到修仙界当催生员了:结局+番外免费全文阅读笔趣阁(程诺诺)
  • 为讨情人欢心,老公将怀孕八个月的我打到流产完整文本_顾裴司白月光顾董完结版_小说后续在线阅读_无删减免费完结_
  • 祝沉风苏轻寰附加(祝沉风苏轻寰)(苏轻寰祝沉风)全本浏览阅读连载中

    关于我们 | 我要投稿 | 免责申明

    Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1