美文网首页音视频开发集锦
FFmpeg之直播ip拉流优化

FFmpeg之直播ip拉流优化

作者: e6dcbd6ead6a | 来源:发表于2017-09-21 17:48 被阅读1155次

    这个系列主要是写关于用FFmpeg来拉取直播流时,会涉及到的优化项,可作为优化的实践。虽然是针对的直播,但对于点播的播放比如直接播放mp4的流,还是有一样的原理。

    什么是ip拉流?

    ip拉流就是指将拉流url里面的域名,比如http://flv-meipai.8686c.com/live/59c3507b20a05d24f928d6cf.flv里面的flv-meipai.8686c.com预先用第三方dns库解析出来,然后直接替换掉,例如http://1.1.1.1/live/59c3507b20a05d24f928d6cf.flv这样的url,传给ffmpeg来拉流播放。

    为什么要用ip拉流?

    如果没有替换ip,那么在ffmpeg中的tcp.c文件中,tcp_open方法会调用getaddrinfo方法进行dns的请求和解析。具体代码如下:

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    snprintf(portstr, sizeof(portstr), "%d", port);
    if (s->listen)
        hints.ai_flags |= AI_PASSIVE;
    int64_t start = av_gettime();
    if (!hostname[0])
        ret = getaddrinfo(NULL, portstr, &hints, &ai);
    else
        ret = getaddrinfo(hostname, portstr, &hints, &ai);
    int64_t end = av_gettime();
    
    struct sockaddr_in *ipv4 = (struct sockaddr_in *)ai->ai_addr;
    char *ipAddress = (char *)malloc(INET_ADDRSTRLEN);
    inet_ntop(AF_INET, &(ipv4->sin_addr), ipAddress, INET_ADDRSTRLEN);
    ffmpeg_dns_success(uri,hostname,(end-start)/1000,ipAddress);
    free(ipAddress);
    

    在这段代码中hints.ai_family = AF_UNSPEC的意思是会请求这个域名对应的ipv4和ipv6的ip地址,如下图:

    dns1.png

    图中192.168.2.16是我的手机ip地址,192.168.2.1是我的电脑发出的热点的ip地址。可以看出DNS请求的时候是发出了两个请求分别是AAAAA,如果你用wireshark展开就会发现A是ipv4,AAAA是ipv6。
    这样的配置会导致两个问题:

    • 第一个问题,因为我们的域名只配置了ipv4的地址,没有配置ipv6的地址,所以,ipv4的地址经过160ms左右就返回了。但ipv6的地址经过300ms左右才返回,并且返回的是错误的,没有具体的地址。这里还有一个点应该是,ipv4的地址在各级域名服务器,如果之前有请求过,就会直接返回了,但ipv6的地址,从来没有获取到过,所以每次都需要回源到根域名服务器去查询,然后返回错误。如果设置hints.ai_family = AF_NET 就表示只请求ipv4的地址,dns耗时就很短。当然我们这里可以直接修改ffmpeg代码,但是这就破坏了ffmpeg的强容错性,如果将来需要ipv6的地址,就容易出bug。所以我们采用在外部传入ip地址进来,这样灵活性就可配置,并且可优化的空间更大。对于直播这种首屏要求很高的应用场景,即使是100ms也是优化的空间。
    • 第二个问题,一般的域名解析都会返回一个有效期,然后由系统来缓存,一般是1分钟左右,也有3分钟的,具体看注册域名的时候是如何配置的(没搞过,所以不清楚)。然后,系统缓存并不会自动去更新,所以1分钟后,缓存失效,也会导致拉流时从新解析耗时300多ms。
      所以,直接传入ip大部分情况下会直接提升这300ms的时间。

    具体如何操作?

    1. 用什么方法解析出ip地址呢?
      目前有几种方法可以解析,可以用开源的HappyDNS,也可以用各厂商的httpdns。HappyDNS是走的系统解析方法并且只请求了ipv4的地址,可以很好的避免这个问题。当然httpdns就有更复杂的方法了,而且配合缓存机制,也能达到很快的速度,并且httpdns通常也能解决小运营商dns 域名劫持的问题,所以还是很有用的。

    2. 拿到ip后直接传入就可以了吗?
      如果对应一些CDN厂商,比如网宿,在ffmpeg里直接用http://1.1.1.1/live/59c3507b20a05d24f928d6cf.flv类似这种http请求,就能拉取到数据了,但在某些CDN厂商那里不行,比如阿里云。这是因为他们的服务器不仅仅支持某一个域名的服务,还支持其他域名的服务,所以需要在http的header里面设置Host这个参数。这样CDN服务器就能处理了。

    在这里,我们可以通过设置参数的形式通过设置Host:这个参数给ffmpeg,这样ffmpeg就可以直接填充了。具体代码如下:(这里参照ijkplayer源码)

    av_dict_set(&ffp->format_opts, "headers", "Host: flv-meipai.8686c.com", 0);
    

    注意这里的Host:后面一定要有一个空格。这样,ffmpeg发起的http请求就有host参数了。

    1. 具体代码如何实现的?

    这里的关键是av_dict_set 方法如何将参数传入到ffmpeg内部。首先看下ffp->format_opts是什么数据结构

    AVDictionary *format_opts;
    
    struct AVDictionary {
        int count;
        AVDictionaryEntry *elems;
    };
    
    typedef struct AVDictionaryEntry {
        char *key;
        char *value;
    } AVDictionaryEntry;
    

    可以看出,AVDictionary就是一个封装了dict类型的数据结构。
    然后是看这个ffp->format_opts合适传进去的。

    err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);
    

    avformat_open_input这个方法就是ffmpeg的打开流,并找到流媒体的头部信息的函数,具体可以参考另一篇文章Avformat_open_input函数的分析之--HTTP篇。传入以后,最终format_opts这个结构体会走到avio.c文件的ffurl_open_whitelist方法,并赋值给URLContext **puc结构体,代码如下:

        if (options &&
            (ret = av_opt_set_dict(*puc, options)) < 0)
            goto fail;
        if (options && (*puc)->prot->priv_data_class &&
            (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
            goto fail;
    

    然后最终会在http.c文件的发起http连接的函数http_connect方法中

    char headers[HTTP_HEADERS_SIZE] = "";
    
    if (!has_header(s->headers, "\r\nHost: "))
        len += av_strlcatf(headers + len, sizeof(headers) - len,
                            "Host: %s\r\n", hoststr);
    
    if (s->headers)
            av_strlcpy(headers + len, s->headers, sizeof(headers) - len);
    

    这段代码的意思,如果dict里面含有Host:这个字符串,然后就取后面的值来作为http的headers。否则取hoststr的值,这个值是之前从url里面解析出来的。

    所以,经过上面几步,就完成了外部设置Host参数的功能,除了Host以外,http协议的所有headers里面的参数都可以设置。都可以通过这种方式来设置。这就是ffmpeg的强大的地方。

    相关文章

      网友评论

        本文标题:FFmpeg之直播ip拉流优化

        本文链接:https://www.haomeiwen.com/subject/uegzsxtx.html