• FFmpeg报错:Connection to tcp://XXX?timeout=XXX failed: Connection timed out


    一、现象

    通过FFmpeg(FFmpeg的版本是5.0.3)拉摄像机的rtsp流获取音视频数据,执行命令:

    ./ffmpeg -timeout 3000000 -i "rtsp://172.16.17.156/stream/video5"

    报错:Connection to tcp://XXX?timeout=XXX failed: Connection timed out。

    二、Wireshark抓包分析

    执行FFmpeg命令的本机ip为192.168.16.13,摄像机的ip为172.16.17.156。通过Wireshark抓包可以发现,执行上述命令时,TCP三次握手失败,出现“TCP Retransmission”:

    这意味着TCP重传了。图中的“TCP Retransmission”是重传标记,No2到No6的包是重传包,也就是TCP客户端(执行FFmpeg命令的本机)的SYN发出去之后没有收到摄像机的syn ack,TCP客户端持续发syn,重传5次之后就停止了。所以这可能就意味着摄像机掉线/本机访问摄像机不正常/防火墙iptables规则存在问题,我们得要进行一下网络检查。

    三、FFmpeg源码分析

    调试FFmpeg源码,可以看到

    “Connection to tcp://XXX?timeout=XXX failed: Connection timed out”这个错误

    是由源文件libavformat/network.c中的函数ff_connect_parallel 中的

    av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n",h->filename, errbuf);

    这一行打印出来的,此时变量last_err的值为-110。

    整个程序的调用堆栈为:avformat_open_input

    ->  if ((ret = s->iformat->read_header(s)) < 0)      [实际执行rtsp_read_header]

    -> ret = ff_rtsp_connect(s);

    ->  if ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,

    &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL)) < 0)

    -> ret = ffurl_connect(*puc, options);

    -> uc->prot->url_open(uc, uc->filename, uc->flags);      [实际执行tcp_open]

    -> ret = ff_connect_parallel(ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s);
     

    -> av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", h->filename, errbuf);

    所以为什么会执行av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", h->filename, errbuf)这条语句呢,是因为last_err的值为-110。而last_err是在执行了函数ff_poll_interrupt后才变为-110的。

    我们 看一下函数ff_poll_interrupt 的源码:

    1. static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout,
    2. AVIOInterruptCB *cb)
    3. {
    4. int runs = timeout / POLLING_TIME;
    5. int ret = 0;
    6. do {
    7. if (ff_check_interrupt(cb))
    8. return AVERROR_EXIT;
    9. ret = poll(p, nfds, POLLING_TIME);
    10. if (ret != 0) {
    11. if (ret < 0)
    12. ret = ff_neterrno();
    13. if (ret == AVERROR(EINTR))
    14. continue;
    15. break;
    16. }
    17. } while (timeout <= 0 || runs-- > 0);
    18. if (!ret)
    19. return AVERROR(ETIMEDOUT);
    20. return ret;
    21. }

    可以看出来,FFmpeg拉流时会执行poll函数(没错,就是跟I/O复用select、epoll 并列的poll函数),当poll函数返回0时,代表没有任何socket描述符准备好读、写,或出错,此时poll超时,超时时间是POLLING_TIME(默认100)毫秒,然后该逻辑执行timeout / POLLING_TIME次后,函数ff_poll_interrupt会返回AVERROR(ETIMEDOUT),也就是返回-110。

    四、总结

    FFmpeg拉流时会执行poll函数,poll函数超时时会返回0,然后FFmpeg的ff_poll_interrupt函数会返回-110,然后打印错误:Connection to tcp://XXX?timeout=XXX failed: Connection timed out

  • 相关阅读:
    ts3.接口和对象类型
    html5&css&js代码 007 文章排版 颜真卿《述张长史笔法十二意》
    Flink中RPC实现原理简介
    java常见问题排查
    binary tree Leetcode 二叉树算法题
    【漏洞复现】maccms苹果cms 命令执行漏洞
    【网络通信层】华为云连接MQTT设备
    一台服务器最大能支持多少条 TCP 连接
    《动手学深度学习 Pytorch版》 7.1 深度卷积神经网络(AlexNet)
    软件工程阶段测试4
  • 原文地址:https://blog.csdn.net/u014552102/article/details/132638830