OBS-rtmp源码剖析之rtmp客户端通信介绍(二)

rtmp的网络
1、rtmp结构体设置RTMP_LNK

#define DEF_VERSTR  OSS " 10,0,32,18"
static const char DEFAULT_FLASH_VER[] = DEF_VERSTR;
const AVal RTMP_DefaultFlashVer =
{ (char *)DEFAULT_FLASH_VER, sizeof(DEFAULT_FLASH_VER) - 1 };

static void
SocksSetup(RTMP *r, AVal *sockshost)
{
    if (sockshost->av_len)
    {
        const char *socksport = strchr(sockshost->av_val, ':');
        char *hostname = strdup(sockshost->av_val);

        if (socksport)
            hostname[socksport - sockshost->av_val] = '\0';
        r->Link.sockshost.av_val = hostname;
        r->Link.sockshost.av_len = (int)strlen(hostname);

        r->Link.socksport = socksport ? atoi(socksport + 1) : 1080;
        RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val,
                 r->Link.socksport);
    }
    else
    {
        r->Link.sockshost.av_val = NULL;
        r->Link.sockshost.av_len = 0;
        r->Link.socksport = 0;
    }
}

2、设置直播地址

int RTMP_SetupURL(RTMP *r, char *url)
{
    int ret, len;
    unsigned int port = 0;

    len = (int)strlen(url);
    ret = RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname,
        &port, &r->Link.app);
    if (!ret)
        return ret;
    r->Link.port = port;

    if (!r->Link.tcUrl.av_len)
    {
        r->Link.tcUrl.av_val = url;
        if (r->Link.app.av_len)
        {
            if (r->Link.app.av_val < url + len)
            {
                /* if app is part of original url, just use it */
                r->Link.tcUrl.av_len = r->Link.app.av_len + (r->Link.app.av_val - url);
            }
            else
            {
                len = r->Link.hostname.av_len + r->Link.app.av_len +
                    sizeof("rtmpte://:65535/");
                r->Link.tcUrl.av_val = malloc(len);
                r->Link.tcUrl.av_len = snprintf(r->Link.tcUrl.av_val, len,
                    "%s://%.*s:%d/%.*s",
                    RTMPProtocolStringsLower[r->Link.protocol],
                    r->Link.hostname.av_len, r->Link.hostname.av_val,
                    r->Link.port,
                    r->Link.app.av_len, r->Link.app.av_val);
                r->Link.lFlags |= RTMP_LF_FTCU;
            }
        }
        else
        {
            r->Link.tcUrl.av_len = (int)strlen(url);
        }
    }

#ifdef CRYPTO
    if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
        RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize,
        (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
#endif

    SocksSetup(r, &r->Link.sockshost);

    if (r->Link.port == 0)
    {
        if (r->Link.protocol & RTMP_FEATURE_SSL)
            r->Link.port = 443;
        else if (r->Link.protocol & RTMP_FEATURE_HTTP)
            r->Link.port = 80;
        else
            r->Link.port = 1935;
    }
    return TRUE;
}

3、添加流信息

int RTMP_AddStream(RTMP *r, const char *playpath)
{
    int idx = -1;
    AVal pp = { (char*)playpath, playpath?(int)strlen(playpath):0 };

    RTMP_ParsePlaypath(&pp, &r->Link.streams[r->Link.nStreams].playpath);
    r->Link.streams[r->Link.nStreams].id = -1;

    idx = r->Link.nStreams;
    r->Link.nStreams++;

    return idx;
}

