Beispiel #1
0
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
					       pjmedia_sdp_fmtp *fmtp)
{
    const char *p = attr->value.ptr;
    const char *end = attr->value.ptr + attr->value.slen;
    pj_str_t token;

    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);

    /* fmtp BNF:
     *	a=fmtp:<format> <format specific parameter>
     */

    /* Get format. */
    token.ptr = (char*)p;
    while (pj_isdigit(*p) && p!=end)
	++p;
    token.slen = p - token.ptr;
    if (token.slen == 0)
	return PJMEDIA_SDP_EINFMTP;

    fmtp->fmt = token;

    /* Expecting space after format. */
    if (*p != ' ') return PJMEDIA_SDP_EINFMTP;

    /* Get space. */
    ++p;

    /* Set the remaining string as fmtp format parameter. */
    fmtp->fmt_param.ptr = (char*)p;
    fmtp->fmt_param.slen = end - p;

    return PJ_SUCCESS;
}
Beispiel #2
0
PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
{
    unsigned long value;
    unsigned i;

    PJ_CHECK_STACK();

    value = 0;
    for (i=0; i<(unsigned)str->slen; ++i) {
	if (!pj_isdigit(str->ptr[i]))
	    break;
	value = value * 10 + (str->ptr[i] - '0');
    }
    return value;
}
Beispiel #3
0
/*
 * Get fmtp mode parameter associated with the codec.
 */
static pj_status_t get_fmtp_mode(const pjmedia_sdp_media *m,
				 const pj_str_t *fmt,
				 int *p_mode)
{
    const pjmedia_sdp_attr *attr;
    pjmedia_sdp_fmtp fmtp;
    const pj_str_t str_mode = { "mode=", 5 };
    char *pos;

    /* Get "fmtp" attribute for the format */
    attr = pjmedia_sdp_media_find_attr2(m, "fmtp", fmt);
    if (attr == NULL)
	return -1;

    /* Parse "fmtp" attribute */
    if (pjmedia_sdp_attr_get_fmtp(attr, &fmtp) != PJ_SUCCESS)
	return -1;

    /* Look for "mode=" string in the fmtp */
    while (fmtp.fmt_param.slen >= str_mode.slen + 1) {
	if (pj_strnicmp(&fmtp.fmt_param, &str_mode, str_mode.slen)==0) {
	    /* Found "mode=" string */
	    break;
	}

	fmtp.fmt_param.ptr++;
	fmtp.fmt_param.slen--;
    }

    if (fmtp.fmt_param.slen < str_mode.slen + 1) {
	/* "mode=" param not found */
	return -1;
    }

    /* Get the mode */
    pos = fmtp.fmt_param.ptr + str_mode.slen;
    *p_mode = 0;
    while (pj_isdigit(*pos)) {
	*p_mode = *p_mode * 10 + (*pos - '0');
	++pos;
    }

    return PJ_SUCCESS;
}
Beispiel #4
0
/*
 * This function converts the Internet host address ccp from the standard
 * numbers-and-dots notation into binary data and stores it in the structure
 * that inp points to. 
 */
PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
{
    pj_uint32_t val;
    int base, n;
    char c;
    unsigned parts[4];
    unsigned *pp = parts;
    char cp_copy[18];
    char *cp = cp_copy;
    
    addr->s_addr = PJ_INADDR_NONE;

    if (ccp->slen > 15) return 0;

    pj_memcpy(cp, ccp->ptr, ccp->slen);
    cp[ccp->slen] = '\0';

    c = *cp;
    for (;;) {
	/*
	 * Collect number up to ``.''.
	 * Values are specified as for C:
	 * 0x=hex, 0=octal, isdigit=decimal.
	 */
	if (!pj_isdigit((int)c))
	    return (0);
	val = 0; base = 10;
	if (c == '0') {
	    c = *++cp;
	    if (c == 'x' || c == 'X')
		base = 16, c = *++cp;
	    else
		base = 8;
	}

	for (;;) {
	    if (pj_isascii((int)c) && pj_isdigit((int)c)) {
		val = (val * base) + (c - '0');
		c = *++cp;
	    } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
		val = (val << 4) |
		      (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
		c = *++cp;
	    } else
		break;
	}

	if (c == '.') {
	    /*
	     * Internet format:
	     *  a.b.c.d
	     *  a.b.c   (with c treated as 16 bits)
	     *  a.b	(with b treated as 24 bits)
	     */
	    if (pp >= parts + 3)
		return (0);
	    *pp++ = val;
	    c = *++cp;
	} else
	    break;
    }

    /*
     * Check for trailing characters.
     */
    if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
        return (0);
    /*
     * Concoct the address according to
     * the number of parts specified.
     */
    n = pp - parts + 1;
    switch (n) {
    case 0:
	return (0);	    /* initial nondigit */
    case 1:		/* a -- 32 bits */
	break;
    case 2:		/* a.b -- 8.24 bits */
	if (val > 0xffffff)
	    return (0);
	val |= parts[0] << 24;
	break;
    case 3:		/* a.b.c -- 8.8.16 bits */
	if (val > 0xffff)
	    return (0);
	val |= (parts[0] << 24) | (parts[1] << 16);
	break;
    case 4:		/* a.b.c.d -- 8.8.8.8 bits */
	if (val > 0xff)
	    return (0);
	val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
	break;
    }

    if (addr)
	addr->s_addr = pj_htonl(val);
    return (1);
}
Beispiel #5
0
/*
 * Parse address
 */
PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options,
				       const pj_str_t *str,
				       pj_sockaddr *addr)
{
    pj_str_t hostpart;
    pj_uint16_t port;
    pj_status_t status;

    PJ_ASSERT_RETURN(addr, PJ_EINVAL);
    PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC ||
		     af==PJ_AF_INET ||
		     af==PJ_AF_INET6, PJ_EINVAL);
    PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);

    status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af);
    if (status != PJ_SUCCESS)
	return status;
    
#if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6
    if (af==PJ_AF_INET6)
	return PJ_EIPV6NOTSUP;
#endif

    status = pj_sockaddr_init(af, addr, &hostpart, port);
#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
    if (status != PJ_SUCCESS && af == PJ_AF_INET6) {
	/* Parsing does not yield valid address. Try to treat the last 
	 * portion after the colon as port number.
	 */
	const char *last_colon_pos=NULL, *p;
	const char *end = str->ptr + str->slen;
	unsigned long long_port;
	pj_str_t port_part;
	int i;

	/* Parse as IPv6:port */
	for (p=str->ptr; p!=end; ++p) {
	    if (*p == ':')
		last_colon_pos = p;
	}

	if (last_colon_pos == NULL)
	    return status;

	hostpart.ptr = (char*)str->ptr;
	hostpart.slen = last_colon_pos - str->ptr;

	port_part.ptr = (char*)last_colon_pos + 1;
	port_part.slen = end - port_part.ptr;

	/* Make sure port number is valid */
	for (i=0; i<port_part.slen; ++i) {
	    if (!pj_isdigit(port_part.ptr[i]))
		return status;
	}
	long_port = pj_strtoul(&port_part);
	if (long_port > 65535)
	    return status;

	port = (pj_uint16_t)long_port;

	status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port);
    }
#endif
    
    return status;
}
Beispiel #6
0
/*
 * Parse address
 */
PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options,
				       const pj_str_t *str,
				       pj_str_t *p_hostpart,
				       pj_uint16_t *p_port,
				       int *raf)
{
    const char *end = str->ptr + str->slen;
    const char *last_colon_pos = NULL;
    unsigned colon_cnt = 0;
    const char *p;

    PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) &&
		     options==0 &&
		     str!=NULL, PJ_EINVAL);

    /* Special handling for empty input */
    if (str->slen==0 || str->ptr==NULL) {
	if (p_hostpart)
	    p_hostpart->slen = 0;
	if (p_port)
	    *p_port = 0;
	if (raf)
	    *raf = PJ_AF_INET;
	return PJ_SUCCESS;
    }

    /* Count the colon and get the last colon */
    for (p=str->ptr; p!=end; ++p) {
	if (*p == ':') {
	    ++colon_cnt;
	    last_colon_pos = p;
	}
    }

    /* Deduce address family if it's not given */
    if (af == PJ_AF_UNSPEC) {
	if (colon_cnt > 1)
	    af = PJ_AF_INET6;
	else
	    af = PJ_AF_INET;
    } else if (af == PJ_AF_INET && colon_cnt > 1)
	return PJ_EINVAL;

    if (raf)
	*raf = af;

    if (af == PJ_AF_INET) {
	/* Parse as IPv4. Supported formats:
	 *  - "10.0.0.1:80"
	 *  - "10.0.0.1"
	 *  - "10.0.0.1:"
	 *  - ":80"
	 *  - ":"
	 */
	pj_str_t hostpart;
	unsigned long port;

	hostpart.ptr = (char*)str->ptr;

	if (last_colon_pos) {
	    pj_str_t port_part;
	    int i;

	    hostpart.slen = last_colon_pos - str->ptr;

	    port_part.ptr = (char*)last_colon_pos + 1;
	    port_part.slen = end - port_part.ptr;

	    /* Make sure port number is valid */
	    for (i=0; i<port_part.slen; ++i) {
		if (!pj_isdigit(port_part.ptr[i]))
		    return PJ_EINVAL;
	    }
	    port = pj_strtoul(&port_part);
	    if (port > 65535)
		return PJ_EINVAL;
	} else {
	    hostpart.slen = str->slen;
	    port = 0;
	}

	if (p_hostpart)
	    *p_hostpart = hostpart;
	if (p_port)
	    *p_port = (pj_uint16_t)port;

	return PJ_SUCCESS;

    } else if (af == PJ_AF_INET6) {

	/* Parse as IPv6. Supported formats:
	 *  - "fe::01:80"  ==> note: port number is zero in this case, not 80!
	 *  - "[fe::01]:80"
	 *  - "fe::01"
	 *  - "fe::01:"
	 *  - "[fe::01]"
	 *  - "[fe::01]:"
	 *  - "[::]:80"
	 *  - ":::80"
	 *  - "[::]"
	 *  - "[::]:"
	 *  - ":::"
	 *  - "::"
	 */
	pj_str_t hostpart, port_part;

	if (*str->ptr == '[') {
	    char *end_bracket;
	    int i;
	    unsigned long port;

	    if (last_colon_pos == NULL)
		return PJ_EINVAL;

	    end_bracket = pj_strchr(str, ']');
	    if (end_bracket == NULL)
		return PJ_EINVAL;

	    hostpart.ptr = (char*)str->ptr + 1;
	    hostpart.slen = end_bracket - hostpart.ptr;

	    if (last_colon_pos < end_bracket) {
		port_part.ptr = NULL;
		port_part.slen = 0;
	    } else {
		port_part.ptr = (char*)last_colon_pos + 1;
		port_part.slen = end - port_part.ptr;
	    }

	    /* Make sure port number is valid */
	    for (i=0; i<port_part.slen; ++i) {
		if (!pj_isdigit(port_part.ptr[i]))
		    return PJ_EINVAL;
	    }
	    port = pj_strtoul(&port_part);
	    if (port > 65535)
		return PJ_EINVAL;

	    if (p_hostpart)
		*p_hostpart = hostpart;
	    if (p_port)
		*p_port = (pj_uint16_t)port;

	    return PJ_SUCCESS;

	} else {
	    /* Treat everything as part of the IPv6 IP address */
	    if (p_hostpart)
		*p_hostpart = *str;
	    if (p_port)
		*p_port = 0;

	    return PJ_SUCCESS;
	}

    } else {
	return PJ_EAFNOTSUP;
    }

}
Beispiel #7
0
/* Validate SDP session descriptor. */
PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
{
    unsigned i;
    const pj_str_t STR_RTPMAP = { "rtpmap", 6 };

    CHECK( sdp != NULL, PJ_EINVAL);

    /* Validate origin line. */
    CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0, 
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
	   pj_strcmp2(&sdp->origin.addr_type, "IP6")==0, 
	   PJMEDIA_SDP_EINORIGIN);
    CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);

    /* Validate subject line. */
    CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);

    /* Ignore start and stop time. */

    /* If session level connection info is present, validate it. */
    if (sdp->conn) {
	pj_status_t status = validate_sdp_conn(sdp->conn);
	if (status != PJ_SUCCESS)
	    return status;
    }

    /* Validate each media. */
    for (i=0; i<sdp->media_count; ++i) {
	const pjmedia_sdp_media *m = sdp->media[i];
	unsigned j;

	/* Validate the m= line. */
	CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
	CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT);

	/* If media level connection info is present, validate it. */
	if (m->conn) {
	    pj_status_t status = validate_sdp_conn(m->conn);
	    if (status != PJ_SUCCESS)
		return status;
	}

	/* If media doesn't have connection info, then connection info
	 * must be present in the session.
	 */
	if (m->conn == NULL) {
	    if (sdp->conn == NULL)
		return PJMEDIA_SDP_EMISSINGCONN;
	}

	/* Verify payload type. */
	for (j=0; j<m->desc.fmt_count; ++j) {

	    /* Arrgh noo!! Payload type can be non-numeric!!
	     * RTC based programs sends "null" for instant messaging!
	     */
	    if (pj_isdigit(*m->desc.fmt[j].ptr)) {
		unsigned pt = pj_strtoul(&m->desc.fmt[j]);

		/* Payload type is between 0 and 127. 
		 */
		CHECK( pt <= 127, PJMEDIA_SDP_EINPT);

		/* If port is not zero, then for each dynamic payload type, an
		 * rtpmap attribute must be specified.
		 */
		if (m->desc.port != 0 && pt >= 96) {
		    const pjmedia_sdp_attr *a;

		    a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP, 
						    &m->desc.fmt[j]);
		    CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
		}
	    }
	}
    }

    /* Looks good. */
    return PJ_SUCCESS;
}
Beispiel #8
0
/* Parse crypto attribute line */
static pj_status_t parse_attr_crypto(pj_pool_t *pool,
				     const pjmedia_sdp_attr *attr,
				     pjmedia_srtp_crypto *crypto,
				     int *tag)
{
    pj_str_t input;
    char *token;
    int token_len;
    pj_str_t tmp;
    pj_status_t status;
    int itmp;

    pj_bzero(crypto, sizeof(*crypto));
    pj_strdup_with_null(pool, &input, &attr->value);

    /* Tag */
    token = strtok(input.ptr, " ");
    if (!token) {
	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag"));
	return PJMEDIA_SDP_EINATTR;
    }
    token_len = pj_ansi_strlen(token);

    /* Tag must not use leading zeroes. */
    if (token_len > 1 && *token == '0')
	return PJMEDIA_SDP_EINATTR;

    /* Tag must be decimal, i.e: contains only digit '0'-'9'. */
    for (itmp = 0; itmp < token_len; ++itmp)
	if (!pj_isdigit(token[itmp]))
	    return PJMEDIA_SDP_EINATTR;

    /* Get tag value. */
    *tag = atoi(token);

    /* Crypto-suite */
    token = strtok(NULL, " ");
    if (!token) {
	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite"));
	return PJMEDIA_SDP_EINATTR;
    }
    crypto->name = pj_str(token);

    /* Key method */
    token = strtok(NULL, ":");
    if (!token) {
	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method"));
	return PJMEDIA_SDP_EINATTR;
    }
    if (pj_ansi_stricmp(token, "inline")) {
	PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!",
	          token));
	return PJMEDIA_SDP_EINATTR;
    }

    /* Key */
    token = strtok(NULL, "| ");
    if (!token) {
	PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key"));
	return PJMEDIA_SDP_EINATTR;
    }
    tmp = pj_str(token);
    crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN);

    /* Decode key */
    itmp = MAX_KEY_LEN;
    status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, 
			      &itmp);
    if (status != PJ_SUCCESS) {
	PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64"));
	return status;
    }
    crypto->key.slen = itmp;

    return PJ_SUCCESS;
}
Beispiel #9
0
/* Try to match offer with answer. */
static pj_status_t match_offer(pj_pool_t *pool,
			       const pjmedia_sdp_media *offer,
			       const pjmedia_sdp_media *preanswer,
			       const pjmedia_sdp_media *orig_local,
			       pjmedia_sdp_media **p_answer)
{
    unsigned i;
    pj_bool_t offer_has_codec = 0,
	      offer_has_telephone_event = 0,
	      offer_has_other = 0,
	      found_matching_codec = 0,
	      found_matching_telephone_event = 0,
	      found_matching_other = 0;
    unsigned pt_answer_count = 0;
    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
    pjmedia_sdp_media *answer;

    /* With the addition of telephone-event and dodgy MS RTC SDP, 
     * the answer generation algorithm looks really shitty...
     */
    for (i=0; i<offer->desc.fmt_count; ++i) {
	unsigned j;
	
	if (pj_isdigit(*offer->desc.fmt[i].ptr)) {
	    /* This is normal/standard payload type, where it's identified
	     * by payload number.
	     */
	    unsigned pt;

	    pt = pj_strtoul(&offer->desc.fmt[i]);
	    
	    if (pt < 96) {
		/* For static payload type, it's enough to compare just
		 * the payload number.
		 */

		offer_has_codec = 1;

		/* We just need to select one codec. 
		 * Continue if we have selected matching codec for previous 
		 * payload.
		 */
		if (found_matching_codec)
		    continue;

		/* Find matching codec in local descriptor. */
		for (j=0; j<preanswer->desc.fmt_count; ++j) {
		    unsigned p;
		    p = pj_strtoul(&preanswer->desc.fmt[j]);
		    if (p == pt && pj_isdigit(*preanswer->desc.fmt[j].ptr)) {
			found_matching_codec = 1;
			pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
			break;
		    }
		}

	    } else {
		/* This is dynamic payload type.
		 * For dynamic payload type, we must look the rtpmap and
		 * compare the encoding name.
		 */
		const pjmedia_sdp_attr *a;
		pjmedia_sdp_rtpmap or_;
		pj_bool_t is_codec;

		/* Get the rtpmap for the payload type in the offer. */
		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", 
						 &offer->desc.fmt[i]);
		if (!a) {
		    pj_assert(!"Bug! Offer should have been validated");
		    return PJMEDIA_SDP_EMISSINGRTPMAP;
		}
		pjmedia_sdp_attr_get_rtpmap(a, &or_);

		if (!pj_strcmp2(&or_.enc_name, "telephone-event")) {
		    offer_has_telephone_event = 1;
		    if (found_matching_telephone_event)
			continue;
		    is_codec = 0;
		} else {
		    offer_has_codec = 1;
		    if (found_matching_codec)
			continue;
		    is_codec = 1;
		}
		
		/* Find paylaod in our initial SDP with matching 
		 * encoding name and clock rate.
		 */
		for (j=0; j<preanswer->desc.fmt_count; ++j) {
		    a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap", 
						     &preanswer->desc.fmt[j]);
		    if (a) {
			pjmedia_sdp_rtpmap lr;
			pjmedia_sdp_attr_get_rtpmap(a, &lr);

			/* See if encoding name, clock rate, and
			 * channel count  match 
			 */
			if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
			    or_.clock_rate == lr.clock_rate &&
			    (pj_strcmp(&or_.param, &lr.param)==0 ||
			     (or_.param.slen==1 && *or_.param.ptr=='1'))) 
			{
			    /* Match! */
			    if (is_codec) {
				/* Further check for G7221, negotiate bitrate. */
				if (pj_strcmp2(&or_.enc_name, "G7221")  == 0 &&
				    match_g7221(offer, i, preanswer, j) == 0)
				{
				    continue;
				}
				found_matching_codec = 1;
			    } else {
				found_matching_telephone_event = 1;
			    }

			    pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
			    break;
			}
		    }
		}
	    }

	} else {
	    /* This is a non-standard, brain damaged SDP where the payload
	     * type is non-numeric. It exists e.g. in Microsoft RTC based
	     * UA, to indicate instant messaging capability.
	     * Example:
	     *	- m=x-ms-message 5060 sip null
	     */
	    offer_has_other = 1;
	    if (found_matching_other)
		continue;

	    for (j=0; j<preanswer->desc.fmt_count; ++j) {
		if (!pj_strcmp(&offer->desc.fmt[i], &preanswer->desc.fmt[j])) {
		    /* Match */
		    found_matching_other = 1;
		    pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
		    break;
		}
	    }
	}
    }

    /* See if all types of offer can be matched. */
    if (offer_has_codec && !found_matching_codec) {
	return PJMEDIA_SDPNEG_NOANSCODEC;
    }

    /* If this comment is removed, negotiation will fail if remote has offered
       telephone-event and local is not configured with telephone-event

    if (offer_has_telephone_event && !found_matching_telephone_event) {
	return PJMEDIA_SDPNEG_NOANSTELEVENT;
    }
    */

    if (offer_has_other && !found_matching_other) {
	return PJMEDIA_SDPNEG_NOANSUNKNOWN;
    }

    /* Seems like everything is in order.
     * Build the answer by cloning from local media, but rearrange the payload
     * to suit the offer.
     */
    answer = pjmedia_sdp_media_clone(pool, orig_local);
    for (i=0; i<pt_answer_count; ++i) {
	unsigned j;
	for (j=i; j<answer->desc.fmt_count; ++j) {
	    if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
		break;
	}
	pj_assert(j != answer->desc.fmt_count);
	str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
    }
    
    /* Remove unwanted local formats. */
    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
	pjmedia_sdp_attr *a;

	/* Remove rtpmap for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}

	/* Remove fmtp for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}
    }
    answer->desc.fmt_count = pt_answer_count;

    /* If offer has zero port, set our answer with zero port too */
    if (offer->desc.port==0)
	answer->desc.port = 0;

    /* Update media direction. */
    update_media_direction(pool, offer, answer);

    *p_answer = answer;
    return PJ_SUCCESS;
}
Beispiel #10
0
/*
 * Create stream info from SDP media line.
 */
PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
					   pjmedia_stream_info *si,
					   pj_pool_t *pool,
					   pjmedia_endpt *endpt,
					   const pjmedia_sdp_session *local,
					   const pjmedia_sdp_session *remote,
					   unsigned stream_idx)
{
    pjmedia_codec_mgr *mgr;
    const pjmedia_sdp_attr *attr;
    const pjmedia_sdp_media *local_m;
    const pjmedia_sdp_media *rem_m;
    const pjmedia_sdp_conn *local_conn;
    const pjmedia_sdp_conn *rem_conn;
    int rem_af, local_af;
    pj_sockaddr local_addr;
    pjmedia_sdp_rtpmap *rtpmap;
    unsigned i, pt, fmti;
    pj_status_t status;

    
    /* Validate arguments: */
    PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
    PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
    PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);


    /* Get codec manager. */
    mgr = pjmedia_endpt_get_codec_mgr(endpt);

    /* Keep SDP shortcuts */
    local_m = local->media[stream_idx];
    rem_m = remote->media[stream_idx];

    local_conn = local_m->conn ? local_m->conn : local->conn;
    if (local_conn == NULL)
	return PJMEDIA_SDP_EMISSINGCONN;

    rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
    if (rem_conn == NULL)
	return PJMEDIA_SDP_EMISSINGCONN;


    /* Reset: */

    pj_bzero(si, sizeof(*si));

