当前位置:网站首页>FFmpeg Huaping solution (modify source code, discard incomplete frames)
FFmpeg Huaping solution (modify source code, discard incomplete frames)
2022-08-10 17:49:00 【Audio and video development LaoJiu】
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,到这里测试发现视频虽然卡顿但不会花屏了,以上h264The frame structure isI和P,不包含B帧.
如果使用h265时,解码错误,but not recordedAVFrame的decode_error_flags中,This needs to be recorded in the source code yourself,可以使用下面的方法.
在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文件中,访问不到.
ic->flags |= AVFMT_FLAG_NOBUFFER;
re = avformat_find_stream_info(ic, NULL);
There are also sometimes incomplete frames in the stream detection stage,This part of the frame needs to be discarded,cannot be placed in the receive buffer,因此需要加上AVFMT_FLAG_NOBUFFER.
同时需要注意h265Some codestream structures such as GPB模式,avformat_open_input函数打开
在h265帧结构为I和P,不含B,以及含I,P,BBut closedGOP时可用,If it is in low latency modeGPB(广义B帧,Refer to the picture at the end of the text for the frame structure),No feasible method has yet been found.
附录用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
libavcodec/hevc_refs.c
[hevc @ 0x7fcc0c59dc00] Could not find ref with POC 14052 //999999
[hevc @ 0x7fcc0c57cc40] CABAC_MAX_BIN : 7
[NULL @ 0x7f3ef02d1500] Invalid NAL unit 35, skipping.
send_error
/usr/app/EncParams/enc_2160p_422_10_50_swap_sq_2chaac/EHParam.sh
设备密码:root hotdog
–enable-parser=hevc
libavformat/hevcdec.c probe
libavcodec/hevcdec.c
-=-=-=HEVC_NAL_SPS
hevc_refs.c 里面
add_candidate_ref 函数
if(!ref){
//return AV_ERROR_INVALIDDATE;
//Error correction is not generated below,就不会错了,注释掉下面的,直接返回,It won't be grey
ret = generate_missing_ref(s,poc);
if(!ref)
return AVERROR(ENOMEM);
}
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓
边栏推荐
- 2021强网杯
- mysql定义存储过程
- 直播回顾|多云时代,如何建设企业级云管理平台?(附建设指南下载)
- JWT 实现登录认证 + Token 自动续期方案
- 奥迪的极致高端属于一个大写的H?重塑时空,谁会是这个夜晚的主角?
- BalsnCTF2021
- pip install fatal error C1083 cannot open include file "io.h" No such file or directory
- Error creating bean with name ‘sqlSessionFactory‘ defined in class path reso「建议收藏」
- 一颗完整意义的LPWAN SOC无线通信芯片——ASR6601
- aliexpress API 接入说明
猜你喜欢
随机推荐
Interpretation of ZLMediaKit server source code---RTSP push and pull
node环境变量配置,npm环境变量配置
skywalking漏洞学习
skywalking vulnerability learning
挑战用五行代码轻松集成登录系统,实现单点登录
Scala中使用 Jackson API 进行JSON序列化和反序列化
PS2手柄通讯协议解析—附资料和源码「建议收藏」
ZLMediaKit 服务器源码解读---RTSP推流拉流
机器人控制器编程实践指导书旧版-实践四 步进电机(执行器)
Colocate Join :ClickHouse的一种高性能分布式join查询模型
Selenium - 如何操作下拉框、弹出框、滚动条?
unr #6day1 T2题解
Live Review|How to build an enterprise-level cloud management platform in the multi-cloud era?(with the download of the construction guide)
【燃】是时候展现真正的实力了!一文看懂2022华为开发者大赛技术亮点
百日刷题挑战--错题01day
FFmpeg 从mp4上提取H264的nalu
Talking about Taishan crowdfunding system development technical description and dapp chain crowdfunding system development analysis
多线程与高并发(11)——经典面试题之实现一个容器,提供两个方法,add,size。
Your local docbook2man was found to work with SGML rather than XML
R语言使用ggpubr包的ggbarplot函数可视化柱状图、设置add参数为mean_se和jitter可视化不同水平均值的柱状图并为柱状图添加误差线(se标准误差)和抖动数据点分布