static int
add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, int port, socklen_t addrlen_hint, int *socket_error)
{
    char *hostname;
    int ret = TRUE;
    if (host->av_val[host->av_len] || host->av_val[0] == '[')
    {
        int v6 = host->av_val[0] == '[';
        hostname = malloc(host->av_len+1 - v6 * 2);
        memcpy(hostname, host->av_val + v6, host->av_len - v6 * 2);
        hostname[host->av_len - v6 * 2] = '\0';
    }
    else
    {
        hostname = host->av_val;
    }

    struct addrinfo hints;
    struct addrinfo *result = NULL;
    struct addrinfo *ptr = NULL;

    memset(&hints, 0, sizeof(hints));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    service->ss_family = AF_UNSPEC;
    *addrlen = 0;

    char portStr[8];

    sprintf(portStr, "%d", port);

    int err = getaddrinfo(hostname, portStr, &hints, &result);

    if (err)
    {
#ifndef _WIN32
#define gai_strerrorA gai_strerror
#endif
        RTMP_Log(RTMP_LOGERROR, "Could not resolve %s: %s (%d)", hostname, gai_strerrorA(GetSockError()), GetSockError());
        *socket_error = GetSockError();
        ret = FALSE;
        goto finish;
    }

    // prefer ipv4 results, since lots of ISPs have broken ipv6 connectivity
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
    {
        if (ptr->ai_family == AF_INET && (!addrlen_hint || ptr->ai_addrlen == addrlen_hint))
        {
            memcpy(service, ptr->ai_addr, ptr->ai_addrlen);
            *addrlen = (socklen_t)ptr->ai_addrlen;
            break;
        }
    }

    if (!*addrlen)
    {
        for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
        {
            if (ptr->ai_family == AF_INET6 && (!addrlen_hint || ptr->ai_addrlen == addrlen_hint))
            {
                memcpy(service, ptr->ai_addr, ptr->ai_addrlen);
                *addrlen = (socklen_t)ptr->ai_addrlen;
                break;
            }
        }
    }

    freeaddrinfo(result);

    if (service->ss_family == AF_UNSPEC || *addrlen == 0)
    {
        // since we're handling multiple addresses internally, fake the correct error response
#ifdef _WIN32
        *socket_error = WSANO_DATA;
#elif __FreeBSD__
        *socket_error = ENOATTR;
#else
        *socket_error = ENODATA;
#endif

        RTMP_Log(RTMP_LOGERROR, "Could not resolve server '%s': no valid address found", hostname);
        ret = FALSE;
        goto finish;
    }

finish:
    if (hostname != host->av_val)
        free(hostname);
    return ret;
}

4、开始连接操作,RTMP_Connect0、RTMP_Connect1、RTMP_TLS_Accept和RTMP_Connect

#ifdef _WIN32
#define E_TIMEDOUT     WSAETIMEDOUT
#define E_CONNREFUSED  WSAECONNREFUSED
#define E_ACCES        WSAEACCES
#else
#define E_TIMEDOUT     ETIMEDOUT
#define E_CONNREFUSED  ECONNREFUSED
#define E_ACCES        EACCES
#endif

int
RTMP_Connect0(RTMP *r, struct sockaddr * service, socklen_t addrlen)
{
    int on = 1;
    r->m_sb.sb_timedout = FALSE;
    r->m_pausing = 0;
    r->m_fDuration = 0.0;

    //best to be explicit, we need overlapped socket
#ifdef _WIN32
    r->m_sb.sb_socket = WSASocket(service->sa_family, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
#else
    r->m_sb.sb_socket = socket(service->sa_family, SOCK_STREAM, IPPROTO_TCP);
#endif

    if (r->m_sb.sb_socket != INVALID_SOCKET)
    {
        if(r->m_bindIP.addrLen)
        {
            if (bind(r->m_sb.sb_socket, (const struct sockaddr *)&r->m_bindIP.addr, r->m_bindIP.addrLen) < 0)
            {
                int err = GetSockError();
                RTMP_Log(RTMP_LOGERROR, "%s, failed to bind socket: %s (%d)",
                         __FUNCTION__, socketerror(err), err);
                r->last_error_code = err;
                RTMP_Close(r);
                return FALSE;
            }
        }

        uint64_t connect_start = os_gettime_ns();

        if (connect(r->m_sb.sb_socket, service, addrlen) < 0)
        {
            int err = GetSockError();
            if (err == E_CONNREFUSED)
                RTMP_Log(RTMP_LOGERROR, "%s is offline. Try a different server (ECONNREFUSED).", r->Link.hostname.av_val);
            else if (err == E_ACCES)
                RTMP_Log(RTMP_LOGERROR, "The connection is being blocked by a firewall or other security software (EACCES).");
            else if (err == E_TIMEDOUT)
                RTMP_Log(RTMP_LOGERROR, "The connection timed out. Try a different server, or check that the connection is not being blocked by a firewall or other security software (ETIMEDOUT).");
            else
                RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket: %s (%d)",
                     __FUNCTION__, socketerror(err), err);
            r->last_error_code = err;
            RTMP_Close(r);
            return FALSE;
        }

        r->connect_time_ms = (int)((os_gettime_ns() - connect_start) / 1000000);

        if (r->Link.socksport)
        {
            RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);
            if (!SocksNegotiate(r))
            {
                RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);
                RTMP_Close(r);
                return FALSE;
            }
        }
    }
    else
    {
        RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__,
                 GetSockError());
        return FALSE;
    }

    /* set timeout */
    {
        SET_RCVTIMEO(tv, r->Link.timeout);
        if (setsockopt
                (r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
        {
            RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
                     __FUNCTION__, r->Link.timeout);
        }
    }

    if(!r->m_bUseNagle)
        setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));

    return TRUE;
}

