------------------------------------ Datastructure Interface Descriptions ------------------------------------- Note: The design is based on RFC 4566. For eg: As per RFC, (i) "The set of letters is deliberately small and not intended to be extensible -- an SDP parser MUST completely ignore any session description that contains a type letter that it does not understand. The attribute mechanism ("a=") is the primary means for extending SDP and tailoring it to particular applications or media." This means that the session structure (sdp_session_t) is pretty much fixed with members representing 's from the standard and the attribute structure (sdp_attr_t) has been designed to be a linked list to hold standard/extended attributes. (ii) the part of =, is a structured text whose format depends on . These values are again defined to be different structures (sdp_origin_t, sdp_conn_t, sdp_time_t, sdp_bandwidth_t, sdp_key_t, sdp_list_t, sdp_attr_t, sdp_zone_t and sdp_media_t) which will be part of structure sdp_session_t. (1) sdp_origin_t (SDP origin field) ----------------------------------- o= typedef struct sdp_origin { char *o_username; uint64_t o_id; uint64_t o_version; char *o_nettype; char *o_addrtype; char *o_address; } sdp_origin_t; "o_username" holds the username of the originating host "o_id" holds the session id "o_version" holds the version number of this session description "o_nettype" holds type of network "o_addrtype" holds type of the address "o_address" holds the address of the machine from which session was created. (2) sdp_conn_t (SDP connection field) ------------------------------------- c= [/ttl]/ typedef struct sdp_conn { char *c_nettype; char *c_addrtype; char *c_address; int c_addrcount; struct sdp_conn *c_next; uint8_t c_ttl; } sdp_conn_t; "c_nettype" holds type of network "c_addrtype" holds type of the address "c_address" holds unicast-address or multicast address "c_addrcount" holds number of addresses (case of multicast address with layered encodings) "c_ttl" holds TTL value for IPV4 multicast address "c_next" pointer to next connection structure as there could be several connection fields in SDP description (3) sdp_bandwidth_t (SDP bandwidth field) ------------------------------------------ b=: typedef struct sdp_bandwidth { char *b_type; uint64_t b_value; struct sdp_bandwidth *b_next; } sdp_bandwidth_t; "b_type" holds info needed to interpret "b_value" "b_value" holds bandwidth value "b_next" pointer to next bandwidth structure as there could be several bandwidth fields in SDP description (4) struct sdp_list ------------------------ This structure is a linked list of void pointers. This structure holds SDP fields like email and phone, in which case the void pointers point to character buffers. Used to hold information in cases where number of elements is not pre-defined. (For eg. offset (in repeat field), where void pointer holds integer valuesr or format (in media field), where void pointers point to character buffers). typedef struct sdp_list { void *value; struct sdp_list *next; } sdp_list_t; "value" void pointer, holds the string values in case of email, phone and format ( in media field) OR holds the integer values in case of offset (in repeat field). "next" pointer to the next node in the list. (5) struct sdp_repeat_t (SDP repeat field) ------------------------------------------ r= typedef struct sdp_repeat { uint64_t r_interval; uint64_t r_duration; sdp_list_t *r_offset; struct sdp_repeat *r_next; } sdp_repeat_t; "r_interval" holds the repeat interval, for eg: 86400 seconds (1 day) "r_duration" holds the duration of session, for eg: 3600 seconds (1 hour). "r_offset" is a linked list of "offset" values and each value represents offset from in SDP time field. "r_next" pointer to next repeat structure as there could be several repeat fields in SDP description. This structure will always be part of the time structure (sdp_time_t ,see below) as repeat field does not appear alone in SDP description and is always associated with time field. (6) struct sdp_time_t (SDP time field) --------------------------------------- t= typedef struct sdp_time { uint64_t t_start; uint64_t t_stop; sdp_repeat_t *t_repeat; struct sdp_time *t_next; } sdp_time_t; "t_start" holds the start-time for a session "t_end" holds the end-time for a session "t_repeat" points to the SDP repeat field (see sdp_repeat_t (5)) "t_next" is a pointer to next time field as there could be several time fields in SDP description. (7) struct sdp_zone_t (SDP zone field) -------------------------------------- z= .... typedef struct sdp_zone { uint64_t z_time; char *z_offset; struct sdp_zone *z_next; } sdp_zone_t; "z_time" represents the base time "z_offset" represents the offset that will be added to "z_time" to determine session time. Mainly used for daylight saving time conversions. "z_next" is a pointer to next zone field as there could be several " " pairs within a zone field. (8) struct sdp_key_t (SDP key field) ------------------------------------- k= or k=: typedef struct sdp_key { char *k_method; char *k_enckey; } sdp_key_t; "k_method" represents the key type "k_enckey" holds the encryption key. (9) struct sdp_attr_t (SDP attribute field) -------------------------------------------- a= or a=: typedef struct sdp_attr { char *a_name; char *a_value; struct sdp_attr *a_next; } sdp_attr_t; "a_name" holds the name of the attribute "a_value" holds the value of the attribute "a_next" holds the pointer to the next attribute structure as there could be several attribute fields within SDP description. (10) struct sdp_media_t (SDP media field) ------------------------------------------ m= [/portcount] ... typedef struct sdp_media { char *m_name; uint_t m_port; int m_portcount; char *m_proto; sdp_list_t *m_format; char *m_info; sdp_conn_t *m_conn; sdp_bandwidth_t *m_bw; sdp_key_t *m_key; sdp_attr_t *m_attr; struct sdp_media *m_next; sdp_session_t *m_session; } sdp_media_t; "m_name" holds the name of the media like "audio", "video", "message" et al "m_port" holds the transport layer port information "m_portcount" holds number of ports in case of hierarchically encoded streams "m_proto" holds the transport protocol "m_format" holds the media format description "m_next" is a pointer to next media structure as there could be several media sections in SDP description. "m_session" is a pointer back to the session structure. Each Media section inside SDP description could contain other fields too. They being information, connection, bandwidth, key and attribute. (11) struct sdp_session_t ---------------------------- holds all the aforementioned structures which form SDP description. typedef struct sdp_session { int sdp_session_version; /* SDP session verstion */ int s_version; /* SDP version field */ sdp_origin_t *s_origin; /* SDP origin field */ char *s_name; /* SDP name field */ char *s_info; /* SDP info field */ char *s_uri; /* SDP uri field */ sdp_list_t *s_email; /* SDP email field */ sdp_list_t *s_phone; /* SDP phone field */ sdp_conn_t *s_conn; /* SDP connection field */ sdp_bandwidth_t *s_bw; /* SDP bandwidth field */ sdp_time_t *s_time; /* SDP time field */ sdp_zone_t *s_zone; /* SDP zone field */ sdp_key_t *s_key; /* SDP key field */ sdp_attr_t *s_attr; /* SDP attribute field */ sdp_media_t *s_media; /* SDP media field */ } sdp_session_t; "sdp_session_version" - is used to track the version of the strucutre. This will help in the unlikely event when strucutre needs to be changed. For now it will be set to SDP_SESSION_VERSION_1 (= 1) --------------------------------- Function Interface description ----------------------------------- (1) int sdp_parse(const char *sdp_info, int len, int flags, sdp_session_t **session, int *p_error); Function: Parses the SDP description passed to it. Arguments: "sdp_info" constant pointer to the SDP descripton buffer "len" length of the buffer being passed "flags" currently not used. For future use and will control parser behavior. For now it needs to be set to "0". "session" This structure will be populated with SDP information once we return from the function. "p_error" Flags any parser error. And those errors can be, 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 Return Values: 0 : Success EINVAL: If the arguments were invalid. ENOMEM: If any of the memory allocation failed while parsing sdp_info. In both of the above cases *session will be NULL. Note: user has to free the session using sdp_free_session() (2) sdp_media_t *sdp_find_media(sdp_media_t *media, const char *name); Function: Given a media list and media name ("audio", "video", et al), it searches the list for that media. (case-insensitive search) Arguments: "media" - media list from session structure. "name" - media name Return Values: NULL - If media not found or arguments were incorrect sdp_media_t * - pointer to matched media in the list. (3) sdp_attr_t *sdp_find_attribute(sdp_attr_t *attr, const char *name) Function: Given a attribute list and name of the attribute ("rtpmap", "fmtp", et al), this API searches the list for that attribute. (case-insensitive search) Arguments: "attr" - attribute list from media or session structure "name" - name of the attribute to be searched Return Values: NULL - If attribute not found or arguments were incorrect sdp_attr_t * - pointer to matched attribute in the list (4) sdp_attr_t *sdp_find_media_rtpmap(sdp_media_t *media, const char *format) Function: Given a media structure and a format description, this API will return the rtpmap attribute matching that format description. Arguments: "media" - media section in SDP within which "rtpmap" attribute need to be searched "format" - media format description Return Values: NULL - If rtpmap attribute not found or arguments were incorrect sdp_attr_t * - pointer to the matched rtpmap attribute. (5) sdp_session_t *sdp_clone_session(const sdp_session_t *session) Function: Given a session structure it clones (deep copy) and returns the cloned copy Arguments: "session" - represents the session structure to be cloned. Return values: NULL - If an error occurred during cloning sdp_session_t * - pointer to copy of the passed session structure. Note: user has to free the cloned session using sdp_free_session() (6) sdp_session_t *sdp_new_session() Function: Allocates memory for new SDP session structure and assigns a version number to session structure. Arguments: None Return values: sdp_session_t * = pointer to allocated session structure. NULL = if memory allocation failed. (7) int sdp_add_origin(sdp_session_t *session, const char *name, uint64_t id, uint64_t ver, const char *nettype, const char *addrtype, const char *address) Function: Adds origin field to the session structure. Arguments: See the definition of sdp_origin_t structure above. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation EPROTO = if origin structure is already part of session, as there can be only one origin field in a SDP description. (8) int sdp_add_name(sdp_session_t *session, const char *name) Function: Adds session name field to the session. Arguments: "name" points to a character buffer containing session name. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation EPROTO = if name is already assigned to session, as there can be only one name field. (9) int sdp_add_information(char **information, const char *value) Function: Adds information field to the session or media section of SDP. Arguments: "information" character pointer present in sdp_session_t or sdp_media_t. "value" points to a character buffer containing info Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation EPROTO = if information is already assigned to session or media, as there can be only one information field. (10) int sdp_add_uri(sdp_session_t *session, const char *uri) Function: Adds uri field to the session. Arguments: "uri" - points to a character buffer containing uri Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation EPROTO = if uri is already assigned to session, as there can be only one uri field. (11) int sdp_add_email(sdp_session_t *session, const char *email) Function: Adds email field to the session. Arguments: "email" - points to a character buffer containing email Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (12) int sdp_add_phone(sdp_session_t *session, const char *phone) Function: Adds phone field to the session Arguments: "phone" - points to a character buffer containing phone Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (13) int sdp_add_connection(sdp_conn_t **conn, const char *nettype, const char *addrtype, const char *address, uint8_t ttl, int addrcount) Function: Adds connection field to the session or media section of SDP Arguments: Please see the sdp_conn_t structure above. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (14) int sdp_add_bandwidth(sdp_bandwidth_t **bw, const char *type, uint64_t value) Function: Adds bandwidth field to the session or media section of SDP Arguments: Please see the sdp_bandwidth_t structure above. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (15) int sdp_add_repeat(sdp_time_t *time, uint64_t interval, uint64_t duration, const char *offset) Function: Adds repeat field to the time structure of session Arguments: Please see the sdp_repeat_t structure above. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (16) int sdp_add_time(sdp_session_t *session, uint64_t starttime, uint64_t stoptime, sdp_time_t **time) Function: Adds time field to the session Arguments: Please see the sdp_time_t structure above. sdp_time_t ** = pointer to the time structure just created. This is needed by API sdp_add_repeat(). If the API failed due to EINVAL or ENOMEM then it will be set to NULL Return Values: 0 = Success EINVAL = if arguements are NULL ENOMEM = failed memory allocation (17) int sdp_add_zone(sdp_session_t *session, uint64_t time, const char *offset) Function: Adds time zone field to the session Arguments: Please see the sdp_zone_t structure above. Return Values: 0 = Success. EINVAL = if arguments are NULL ENOMEM = failed memory allocation (18) int sdp_add_key(sdp_key_t **key, const char *method, const char *enckey) Function: Adds key field to session or media section of SDP. Arguments: Please see the sdp_key_t structure above. Return Values: 0 = Success EINVAL = if arguments are NULL ENOMEM = failed memory allocation EPROTO = if more than one key is being added to session, as there can be only one key field in SDP (19) int sdp_add_attribute(sdp_attr_t **attr, const char *name, const char *value) Function: Adds attribute field to session or media section of SDP Arguments: Please see the sdp_attr_t structure above. Return Values: 0 = Success EINVAL = if arguments are NULL ENOMEM = failed memory allocation (20) int sdp_add_media(sdp_session_t *session, const char *name, uint_t port, int portcount, const char *protocol, const char *format, sdp_media_t **media) Function: Adds media field to the session. Arguments: please see the sdp_media_t structure above. sdp_media_t ** = pointer to the media structure just created. To this user can further add other media specific fields like connection, bandwidth et al. Will be set to NULL, if the API failed due to EINVAL (invalid arguments) or ENOMEM (memory allocation error) Return Values: 0 = Success EINVAL = if arguments are NULL ENOMEM = failed memory allocation (21) char *sdp_session_to_str(const sdp_session_t *session, int *error) Function: Given a session structure, this API converts it into byte-string which will be used as a payload later on. Arguments: session = pointer to a session structure which needs to be converted into byte-string error = if error != NULL then it will be 0 (no failure) or will be EINVAL or ENOMEM, after returning from function. Return Values: NULL = if session was NULL char * = pointer to the character buffer containing structure contents. Note: Caller is responsible for freeing the memory allocated for character buffer. (22) int sdp_delete_all_field(sdp_session_t *session, const char field) Function: Given a session structure and the field ('v', 'o', 's', et al), this API deletes the corresponding structure element. If multiple fields of same kind is present then it deletes them too. It frees the memory and sets that pointer to NULL Arguments: session = session structure whose member needs to be deleted field = SDP field that needs to be deleted. Return Values: 0 = Success EINVAL = session is NULL or unknown field type (23) int sdp_delete_all_media_field(sdp_media_t *media, const char field) Function: Given a media structure and the field ('i', 'b', 'c', et al), this API deletes the corresponding structure element. If multiple fields of same kind is present then it deletes them too. It frees the memory and sets the pointer to NULL. Arguments: media = media structure whose member needs to be deleted. field = SDP field that needs to be deleted. Return Values: 0 = Success EINVAL = session is NULL or unknown field type (24) int sdp_delete_media(sdp_media_t **l_media, sdp_media_t *media) Function: Given a media list and the media, this API deletes that media from the list. It frees the memory corresponding to that media. Arguments: l_media = media list from which media needs to be removed media = media that needs to be removed from the list. This media is obtained using sdp_find_media() Return Values: 0 = Success EINVAL = *l_media or media is NULL (25) int sdp_delete_attribute(sdp_attr_t **l_attr, sdp_attr_t *attr) Function: Given an attribute list and an attribute, this API deletes that attribute from the list. It frees the memory corresponding to that attribute. Arguments: l_attr = attribute list from which attribute needs to be removed attr = attribute that needs to be removed from the list. This attribute is obtained using sdp_find_attribute(). Return Values: 0 = Success EINVAL = *l_attr or attr is NULL (26) void sdp_free_session(sdp_session_t *session) Function: Frees the memory allocated to session structure. Arguments: session = structure to be freed Return Values: (void). --------------------------------------------- "libcommputil" Interface Usage by User Application ---------------------------------------------- #include #include #include #include /* SDP Message, we will be using in request "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"; */ /* SDP Message, we will be using in responce "v=0\r\n\ o=Bob 2890844536 2890844540 IN IP4 10.47.16.6\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=bob@example.com (Bob Doe)\r\n\ p=+1 612-802-7489\r\n\ c=IN IP4 10.47.16.6\r\n\ b=CT:1024\r\n\ t=2854678930 2854679000\r\n\ r=604800 3600 0 90000\r\n\ m=audio 49270 RTP/AVP 0\r\n\ i=audio media\r\n\ m=video 51572 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"; */ int main() { int error = 0; /* indicates EINVAL or ENOMEM situation, while parsing */ int parse_error = 0; /* indicates field(s) for which parsing error exists */ int flags = 0; /* Not used now */ sdp_session_t *my_sess; /* constructed session */ sdp_media_t *my_media; sdp_time_t *my_time; char *b_sdp; sdp_session_t *p_sess; /* parsed session */ 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); /* * Now send the above character buffer to destination */ printf("Sending message:\n%s\n", b_sdp); /* * Now parse the recieved buffer. */ if(sdp_parse(b_sdp, strlen(b_sdp), flags, &p_sess, &parse_error) != 0) goto parse_err_ret; /* * Note we parse other fields even if some fields have error. The user * can determine if he wants to use other fields. Using flags (future) * we could change this behavior. */ if (parse_error != 0) printf("Parsing error exists: %d\n", parse_error); /* * we have now the SDP info in p_sess. Application uses it as * required i.e. retrieves the values from the structures et al. */ /* * Clone's the session and modify's the structure OR directly modify's * the above structure. */ sdp_delete_all_field(p_sess, SDP_ORIGIN_FIELD); if (sdp_add_origin(p_sess, "Bob", 2890844536ULL, 2890844540ULL, "IN", "IP4", "10.47.16.6") != 0) goto parse_err_ret; sdp_delete_all_field(p_sess, SDP_EMAIL_FIELD); if (sdp_add_email(p_sess, "bob@example.com (Bob Doe)") != 0) goto parse_err_ret; sdp_delete_all_field(p_sess, SDP_CONNECTION_FIELD); if (sdp_add_connection(&p_sess->s_conn, "IN", "IP4", "10.47.16.6", 0, 0) != 0) goto parse_err_ret; /* to change media ports you could do this */ if ((my_media = sdp_find_media(p_sess->s_media, SDP_AUDIO)) == NULL) goto parse_err_ret; my_media->s_port = 49270; if ((my_media = sdp_find_media(p_sess->s_media, SDP_VIDEO)) == NULL) goto parse_err_ret; my_media->s_port = 51572; /* * on the other hand if user wants to add media or delete media he can * do so with respective sdp_add_media or sdp_delete_media functions */ /* Now we have the structure. convert it to string and send */ free(b_sdp); b_sdp = sdp_session_to_str(p_sess, &error); printf("sending the following response:\n%s\n",b_sdp); return (0); err_ret: sdp_free_session(my_sess); printf("Error while adding\n"); return (1); parse_err_ret: free(b_sdp); sdp_free_session(p_sess); sdp_free_session(my_sess); printf("Error while parsing the recieved buffer\n"); return (1); }