问题描述
在使用ffmpeg做一个播放器的时候,拉取https流,av_read_frame 读包的时候总是偶尔出现返回-5的情况,-5其实是EIO错误,AVERROR(EIO) 返回为5。
#define EIO 5
这个问题给我造成了很大的麻烦,所以这里记录一下排查的过程以及解决方法,希望能帮忙遇到相同场景的同学。
原因调查
目前使用ffmpeg自带log日志,进行回调写入日志发现,返回-5的时候日志输出为下面,啥用没有
Error in the pull function.
猜测原因
猜测原因 | 验证 |
---|---|
由于资源文件损坏 | 使用VLC拉流同样可以成功,排除 |
由于网络抖动 | 使用工具设置丢包率50%,未能必现,排除 |
代码流程 | 播放本地文件,发现未能出现该场景,排除 |
解码耗时 | 打印拉流和解码时间,只有几十微秒,排除 |
因为我的拉流以及解码是放在同一个线程里的,之前在网上寻找帮助看过有人说是因为解码耗时导致线程占用CPU高,需要放在不同线程,但是我只拉MP3,也就是只有音频解码,同时打印了时间,发现整个下来才几十微秒,所以没有尝试编解码放两个线程的方案。
但是验证问题已经进行不下去了,所以笔者只能尝试是否能绕过该问题
目前尝试绕过该问题,解决方案:
解决方案 | 效果 |
---|---|
出现问题时再次拉流 | 会一直出现EIO错误,没有效果 |
出现问题时,定位后面一秒播放 | 无作用,同时av_log 输出invalid new backstep -1 |
出现问题时,释放资源再拉流 | 可以再次重新拉流,但是有几率会再次出现EIO错误,同时,重置资源会造成耗时 |
尝试将解码缓存加大 | 可以降低出错频率,不能根治 |
|版本声明:山河君,未经博主允许,禁止转载
解决方案:
后面阅读ffmpeg官方文档,发现有一个文档专门介绍协议的:文档连接
里面有对于http协议属性设置,遂增加了以下几个属性
AVDictionary *format_opts = NULL;
av_dict_set(&format_opts, "stimeout", std::to_string(1 * 1000000).c_str(), 0); //设置链接超时时间(us)
av_dict_set_int(&format_opts, "multiple_requests", 1, 0);
av_dict_set_int(&format_opts, "read_ahead_limit", INT_MAX, 0);
//Open
if (avformat_open_input(&pFormatCtx_, UTF8ToGBK(strFileNameUTF8).c_str(), NULL, &format_opts) != 0)
stimeout:连接超时时间
multiple_requests:Use persistent connections if set to 1, default is 0. //设置为1为长连接
read_ahead_limit:Amount in bytes that may be read ahead when seeking isn’t supported. Range is -1 to INT_MAX. -1 for unlimited. Default is 65536. //预读限制 默认值为 65536
后没有发生错误
原因分析:
根据测试,应该是multiple_requests该属性导致了读包返回-5
因为使用的是http协议,http分为长连接和短连接,如果不设置属性,默认为短连接
而长连接和短连接的区别在于:
短连接定义:
Client方与server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此方式常用于一点对多点通讯。
短连接的操作步骤是:建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
长连接定义:
client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收。这种方式下由于通讯连接一直存在。此种方式常用于P2P点对点的通信。
长连接的操作步骤是:建立连接——数据传输…(保持连接)…数据传输——关闭连接
个人觉得可能是每次连接拉取一个包后,就会断开连接,然后再进行连接导致该问题的出现。