#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
    /* Set default RTCP XR enabled/disabled */
    si->rtcp_xr_enabled = PJ_TRUE;
#endif

    /* Media type: */

    if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) {

	si->type = PJMEDIA_TYPE_AUDIO;

    } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) {

	si->type = PJMEDIA_TYPE_VIDEO;

    } else {

	si->type = PJMEDIA_TYPE_UNKNOWN;

	/* Avoid rejecting call because of unrecognized media, 
	 * just return PJ_SUCCESS, this media will be deactivated later.
	 */
	//return PJMEDIA_EINVALIMEDIATYPE;
	return PJ_SUCCESS;

    }

    /* Transport protocol */

    /* At this point, transport type must be compatible, 
     * the transport instance will do more validation later.
     */
    status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, 
				       &local_m->desc.transport);
    if (status != PJ_SUCCESS)
	return PJMEDIA_SDPNEG_EINVANSTP;

    if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {

	si->proto = PJMEDIA_TP_PROTO_RTP_AVP;

    } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {

	si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;

    } else {

	si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
	return PJ_SUCCESS;
    }


    /* Check address family in remote SDP */
    rem_af = pj_AF_UNSPEC();
    if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
	if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
	    rem_af = pj_AF_INET();
	} else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
	    rem_af = pj_AF_INET6();
	}
    }

    if (rem_af==pj_AF_UNSPEC()) {
	/* Unsupported address family */
	return PJ_EAFNOTSUP;
    }

    /* Set remote address: */
    status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, 
			      rem_m->desc.port);
    if (status != PJ_SUCCESS) {
	/* Invalid IP address. */
	return PJMEDIA_EINVALIDIP;
    }

    /* Check address family of local info */
    local_af = pj_AF_UNSPEC();
    if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
	if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
	    local_af = pj_AF_INET();
	} else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
	    local_af = pj_AF_INET6();
	}
    }

    if (local_af==pj_AF_UNSPEC()) {
	/* Unsupported address family */
	return PJ_SUCCESS;
    }

    /* Set remote address: */
    status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, 
			      local_m->desc.port);
    if (status != PJ_SUCCESS) {
	/* Invalid IP address. */
	return PJMEDIA_EINVALIDIP;
    }

    /* Local and remote address family must match */
    if (local_af != rem_af)
	return PJ_EAFNOTSUP;

    /* Media direction: */

    if (local_m->desc.port == 0 || 
	pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
	pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
	pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
    {
	/* Inactive stream. */

	si->dir = PJMEDIA_DIR_NONE;

    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {

	/* Send only stream. */

	si->dir = PJMEDIA_DIR_ENCODING;

    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {

	/* Recv only stream. */

	si->dir = PJMEDIA_DIR_DECODING;

    } else {

	/* Send and receive stream. */

	si->dir = PJMEDIA_DIR_ENCODING_DECODING;

    }

    /* No need to do anything else if stream is rejected */
    if (local_m->desc.port == 0) {
	return PJ_SUCCESS;
    }

    /* If "rtcp" attribute is present in the SDP, set the RTCP address
     * from that attribute. Otherwise, calculate from RTP address.
     */
    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
				  "rtcp", NULL);
    if (attr) {
	pjmedia_sdp_rtcp_attr rtcp;
	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
	if (status == PJ_SUCCESS) {
	    if (rtcp.addr.slen) {
		status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
					  (pj_uint16_t)rtcp.port);
	    } else {
		pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, 
				 (pj_uint16_t)rtcp.port);
		pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
		          pj_sockaddr_get_addr(&si->rem_addr),
			  pj_sockaddr_get_addr_len(&si->rem_addr));
	    }
	}
    }
    
    if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
	int rtcp_port;

	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
	rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
	pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
    }


    /* Get the payload number for receive channel. */
    /*
       Previously we used to rely on fmt[0] being the selected codec,
       but some UA sends telephone-event as fmt[0] and this would
       cause assert failure below.

       Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.

    // And codec must be numeric!
    if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 
	!pj_isdigit(*rem_m->desc.fmt[0].ptr))
    {
	return PJMEDIA_EINVALIDPT;
    }

    pt = pj_strtoul(&local_m->desc.fmt[0]);
    pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
	      pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
    */

    /* This is to suppress MSVC warning about uninitialized var */
    pt = 0;

    /* Find the first codec which is not telephone-event */
    for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
	if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
	    return PJMEDIA_EINVALIDPT;
	pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
		pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
		break;
    }
    if ( fmti >= local_m->desc.fmt_count )
	return PJMEDIA_EINVALIDPT;

    /* Get codec info.
     * For static payload types, get the info from codec manager.
     * For dynamic payload types, MUST get the rtpmap.
     */
    if (pt < 96) {
	pj_bool_t has_rtpmap;

	rtpmap = NULL;
	has_rtpmap = PJ_TRUE;

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL) {
	    has_rtpmap = PJ_FALSE;
	}
	if (attr != NULL) {
	    status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	    if (status != PJ_SUCCESS)
		has_rtpmap = PJ_FALSE;
	}

	/* Build codec format info: */
	if (has_rtpmap) {
	    si->fmt.type = si->type;
	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
	    si->fmt.clock_rate = rtpmap->clock_rate;
	    
#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
	    /* The session info should have the actual clock rate, because 
	     * this info is used for calculationg buffer size, etc in stream 
	     */
	    if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
		si->fmt.clock_rate = 16000;
#endif

	    /* For audio codecs, rtpmap parameters denotes the number of
	     * channels.
	     */
	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
		si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
	    } else {
		si->fmt.channel_cnt = 1;
	    }

	} else {	    
	    const pjmedia_codec_info *p_info;

	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
	    if (status != PJ_SUCCESS)
		return status;

	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
	}

	/* For static payload type, pt's are symetric */
	si->tx_pt = pt;

    } else {

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL)
	    return PJMEDIA_EMISSINGRTPMAP;

	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	if (status != PJ_SUCCESS)
	    return status;

	/* Build codec format info: */

	si->fmt.type = si->type;
	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
	si->fmt.clock_rate = rtpmap->clock_rate;

	/* For audio codecs, rtpmap parameters denotes the number of
	 * channels.
	 */
	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
	    si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
	} else {
	    si->fmt.channel_cnt = 1;
	}

	/* Determine payload type for outgoing channel, by finding
	 * dynamic payload type in remote SDP that matches the answer.
	 */
	si->tx_pt = 0xFFFF;
	for (i=0; i<rem_m->desc.fmt_count; ++i) {
	    unsigned rpt;
	    pjmedia_sdp_attr *r_attr;
	    pjmedia_sdp_rtpmap r_rtpmap;

	    rpt = pj_strtoul(&rem_m->desc.fmt[i]);
	    if (rpt < 96)
		continue;

	    r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
						 &rem_m->desc.fmt[i]);
	    if (!r_attr)
		continue;

	    if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
		continue;

	    if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
		rtpmap->clock_rate == r_rtpmap.clock_rate)
	    {
		/* Found matched codec. */
		si->tx_pt = rpt;

		break;
	    }
	}

	if (si->tx_pt == 0xFFFF)
	    return PJMEDIA_EMISSINGRTPMAP;
    }

  
    /* Now that we have codec info, get the codec param. */
    si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
    status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param);

    /* Get remote fmtp for our encoder. */
    parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp);

    /* Get local fmtp for our decoder. */
    parse_fmtp(pool, local_m, si->fmt.pt, &si->param->setting.dec_fmtp);

    /* Get remote maxptime for our encoder. */
    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
				  "maxptime", NULL);
    if (attr) {
	pj_str_t tmp_val = attr->value;

	pj_strltrim(&tmp_val);
	si->tx_maxptime = pj_strtoul(&tmp_val);
    }

    /* When direction is NONE (it means SDP negotiation has failed) we don't
     * need to return a failure here, as returning failure will cause
     * the whole SDP to be rejected. See ticket #:
     *	http://
     *
     * Thanks Alain Totouom 
     */
    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
	return status;


    /* Get incomming payload type for telephone-events */
    si->rx_event_pt = -1;
    for (i=0; i<local_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = local_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->rx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    /* Get outgoing payload type for telephone-events */
    si->tx_event_pt = -1;
    for (i=0; i<rem_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = rem_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->tx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    /* Leave SSRC to random. */
    si->ssrc = pj_rand();

    /* Set default jitter buffer parameter. */
    si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;

    return PJ_SUCCESS;
}
Beispiel #11
0
/*
 * Internal function for collecting codec info and param from the SDP media.
 */
static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si,
					      pj_pool_t *pool,
					      pjmedia_codec_mgr *mgr,
					      const pjmedia_sdp_media *local_m,
					      const pjmedia_sdp_media *rem_m)
{
    const pjmedia_sdp_attr *attr;
    pjmedia_sdp_rtpmap *rtpmap;
    unsigned i, fmti, pt = 0;
    pj_status_t status;

    /* Find the first codec which is not telephone-event */
    for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
	pjmedia_sdp_rtpmap r;

	if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
	    return PJMEDIA_EINVALIDPT;
	pt = pj_strtoul(&local_m->desc.fmt[fmti]);

	if (pt < 96) {
	    /* This is known static PT. Skip rtpmap checking because it is
	     * optional. */
	    break;
	}

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL)
	    continue;

	status = pjmedia_sdp_attr_get_rtpmap(attr, &r);
	if (status != PJ_SUCCESS)
	    continue;

	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) != 0)
	    break;
    }
    if ( fmti >= local_m->desc.fmt_count )
	return PJMEDIA_EINVALIDPT;

    /* Get payload type for receiving direction */
    si->rx_pt = pt;

    /* Get codec info.
     * For static payload types, get the info from codec manager.
     * For dynamic payload types, MUST get the rtpmap.
     */
    if (pt < 96) {
	pj_bool_t has_rtpmap;

	rtpmap = NULL;
	has_rtpmap = PJ_TRUE;

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL) {
	    has_rtpmap = PJ_FALSE;
	}
	if (attr != NULL) {
	    status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	    if (status != PJ_SUCCESS)
		has_rtpmap = PJ_FALSE;
	}

	/* Build codec format info: */
	if (has_rtpmap) {
	    si->fmt.type = si->type;
	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
	    si->fmt.clock_rate = rtpmap->clock_rate;

#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
	    /* The session info should have the actual clock rate, because
	     * this info is used for calculationg buffer size, etc in stream
	     */
	    if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
		si->fmt.clock_rate = 16000;
#endif

	    /* For audio codecs, rtpmap parameters denotes the number of
	     * channels.
	     */
	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
		si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
	    } else {
		si->fmt.channel_cnt = 1;
	    }

	} else {
	    const pjmedia_codec_info *p_info;

	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
	    if (status != PJ_SUCCESS)
		return status;

	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
	}

	/* For static payload type, pt's are symetric */
	si->tx_pt = pt;

    } else {
	pjmedia_codec_id codec_id;
	pj_str_t codec_id_st;
	const pjmedia_codec_info *p_info;

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL)
	    return PJMEDIA_EMISSINGRTPMAP;

	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	if (status != PJ_SUCCESS)
	    return status;

	/* Build codec format info: */

	si->fmt.type = si->type;
	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	si->fmt.encoding_name = rtpmap->enc_name;
	si->fmt.clock_rate = rtpmap->clock_rate;

	/* For audio codecs, rtpmap parameters denotes the number of
	 * channels.
	 */
	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
	    si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
	} else {
	    si->fmt.channel_cnt = 1;
	}

	/* Normalize the codec info from codec manager. Note that the
	 * payload type will be resetted to its default (it might have
	 * been rewritten by the SDP negotiator to match to the remote
	 * offer), this is intentional as currently some components may
	 * prefer (or even require) the default PT in codec info.
	 */
	pjmedia_codec_info_to_id(&si->fmt, codec_id, sizeof(codec_id));

	i = 1;
	codec_id_st = pj_str(codec_id);
	status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id_st,
						     &i, &p_info, NULL);
	if (status != PJ_SUCCESS)
	    return status;

	pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));

	/* Determine payload type for outgoing channel, by finding
	 * dynamic payload type in remote SDP that matches the answer.
	 */
	si->tx_pt = 0xFFFF;
	for (i=0; i<rem_m->desc.fmt_count; ++i) {
	    if (pjmedia_sdp_neg_fmt_match(pool,
					  (pjmedia_sdp_media*)local_m, fmti,
					  (pjmedia_sdp_media*)rem_m, i, 0) ==
		PJ_SUCCESS)
	    {
		/* Found matched codec. */
		si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]);
		break;
	    }
	}

	if (si->tx_pt == 0xFFFF)
	    return PJMEDIA_EMISSINGRTPMAP;
    }


    /* Now that we have codec info, get the codec param. */
    si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
    status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt,
					         si->param);

    /* Get remote fmtp for our encoder. */
    pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
				   &si->param->setting.enc_fmtp);

    /* Get local fmtp for our decoder. */
    pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt,
				   &si->param->setting.dec_fmtp);

    if (!pj_stricmp2(&si->fmt.encoding_name, "opus")) {
	get_opus_channels_and_clock_rate(&si->param->setting.enc_fmtp,
					 &si->param->setting.dec_fmtp,
					 &si->fmt.channel_cnt,
					 &si->fmt.clock_rate);
    }


    /* Get the remote ptime for our encoder. */
    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
				  "ptime", NULL);
    if (attr) {
	pj_str_t tmp_val = attr->value;
	unsigned frm_per_pkt;

	pj_strltrim(&tmp_val);

	/* Round up ptime when the specified is not multiple of frm_ptime */
	frm_per_pkt = (pj_strtoul(&tmp_val) +
		      si->param->info.frm_ptime/2) /
		      si->param->info.frm_ptime;
	if (frm_per_pkt != 0) {
            si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt;
        }
    }

    /* Get remote maxptime for our encoder. */
    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
				  "maxptime", NULL);
    if (attr) {
	pj_str_t tmp_val = attr->value;

	pj_strltrim(&tmp_val);
	si->tx_maxptime = pj_strtoul(&tmp_val);
    }

    /* When direction is NONE (it means SDP negotiation has failed) we don't
     * need to return a failure here, as returning failure will cause
     * the whole SDP to be rejected. See ticket #:
     *	http://
     *
     * Thanks Alain Totouom
     */
    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
	return status;


    /* Get incomming payload type for telephone-events */
    si->rx_event_pt = -1;
    for (i=0; i<local_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = local_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->rx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    /* Get outgoing payload type for telephone-events */
    si->tx_event_pt = -1;
    for (i=0; i<rem_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = rem_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->tx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    return PJ_SUCCESS;
}
Beispiel #12
0
/* Try to match offer with answer. */
static pj_status_t match_offer(pj_pool_t *pool,
			       pj_bool_t prefer_remote_codec_order,
                               pj_bool_t answer_with_multiple_codecs,
			       const pjmedia_sdp_media *offer,
			       const pjmedia_sdp_media *preanswer,
			       const pjmedia_sdp_session *preanswer_sdp,
			       pjmedia_sdp_media **p_answer)
{
    unsigned i;
    pj_bool_t master_has_codec = 0,
	      master_has_other = 0,
	      found_matching_codec = 0,
	      found_matching_telephone_event = 0,
	      found_matching_other = 0;
    unsigned pt_answer_count = 0;
    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
    pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT];
    pjmedia_sdp_media *answer;
    const pjmedia_sdp_media *master, *slave;

    /* If offer has zero port, just clone the offer */
    if (offer->desc.port == 0) {
	answer = sdp_media_clone_deactivate(pool, offer, preanswer,
					    preanswer_sdp);
	*p_answer = answer;
	return PJ_SUCCESS;
    }

    /* If the preanswer define zero port, this media is being rejected,
     * just clone the preanswer.
     */
    if (preanswer->desc.port == 0) {
	answer = pjmedia_sdp_media_clone(pool, preanswer);
	*p_answer = answer;
	return PJ_SUCCESS;
    }

    /* Set master/slave negotiator based on prefer_remote_codec_order. */
    if (prefer_remote_codec_order) {
	master = offer;
	slave  = preanswer;
    } else {
	master = preanswer;
	slave  = offer;
    }
    
    /* With the addition of telephone-event and dodgy MS RTC SDP, 
     * the answer generation algorithm looks really shitty...
     */
    for (i=0; i<master->desc.fmt_count; ++i) {
	unsigned j;
	
	if (pj_isdigit(*master->desc.fmt[i].ptr)) {
	    /* This is normal/standard payload type, where it's identified
	     * by payload number.
	     */
	    unsigned pt;

	    pt = pj_strtoul(&master->desc.fmt[i]);
	    
	    if (pt < 96) {
		/* For static payload type, it's enough to compare just
		 * the payload number.
		 */

		master_has_codec = 1;

		/* We just need to select one codec if not allowing multiple.
		 * Continue if we have selected matching codec for previous 
		 * payload.
		 */
		if (!answer_with_multiple_codecs && found_matching_codec)
		    continue;

		/* Find matching codec in local descriptor. */
		for (j=0; j<slave->desc.fmt_count; ++j) {
		    unsigned p;
		    p = pj_strtoul(&slave->desc.fmt[j]);
		    if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) {
			found_matching_codec = 1;
			pt_offer[pt_answer_count] = slave->desc.fmt[j];
			pt_answer[pt_answer_count++] = slave->desc.fmt[j];
			break;
		    }
		}

	    } else {
		/* This is dynamic payload type.
		 * For dynamic payload type, we must look the rtpmap and
		 * compare the encoding name.
		 */
		const pjmedia_sdp_attr *a;
		pjmedia_sdp_rtpmap or_;
		pj_bool_t is_codec;

		/* Get the rtpmap for the payload type in the master. */
		a = pjmedia_sdp_media_find_attr2(master, "rtpmap", 
						 &master->desc.fmt[i]);
		if (!a) {
		    pj_assert(!"Bug! Offer should have been validated");
		    return PJMEDIA_SDP_EMISSINGRTPMAP;
		}
		pjmedia_sdp_attr_get_rtpmap(a, &or_);

		if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
		    if (found_matching_telephone_event)
			continue;
		    is_codec = 0;
		} else {
		    master_has_codec = 1;
		    if (!answer_with_multiple_codecs && found_matching_codec)
			continue;
		    is_codec = 1;
		}
		
		/* Find paylaod in our initial SDP with matching 
		 * encoding name and clock rate.
		 */
		for (j=0; j<slave->desc.fmt_count; ++j) {
		    a = pjmedia_sdp_media_find_attr2(slave, "rtpmap", 
						     &slave->desc.fmt[j]);
		    if (a) {
			pjmedia_sdp_rtpmap lr;
			pjmedia_sdp_attr_get_rtpmap(a, &lr);

			/* See if encoding name, clock rate, and
			 * channel count  match 
			 */
			if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
			    or_.clock_rate == lr.clock_rate &&
			    (pj_stricmp(&or_.param, &lr.param)==0 ||
			     (lr.param.slen==0 && or_.param.slen==1 && 
						 *or_.param.ptr=='1') || 
			     (or_.param.slen==0 && lr.param.slen==1 && 
						  *lr.param.ptr=='1'))) 
			{
			    /* Match! */
			    if (is_codec) {
				pjmedia_sdp_media *o_med, *a_med;
				unsigned o_fmt_idx, a_fmt_idx;

				o_med = (pjmedia_sdp_media*)offer;
				a_med = (pjmedia_sdp_media*)preanswer;
				o_fmt_idx = prefer_remote_codec_order? i:j;
				a_fmt_idx = prefer_remote_codec_order? j:i;

				/* Call custom format matching callbacks */
				if (custom_fmt_match(pool, &or_.enc_name,
						     o_med, o_fmt_idx,
						     a_med, a_fmt_idx,
						     ALLOW_MODIFY_ANSWER) !=
				    PJ_SUCCESS)
				{
				    continue;
				}
				found_matching_codec = 1;
			    } else {
				found_matching_telephone_event = 1;
			    }

			    pt_offer[pt_answer_count] = 
						prefer_remote_codec_order?
						offer->desc.fmt[i]:
						offer->desc.fmt[j];
			    pt_answer[pt_answer_count++] = 
						prefer_remote_codec_order? 
						preanswer->desc.fmt[j]:
						preanswer->desc.fmt[i];
			    break;
			}
		    }
		}
	    }

	} else {
	    /* This is a non-standard, brain damaged SDP where the payload
	     * type is non-numeric. It exists e.g. in Microsoft RTC based
	     * UA, to indicate instant messaging capability.
	     * Example:
	     *	- m=x-ms-message 5060 sip null
	     */
	    master_has_other = 1;
	    if (found_matching_other)
		continue;

	    for (j=0; j<slave->desc.fmt_count; ++j) {
		if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) {
		    /* Match */
		    found_matching_other = 1;
		    pt_offer[pt_answer_count] = prefer_remote_codec_order?
						offer->desc.fmt[i]:
						offer->desc.fmt[j];
		    pt_answer[pt_answer_count++] = prefer_remote_codec_order? 
						   preanswer->desc.fmt[j]:
						   preanswer->desc.fmt[i];
		    break;
		}
	    }
	}
    }

    /* See if all types of master can be matched. */
    if (master_has_codec && !found_matching_codec) {
	return PJMEDIA_SDPNEG_NOANSCODEC;
    }

    /* If this comment is removed, negotiation will fail if remote has offered
       telephone-event and local is not configured with telephone-event

    if (offer_has_telephone_event && !found_matching_telephone_event) {
	return PJMEDIA_SDPNEG_NOANSTELEVENT;
    }
    */

    if (master_has_other && !found_matching_other) {
	return PJMEDIA_SDPNEG_NOANSUNKNOWN;
    }

    /* Seems like everything is in order.
     * Build the answer by cloning from preanswer, but rearrange the payload
     * to suit the offer.
     */
    answer = pjmedia_sdp_media_clone(pool, preanswer);
    for (i=0; i<pt_answer_count; ++i) {
	unsigned j;
	for (j=i; j<answer->desc.fmt_count; ++j) {
	    if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
		break;
	}
	pj_assert(j != answer->desc.fmt_count);
	str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
    }
    
    /* Remove unwanted local formats. */
    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
	pjmedia_sdp_attr *a;

	/* Remove rtpmap for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}

	/* Remove fmtp for this format */
	a = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
					 &answer->desc.fmt[i]);
	if (a) {
	    pjmedia_sdp_media_remove_attr(answer, a);
	}
    }
    answer->desc.fmt_count = pt_answer_count;

