GB28181開發(二) pjsip庫SDP協議擴展

項目使用pjsip庫作爲底層協議庫,擴展支持GB28181協議,但pjsip的sdp編解碼不支持額外參數解析,需要修改源代碼,以便支持GB28181中關於SDP協議的擴展(例如y參數)。源碼主要在pjmedia/include/pjmedia/sdp.h和pjmedia/src/pjmedia/sdp.c上修改。

1、在sdp.h中擴展pjmedia_sdp_session定義,增加other_count和other字段,後面的修改都是在sdp.c中

/**
 * This structure describes SDP session description. A SDP session descriptor
 * contains complete information about a session, and normally is exchanged
 * with remote media peer using signaling protocol such as SIP.
 */
struct pjmedia_sdp_session
{
    /** Session origin (o= line) */
    struct
    {
	pj_str_t    user;	    /**< User 				*/
	pj_uint32_t id;		    /**< Session ID			*/
	pj_uint32_t version;	    /**< Session version		*/
	pj_str_t    net_type;	    /**< Network type ("IN")		*/
	pj_str_t    addr_type;	    /**< Address type ("IP4", "IP6")	*/
	pj_str_t    addr;	    /**< The address.			*/
    } origin;

    pj_str_t	       name;	    /**< Subject line (s=)		*/
    pjmedia_sdp_conn  *conn;	    /**< Connection line (c=)		*/
    unsigned	       bandw_count; /**< Number of bandwidth info (b=)	*/
    pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW];
				    /**< Bandwidth info array (b=)	*/
    
    /** Session time (t= line)	*/
    struct
    {
	pj_uint32_t start;	    /**< Start time.			*/
	pj_uint32_t stop;	    /**< Stop time.			*/
    } time;

    unsigned	       attr_count;		/**< Number of attributes.  */
    pjmedia_sdp_attr  *attr[PJMEDIA_MAX_SDP_ATTR]; /**< Attributes array.   */

    unsigned	       media_count;		/**< Number of media.	    */
    pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA];	/**< Media array.   */

	// 其他參數
	unsigned	       other_count;		/**< Number of Others.  */
	pjmedia_sdp_attr  *other[PJMEDIA_MAX_SDP_ATTR]; /**< Others array.   */
};

2、解碼部分增加對other參數的解析,在解碼函數pjmedia_sdp_parse裏,處理其他參數的位置,調用新增加的函數

case 'b':
		    bandw = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_bandw);
		    parse_bandwidth_info(&scanner, bandw, &ctx);
		    if (media) {
			if (media->bandw_count < PJMEDIA_MAX_SDP_BANDW)
			    media->bandw[media->bandw_count++] = bandw;
			else
			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					  "Error adding media bandwidth "
					  "info, info is ignored"));
		    } else {
			if (session->bandw_count < PJMEDIA_MAX_SDP_BANDW)
			    session->bandw[session->bandw_count++] = bandw;
			else
			    PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY,
					  "Error adding session bandwidth "
					  "info, info is ignored"));
		    }
		    break;
		default:
			if (cur_name >= 'a' && cur_name <= 'z') {
				// 處理其他參數
				parse_other_line(&scanner, session, &ctx, pool);
			}
		    else  {
			ctx.last_error = PJMEDIA_SDP_EINSDP;
			on_scanner_error(&scanner);
		    }
		    break;
		}

新增parse_other_line函數處理其他參數的解析,這裏參照attr參數的解析流程

增加函數定義和實現

static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
			       parse_context *ctx);
// 擴展支持其他參數的解析
static void parse_other_line(pj_scanner *scanner, pjmedia_sdp_session *ses,
	parse_context *ctx, pj_pool_t *pool);

static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
				  parse_context *ctx);
static void parse_other_line(pj_scanner *scanner, pjmedia_sdp_session *ses,
	parse_context *ctx, pj_pool_t *pool)
{
	pjmedia_sdp_attr *attr;

	ctx->last_error = PJMEDIA_SDP_EINATTR;

	attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);

	/* check equal sign */
	if (*(scanner->curptr + 1) != '=') {
		on_scanner_error(scanner);
		return;
	}

	/* x= */
	attr->name.ptr = (char*)pj_pool_alloc(pool, 1);
	*(attr->name.ptr) = *(scanner->curptr);
	attr->name.slen = 1;

	pj_scan_advance_n(scanner, 2, SKIP_WS);

	/* get anything until newline (including whitespaces). */
	pj_scan_get_until_chr(scanner, "\r\n", &attr->value);

	/* newline. */
	pj_scan_get_newline(scanner);

	if (ses->other_count < PJMEDIA_MAX_SDP_ATTR)
	{
		ses->other[ses->other_count] = attr;
		++ses->other_count;
	}
}