int
RTMP_TLS_Accept(RTMP *r, void *ctx)
{
#if defined(CRYPTO) && !defined(NO_SSL)
    TLS_server(ctx, r->m_sb.sb_ssl);
    TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);
    if (TLS_accept(r->m_sb.sb_ssl) < 0)
    {
        RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
        return FALSE;
    }
    return TRUE;
#else
    (void)r;
    (void)ctx;
    return FALSE;
#endif
}

int
RTMP_Connect1(RTMP *r, RTMPPacket *cp)
{
    if (r->Link.protocol & RTMP_FEATURE_SSL)
    {
#if defined(CRYPTO) && !defined(NO_SSL)
        TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl);
        TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);
        if (TLS_connect(r->m_sb.sb_ssl) < 0)
        {
            RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
            RTMP_Close(r);
            return FALSE;
        }
#else
        RTMP_Log(RTMP_LOGERROR, "%s, no SSL/TLS support", __FUNCTION__);
        RTMP_Close(r);
        return FALSE;

#endif
    }
    if (r->Link.protocol & RTMP_FEATURE_HTTP)
    {
        r->m_msgCounter = 1;
        r->m_clientID.av_val = NULL;
        r->m_clientID.av_len = 0;
        HTTP_Post(r, RTMPT_OPEN, "", 1);
        if (HTTP_read(r, 1) != 0)
        {
            r->m_msgCounter = 0;
            RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__);
            RTMP_Close(r);
            return 0;
        }
        r->m_msgCounter = 0;
    }
    RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);
    if (!HandShake(r, TRUE))
    {
        RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__);
        RTMP_Close(r);
        return FALSE;
    }
    RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__);

    if (!SendConnectPacket(r, cp))
    {
        RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__);
        RTMP_Close(r);
        return FALSE;
    }
    return TRUE;
}

int
RTMP_Connect(RTMP *r, RTMPPacket *cp)
{
#ifdef _WIN32
    HOSTENT *h;
#endif
    struct sockaddr_storage service;
    socklen_t addrlen = 0;
    socklen_t addrlen_hint = 0;
    int socket_error = 0;

    if (!r->Link.hostname.av_len)
        return FALSE;

#ifdef _WIN32
    //COMODO security software sandbox blocks all DNS by returning "host not found"
    h = gethostbyname("localhost");
    if (!h && GetLastError() == WSAHOST_NOT_FOUND)
    {
        r->last_error_code = WSAHOST_NOT_FOUND;
        RTMP_Log(RTMP_LOGERROR, "RTMP_Connect: Connection test failed. This error is likely caused by Comodo Internet Security running OBS in sandbox mode. Please add OBS to the Comodo automatic sandbox exclusion list, restart OBS and try again (11001).");
        return FALSE;
    }
#endif

    memset(&service, 0, sizeof(service));

    if (r->m_bindIP.addrLen)
        addrlen_hint = r->m_bindIP.addrLen;

    if (r->Link.socksport)
    {
        /* Connect via SOCKS */
        if (!add_addr_info(&service, &addrlen, &r->Link.sockshost, r->Link.socksport, addrlen_hint, &socket_error))
        {
            r->last_error_code = socket_error;
            return FALSE;
        }
    }
    else
    {
        /* Connect directly */
        if (!add_addr_info(&service, &addrlen, &r->Link.hostname, r->Link.port, addrlen_hint, &socket_error))
        {
            r->last_error_code = socket_error;
            return FALSE;
        }
    }

    if (!RTMP_Connect0(r, (struct sockaddr *)&service, addrlen))
        return FALSE;

    r->m_bSendCounter = TRUE;

    return RTMP_Connect1(r, cp);
}

猜你喜欢

转载自blog.csdn.net/tong5956/article/details/81479773