#if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT
    apply_answer_symmetric_pt(pool, answer, pt_answer_count,
			      pt_offer, pt_answer);
#endif

    /* Update media direction. */
    update_media_direction(pool, offer, answer);

    *p_answer = answer;
    return PJ_SUCCESS;
}
Beispiel #13
0
/*
 * Create stream info from SDP media line.
 */
PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
					   pjmedia_stream_info *si,
					   pj_pool_t *pool,
					   pjmedia_endpt *endpt,
					   const pjmedia_sdp_session *local,
					   const pjmedia_sdp_session *remote,
					   unsigned stream_idx)
{
    pjmedia_codec_mgr *mgr;
    const pjmedia_sdp_attr *attr;
    const pjmedia_sdp_media *local_m;
    const pjmedia_sdp_media *rem_m;
    const pjmedia_sdp_conn *local_conn;
    const pjmedia_sdp_conn *rem_conn;
    pjmedia_sdp_rtpmap *rtpmap;
    int local_fmtp_mode = 0, rem_fmtp_mode = 0;
    unsigned i, pt, fmti;
    pj_status_t status;

    
    /* Validate arguments: */
    PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
    PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
    PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);


    /* Get codec manager. */
    mgr = pjmedia_endpt_get_codec_mgr(endpt);

    /* Keep SDP shortcuts */
    local_m = local->media[stream_idx];
    rem_m = remote->media[stream_idx];

    local_conn = local_m->conn ? local_m->conn : local->conn;
    if (local_conn == NULL)
	return PJMEDIA_SDP_EMISSINGCONN;

    rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
    if (rem_conn == NULL)
	return PJMEDIA_SDP_EMISSINGCONN;


    /* Reset: */

    pj_bzero(si, sizeof(*si));

    /* Media type: */

    if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) {

	si->type = PJMEDIA_TYPE_AUDIO;

    } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) {

	si->type = PJMEDIA_TYPE_VIDEO;

    } else {

	si->type = PJMEDIA_TYPE_UNKNOWN;
	return PJMEDIA_EINVALIMEDIATYPE;
    }

    /* Transport type must be equal */
    if (pj_stricmp(&rem_m->desc.transport, 
		   &local_m->desc.transport) != 0) 
    {
	si->type = PJMEDIA_TYPE_UNKNOWN;
	return PJMEDIA_SDPNEG_EINVANSTP;
    }

    /* Media direction: */

    if (local_m->desc.port == 0 || 
	pj_inet_addr(&local_conn->addr).s_addr==0 ||
	pj_inet_addr(&rem_conn->addr).s_addr==0 ||
	pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
    {
	/* Inactive stream. */

	si->dir = PJMEDIA_DIR_NONE;

    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {

	/* Send only stream. */

	si->dir = PJMEDIA_DIR_ENCODING;

    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {

	/* Recv only stream. */

	si->dir = PJMEDIA_DIR_DECODING;

    } else {

	/* Send and receive stream. */

	si->dir = PJMEDIA_DIR_ENCODING_DECODING;

    }


    /* Set remote address: */
    status = pj_sockaddr_in_init(&si->rem_addr, &rem_conn->addr, 
				 rem_m->desc.port);
    if (status != PJ_SUCCESS) {
	/* Invalid IP address. */
	return PJMEDIA_EINVALIDIP;
    }

    /* If "rtcp" attribute is present in the SDP, set the RTCP address
     * from that attribute. Otherwise, calculate from RTP address.
     */
    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
				  "rtcp", NULL);
    if (attr) {
	pjmedia_sdp_rtcp_attr rtcp;
	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
	if (status == PJ_SUCCESS) {
	    if (rtcp.addr.slen) {
		status = pj_sockaddr_in_init(&si->rem_rtcp, &rtcp.addr,
					     (pj_uint16_t)rtcp.port);
	    } else {
		pj_sockaddr_in_init(&si->rem_rtcp, NULL, 
				    (pj_uint16_t)rtcp.port);
		si->rem_rtcp.sin_addr.s_addr = si->rem_addr.sin_addr.s_addr;
	    }
	}
    }
    
    if (si->rem_rtcp.sin_addr.s_addr == 0) {
	int rtcp_port;

	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr_in));
	rtcp_port = pj_ntohs(si->rem_addr.sin_port) + 1;
	si->rem_rtcp.sin_port = pj_htons((pj_uint16_t)rtcp_port);
    }


    /* Get the payload number for receive channel. */
    /*
       Previously we used to rely on fmt[0] being the selected codec,
       but some UA sends telephone-event as fmt[0] and this would
       cause assert failure below.

       Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.

    // And codec must be numeric!
    if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 
	!pj_isdigit(*rem_m->desc.fmt[0].ptr))
    {
	return PJMEDIA_EINVALIDPT;
    }

    pt = pj_strtoul(&local_m->desc.fmt[0]);
    pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
	      pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
    */

    /* This is to suppress MSVC warning about uninitialized var */
    pt = 0;

    /* Find the first codec which is not telephone-event */
    for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
	if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
	    return PJMEDIA_EINVALIDPT;
	pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
		pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
		break;
    }
    if ( fmti >= local_m->desc.fmt_count )
	return PJMEDIA_EINVALIDPT;

    /* Get codec info.
     * For static payload types, get the info from codec manager.
     * For dynamic payload types, MUST get the rtpmap.
     */
    if (pt < 96) {
	pj_bool_t has_rtpmap;

	rtpmap = NULL;
	has_rtpmap = PJ_TRUE;

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL) {
	    has_rtpmap = PJ_FALSE;
	}
	if (attr != NULL) {
	    status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	    if (status != PJ_SUCCESS)
		has_rtpmap = PJ_FALSE;
	}

	/* Build codec format info: */
	if (has_rtpmap) {
	    si->fmt.type = si->type;
	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
	    si->fmt.clock_rate = rtpmap->clock_rate;
	    
	    /* For audio codecs, rtpmap parameters denotes the number of
	     * channels.
	     */
	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
		
		if (rtpmap->param.slen == 2) {
		    si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0';
		} else {
		    pj_str_t cnt;
		    cnt.ptr = rtpmap->param.ptr + 1;
		    cnt.slen = rtpmap->param.slen - 1;
		    si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt);
		}

	    } else {
		si->fmt.channel_cnt = 1;
	    }

	} else {	    
	    const pjmedia_codec_info *p_info;

	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
	    if (status != PJ_SUCCESS)
		return status;

	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
	}

	/* For static payload type, pt's are symetric */
	si->tx_pt = pt;

    } else {

	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
					   &local_m->desc.fmt[fmti]);
	if (attr == NULL)
	    return PJMEDIA_EMISSINGRTPMAP;

	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
	if (status != PJ_SUCCESS)
	    return status;

	/* Build codec format info: */

	si->fmt.type = si->type;
	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
	pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
	si->fmt.clock_rate = rtpmap->clock_rate;

	/* For audio codecs, rtpmap parameters denotes the number of
	 * channels.
	 */
	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
	    
	    if (rtpmap->param.slen == 2) {
		si->fmt.channel_cnt = rtpmap->param.ptr[1] - '0';
	    } else {
		pj_str_t cnt;
		cnt.ptr = rtpmap->param.ptr + 1;
		cnt.slen = rtpmap->param.slen - 1;
		si->fmt.channel_cnt = (unsigned) pj_strtoul(&cnt);
	    }

	} else {
	    si->fmt.channel_cnt = 1;
	}

	/* Get fmtp mode= param in local SDP, if any */
	get_fmtp_mode(local_m, &local_m->desc.fmt[fmti], &local_fmtp_mode);

	/* Determine payload type for outgoing channel, by finding
	 * dynamic payload type in remote SDP that matches the answer.
	 */
	si->tx_pt = 0xFFFF;
	for (i=0; i<rem_m->desc.fmt_count; ++i) {
	    unsigned rpt;
	    pjmedia_sdp_attr *r_attr;
	    pjmedia_sdp_rtpmap r_rtpmap;

	    rpt = pj_strtoul(&rem_m->desc.fmt[i]);
	    if (rpt < 96)
		continue;

	    r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
						 &rem_m->desc.fmt[i]);
	    if (!r_attr)
		continue;

	    if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
		continue;

	    if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
		rtpmap->clock_rate == r_rtpmap.clock_rate)
	    {
		/* Found matched codec. */
		si->tx_pt = rpt;

		/* Get fmtp mode param in remote SDP, if any */
		get_fmtp_mode(rem_m, &rtpmap->pt, &rem_fmtp_mode);

		break;
	    }
	}

	if (si->tx_pt == 0xFFFF)
	    return PJMEDIA_EMISSINGRTPMAP;
    }

  
    /* Now that we have codec info, get the codec param. */
    si->param = pj_pool_alloc(pool, sizeof(*si->param));
    status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param);
    if (status != PJ_SUCCESS)
	return status;

    /* Set fmtp mode for both local and remote */
    if (local_fmtp_mode != 0)
	si->param->setting.dec_fmtp_mode = (pj_int8_t)local_fmtp_mode;
    if (rem_fmtp_mode != 0)
	si->param->setting.enc_fmtp_mode = (pj_int8_t)rem_fmtp_mode;


    /* Get incomming payload type for telephone-events */
    si->rx_event_pt = -1;
    for (i=0; i<local_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = local_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->rx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    /* Get outgoing payload type for telephone-events */
    si->tx_event_pt = -1;
    for (i=0; i<rem_m->attr_count; ++i) {
	pjmedia_sdp_rtpmap r;

	attr = rem_m->attr[i];
	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
	    continue;
	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
	    continue;
	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
	    si->tx_event_pt = pj_strtoul(&r.pt);
	    break;
	}
    }

    /* Leave SSRC to random. */
    si->ssrc = pj_rand();

    /* Set default jitter buffer parameter. */
    si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;

    return PJ_SUCCESS;
}
Beispiel #14
0
/*
 * Input URL.
 */