3、編碼部分,在print_session函數中,增加輸出其他參數

static int print_session(const pjmedia_sdp_session *ses, 
			 char *buf, pj_ssize_t len)
{
    char *p = buf;
    char *end = buf+len;
    unsigned i;
    int printed;

    /* Check length for v= and o= lines. */
    if (len < 5+ 
	      2+ses->origin.user.slen+18+
	      ses->origin.net_type.slen+ses->origin.addr.slen + 2)
    {
	return -1;
    }

    /* SDP version (v= line) */
    pj_memcpy(p, "v=0\r\n", 5);
    p += 5;

    /* Owner (o=) line. */
    *p++ = 'o';
    *p++ = '=';
    pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
    p += ses->origin.user.slen;
    *p++ = ' ';
    printed = pj_utoa(ses->origin.id, p);
    p += printed;
    *p++ = ' ';
    printed = pj_utoa(ses->origin.version, p);
    p += printed;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
    p += ses->origin.net_type.slen;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
    p += ses->origin.addr_type.slen;
    *p++ = ' ';
    pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
    p += ses->origin.addr.slen;
    *p++ = '\r';
    *p++ = '\n';

    /* Session name (s=) line. */
    if ((end-p)  < 8+ses->name.slen) {
	return -1;
    }
    *p++ = 's';
    *p++ = '=';
    pj_memcpy(p, ses->name.ptr, ses->name.slen);
    p += ses->name.slen;
    *p++ = '\r';
    *p++ = '\n';

    /* Connection line (c=) if exist. */
    if (ses->conn) {
	printed = print_connection_info(ses->conn, p, (int)(end-p));
	if (printed < 1) {
	    return -1;
	}
	p += printed;
    }

    /* print optional bandwidth info. */
    for (i=0; i<ses->bandw_count; ++i) {
	printed = (int)print_bandw(ses->bandw[i], p, end-p);
	if (printed < 1) {
	    return -1;
	}
	p += printed;
    }

    /* Time */
    if ((end-p) < 24) {
	return -1;
    }
    *p++ = 't';
    *p++ = '=';
    printed = pj_utoa(ses->time.start, p);
    p += printed;
    *p++ = ' ';
    printed = pj_utoa(ses->time.stop, p);
    p += printed;
    *p++ = '\r';
    *p++ = '\n';

    /* Print all attribute (a=) lines. */
    for (i=0; i<ses->attr_count; ++i) {
	printed = (int)print_attr(ses->attr[i], p, end-p);
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

    /* Print media (m=) lines. */
    for (i=0; i<ses->media_count; ++i) {
	printed = print_media_desc(ses->media[i], p, (int)(end-p));
	if (printed < 0) {
	    return -1;
	}
	p += printed;
    }

	// 輸出其他參數
	for (i = 0; i < ses->other_count; ++i) {
		if (0 >= ses->other[i]->name.slen)
		{
			continue;
		}
		pj_memcpy(p, ses->other[i]->name.ptr, ses->other[i]->name.slen);
		p += ses->other[i]->name.slen;
		*p++ = '=';
		if (0 <  ses->other[i]->value.slen)
		{
			pj_memcpy(p, ses->other[i]->value.ptr, ses->other[i]->value.slen);
			p += ses->other[i]->value.slen;
		}
		*p++ = '\r';
		*p++ = '\n';
	}

    return (int)(p-buf);
}

4、複製部分,在函數pjmedia_sdp_session_clone中,增加對其他參數的複製

 /* Duplicate session attributes. */
    sess->attr_count = rhs->attr_count;
    for (i=0; i<rhs->attr_count; ++i) {
	sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]);
    }

    /* Duplicate media descriptors. */
    sess->media_count = rhs->media_count;
    for (i=0; i<rhs->media_count; ++i) {
	sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]);
    }

	// 複製其他參數
	sess->other_count = rhs->other_count;
	for (i = 0; i < rhs->other_count; ++i) {
		sess->other[i] = pjmedia_sdp_attr_clone(pool, rhs->other[i]);
	}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章