API 库函数支持以下操作:
创建 SDP 会话结构
在 SDP 会话结构中搜索
关闭 SDP 会话结构
实用程序函数
创建新的 SDP 会话结构的第一步是通过调用 sdp_new_session() 函数来为新结构分配内存。此函数可返回指向新会话结构的指针。本节中的其他函数使用该指针来构建新会话结构。完成新会话结构的构建之后,请使用 sdp_session_to_str() 函数将其转换为字符串表示形式。
sdp_new_session() 函数可为 session 参数指定的新 SDP 会话结构分配内存,并向该新结构分配版本号。可以通过调用 sdp_free_session() 函数来释放分配给此会话结构的内存。
返回值:如果 sdp_new_session() 函数成功完成,该函数将返回新分配的 SDP 会话结构。如果失败,该函数将返回 NULL。
sdp_add_origin() 函数使用 name、id、ver、nettype、addrtype 和 address 参数将 ORIGIN (o=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。
返回值:如果 sdp_add_origin() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_name() 函数使用 name 参数将 NAME (s=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。
返回值:如果 sdp_add_name() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_information() 函数使用 value 参数将 INFO (i=) SDP 字段添加到会话结构 (sdp_session_t) 或介质结构 (sdp_media_t)。此字段可以进入 SDP 描述的介质部分或会话部分。您必须将 &session->s_info 或 &media->m_info 作为第一个参数进行传递,以指定该部分。
返回值:如果 sdp_add_information() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_uri() 函数使用 uri 参数将 URI (u=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。
返回值:如果 sdp_add_uri() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_email() 函数使用 email 参数将 EMAIL (e=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。
返回值:如果 sdp_add_email() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_phone() 函数使用 phone 参数将 PHONE (p=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。
返回值:如果 sdp_add_phone() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_connection() 函数使用 nettype、addrtype、address、ttl 和 addrcount 参数将 CONNECTION (c=) SDP 字段添加到会话结构 (sdp_session_t) 或介质结构 (sdp_media_t)。对于 IPv4 或 IPv6 单播地址,将 ttl 和 addrcount 参数的值设置为零。对于多播地址,将 ttl 参数的值设置为零到 255 之间的某个值。多播地址不能包含值为零的 addrcount 参数。
此字段可以进入 SDP 描述的介质部分或会话部分。您必须将 &session->s_info 或 &media->m_info 作为第一个参数进行传递,以指定该部分。
返回值:如果 sdp_add_connection() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_bandwidth() 函数使用 type 参数和 value 参数将 BANDWIDTH (b=) SDP 字段添加到会话结构 (sdp_session_t) 或介质结构 (sdp_media_t)。
此字段可以进入 SDP 描述的介质部分或会话部分。您必须将 &session->s_info 或 &media->m_info 作为第一个参数进行传递,以指定该部分。
返回值:如果 sdp_add_bandwidth() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_time() 函数使用 starttime 和 stoptime 参数的值将 TIME (t=) SDP 字段添加到会话结构。此函数使用 time 参数创建新的时间结构并返回指向该结构的指针。
返回值:如果 sdp_add_time() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_repeat() 函数使用 interval、duration 和 offset 参数的值将 REPEAT (r=) SDP 字段添加到会话结构。offset 参数的值是包含一个或多个偏移值的字符串,如 60 或 60 1d 3h。time 参数的值是指向 sdp_add_time() 函数创建的时间结构的指针。
返回值:如果 sdp_add_repeat() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_zone() 函数使用 time 参数和 offset 参数将 ZONE (z=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。通过针对每个时间/偏移值对调用此函数,可以为单个区域字段添加多个时间和偏移值。
返回值:如果 sdp_add_zone() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_key() 函数使用 method 参数和 enckey 参数将 KEY (k=) SDP 字段添加到会话结构 (sdp_session_t) 或介质结构 (sdp_media_t)。此字段可以进入 SDP 描述的介质部分或会话部分。您必须将 &session->s_info 或 &media->m_info 作为第一个参数进行传递,以指定该部分。
返回值:如果 sdp_add_key() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_attribute() 函数使用 name 参数和 value 参数将 ATTRIBUTE (a=) SDP 字段添加到会话结构 (sdp_session_t) 或介质结构 (sdp_media_t)。此字段可以进入 SDP 描述的介质部分或会话部分。您必须将 &session->s_info 或 &media->m_info 作为第一个参数进行传递,以指定该部分。
返回值:如果 sdp_add_attribute() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
sdp_add_media() 函数使用 name、port、portcount、protocol 和 format 参数的值将 MEDIA (m=) SDP 字段添加到由 session 参数 (sdp_session_t) 的值指定的会话结构。format 参数是包含一个或多个值的字符串,如字符串 0 32 97。
此函数使用 media 参数创建新的介质结构并返回指向该结构的指针。将 SDP 字段添加到介质结构的函数会使用该指针。
返回值:如果 sdp_add_media() 函数成功完成,该函数将返回 0。如果强制参数不存在,该函数将返回 EINVAL。如果内存分配失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
本示例使用本节中述及的函数来创建新的 SDP 会话结构、将字段添加到该结构,并将已完成的结构转换为该结构的字符串表示形式。在示例的结尾,程序将调用 sdp_free_session() 函数以释放会话。
示例 3-1 构建 SDP 会话结构
/* SDP Message we will be building
"v=0\r\n\
o=Alice 2890844526 2890842807 IN IP4 10.47.16.5\r\n\
s=-\r\n\
i=A Seminar on the session description protocol\r\n\
u=http://www.example.com/seminars/sdp.pdf\r\n\
e=alice@example.com (Alice Smith)\r\n\
p=+1 911-345-1160\r\n\
c=IN IP4 10.47.16.5\r\n\
b=CT:1024\r\n\
t=2854678930 2854679000\r\n\
r=604800 3600 0 90000\r\n\
z=2882844526 -1h 2898848070 0h\r\n\
a=recvonly\r\n\
m=audio 49170 RTP/AVP 0\r\n\
i=audio media\r\n\
b=CT:1000\r\n\
k=prompt\r\n\
m=video 51372 RTP/AVP 99 90\r\n\
i=video media\r\n\
a=rtpmap:99 h232-199/90000\r\n\
a=rtpmap:90 h263-1998/90000\r\n"
*/
#include stdio.h>
#include string.h>
#include errno.h>
#include sdp.h>
int main ()
{
sdp_session_t *my_sess;
sdp_media_t *my_media;
sdp_time_t *my_time;
char *b_sdp;
my_sess = sdp_new_session();
if (my_sess == NULL) {
return (ENOMEM);
}
my_sess->version = 0;
if (sdp_add_name(my_sess, "-") != 0)
goto err_ret;
if (sdp_add_origin(my_sess, "Alice", 2890844526ULL, 2890842807ULL,
"IN", "IP4", "10.47.16.5") != 0)
goto err_ret;
if (sdp_add_information(&my_sess->s_info, "A Seminar on the session"
"description protocol") != 0)
goto err_ret;
if (sdp_add_uri (my_sess, "http://www.example.com/seminars/sdp.pdf")
!= 0)
goto err_ret;
if (sdp_add_email(my_sess, "alice@example.com (Alice smith)") != 0)
goto err_ret;
if (sdp_add_phone(my_sess, "+1 911-345-1160") != 0)
goto err_ret;
if (sdp_add_connection(&my_sess->s_conn, "IN", "IP4", "10.47.16.5",
0, 0) != 0)
goto err_ret;
if (sdp_add_bandwidth(&my_sess->s_bw, "CT", 1024) != 0)
goto err_ret;
if (sdp_add_time(my_sess, 2854678930ULL, 2854679000ULL, &my_time)
!= 0)
goto err_ret;
if (sdp_add_repeat(my_time, 604800ULL, 3600ULL, "0 90000") != 0)
goto err_ret;
if (sdp_add_zone(my_sess, 2882844526ULL, "-1h") != 0)
goto err_ret;
if (sdp_add_zone(my_sess, 2898848070ULL, "0h") != 0)
goto err_ret;
if (sdp_add_attribute(&my_sess->s_attr, "sendrecv", NULL) != 0)
goto err_ret;
if (sdp_add_media(my_sess, "audio", 49170, 1, "RTP/AVP",
"0", &my_media) != 0)
goto err_ret;
if (sdp_add_information(&my_media->m_info, "audio media") != 0)
goto err_ret;
if (sdp_add_bandwidth(&my_media->m_bw, "CT", 1000) != 0)
goto err_ret;
if (sdp_add_key(&my_media->m_key, "prompt", NULL) != 0)
goto err_ret;
if (sdp_add_media(my_sess, "video", 51732, 1, "RTP/AVP",
"99 90", &my_media) != 0)
goto err_ret;
if (sdp_add_information(&my_media->m_info, "video media") != 0)
goto err_ret;
if (sdp_add_attribute(&my_media->m_attr, "rtpmap",
"99 h232-199/90000") != 0)
goto err_ret;
if (sdp_add_attribute(&my_media->m_attr, "rtpmap",
"90 h263-1998/90000") != 0)
goto err_ret;
b_sdp = sdp_session_to_str(my_sess, &error);
/*
* b_sdp is the string representation of my_sess structure
*/
free(b_sdp);
sdp_free_session(my_sess);
return (0);
err_ret:
free(b_sdp);
sdp_free_session(my_sess);
return (1);
}
本节中述及的函数可在 SDP 会话结构中搜索特定值,并返回指向这些值的指针。
sdp_find_attribute() 函数可在 attr 参数指定的属性列表中搜索 name 参数指定的属性名称。
返回值:如果 sdp_find_attribute() 函数成功完成,该函数将返回指向 name 参数指定的属性 (sdp_attr_t *) 的指针。在其他所有情况下,sdp_find_attribute() 函数返回 NULL 值。
示例 3-2 使用 sdp_find_attribute() 函数
本示例中的不完整 SDP 描述包含音频部分。
m=audio 49170 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=sendonly a=ptime:10000 a=maxptime:20000 /* * Assuming that above description is parsed using sdp_parse and that * the parsed structure is in "session" sdp_session_t structure. */ sdp_attr_t *ptime; sdp_attr_t *max_ptime; sdp_media_t *media = session->s_media; if ((ptime = sdp_find_attribute(media->m_attr, "ptime")) == NULL) /* ptime attribute not present */ else if((max_ptime = sdp_find_attribute(media->m_attr, "maxptime")) == NULL) /* max_ptime attribute not present */
sdp_find_media() 函数可在 media 参数指定的介质列表中搜索 name 参数指定的介质条目。
返回值:如果 sdp_find_media() 函数成功完成,该函数将返回指向 name 参数指定的介质列表条目 (sdp_media_t *) 的指针。在其他所有情况下,sdp_find_media() 函数返回 NULL 值。
示例 3-3 使用 sdp_find_media() 函数
本示例中的不完整 SDP 描述包含两个部分,分别是音频部分和视频部分。
m=audio 49170 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 m=video 51372 RTP/AVP 31 32 a=rtpmap:31 H261/90000 a=rtpmap:32 MPV/90000 /* * Assuming that above description is parsed using sdp_parse() and that * the parsed structure is in "session" sdp_session_t structure. */ sdp_media_t *my_media; my_media = sdp_find_media(session->s_media, "video"); /* * my_media now points to the structure containg video media section * information */
sdp_find_media_rtpmap() 函数可在 media 参数指定的介质结构的属性列表中搜索 format 参数指定的格式条目。
返回值:如果 sdp_find_media_rtpmap() 函数成功完成,该函数将返回指向 name 参数指定的格式条目 (sdp_attr_t *) 的指针。在其他所有情况下,sdp_find_media() 函数返回 NULL 值。
示例 3-4 使用 sdp_find_media_rtpmap() 函数
本示例中的不完整 SDP 描述包含两个部分,分别是音频部分和视频部分。
m=audio 49170 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 m=video 51372 RTP/AVP 31 32 a=rtpmap:31 H261/90000 a=rtpmap:32 MPV/90000 /* * Assuming that above description is parsed using sdp_parse() and that * the parsed structure is in "session" sdp_session_t structure. */ sdp_media_t *video; sdp_attr_t *mpv; video = sdp_find_media(session->s_media, "video); mpv = sdp_find_media_rtpmap(video, "32"); /* * Now the attribute structure sdp_attr_t, mpv will be having * values from the attribute field "a=rtpmap:32 MPV/90000" */
本节中述及的函数可实现以下功能:
从 SDP 会话结构中删除字段
释放 SDP 会话结构
sdp_delete_all_field() 函数可从 SDP 结构中删除 field 参数指定的所出现的所有 SDP 字段。例如,如果 SDP 结构具有三个 BANDWIDTH (b=) 字段,使用 field 参数中的值 SDP_BANDWIDTH_FIELD 调用此函数可从会话结构中删除所有三个 BANDWIDTH 字段。
返回值:如果 sdp_delete_all_field() 函数成功完成,该函数将返回 0。如果会话参数为 NULL 或字段类型未知,该函数将返回 EINVAL。在出现错误时,errno 的值不会发生更改。
sdp_delete_all_media_field() 函数可从 SDP 介质结构中删除 field 参数指定的所出现的所有 SDP 字段。
返回值:如果 sdp_delete_all_media_field() 函数成功完成,该函数将返回 0。如果会话参数为 NULL 或字段类型未知,该函数将返回 EINVAL。在出现错误时,errno 的值不会发生更改。
sdp_delete_media() 函数可从介质列表中删除 media 参数指定的介质条目。此函数可通过调用 sdp_find_media() 函数来查找指定的介质条目。此函数将在删除介质条目之后释放分配给介质结构的内存。
返回值:如果 sdp_delete_media() 函数成功完成,该函数将返回 0。如果会话参数为 NULL 或强制参数不存在,该函数将返回 EINVAL。在出现错误时,errno 的值不会发生更改。
sdp_delete_attribute() 函数可从介质列表中删除 attr 参数指定的属性。此函数可通过调用 sdp_find_media_rtpmap() 函数或 sdp_find_attribute() 函数来查找指定的属性。此函数将在删除该属性之后释放分配给属性结构的内存。
返回值:如果 sdp_delete_attribute() 函数成功完成,该函数将返回 0。如果会话参数为 NULL 或强制参数不存在,该函数将返回 EINVAL。在出现错误时,errno 的值不会发生更改。
sdp_free_session() 函数可销毁 session 参数指定的会话,并释放与该结构关联的资源。
本节中述及的函数可解析并填充 SDP 会话结构、克隆现有会话,以及将现有会话转换为字符串表示形式。
sdp_parse() 函数可解析 sdp_info 参数中的 SDP 描述并填充 sdp_session_t 结构。len 参数可指定字符缓冲区 sdp_info 的长度。该函数可分配 sdp_session_t 结构所需的内存。要释放该内存,请调用 sdp_free_session() 函数。
flags 参数的值必须设置为零。如果 flags 参数具有非零值,sdp_parse() 函数将失败并返回值 EINVAL,并且会将 *session 的值设置为 NULL。
p_error 参数可采用具有解析错误的任何字段的值。此参数不能采用 NULL 值。下面列出了 p_error 参数的可能值:
SDP_VERSION_ERROR 0x00000001 SDP_ORIGIN_ERROR 0x00000002 SDP_NAME_ERROR 0x00000004 SDP_INFO_ERROR 0x00000008 SDP_URI_ERROR 0x00000010 SDP_EMAIL_ERROR 0x00000020 SDP_PHONE_ERROR 0x00000040 SDP_CONNECTION_ERROR 0x00000080 SDP_BANDWIDTH_ERROR 0x00000100 SDP_TIME_ERROR 0x00000200 SDP_REPEAT_TIME_ERROR 0x00000400 SDP_ZONE_ERROR 0x00000800 SDP_KEY_ERROR 0x00001000 SDP_ATTRIBUTE_ERROR 0x00002000 SDP_MEDIA_ERROR 0x00004000 SDP_FIELDS_ORDER_ERROR 0x00008000 SDP_MISSING_FIELDS 0x00010000
如果 SDP 结构中的字段次序颠倒,与 RFC 4566 有冲突,sdp_parse() 函数会将 p_error 参数的值设置为 SDP_FIELDS_ORDER_ERROR。如果 SDP 结构中缺少必填字段,与 RFC 4566 有冲突,sdp_parse() 函数会将 p_error 参数的值设置为 SDP_MISSING_FIELDS。
sdp_parse() 函数在处理具有解析错误的字段之后将继续进行解析,但是具有解析错误的字段将不会显示在生成的 sdp_session_t 结构中。
返回值:如果 sdp_parse() 函数成功完成,该函数将返回 0。如果会话参数无效,sdp_parse() 函数将返回 EINVAL。如果内存分配在 sdp_parse() 函数正在解析 sdp_info 时失败,该函数将返回 ENOMEM。在出现错误时,errno 的值不会发生更改。
示例 3-5 示例:解析 SDP 会话结构
在本示例中,SDP 会话结构如下所示:
v=0\r\n o=jdoe 23423423 234234234 IN IP4 192.168.1.1\r\n s=SDP seminar\r\n i=A seminar on the session description protocol\r\n e=test@host.com c=IN IP4 156.78.90.1\r\n t=2873397496 2873404696\r\n
在调用 sdp_parse_t() 函数之后,生成的 sdp_session_t 结构如下所示:
session {
sdp_session_version = 1
s_version = 0
s_origin {
o_username = "jdoe"
o_id = 23423423ULL
o_version = 234234234ULL
o_nettype = "IN"
o_addrtype = "IP4"
o_address = "192.168.1.1"
}
s_name = "SDP seminar"
s_info = "A seminar on the session description protocol"
s_uri = (nil)
s_email {
value = "test@host.com"
next = (nil)
}
s_phone = (nil)
s_conn {
c_nettype = "IN"
c_addrtype = "IP4"
c_address = "156.78.90.1"
c_addrcount = 0
c_ttl = 0
c_next = (nil)
}
s_bw = (nil)
s_time {
t_start = 2873397496ULL
t_stop = 2873404696ULL
t_repeat = (nil)
t_next = (nil)
}
s_zone = (nil)
s_key = (nil)
s_attr = (nil)
s_media = (nil)
}
sdp_clone_session() 函数可创建新的 SDP 会话结构,该会话结构与 session 参数中指定的 SDP 会话结构相同。sdp_clone_session() 函数将在成功完成时返回克隆的会话结构。sdp_clone_session() 函数将在失败时返回 NULL。
sdp_session_to_str() 函数返回 session 参数指定的 SDP 会话结构的字符串表示形式。sdp_session_to_str() 函数先将回车/换行符附加到每个 SDP 字段的结尾,然后再将此字段附加到字符串。
返回值:sdp_session_to_str() 函数将在成功完成时返回 SDP 会话结构的字符串表示形式。在其他所有情况下,sdp_session_to_str() 函数将返回 NULL。如果输入为空,sdp_session_to_str() 函数将返回指向 EINVAL 的错误指针。如果内存分配失败,sdp_session_to_str() 函数将返回指向 ENOMEM 的错误指针。在出现错误时,errno 的值不会发生更改。