static void ui_input_url(const char *title, char *buf, pj_size_t len,
			 input_result *result)
{
    result->nb_result = PJSUA_APP_NO_NB;
    result->uri_result = NULL;

    print_buddy_list();

    printf("Choices:\n"
	   "   0         For current dialog.\n"
	   "  -1         All %d buddies in buddy list\n"
	   "  [1 -%2d]    Select from buddy list\n"
	   "  URL        An URL\n"
	   "  <Enter>    Empty input (or 'q') to cancel\n"
	   , pjsua_get_buddy_count(), pjsua_get_buddy_count());
    printf("%s: ", title);

    fflush(stdout);
    if (fgets(buf, (int)len, stdin) == NULL)
	return;
    len = strlen(buf);

    /* Left trim */
    while (pj_isspace(*buf)) {
	++buf;
	--len;
    }

    /* Remove trailing newlines */
    while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
	buf[--len] = '\0';

    if (len == 0 || buf[0]=='q')
	return;

    if (pj_isdigit(*buf) || *buf=='-') {

	unsigned i;

	if (*buf=='-')
	    i = 1;
	else
	    i = 0;

	for (; i<len; ++i) {
	    if (!pj_isdigit(buf[i])) {
		puts("Invalid input");
		return;
	    }
	}

	result->nb_result = my_atoi(buf);

	if (result->nb_result >= 0 &&
	    result->nb_result <= (int)pjsua_get_buddy_count())
	{
	    return;
	}
	if (result->nb_result == -1)
	    return;

	puts("Invalid input");
	result->nb_result = PJSUA_APP_NO_NB;
	return;

    } else {
	pj_status_t status;

	if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Invalid URL", status);
	    return;
	}

	result->uri_result = buf;
    }
}