当前位置:网站首页>FFmpeg花屏解决(修改源码,丢弃不完整帧)
FFmpeg花屏解决(修改源码,丢弃不完整帧)
2022-08-10 17:23:00 【音视频开发老舅】
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,到这里测试发现视频虽然卡顿但不会花屏了,以上h264帧结构是I和P,不包含B帧。
如果使用h265时,解码错误,但并不会记录在AVFrame的decode_error_flags中,这就需要自己在源码中记录了,可以使用下面的方法。
在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);
码流探测阶段也有时有不完整的帧,这部分帧需要舍弃,不能放到接收缓存中,因此需要加上AVFMT_FLAG_NOBUFFER。
同时需要注意h265的一些码流结构如GPB模式,avformat_open_input函数打开
在h265帧结构为I和P,不含B,以及含I,P,B但是闭合GOP时可用,如果是低延时模式GPB(广义B帧,帧结构参考文末图片),目前还未找到可行的方法。
附录用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;
//以下不产生纠错,就不会错了,注释掉下面的,直接返回,就不会灰了
ret = generate_missing_ref(s,poc);
if(!ref)
return AVERROR(ENOMEM);
}
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓
边栏推荐
猜你喜欢
架构-三层架构:三层架构
leetcode:1137. 第 N 个泰波那契数
The DGIOT platform displays the whole process code analysis of OPC reporting data in real time
聚焦企业流程智能化发展新趋势,中国信通院2022 RPA创新产业峰会即将开启
「软件架构」10种常见的软件架构模式
Redis下载安装教程 (windows)
ARM开发(三)ARM寻址方式,异常中断,异常向量表
【燃】是时候展现真正的实力了!一文看懂2022华为开发者大赛技术亮点
【JDK】Oracle又一个JDK大版本停止扩展技术支持
挑战用五行代码轻松集成登录系统,实现单点登录
随机推荐
docker中安装mysql
全新接口——邻家好货 API
oracle11g体系结构
Error creating bean with name ‘sqlSessionFactory‘ defined in class path reso「建议收藏」
DASCTF2022.07赋能赛 WEB题目复现
R语言创建列表数据(list):根据名称索引列表元素、双方括号访问单个元素、单方括号访问子列表
CAS客户端对接
【JDK】Oracle又一个JDK大版本停止扩展技术支持
奥迪的极致高端属于一个大写的H?重塑时空,谁会是这个夜晚的主角?
【图像去雾】基于颜色衰减先验的图像去雾附matlab代码
【独立站运营】做社交媒体营销的两大关键点
【云原生| Docker】 部署 Django & mysql 项目
Moonbeam于Moonbase Alpha构建新式XCM对EVM跨链功能
成为一个优秀的测试工程师需要具备哪些知识和经验?
施工企业数字化转型解决方案设计思路
【硬件架构的艺术】学习笔记(4)流水线的艺术
R语言检验时间序列的平稳性:使用fUnitRoots包中的adfTest函数检验时间序列数据是否具有平稳性(设置参数type为nc时、既不去除趋势也不进行中心化处理)
PS2手柄通讯协议解析—附资料和源码「建议收藏」
神经网络有哪些激活函数,卷积神经网络有哪些
验算移位距离和假设的通用性