Index: ChangeLog =================================================================== --- ChangeLog (révision 18695) +++ ChangeLog (copie de travail) @@ -7,6 +7,7 @@ Demuxers: * support for audio stream switching in MPEG-TS + * new native MPEG-TS over RTSP demuxer pre8: "NeuTeam strikes back" June 11, 2006 Index: DOCS/man/en/mplayer.1 =================================================================== --- DOCS/man/en/mplayer.1 (révision 18695) +++ DOCS/man/en/mplayer.1 (copie de travail) @@ -1383,7 +1383,7 @@ .PD 1 . .TP -.B \-rtsp-port (LIVE555 only) +.B \-rtsp-port Used with 'rtsp://' URLs to force the client's port number. This option may be useful if you are behind a router and want to forward the RTSP stream from the server to a specific client. Index: DOCS/man/cs/mplayer.1 =================================================================== --- DOCS/man/cs/mplayer.1 (révision 18695) +++ DOCS/man/cs/mplayer.1 (copie de travail) @@ -1368,7 +1368,7 @@ .PD 1 . .TP -.B \-rtsp-port (pouze LIVE555) +.B \-rtsp-port Pou¾ívá se s 'rtsp://' URL pro vynucení èísla portu klienta. To se mù¾e hodit, pokud jste pøipojeni za routerem a chcete pøeposílat RTSP datový proud ze serveru konkrétnímu klientu. Index: DOCS/man/fr/mplayer.1 =================================================================== --- DOCS/man/fr/mplayer.1 (révision 18695) +++ DOCS/man/fr/mplayer.1 (copie de travail) @@ -1465,7 +1465,7 @@ .PD 1 . .TP -.B \-rtsp-port (LIVE555 uniquement) +.B \-rtsp-port Utilisé avec 'rtsp://', URLs pour forcer l'utilisation d'un port particulier au niveau du client. Cette option peut être utile si vous êtes derrière un routeur et souhaitez Index: DOCS/man/de/mplayer.1 =================================================================== --- DOCS/man/de/mplayer.1 (révision 18695) +++ DOCS/man/de/mplayer.1 (copie de travail) @@ -1460,7 +1460,7 @@ .PD 1 . .TP -.B \-rtsp-port (nur bei LIVE555) +.B \-rtsp-port Wird bei 'rtsp://'-URLs benutzt, um die Portnummer des Clients zu erzwingen. Diese Option kann nützlich sein, wenn du hinter einem Router bist und den RTSP-Stream vom Server an einen bestimmten Client weiterleiten möchtest. Index: DOCS/man/hu/mplayer.1 =================================================================== --- DOCS/man/hu/mplayer.1 (révision 18695) +++ DOCS/man/hu/mplayer.1 (copie de travail) @@ -1391,7 +1391,7 @@ .PD 1 . .TP -.B \-rtsp-port (csak LIVE555) +.B \-rtsp-port Az 'rtsp://' URL-ekkel használatos a kliens port számának kényszerítésére. Ez az opció hasznos, ha egy router mögött vagy és továbbítani akarod az RTSP folyamot a szerverrõl egy megadott kliensre. Index: DOCS/man/it/mplayer.1 =================================================================== --- DOCS/man/it/mplayer.1 (révision 18695) +++ DOCS/man/it/mplayer.1 (copie de travail) @@ -1413,7 +1413,7 @@ .PD 1 . .TP -.B \-rtsp-port (solo LIVE555) +.B \-rtsp-port Utilizzato con URL del tipo 'rtsp://' per forzare il numero di porta del client. Questa opzione può essere utile se sei dietro a un router e vuoi inoltrare il flusso RTSP dal server verso un client specifico. Index: cfg-common.h =================================================================== --- cfg-common.h (révision 18695) +++ cfg-common.h (copie de travail) @@ -75,11 +75,10 @@ {"sdp", "-sdp is obsolete, use sdp://file instead.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL}, // -rtsp-stream-over-tcp option, specifying TCP streaming of RTP/RTCP {"rtsp-stream-over-tcp", &rtspStreamOverTCP, CONF_TYPE_FLAG, 0, 0, 1, NULL}, - {"rtsp-port", &rtsp_port, CONF_TYPE_INT, CONF_RANGE, -1, 65535, NULL}, #else {"rtsp-stream-over-tcp", "RTSP support requires the \"LIVE555 Streaming Media\" libraries.\n", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0, NULL}, - {"rtsp-port", "RTSP support requires the \"LIVE555 Streaming Media\" libraries.\n", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0, NULL}, #endif + {"rtsp-port", &rtsp_port, CONF_TYPE_INT, CONF_RANGE, -1, 65535, NULL}, // ------------------------- demuxer options -------------------- @@ -420,10 +419,9 @@ #ifdef STREAMING_LIVE555 extern int rtspStreamOverTCP; -extern int rtsp_port; #endif +extern int rtsp_port; - extern int audio_stream_cache; extern int sws_chr_vshift; Index: libmpdemux/freesdp/parser.h =================================================================== --- libmpdemux/freesdp/parser.h (révision 0) +++ libmpdemux/freesdp/parser.h (révision 0) @@ -0,0 +1,728 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Benjamin Zores, (C) 2006 + added support in parser for the a=control: lines. + added support in parser for the a=range: lines. +*/ + +/** + * @file parser.h + * @ingroup parser + * @short Specific public header for parsing module. + **/ + +#ifndef FSDP_PARSER_H +#define FSDP_PARSER_H + +#include "common.h" + +BEGIN_C_DECLS +/** + * @defgroup parser FreeSDP Parsing Module + * + * SDP descriptions parsing routines. + * @{ + **/ +/** + * Parse a SDP description in description, extracting the + * session properties into dsc. These properties can be + * obtained individually later using the fsdp_get_xxxx + * functions. + * + * @param description a multimedia session description formatted in + * SDP. + * @param dsc pointer that is updated to point to a fsdp_description_t + * object. This fsdp_description_t object should have been previously + * allocated using fsdp_description_new(); to free it, + * fsdp_description_delete() should be used. + * + * @return FSDPE_OK when parsing completes successfully. Otherwise, + * another error code is returned. + **/ +fsdp_error_t fsdp_parse (const char *description, fsdp_description_t * dsc); + +/** + * Get the SDP protocol version of the description. + * + * @return SDP protocol version number. + **/ +unsigned int fsdp_get_version (const fsdp_description_t * dsc); + +/** + * Get the username provided by the originator of the session. + * + * @param dsc SDP description object. + * @return username of the session owner + **/ +const char *fsdp_get_owner_username (const fsdp_description_t * dsc); + +/** + * Get the id for the session described in dsc. + * + * @param dsc SDP description object. + * @return id string for this session. + **/ +const char *fsdp_get_session_id (const fsdp_description_t * dsc); + +/** + * Get the announcement version for the session description in + * dsc. + * + * @param dsc SDP description object. + * @return announcement version string for this description. + **/ +const char *fsdp_get_announcement_version (const fsdp_description_t * dsc); + +/** + * Get the the type of network the owner of the session described in + * dsc is based on. + * + * @param dsc SDP description object. + * @return network type for the owner of this session. + **/ +fsdp_network_type_t +fsdp_get_owner_network_type (const fsdp_description_t * dsc); + +/** + * Get the the type of address the owner of the session described in + * dsc is based on. + * + * @param dsc SDP description object. + * @return network address type for the owner of this session. + **/ +fsdp_address_type_t +fsdp_get_owner_address_type (const fsdp_description_t * dsc); + +/** + * Get the network address of the owner of the session described in + * dsc. + * + * @param dsc SDP description object. + * @return network address for the owner this session. + **/ +const char *fsdp_get_owner_address (const fsdp_description_t * dsc); + +/** + * Get the name of the session described in dsc. + * + * @param dsc SDP description object. + * @return name of this session. + **/ +const char *fsdp_get_name (const fsdp_description_t * dsc); + +/** + * Get the information about the session provided in the description + * dsc. + * + * @param dsc SDP description object. + * @return information of this session. + **/ +const char *fsdp_get_information (const fsdp_description_t * dsc); + +/** + * Get an URI about the session provided in the description + * dsc. + * + * @param dsc SDP description object. + * @return string containing an URI about the session. NULL if the + * session uri is missing. + **/ +const char *fsdp_get_uri (const fsdp_description_t * dsc); + +/** + * Get the number of emails specified for the session in the description + * dsc. + * + * @param dsc SDP description object. + * @return number of emails. + **/ +unsigned int fsdp_get_emails_count (const fsdp_description_t * dsc); + +/** + * Get the n-th email specified for the session in the description + * dsc. + * + * @param dsc SDP description object. + * @param index number of URI. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_emails_count() - 1. + * @return string containing an email about the session. NULL if there + * is no such index. + **/ +const char *fsdp_get_email (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the number of phones specified for the session in the description + * dsc. + * + * @param dsc SDP description object. + * @return number of emails. + **/ +unsigned int fsdp_get_phones_count (const fsdp_description_t * dsc); + +/** + * Get the n-th phone specified for the session in the description + * dsc. + * + * @param dsc SDP description object. + * @param index number of URI. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_phones_count() - 1. + * @return string containing a phone about the session. NULL if there + * is no such index. + **/ +const char *fsdp_get_phone (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the the global type of network of the multimedia session + * connection. + * + * @param dsc SDP description object. + * @return global network type for this + * connection. FSDP_NETWORK_TYPE_UNDEFINED if no global network + * address type is included in the description. + **/ +fsdp_network_type_t +fsdp_get_global_conn_network_type (const fsdp_description_t * dsc); + +/** + * Get the the global type of network address of the multimedia + * session connection. + * + * @param dsc SDP description object. + * @return global network address type for this connection. + * FSDP_ADDRESS_TYPE_UNDEFINED if no global network address type is + * included in the description. + **/ +fsdp_address_type_t +fsdp_get_global_conn_address_type (const fsdp_description_t * dsc); + +/** + * Get the the global address of the multimedia session connection. + * + * @param dsc SDP description object. + * @return global address for this connection. + **/ +const char *fsdp_get_global_conn_address (const fsdp_description_t * dsc); + +unsigned int +fsdp_get_global_conn_address_ttl (const fsdp_description_t * dsc); + +unsigned int +fsdp_get_global_conn_address_count (const fsdp_description_t * dsc); + +/** + * Get the number of bandwidth modifiers specified for this session. + * + * @param dsc SDP description object. + * @return number of bandwidth modifiers. + **/ +unsigned int fsdp_get_bw_modifier_count (const fsdp_description_t * dsc); + +/** + * Get the bandwidth modifier type for the session. + * + * @param dsc SDP description object. + * @param index number of bandwidth modifier. + * + * @return global bandwidth modifier type. + * @retval FSDP_BW_MOD_TYPE_UNDEFINED if no global bandwith modifier + * type is defined or invalid index. + * @retval FSDP_BW_MOD_TYPE_UNKNOWN if an unknown bandwith modifier is + * specified or an invalid index is provided. In this case + * fsdp_get_bw_modifer_type_unknown() can be called to get the + * modifier as a character string. + **/ +fsdp_bw_modifier_type_t +fsdp_get_bw_modifier_type (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the textual bandwidth modifier type when it is unknown. + * + * @param dsc SDP description object. + * @param index number of bandwidth modifier. + * + * @return global bandwidth modifier type. + * @retval empty string if the provided bandwidth type is not unknown, + * the provided index is invalid or or there was a parse error. + **/ +const char *fsdp_get_bw_modifier_type_unknown (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the value for the bandwidth modifier. + * + * @param dsc SDP description object. + * @param index number of bandwidth modifier. + * @return global bandwidth value. + * @retval 0 if no bandwidth is specified for this session or an + * invalid index has been provided. + **/ +unsigned long int +fsdp_get_bw_value (const fsdp_description_t * dsc, unsigned int index); + +/** + * Get the number of time periods specified for this session + * + * @param dsc SDP description object. + * @return number of time periods + **/ +unsigned long int fsdp_get_period_count (const fsdp_description_t * dsc); + +/** + * Get the start time for the period selected by index. + * + * @param dsc SDP description object. + * @param index number of time period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @return start time + * @retval 0 if an invalid index is provided. + **/ +time_t +fsdp_get_period_start (const fsdp_description_t * dsc, unsigned int index); + +/** + * Get the stop time for the period selected by index. + * + * @param dsc SDP description object. + * @param index number of time period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @return stop time + * @retval 0 if an invalid index is provided. + **/ +time_t +fsdp_get_period_stop (const fsdp_description_t * dsc, unsigned int index); + +/** + * Get the number of repeats for the period selected by index. + * + * @param dsc SDP description object. + * @param index number of the period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @return number of repeats + * @retval 0 if an invalid index is provided. + **/ +unsigned int +fsdp_get_period_repeats_count (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the interval time of the repeat selected by rindex for the + * period selected by index. + * + * @param dsc SDP description object. + * @param index number of time period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @param rindex number of repeat + * @return interval time + * @retval 0 if an invalid index is provided. + **/ +unsigned long int +fsdp_get_period_repeat_interval (const fsdp_description_t * dsc, + unsigned int index, unsigned int rindex); + +/** + * Get the duration of the repeat selected by rindex for the period + * selected by index. + * + * @param dsc SDP description object. + * @param index number of time period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @param rindex number of repeat + * @return duration + * @retval 0 if an invalid index is provided. + **/ +unsigned long int +fsdp_get_period_repeat_duration (const fsdp_description_t * dsc, + unsigned int index, unsigned int rindex); + +/** + * Get the offsets of the repeat selected by rindex for the period + * selected by index. + * + * @param dsc SDP description object. + * @param index number of time period. Note that this index follows the + * traditional C convention: from 0 to fsdp_get_period_count() - 1. + * @param rindex number of repeat + * @return array of offsets + * @retval NULL if an invalid index is provided. + **/ +const unsigned long int *fsdp_get_period_repeat_offsets (const + fsdp_description_t * + dsc, + unsigned int index, + unsigned int rindex); + +/** + * Get the encryption method defined for this session. + * + * @param dsc SDP description object. + * @return encryption method. FSDP_ENCRYPTION_METHOD_UNDEFINED if no + * encryption method is specified. + **/ +fsdp_encryption_method_t +fsdp_get_encryption_method (const fsdp_description_t * dsc); + +/** + * Get the encryption key or a URI pointing to the encryption key for + * this session. + * + * @param dsc SDP description object. + * @return encryption key unless FSDP_ENCRYPTION_METHOD_URI is + * specified, in which case a URI pointing to the key is returned. If + * the global encryption method is undefined, NULL is returned. + **/ +const char *fsdp_get_encryption_content (const fsdp_description_t * dsc); + +/** + * Get timezone adjustments. + * + * @param dsc SDP description object. + * @return string with list of timezone adjustments + * @retval NULL if no timezone adjustment list was specified or there + * was a parse error. + **/ +const char *fsdp_get_timezone_adj (const fsdp_description_t * dsc); + +/** + * + **/ +unsigned int +fsdp_get_unidentified_attribute_count (const fsdp_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_unidentified_attribute (const fsdp_description_t * dsc, + unsigned int index); + +/** + * + **/ +unsigned int +fsdp_get_media_rtpmap_count (const fsdp_media_description_t * mdsc); + +/** + * + **/ +const char *fsdp_get_media_rtpmap_payload_type (const fsdp_media_description_t + * mdsc, unsigned int index); + +/** + * + **/ +const char *fsdp_get_media_rtpmap_encoding_name (const + fsdp_media_description_t * + mdsc, unsigned int index); + +/** + * + **/ +unsigned int +fsdp_get_media_rtpmap_clock_rate (const fsdp_media_description_t * mdsc, + unsigned int index); + +/** + * + **/ +const char *fsdp_get_media_rtpmap_encoding_parameters (const + fsdp_description_t * + mdsc, + unsigned int index); + +/** + * Get the value of the session attribute specified in + * att. This function works for all the session + * attributes whose value is a character string. These attributes are + * defined in the session_string_attribute_t enumerated type. + * + * @param dsc SDP description object. + * @param att attribute to get. + * + * @return value of the attribute att. + * @retval NULL if the attribute was not specified or there was a + * parse error or an invalid att is given. + **/ +const char *fsdp_get_str_att (const fsdp_description_t * dsc, + fsdp_session_str_att_t att); + +/** + * + **/ +unsigned int fsdp_get_sdplang_count (const fsdp_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_sdplang (const fsdp_description_t * dsc, + unsigned int index); + +/** + * Get the mode of the conference, specified with attributes sendrecv, + * sendonly, recvonly and inactive. + * + * @param dsc SDP description object. + * @return send/rec conference mode. + * @retval FSDP_SENDRECV_UNDEFINED if conference mode not provided. + **/ +fsdp_sendrecv_mode_t fsdp_get_sendrecv_mode (const fsdp_description_t * dsc); + +/** + * Get the type of conference, such as broadcast, meeting, moderated, + * test or H332. + * + * @param dsc SDP description object. + * @return conference type. + * @retval FSDP_SESSION_TYPE_UNDEFINED if conference type not provided. + **/ +fsdp_session_type_t fsdp_get_session_type (const fsdp_description_t * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_count (const fsdp_description_t * dsc); + +/** + * + **/ +const fsdp_media_description_t *fsdp_get_media (const fsdp_description_t * + dsc, unsigned int index); + +/** + * + **/ +fsdp_media_t fsdp_get_media_type (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_port (const fsdp_media_description_t * dsc); + +unsigned int fsdp_get_media_port_count (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_transport_protocol_t +fsdp_get_media_transport_protocol (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_formats (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int +fsdp_get_media_formats_count (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_format (const fsdp_media_description_t * dsc, + unsigned int index); + +/** + * + **/ +const char *fsdp_get_media_title (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_network_type_t +fsdp_get_media_network_type (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_address_type_t +fsdp_get_media_address_type (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_address (const fsdp_media_description_t * dsc); + +unsigned int +fsdp_get_media_address_ttl (const fsdp_media_description_t * mdsc); + +unsigned int +fsdp_get_media_address_count (const fsdp_media_description_t * mdsc); + +/** + * + **/ +fsdp_bw_modifier_type_t +fsdp_get_media_bw_modifier_type (const fsdp_media_description_t * dsc, + unsigned int index); + +/** + * + **/ +const char *fsdp_get_media_bw_modifier_type_unknown (const + fsdp_media_description_t + * dsc, + unsigned int index); + +/** + * + **/ +unsigned long int +fsdp_get_media_bw_value (const fsdp_media_description_t * dsc, + unsigned int index); + +/** + * + **/ +fsdp_encryption_method_t +fsdp_get_media_encryption_method (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_encryption_content (const fsdp_media_description_t + * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_ptime (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_maxptime (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int +fsdp_get_media_fmtp_count (const fsdp_media_description_t * mdsc); + +/** + * + **/ +const char *fsdp_get_media_fmtp (const fsdp_media_description_t * mdsc, + unsigned int index); + +/** + * + **/ +unsigned int +fsdp_get_media_sdplang_count (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_sdplang (const fsdp_media_description_t * dsc, + unsigned int index); + +/** + * + **/ +unsigned int fsdp_get_media_lang_count (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_lang (const fsdp_media_description_t * dsc, + unsigned int index); + + +unsigned int fsdp_get_control_count (const fsdp_description_t * dsc); + +const char *fsdp_get_control (const fsdp_description_t * dsc, + unsigned int index); + +const char *fsdp_get_range (const fsdp_description_t * dsc); + +unsigned int +fsdp_get_media_control_count (const fsdp_media_description_t * mdsc); + +char *fsdp_get_media_control (const fsdp_media_description_t * mdsc, + unsigned int index); + +char *fsdp_get_media_range (const fsdp_media_description_t * mdsc); + +/** + * + **/ +fsdp_orient_t fsdp_get_media_orient (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_sendrecv_mode_t +fsdp_get_media_sendrecv (const fsdp_media_description_t * dsc); + +/** + * + **/ +float fsdp_get_media_framerate (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_quality (const fsdp_media_description_t * dsc); + +/** + * + **/ +unsigned int fsdp_get_media_rtcp_port (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_network_type_t +fsdp_get_media_rtcp_network_type (const fsdp_media_description_t * dsc); + +/** + * + **/ +fsdp_address_type_t +fsdp_get_media_rtcp_address_type (const fsdp_media_description_t * dsc); + +/** + * + **/ +const char *fsdp_get_media_rtcp_address (const fsdp_media_description_t * + dsc); + +/** + * + **/ +unsigned int +fsdp_get_media_unidentified_attribute_count (const fsdp_media_description_t + * mdsc); + +/** + * + **/ +const char *fsdp_get_media_unidentified_attribute (const + fsdp_media_description_t * + mdsc, unsigned int index); + + + /** @} *//* closes parser group */ + +END_C_DECLS +#endif /* FSDP_PARSER_H */ Index: libmpdemux/freesdp/parserpriv.h =================================================================== --- libmpdemux/freesdp/parserpriv.h (révision 0) +++ libmpdemux/freesdp/parserpriv.h (révision 0) @@ -0,0 +1,118 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + * @file parserpriv.h + * + * @short Private header for parser module. + **/ + +#ifndef FSDP_PARSERPRIV_H +#define FSDP_PARSERPRIV_H + +#include "priv.h" +#include "parser.h" + +/** + * Parse a connection (c=
) line. If the textual description in p begins + * with a connection line, it is parsed. If not, nothing is done. + * + * @param p fraction of textual SDP description. + * @param ntype where to store the network type. + * @param atype where to store the address type. + * @param address where to store the connection address as a string. + * + * @return parse error code. + **/ +static fsdp_error_t +fsdp_parse_c (const char **p, fsdp_network_type_t * ntype, + fsdp_address_type_t * atype, + fsdp_connection_address_t * address); + +/** + * Parse b (b=:) consecutive lines. If the + * textual description in p begins with a bandwidth line, + * it is parsed as well as all b lines inmediately after it. If not, + * nothing is done. + * + * @param p fraction of textual SDP description. + * @param bw_modifiers pointer to empty array of bandwidth modifiers to fill. + * @param bw_modifiers_count where to set the number of bandwidth + * modifiers successfully parsed. + * + * @return parse error code. + **/ +static fsdp_error_t +fsdp_parse_b (const char **p, fsdp_bw_modifier_t ** bw_modifiers, + unsigned int *bw_modifiers_count); + +/** + * Parse a k (k=) or (k=:) line. If + * the textual description in p begins with an encryption + * line, it is parsed. If not, nothing is done. + * + * @param p fraction of textual SDP description. + * @param method where to store the encryption method. + * @param content where to store the encryption key if provided. + * + * @return parse error code. + **/ +static fsdp_error_t +fsdp_parse_k (const char **p, fsdp_encryption_method_t * method, + char **content); + + +/** + * Parses a string whose first token (first characters before the + * first space or end of string) is supposed to be a time in SDP + * syntax. Some examples of SDP times are: 2d, 5h, 3444, 7778s, + * + * @param time time in SDP syntax as a string. + * @param seconds where to store the value in seconds as an integer. + * + * @return parse error code. + **/ +static fsdp_error_t +fsdp_repeat_time_to_uint (const char *time, unsigned long int *seconds); + +static fsdp_error_t +fsdp_parse_rtpmap (fsdp_rtpmap_t *** rtpmap, unsigned int *counter, + const char *value); + +/** + * Maximun default field len for "expected to be short" fields, like + * username, session_id or inet addresses. + * + * MDFLENS value must be MAXSHORTFIELDLEN - 1 + **/ +#define MAXSHORTFIELDLEN 96 +#define MSFLENS "95" + +/** + * Maximun default field len for "maybe very long" fields, like + * information, attribute values. This can also be used for lines + * where there is only a string field, like phone and email. + * + * MLFLENS value must be MAXLONGFIELDLEN - 1 + **/ +#define MAXLONGFIELDLEN 1024 +#define MLFLENS "1023" + +#endif /* FSDP_PARSERPRIV_H */ Index: libmpdemux/freesdp/errorlist.c =================================================================== --- libmpdemux/freesdp/errorlist.c (révision 0) +++ libmpdemux/freesdp/errorlist.c (révision 0) @@ -0,0 +1,72 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001, 2002 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + * @file errorlist.c + * + * @short Translation table for error numbers + * + */ + +#ifndef FSDP_ERRORLIST_C +#define FSDP_ERRORLIST_C + +#include "common.h" + +const char *fsdp_error_t_s[] = { + "No error",/** FSDPE_OK **/ + "Illegal character detected",/** FSDPE_ILLEGAL_CHARACTER **/ + "Missing version item", /** FSDPE_MISSING_VERSION **/ + "Invalid version item", /** FSDPE_INVALID_VERSION **/ + "Owner item not present", /** FSDPE_MISSING_OWNER **/ + "Parse error in owner item", /** FSDPE_INVALID_OWNER **/ + "Session name not present", /** FSDPE_MISSING_NAME **/ + "Empty session name item", /** FSDPE_EMPTY_NAME **/ + "Syntax error in connection item", /** FSDPE_INVALID_CONNECTION **/ + "Unrecognized address type in connection item", /** FSDPE_INVALID_CONNECTION_ADDRTYPE **/ + "Unrecognized network type in connection item", /** FSDPE_INVALID_CONNECTION_NETTYPE **/ + "Parse error in bandwith item", /** FSDPE_INVALID_BANDWIDTH **/ + "No time period for the session", /** FSDPE_MISSING_TIME **/ + "Parse error in time item", /** FSDPE_INVALID_TIME **/ + "Parse error in repeat time item", /** FSDPE_INVALID_REPEAT **/ + "Parse error in timezone item", /** FSDPE_INVALID_TIMEZONE **/ + "Unknown encryption method", /** FSDPE_INVALID_ENCRYPTION_METHOD **/ + "Syntax error in an attribute item", /** FSDPE_INVALID_ATTRIBUTE **/ + "Syntax error in an rtpmap attribute item", /** FSDPE_INVALID_ATTRIBUTE_RTPMAP **/ + "Unknown session type in a session-level attribute", /** FSDPE_INVALID_SESSION_TYPE **/ + "Parse error in media item", /** FSDPE_INVALID_MEDIA **/ + "Unknown media type in media item", /** FSDPE_UNKNOWN_MEDIA_TYPE **/ + "Unknown media transport", /** FSDPE_UNKNOWN_MEDIA_TRANSPORT **/ + "Unknown extra lines in description item", /** FSDPE_OVERFILLED **/ + "Unknown line found", /** FSDPE_INVALID_LINE **/ + "No connection information provided", /** FSDPE_MISSING_CONNECTION_INFO **/ + "Description item does not fit in MAXSIZE", /** FSDPE_INVALID_INDEX **/ + "Internal error", /** FSDPE_INTERNAL_ERROR **/ + "Invalid function parameters", /** FSDPE_INVALID_PARAMETER **/ + "Buffer overflow" /** FSDPE_BUFFER_OVERFLOW **/ +}; + + +const char * +fsdp_strerror (fsdp_error_t err_no) +{ + return (fsdp_error_t_s[err_no]); +} + +#endif Index: libmpdemux/freesdp/priv.h =================================================================== --- libmpdemux/freesdp/priv.h (révision 0) +++ libmpdemux/freesdp/priv.h (révision 0) @@ -0,0 +1,274 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Benjamin Zores, (C) 2006 + added support in parser for the a=control: lines. + added support in parser for the a=range: lines. +*/ + +/** + * @file priv.h + * + * @short Common private header for both formatting and parsing modules. + **/ + +#ifndef FSDP_PRIV_H +#define FSDP_PRIV_H + +#define _GNU_SOURCE + +#include +#include +#include + +#include "common.h" + +#define NTP_EPOCH_OFFSET 2208988800UL + +#define FSDP_MAX_LENGTH 2000 + +/* Tags for doxygen documentation */ + +/** + * @mainpage FreeSDP Library Reference Manual + * @section overview Overview (README) + * @verbinclude ../../README + * + **/ + +/** + * @example formatdemo.c + * + * A basic SDP descriptions formatter based on FreeSDP. + **/ + +/** + * @example parsedemo.c + * + * A basic SDP descriptions parser based on FreeSDP. + **/ + +/* Private routines declarations */ + +BEGIN_C_DECLS +/** + * @short bandwidth modifier + * + * Holds type of modifier and value. Also holds the literal bandwidth + * modifier if unknown. + **/ + typedef struct +{ + fsdp_bw_modifier_type_t b_mod_type; + unsigned long int b_value; + char *b_unknown_bw_modt; +} fsdp_bw_modifier_t; + +/** + * @short a=rtpmap: attribute + * + * Holds payload type, enconding name, RTP clock rate, and encofing + * parameters. + **/ +typedef struct +{ + char *pt; + char *encoding_name; + unsigned int clock_rate; + char *parameters; +} fsdp_rtpmap_t; + +/** + * @short Connection address specification + * + * Holds address (unicast or multicast) as well as TTL and number of + * ports, when it is an IP4 multicast address. + **/ +typedef struct fsdp_connection_address_t_s +{ + char *address; + unsigned int address_ttl; + unsigned int address_count; +} fsdp_connection_address_t; + +/** + * @short Struct for each media in a session description. + **/ +struct fsdp_media_description_t_s +{ + /* from `m= ' line */ + fsdp_media_t media_type; + unsigned int port; + unsigned int port_count; + fsdp_transport_protocol_t transport; + char **formats; + unsigned int formats_count; + /* from i= */ + char *i_title; + /* from `c=
' line + (optional) */ + fsdp_network_type_t c_network_type; + fsdp_address_type_t c_address_type; + fsdp_connection_address_t c_address; + /* from `b=:' lines (optional) */ + fsdp_bw_modifier_t *bw_modifiers; + unsigned int bw_modifiers_count; + /* from `k=' or `k=:' line + (optional) */ + fsdp_encryption_method_t k_encryption_method; + char *k_encryption_content; + /* from `a=' or `a=:' lines (opt) */ + unsigned long int a_ptime; + unsigned long int a_maxptime; + /* rtpmap */ + fsdp_rtpmap_t **a_rtpmaps; + unsigned int a_rtpmaps_count; + fsdp_orient_t a_orient; + fsdp_sendrecv_mode_t a_sendrecv_mode; + + char **a_sdplangs; + unsigned int a_sdplangs_count; + char **a_langs; + unsigned int a_langs_count; + + char **a_controls; + unsigned int a_controls_count; + + char *a_range; + + float a_framerate; + unsigned int a_quality; + char **a_fmtps; + unsigned int a_fmtps_count; + /* rtcp attribute */ + unsigned int a_rtcp_port; + fsdp_network_type_t a_rtcp_network_type; + fsdp_address_type_t a_rtcp_address_type; + char *a_rtcp_address; + /* media attributes that are not directly supported */ + char **unidentified_attributes; + unsigned int unidentified_attributes_count; +}; + +typedef struct fsdp_media_description_t_s fsdp_media_announcement_t; + +/** + * @short Information for a repeat (struct for r= lines) + **/ +typedef struct +{ + /* times in seconds */ + unsigned long int interval; + unsigned long int duration; + unsigned long int *offsets; + unsigned int offsets_count; +} fsdp_repeat_t; + +/** + * @short Information about a time period + * + * The start and stop times as well as the information from the r= + * lines for a t= line are stored in this structures. + **/ +typedef struct +{ + time_t start; + time_t stop; + fsdp_repeat_t **repeats; + unsigned int repeats_count; +} fsdp_time_period_t; + +/** + * @short Struct for session descriptions. + **/ +struct fsdp_description_t_s +{ + /* from v=... line */ + unsigned int version; + /* from o=... line */ + char *o_username; + char *o_session_id; + char *o_announcement_version; + fsdp_network_type_t o_network_type; + fsdp_address_type_t o_address_type; + char *o_address; + /* from s=... line */ + char *s_name; + /* from i=... line (opt) */ + char *i_information; + /* from u=... line (opt) */ + char *u_uri; + /* from e=... lines (0 or more) */ + const char **emails; + unsigned int emails_count; + /* from p=... lines (0 or more) */ + const char **phones; + unsigned int phones_count; + /* from `c=
' line */ + fsdp_network_type_t c_network_type; + fsdp_address_type_t c_address_type; + fsdp_connection_address_t c_address; + /* from `b=:' lines (optional) */ + fsdp_bw_modifier_t *bw_modifiers; + unsigned int bw_modifiers_count; + /* from `t= ' lines (1 or more) */ + /* from `r= ' */ + fsdp_time_period_t **time_periods; + unsigned int time_periods_count; + /* from `z= + ....' lines */ + char *timezone_adj; + /* from `k=' or `k=:' line (opt) */ + fsdp_encryption_method_t k_encryption_method; + char *k_encryption_content; + /* from `a=' or `a=:' lines (opt) */ + char *a_category; + char *a_keywords; + char *a_tool; + char *a_range; + /* rtpmap */ + fsdp_rtpmap_t **a_rtpmaps; + unsigned int a_rtpmaps_count; + fsdp_sendrecv_mode_t a_sendrecv_mode; + fsdp_session_type_t a_type; + char *a_charset; + + char **a_sdplangs; + unsigned int a_sdplangs_count; + char **a_langs; + unsigned int a_langs_count; + + char **a_controls; + unsigned int a_controls_count; + /* from `m= / ' + lines [one or more] */ + fsdp_media_announcement_t **media_announcements; + unsigned int media_announcements_count; + /* session attributes that are not directly supported */ + char **unidentified_attributes; + unsigned int unidentified_attributes_count; +}; + +#define MEDIA_RTPMAPS_MAX_COUNT 5 +#define SDPLANGS_MAX_COUNT 5 +#define SDPCONTROLS_MAX_COUNT 10 +#define UNIDENTIFIED_ATTRIBUTES_MAX_COUNT 5 + +END_C_DECLS +#endif /* FSDP_PRIV_H */ Index: libmpdemux/freesdp/common.c =================================================================== --- libmpdemux/freesdp/common.c (révision 0) +++ libmpdemux/freesdp/common.c (révision 0) @@ -0,0 +1,226 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + * @file common.c + * + * @short Implementation of routines common to parse and formatting + * modules . + * + * This file implements the routines that operate over data structures + * that are used in both the parse and formatting modules. + **/ + +#include "priv.h" +#include "common.h" + +static void +safe_free (void *ptr) +{ + if (ptr) + free (ptr); +} + +fsdp_description_t * +fsdp_description_new (void) +{ + fsdp_description_t *result = malloc (sizeof (fsdp_description_t)); + + result->version = 0; + result->o_username = result->o_session_id = + result->o_announcement_version = NULL; + result->o_network_type = FSDP_NETWORK_TYPE_UNDEFINED; + result->o_address_type = FSDP_ADDRESS_TYPE_UNDEFINED; + result->o_address = NULL; + result->s_name = NULL; + result->i_information = NULL; + result->u_uri = NULL; + result->emails = NULL; + result->emails_count = 0; + result->phones = NULL; + result->phones_count = 0; + /* At first, there is no session-level definition for these + parameters */ + result->c_network_type = FSDP_NETWORK_TYPE_UNDEFINED; + result->c_address_type = FSDP_ADDRESS_TYPE_UNDEFINED; + result->c_address.address = NULL; + /* there is no session-level definition for these parameters */ + result->bw_modifiers = NULL; + result->bw_modifiers_count = 0; + result->time_periods = NULL; + result->time_periods_count = 0; + result->timezone_adj = NULL; + result->k_encryption_method = FSDP_ENCRYPTION_METHOD_UNDEFINED; + result->k_encryption_content = NULL; + /* Default/undefined values for attributes */ + result->a_category = result->a_keywords = result->a_tool = NULL; + result->a_type = FSDP_SESSION_TYPE_UNDEFINED; + result->a_sendrecv_mode = FSDP_SENDRECV_UNDEFINED; + result->a_charset = NULL; + result->a_sdplangs = result->a_langs = NULL; + result->a_controls = NULL; + result->a_range = NULL; + result->a_rtpmaps = NULL; + result->a_rtpmaps_count = 0; + result->a_sdplangs_count = 0; + result->a_langs_count = 0; + result->a_controls_count = 0; + result->unidentified_attributes = NULL; + result->unidentified_attributes_count = 0; + result->media_announcements = NULL; + result->media_announcements_count = 0; + + return result; +} + +void +fsdp_description_delete (fsdp_description_t * dsc) +{ + fsdp_description_recycle (dsc); + safe_free (dsc); +} + +void +fsdp_description_recycle (fsdp_description_t * dsc) +{ + /* Recursively free all strings and arrays */ + unsigned int i, j; + + if (!dsc) + return; + + safe_free (dsc->o_username); + safe_free (dsc->o_session_id); + safe_free (dsc->o_announcement_version); + safe_free (dsc->o_address); + safe_free (dsc->s_name); + safe_free (dsc->i_information); + safe_free (dsc->u_uri); + + for (i = 0; i < dsc->emails_count; i++) + safe_free ((char *) dsc->emails[i]); + safe_free (dsc->emails); + + for (i = 0; i < dsc->phones_count; i++) + safe_free ((char *) dsc->phones[i]); + safe_free (dsc->phones); + + safe_free (dsc->c_address.address); + + for (i = 0; i < dsc->bw_modifiers_count; i++) + safe_free (dsc->bw_modifiers[i].b_unknown_bw_modt); + safe_free (dsc->bw_modifiers); + + for (i = 0; i < dsc->time_periods_count; i++) + { + for (j = 0; j < dsc->time_periods[i]->repeats_count; j++) + { + safe_free (dsc->time_periods[i]->repeats[j]->offsets); + safe_free (dsc->time_periods[i]->repeats[j]); + } + safe_free (dsc->time_periods[i]->repeats); + safe_free (dsc->time_periods[i]); + } + safe_free (dsc->time_periods); + + safe_free (dsc->timezone_adj); + safe_free (dsc->a_category); + safe_free (dsc->a_keywords); + safe_free (dsc->a_tool); + + for (i = 0; i < dsc->a_rtpmaps_count; i++) + safe_free (dsc->a_rtpmaps[i]); + safe_free (dsc->a_rtpmaps); + + safe_free (dsc->a_charset); + + for (i = 0; i < dsc->a_sdplangs_count; i++) + safe_free (dsc->a_sdplangs[i]); + safe_free (dsc->a_sdplangs); + + for (i = 0; i < dsc->a_langs_count; i++) + safe_free (dsc->a_langs[i]); + safe_free (dsc->a_langs); + + for (i = 0; i < dsc->a_controls_count; i++) + safe_free (dsc->a_controls[i]); + safe_free (dsc->a_controls); + + safe_free (dsc->a_range); + + for (i = 0; i < dsc->media_announcements_count; i++) + { + for (j = 0; j < dsc->media_announcements[i]->formats_count; j++) + safe_free (dsc->media_announcements[i]->formats[j]); + safe_free (dsc->media_announcements[i]->formats); + safe_free (dsc->media_announcements[i]->i_title); + + for (j = 0; j < dsc->media_announcements[i]->bw_modifiers_count; j++) + { + if (FSDP_BW_MOD_TYPE_UNKNOWN == + dsc->media_announcements[i]->bw_modifiers[j].b_mod_type) + safe_free (dsc->media_announcements[i]->bw_modifiers[j]. + b_unknown_bw_modt); + } + safe_free (dsc->media_announcements[i]->bw_modifiers); + + safe_free (dsc->media_announcements[i]->k_encryption_content); + + for (j = 0; j < dsc->media_announcements[i]->a_rtpmaps_count; j++) + { + safe_free (dsc->media_announcements[i]->a_rtpmaps[j]->pt); + safe_free (dsc->media_announcements[i]->a_rtpmaps[j]-> + encoding_name); + safe_free (dsc->media_announcements[i]->a_rtpmaps[j]->parameters); + safe_free (dsc->media_announcements[i]->a_rtpmaps[j]); + } + safe_free (dsc->media_announcements[i]->a_rtpmaps); + + for (j = 0; j < dsc->media_announcements[i]->a_sdplangs_count; j++) + safe_free (dsc->media_announcements[i]->a_sdplangs[j]); + safe_free (dsc->media_announcements[i]->a_sdplangs); + + for (j = 0; j < dsc->media_announcements[i]->a_langs_count; j++) + safe_free (dsc->media_announcements[i]->a_langs[j]); + safe_free (dsc->media_announcements[i]->a_langs); + + for (j = 0; j < dsc->media_announcements[i]->a_controls_count; j++) + safe_free (dsc->media_announcements[i]->a_controls[j]); + safe_free (dsc->media_announcements[i]->a_controls); + + for (j = 0; j < dsc->media_announcements[i]->a_fmtps_count; j++) + safe_free (dsc->media_announcements[i]->a_fmtps[j]); + safe_free (dsc->media_announcements[i]->a_fmtps); + + for (j = 0; + j < dsc->media_announcements[i]->unidentified_attributes_count; j++) + safe_free (dsc->media_announcements[i]->unidentified_attributes[j]); + safe_free (dsc->media_announcements[i]->unidentified_attributes); + safe_free (dsc->media_announcements[i]); + } + safe_free (dsc->media_announcements); + + /* This prevents the user to make the library crash when incorrectly + using recycled but not rebuilt descriptions */ + dsc->emails_count = 0; + dsc->phones_count = 0; + dsc->bw_modifiers_count = 0; + dsc->time_periods_count = 0; + dsc->media_announcements_count = 0; +} Index: libmpdemux/freesdp/parser.c =================================================================== --- libmpdemux/freesdp/parser.c (révision 0) +++ libmpdemux/freesdp/parser.c (révision 0) @@ -0,0 +1,1958 @@ +/* + This file is part of FreeSDP + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Benjamin Zores, (C) 2006 + added support in parser for the a=control: lines. + added support in parser for the a=range: lines. +*/ + +/** + * @file parser.c + * + * @short Parsing module implementation. + * + * This file implements the parsing routine fsdp_parse + * and the fsdp_get_xxxx routines that allow to get the + * session properties from a session description object build through + * the application of fsdp_parse to a textual SDP session + * description. + **/ + +#include "parserpriv.h" + +/** + * Moves the c pointer up to the beginning of the next + * line. + * + * @param c char pointer to pointer + * @retval FSDPE_ILLEGAL_CHARACTER, when an illegal '\r' character + * (not followed by a '\n') is found, returns + */ +#define NEXT_LINE(c) \ +({ \ + while ((*(c) != '\0') && (*(c) != '\r') && (*(c) != '\n')) { \ + (c)++; \ + } \ + if (*(c) == '\n') { \ + (c)++; \ + } else if (*(c) == '\r') { \ + (c)++; \ + if (*(c) == '\n') { \ + (c)++; \ + } else { \ + return FSDPE_ILLEGAL_CHARACTER; \ + } \ + } \ +}) + +fsdp_error_t +fsdp_parse (const char *text_description, fsdp_description_t * dsc) +{ + fsdp_error_t result; + const char *p = text_description, *p2; + unsigned int index, j; + /* temps for sscanf */ + const unsigned int TEMPCHARS = 6; + char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN]; + char longfsdp_buf[MAXLONGFIELDLEN]; + const unsigned int TEMPINTS = 2; + unsigned long int wuint[TEMPINTS]; + + if ((NULL == text_description) || (NULL == dsc)) + return FSDPE_INVALID_PARAMETER; + + /***************************************************************************/ + /* A) parse session-level description */ + /***************************************************************************/ + + /* `v=' line (protocol version) */ + /* according to the RFC, only `v=0' is valid */ + if (sscanf (p, "v=%1lu", &wuint[0])) + { + if (wuint[0] != 0) + return FSDPE_INVALID_VERSION; + } + else + { + return FSDPE_MISSING_VERSION; + } + NEXT_LINE (p); + + /* `o=' line (owner/creator and session identifier) */ + /* o=
+
*/ + if (!strncmp (p, "o=", 2)) + { + p += 2; + /* note that the following max lengths may vary in the future and + are quite arbitary */ + if (sscanf + (p, + "%" MSFLENS "[\x21-\xFF] %" MSFLENS "[0-9] %" MSFLENS + "[0-9] %2s %3s %" MSFLENS "s", fsdp_buf[0], fsdp_buf[1], + fsdp_buf[2], fsdp_buf[3], fsdp_buf[4], fsdp_buf[5]) != 6) + return FSDPE_INVALID_OWNER; + dsc->o_username = strdup (fsdp_buf[0]); + dsc->o_session_id = strdup (fsdp_buf[1]); + dsc->o_announcement_version = strdup (fsdp_buf[2]); + if (!strncmp (fsdp_buf[3], "IN", 2)) + { + dsc->o_network_type = FSDP_NETWORK_TYPE_INET; + if (!strncmp (fsdp_buf[4], "IP4", 3)) + dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV4; + else if (!strncmp (fsdp_buf[4], "IP6", 3)) + dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV6; + else + return FSDPE_INVALID_OWNER; + } + else + { + return FSDPE_INVALID_OWNER; + } + /* TODO? check valid unicast address/FQDN */ + dsc->o_address = strdup (fsdp_buf[5]); + } + else + { + return FSDPE_MISSING_OWNER; + } + NEXT_LINE (p); + + /* `s=' line (session name) -note that the name string cannot be empty */ + /* s= */ + if (!strncmp (p, "s=", 2)) + { + if (sscanf (p, "s=%" MLFLENS "[^\r\n]", longfsdp_buf) < 1) + return FSDPE_EMPTY_NAME; + dsc->s_name = strdup (longfsdp_buf); + } + else + { + return FSDPE_MISSING_NAME; + } + NEXT_LINE (p); + + /* `i=' line (session information) [optional] */ + /* i= */ + if (!strncmp (p, "i=", 2) + && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf)) + { + dsc->i_information = strdup (longfsdp_buf); + NEXT_LINE (p); + } + else + { + /* (optional) information absent */ + } + + /* `u=' line (URI of description) [optional] */ + /* u= */ + if (!strncmp (p, "u=", 2) + && sscanf (p, "u=%" MLFLENS "[^\r\n]", longfsdp_buf)) + { + /* TODO? check valid uri */ + dsc->u_uri = strdup (longfsdp_buf); + NEXT_LINE (p); + } + else + { + /* (optional) uri absent */ + } + + /* `e=' lines (email address) [zero or more] */ + /* e= */ + p2 = p; + j = 0; + while (!strncmp (p2, "e=", 2)) + { + /* First, count how many emails are there */ + j++; + NEXT_LINE (p2); + } + dsc->emails_count = j; + if (dsc->emails_count > 0) + { + /* Then, build the array of emails */ + dsc->emails = calloc (j, sizeof (const char *)); + for (j = 0; j < dsc->emails_count; j++) + { + sscanf (p, "e=%" MLFLENS "[^\r\n]", longfsdp_buf); + /* TODO? check valid email-address. */ + dsc->emails[j] = strdup (longfsdp_buf); + NEXT_LINE (p); + } + } + + /* `p=' lines (phone number) [zero or more] */ + /* p= */ + j = 0; + /* assert ( p2 == p ); */ + while (!strncmp (p2, "p=", 2)) + { + j++; + NEXT_LINE (p2); + } + dsc->phones_count = j; + if (dsc->phones_count > 0) + { + dsc->phones = calloc (j, sizeof (const char *)); + for (j = 0; j < dsc->phones_count; j++) + { + sscanf (p, "p=%" MLFLENS "[^\r\n]", longfsdp_buf); + /* TODO? check valid phone-number. */ + dsc->phones[j] = strdup (longfsdp_buf); + NEXT_LINE (p); + } + } + + /* `c=' line (connection information - not required if included in all media) [optional] */ + /* c=
*/ + result = fsdp_parse_c (&p, &(dsc->c_network_type), &(dsc->c_address_type), + &(dsc->c_address)); + if (FSDPE_OK != result) + return result; + + /* `b=' lines (bandwidth information) [optional] */ + /* b=: */ + result = + fsdp_parse_b (&p, &(dsc->bw_modifiers), &(dsc->bw_modifiers_count)); + if (FSDPE_OK != result) + return result; + + /* A.1) Time descriptions: */ + + /* `t=' lines (time the session is active) [1 or more] */ + /* t= */ + j = 0; + p2 = p; + while (!strncmp (p2, "t=", 2)) + { + j++; + NEXT_LINE (p2); + while (!strncmp (p2, "r=", 2)) + NEXT_LINE (p2); + } + dsc->time_periods_count = j; + if (dsc->time_periods_count == 0) + return FSDPE_MISSING_TIME; + dsc->time_periods = calloc (dsc->time_periods_count, + sizeof (fsdp_time_period_t *)); + index = 0; + for (j = 0; j < dsc->time_periods_count; j++) + { + unsigned int h = 0; + if (sscanf (p, "t=%10lu %10lu", &wuint[0], &wuint[1]) != 2) + { + /* not all periods have been successfully parsed */ + dsc->time_periods_count = j; + return FSDPE_INVALID_TIME; + } + dsc->time_periods[j] = calloc (1, sizeof (fsdp_time_period_t)); + + /* convert from NTP to time_t time */ + if (wuint[0] != 0) + wuint[0] -= NTP_EPOCH_OFFSET; + if (wuint[1] != 0) + wuint[1] -= NTP_EPOCH_OFFSET; + dsc->time_periods[j]->start = wuint[0]; + dsc->time_periods[j]->stop = wuint[1]; + NEXT_LINE (p); + + /* `r' lines [zero or more repeat times for each t=] */ + /*r= */ + p2 = p; + while (!strncmp (p2, "r=", 2)) + { + h++; + NEXT_LINE (p2); + } + dsc->time_periods[j]->repeats_count = h; + if (h > 0) + { + unsigned int index2 = 0; + dsc->time_periods[j]->repeats = + calloc (h, sizeof (fsdp_repeat_t *)); + for (h = 0; h < dsc->time_periods[j]->repeats_count; h++) + { + /* + get_repeat_values(p,&(dsc->time_periods[index].repeats[index2])); + fsdp_error_t get_repeat_values (const char *r, fsdp_repeat_t + *repeat); + */ + if (sscanf (p, "r=%10s %10s %" MLFLENS "[^\r\n]", + fsdp_buf[0], fsdp_buf[1], longfsdp_buf) == 3) + { + fsdp_repeat_t *repeat; + dsc->time_periods[j]->repeats[h] = + calloc (1, sizeof (fsdp_repeat_t)); + repeat = dsc->time_periods[j]->repeats[h]; + /* get interval, duration and list of offsets */ + result = + fsdp_repeat_time_to_uint (fsdp_buf[0], + &(repeat->interval)); + if (result == FSDPE_OK) + { + result = + fsdp_repeat_time_to_uint (fsdp_buf[1], + &(repeat->duration)); + if (result == FSDPE_OK) + { + unsigned int k = 1; + const char *i = longfsdp_buf; + while (NULL != (i = strchr (i, ' '))) + { + k++; + if (NULL != i) + i++; + } + repeat->offsets_count = k; + repeat->offsets = calloc (k, sizeof (time_t)); + i = longfsdp_buf; + for (k = 0; + (k < repeat->offsets_count) + && (result == FSDPE_OK); k++) + { + result = + fsdp_repeat_time_to_uint (i, + &(repeat-> + offsets[k])); + i = strchr (i, ' '); + if (NULL != i) + i++; + } + if (k < repeat->offsets_count) + { + /* there where invalid repeat offsets */ + dsc->time_periods[j]->repeats_count = k; + return FSDPE_INVALID_REPEAT; + } + } + } + if (result != FSDPE_OK) + { + /* not all repeats have been succesfully parsed */ + dsc->time_periods[j]->repeats_count = h; + return FSDPE_INVALID_REPEAT; + } + NEXT_LINE (p); + } + else + { + /* not all repeats have been succesfully parsed */ + dsc->time_periods[j]->repeats_count = h; + return FSDPE_INVALID_REPEAT; + } + index2++; + } + } + } + + /* `z=' line (time zone adjustments) [zero or more] */ + /* z= .... */ + if (!strncmp (p, "z=", 2)) + { + if (sscanf (p, "z=%" MLFLENS "[^\r\n]", longfsdp_buf)) + { + /* TODO: guess how many pairs are there and process them */ + dsc->timezone_adj = strdup (longfsdp_buf); + NEXT_LINE (p); + } + else + { + return FSDPE_INVALID_TIMEZONE; + } + } + + /* `k=' line (encryption key) [optional] */ + /* k= + k=: */ + result = fsdp_parse_k (&p, &(dsc->k_encryption_method), + &(dsc->k_encryption_content)); + if (result != FSDPE_OK) + return result; + + /* A.2) Attributes */ + /* `a=' lines (session attribute) [0 or more] */ + /* a= + a=: */ + while (!strncmp (p, "a=", 2)) + { + /* The "9" lenght specifier of the first string is subject to + changes */ + if (sscanf + (p, "a=%9[^:\r\n]:%" MSFLENS "[^\r\n]", fsdp_buf[0], + fsdp_buf[1]) == 2) + { + /* session-level value attributes */ + if (!strncmp (fsdp_buf[0], "cat", 3)) + dsc->a_category = strdup (fsdp_buf[1]); + else if (!strncmp (fsdp_buf[0], "keywds", 6)) + dsc->a_keywords = strdup (fsdp_buf[1]); + else if (!strncmp (fsdp_buf[0], "tool", 4)) + dsc->a_keywords = strdup (fsdp_buf[1]); + else if (!strncmp (fsdp_buf[0], "rtpmap", 6)) + fsdp_parse_rtpmap (&(dsc->a_rtpmaps), + &(dsc->a_rtpmaps_count), fsdp_buf[1]); + else if (!strncmp (fsdp_buf[0], "type", 4)) + { + if (!strncmp (fsdp_buf[1], "broadcast", 9)) + dsc->a_type = FSDP_SESSION_TYPE_BROADCAST; + else if (!strncmp (fsdp_buf[1], "meeting", 7)) + dsc->a_type = FSDP_SESSION_TYPE_MEETING; + else if (!strncmp (fsdp_buf[1], "moderated", 9)) + dsc->a_type = FSDP_SESSION_TYPE_MODERATED; + else if (!strncmp (fsdp_buf[1], "test", 4)) + dsc->a_type = FSDP_SESSION_TYPE_TEST; + else if (!strncmp (fsdp_buf[1], "H332", 4)) + dsc->a_type = FSDP_SESSION_TYPE_H332; + else + return FSDPE_INVALID_SESSION_TYPE; + } + else if (!strncmp (fsdp_buf[0], "charset", 7)) + dsc->a_charset = strdup (fsdp_buf[1]); + else if (!strncmp (fsdp_buf[0], "sdplang", 7)) + { + if (NULL == dsc->a_sdplangs) + { + dsc->a_sdplangs_count = 0; + dsc->a_sdplangs = + calloc (SDPLANGS_MAX_COUNT, sizeof (char *)); + } + if (dsc->a_sdplangs_count < SDPLANGS_MAX_COUNT) + { + dsc->a_sdplangs[dsc->a_sdplangs_count] = + strdup (fsdp_buf[1]); + dsc->a_sdplangs_count++; + } + } + else if (!strncmp (fsdp_buf[0], "lang", 4)) + { + if (NULL == dsc->a_langs) + { + dsc->a_langs_count = 0; + dsc->a_langs = calloc (SDPLANGS_MAX_COUNT, sizeof (char *)); + } + if (dsc->a_langs_count < SDPLANGS_MAX_COUNT) + { + dsc->a_langs[dsc->a_langs_count] = strdup (fsdp_buf[1]); + dsc->a_langs_count++; + } + } + else if (!strncmp (fsdp_buf[0], "control", 7)) + { + if (NULL == dsc->a_controls) + { + dsc->a_controls_count = 0; + dsc->a_controls = + calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *)); + } + if (dsc->a_controls_count < SDPCONTROLS_MAX_COUNT) + { + dsc->a_controls[dsc->a_controls_count] = + strdup (fsdp_buf[1]); + dsc->a_controls_count++; + } + } + else if (!strncmp (fsdp_buf[0], "range", 5)) + { + if (dsc->a_range) + free (dsc->a_range); + dsc->a_range = strdup (fsdp_buf[1]); + } + else + { + /* ignore unknown attributes, but provide access to them */ + *longfsdp_buf = '\0'; + strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN); + strncat (longfsdp_buf, ":", MAXLONGFIELDLEN); + strncat (longfsdp_buf, fsdp_buf[1], MAXLONGFIELDLEN); + if (NULL == dsc->unidentified_attributes) + { + dsc->unidentified_attributes_count = 0; + dsc->unidentified_attributes = + calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT, + sizeof (char *)); + } + if (dsc->unidentified_attributes_count < + UNIDENTIFIED_ATTRIBUTES_MAX_COUNT) + { + dsc->unidentified_attributes + [dsc->unidentified_attributes_count] = + strdup (longfsdp_buf); + dsc->unidentified_attributes_count++; + } + } + NEXT_LINE (p); + } + else if (sscanf (p, "a=%20s", fsdp_buf[0]) == 1) + { + /* session-level property attributes */ + if (!strncmp (fsdp_buf[0], "recvonly", 8)) + dsc->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY; + else if (!strncmp (fsdp_buf[0], "sendonly", 8)) + dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY; + else if (!strncmp (fsdp_buf[0], "inactive", 8)) + dsc->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE; + else if (!strncmp (fsdp_buf[0], "sendrecv", 8)) + dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV; + else + { + /* ignore unknown attributes, but provide access to them */ + *longfsdp_buf = '\0'; + strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN); + if (NULL == dsc->unidentified_attributes) + { + dsc->unidentified_attributes_count = 0; + dsc->unidentified_attributes = + calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT, + sizeof (char *)); + } + if (dsc->unidentified_attributes_count < + UNIDENTIFIED_ATTRIBUTES_MAX_COUNT) + { + dsc->unidentified_attributes + [dsc->unidentified_attributes_count] = + strdup (longfsdp_buf); + dsc->unidentified_attributes_count++; + } + } + NEXT_LINE (p); + } + else + return FSDPE_INVALID_ATTRIBUTE; + } + + /***************************************************************************/ + /* B) parse media-level descriptions */ + /***************************************************************************/ + p2 = p; + j = 0; + while ((*p2 != '\0') && !strncmp (p2, "m=", 2)) + { + char c; + j++; + NEXT_LINE (p2); + while (sscanf (p2, "%c=", &c) == 1) + { + if (c == 'i' || c == 'c' || c == 'b' || c == 'k' || c == 'a') + { + NEXT_LINE (p2); + } + else if (c == 'm') + { + break; + } + else + { + return FSDPE_INVALID_LINE; + } + } + } + dsc->media_announcements_count = j; + if (dsc->media_announcements_count == 0) + { + ; + /*return FSDPE_MISSING_MEDIA; */ + } + else + { /* dsc->media_announcements_count > 0 */ + dsc->media_announcements = + calloc (j, sizeof (fsdp_media_announcement_t *)); + for (j = 0; j < dsc->media_announcements_count; j++) + { + fsdp_media_announcement_t *media = NULL; + /* `m=' line (media name, transport address and format list) */ + /* m= */ + /* The max. string lengths are subject to change */ + if (sscanf (p, "m=%11s %8s %7s %" MLFLENS "[^\r\n]", + fsdp_buf[0], fsdp_buf[1], fsdp_buf[2], + longfsdp_buf) != 4) + { + return FSDPE_INVALID_MEDIA; + } + else + { + dsc->media_announcements[j] = + calloc (1, sizeof (fsdp_media_announcement_t)); + media = dsc->media_announcements[j]; + if (!strncmp (fsdp_buf[0], "audio", 5)) + media->media_type = FSDP_MEDIA_AUDIO; + else if (!strncmp (fsdp_buf[0], "video", 5)) + media->media_type = FSDP_MEDIA_VIDEO; + else if (!strncmp (fsdp_buf[0], "application", 11)) + media->media_type = FSDP_MEDIA_APPLICATION; + else if (!strncmp (fsdp_buf[0], "data", 4)) + media->media_type = FSDP_MEDIA_DATA; + else if (!strncmp (fsdp_buf[0], "control", 7)) + media->media_type = FSDP_MEDIA_CONTROL; + else + return FSDPE_UNKNOWN_MEDIA_TYPE; + { /* try to get port specification as port/number */ + char *slash; + if ((slash = strchr (fsdp_buf[1], '/'))) + { + *slash = '\0'; + slash++; + media->port = strtol (fsdp_buf[1], NULL, 10); + media->port_count = strtol (slash, NULL, 10); + } + else + { + media->port = strtol (fsdp_buf[1], NULL, 10); + media->port_count = 0; + } + } + if (!strncmp (fsdp_buf[2], "RTP/AVP", 7)) + media->transport = FSDP_TP_RTP_AVP; + else if (!strncmp (fsdp_buf[2], "udp", 3)) + media->transport = FSDP_TP_UDP; + else if (!strncmp (fsdp_buf[2], "TCP", 3)) + media->transport = FSDP_TP_TCP; + else if (!strncmp (fsdp_buf[2], "UDPTL", 5)) + media->transport = FSDP_TP_UDPTL; + else if (!strncmp (fsdp_buf[2], "vat", 3)) + media->transport = FSDP_TP_VAT; + else if (!strncmp (fsdp_buf[2], "rtp", 3)) + media->transport = FSDP_TP_OLD_RTP; + else + return FSDPE_UNKNOWN_MEDIA_TRANSPORT; + { + unsigned int k = 0; + char *s = longfsdp_buf; + while (NULL != (s = strchr (s, ' '))) + { + k++; + if (NULL != s) + s++; + } + k++; /* when there is no space left, count the last format */ + media->formats_count = k; + media->formats = calloc (k, sizeof (char *)); + s = longfsdp_buf; + for (k = 0; k < media->formats_count; k++) + { + char *space = strchr (s, ' '); + if (NULL != space) + *space = '\0'; + media->formats[k] = strdup (s); + s = space + 1; + } + } + NEXT_LINE (p); + } + + /* `i=' line (media title) [optional] */ + /* i= */ + if (!strncmp (p, "i=", 2) + && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf)) + { + media->i_title = strdup (longfsdp_buf); + NEXT_LINE (p); + } + else + { + /* (optional) information absent */ + } + + /* `c=' line (connection information - overrides session-level + line) [optional if provided at session-level] */ + /* c=
*/ + result = fsdp_parse_c (&p, &(media->c_network_type), + &(media->c_address_type), + &(media->c_address)); + if (result != FSDPE_OK) + return result; + + /* `b=' lines (bandwidth information) [optional] */ + /* b=: */ + result = fsdp_parse_b (&p, &(media->bw_modifiers), + &(media->bw_modifiers_count)); + if (FSDPE_OK != result) + return result; + + /* `k=' line (encryption key) [optional] */ + /* k= + k=: */ + result = fsdp_parse_k (&p, &(media->k_encryption_method), + &(media->k_encryption_content)); + if (result != FSDPE_OK) + return result; + + /* B.1) Attributes */ + + /* `a=' lines (zero or more media attribute lines) [optional] */ + /* a= + a=: */ + while (!strncmp (p, "a=", 2)) + { + if (sscanf + (p, "a=%9[^:\r\n]:%" MLFLENS "[^\r\n]", fsdp_buf[0], + longfsdp_buf) == 2) + { + /* media-level value attributes */ + if (!strncmp (fsdp_buf[0], "ptime", 5)) + media->a_ptime = strtoul (longfsdp_buf, NULL, 10); + else if (!strncmp (fsdp_buf[0], "maxptime", 8)) + media->a_maxptime = strtoul (longfsdp_buf, NULL, 10); + else if (!strncmp (fsdp_buf[0], "rtpmap", 6)) + fsdp_parse_rtpmap (&(media->a_rtpmaps), + &(media->a_rtpmaps_count), + longfsdp_buf); + else if (!strncmp (fsdp_buf[0], "orient", 6)) + { + if (!strncmp (longfsdp_buf, "portrait", 8)) + media->a_orient = FSDP_ORIENT_PORTRAIT; + else if (!strncmp (longfsdp_buf, "landscape", 9)) + media->a_orient = FSDP_ORIENT_LANDSCAPE; + else if (!strncmp (longfsdp_buf, "seascape", 9)) + media->a_orient = FSDP_ORIENT_SEASCAPE; + } + else if (!strncmp (fsdp_buf[0], "sdplang", 7)) + { + if (NULL == dsc->a_sdplangs) + { + media->a_sdplangs_count = 0; + media->a_sdplangs = + calloc (SDPLANGS_MAX_COUNT, sizeof (char *)); + } + if (media->a_sdplangs_count < SDPLANGS_MAX_COUNT) + { + media->a_sdplangs[dsc->a_sdplangs_count] = + strdup (longfsdp_buf); + media->a_sdplangs_count++; + } + } + else if (!strncmp (fsdp_buf[0], "lang", 4)) + { + if (NULL == dsc->a_langs) + { + media->a_langs_count = 0; + media->a_langs = + calloc (SDPLANGS_MAX_COUNT, sizeof (char *)); + } + if (media->a_langs_count < SDPLANGS_MAX_COUNT) + { + media->a_langs[dsc->a_langs_count] = + strdup (longfsdp_buf); + media->a_langs_count++; + } + } + else if (!strncmp (fsdp_buf[0], "control", 7)) + { + if (NULL == media->a_controls) + { + media->a_controls_count = 0; + media->a_controls = + calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *)); + } + if (media->a_controls_count < SDPCONTROLS_MAX_COUNT) + { + media->a_controls[media->a_controls_count] = + strdup (longfsdp_buf); + media->a_controls_count++; + } + } + else if (!strncmp (fsdp_buf[0], "range", 5)) + { + if (media->a_range) + free (media->a_range); + media->a_range = strdup (fsdp_buf[1]); + } + else if (!strncmp (fsdp_buf[0], "framerate", 9)) + media->a_framerate = strtof (longfsdp_buf, NULL); + else if (!strncmp (fsdp_buf[0], "fmtp", 4)) + { + if (NULL == media->a_fmtps) + { + media->a_fmtps_count = 0; + media->a_fmtps = + calloc (SDPLANGS_MAX_COUNT, sizeof (char *)); + } + if (media->a_fmtps_count < SDPLANGS_MAX_COUNT) + { + media->a_fmtps[media->a_fmtps_count] = + strdup (longfsdp_buf); + media->a_fmtps_count++; + } + } + else if (!strncmp (fsdp_buf[0], "rtcp", 4)) + { + int opts = 0; + /* rtcp attribute: a=rtcp:
*/ + opts = + sscanf (longfsdp_buf, "%lu %2s %3s %" MSFLENS "s", + &wuint[0], fsdp_buf[0], fsdp_buf[1], + fsdp_buf[2]); + if (opts >= 1) + { + media->a_rtcp_port = wuint[0]; + if (opts >= 2) + { + if (!strncmp (fsdp_buf[0], "IN", 2)) + { + media->a_rtcp_network_type = + FSDP_NETWORK_TYPE_INET; + } /* else + ; TODO: define error code? */ + if (opts >= 3) + { + if (!strncmp (fsdp_buf[1], "IP4", 3)) + media->a_rtcp_address_type = + FSDP_ADDRESS_TYPE_IPV4; + else if (!strncmp (fsdp_buf[1], "IP6", 3)) + media->a_rtcp_address_type = + FSDP_ADDRESS_TYPE_IPV6; + else + return FSDPE_INVALID_CONNECTION_NETTYPE; + /*add specific code? */ + if (opts >= 4) + media->a_rtcp_address = + strdup (fsdp_buf[2]); + } + } + } + } + else + { + /* ignore unknown attributes, but provide access to them */ + *fsdp_buf[1] = '\0'; + strncat (fsdp_buf[1], fsdp_buf[0], MAXLONGFIELDLEN); + strncat (fsdp_buf[1], ":", MAXLONGFIELDLEN); + strncat (fsdp_buf[1], longfsdp_buf, MAXLONGFIELDLEN); + if (NULL == media->unidentified_attributes) + { + media->unidentified_attributes_count = 0; + media->unidentified_attributes = + calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT, + sizeof (char *)); + } + if (media->unidentified_attributes_count < + UNIDENTIFIED_ATTRIBUTES_MAX_COUNT) + { + media->unidentified_attributes + [media->unidentified_attributes_count] = + strdup (fsdp_buf[1]); + media->unidentified_attributes_count++; + } + } + NEXT_LINE (p); + } + else if (sscanf (p, "a=%8s", fsdp_buf[0]) == 1) + { + /* media-level property attributes */ + if (!strncmp (fsdp_buf[0], "recvonly", 8)) + media->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY; + else if (!strncmp (fsdp_buf[0], "sendonly", 8)) + media->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY; + else if (!strncmp (fsdp_buf[0], "inactive", 8)) + media->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE; + else if (!strncmp (fsdp_buf[0], "sendrecv", 8)) + media->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV; + else + { + /* ignore unknown attributes, but provide access to them */ + *longfsdp_buf = '\0'; + strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN); + if (NULL == media->unidentified_attributes) + { + media->unidentified_attributes_count = 0; + media->unidentified_attributes = + calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT, + sizeof (char *)); + } + if (media->unidentified_attributes_count < + UNIDENTIFIED_ATTRIBUTES_MAX_COUNT) + { + media->unidentified_attributes + [media->unidentified_attributes_count] = + strdup (longfsdp_buf); + media->unidentified_attributes_count++; + } + } + NEXT_LINE (p); + } + else + return FSDPE_INVALID_ATTRIBUTE; + } + } /* end of for */ + } + + /* Check c= has been given at session level or at media level for + all media */ + if (NULL == dsc->c_address.address) + { + unsigned int c; + for (c = 0; c < dsc->media_announcements_count; c++) + if (NULL == dsc->media_announcements[c]->c_address.address) + return FSDPE_MISSING_CONNECTION_INFO; + } + + /* finish */ + if (*p == '\0') + return FSDPE_OK; + else + return FSDPE_OVERFILLED; +} + +static fsdp_error_t +fsdp_parse_c (const char **p, fsdp_network_type_t * ntype, + fsdp_address_type_t * atype, + fsdp_connection_address_t * address) +{ + const unsigned int TEMPCHARS = 3; + char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN]; + + if (!strncmp (*p, "c=", 2)) + { + if (sscanf (*p, "c=%2s %3s %" MSFLENS "s", + fsdp_buf[0], fsdp_buf[1], fsdp_buf[2])) + { + if (!strncmp (fsdp_buf[0], "IN", 2)) + { + *ntype = FSDP_NETWORK_TYPE_INET; + if (!strncmp (fsdp_buf[1], "IP4", 3)) + *atype = FSDP_ADDRESS_TYPE_IPV4; + else if (!strncmp (fsdp_buf[1], "IP6", 3)) + *atype = FSDP_ADDRESS_TYPE_IPV6; + else + return FSDPE_INVALID_CONNECTION_NETTYPE; + } + else + { + return FSDPE_INVALID_CONNECTION_ADDRTYPE; + } + { + char *slash = strchr (fsdp_buf[2], '/'); + if (NULL == slash) + { + address->address = strdup (fsdp_buf[2]); + address->address_ttl = 0; + address->address_count = 0; + } + else + { + /* address is IP4 multicast */ + char *slash2; + *slash = '\0'; + slash++; + address->address = strdup (fsdp_buf[2]); + slash2 = strchr (slash + 1, '/'); + if (NULL == slash2) + { + address->address_ttl = strtol (slash, NULL, 10); + address->address_count = 0; + } + else + { + *slash2 = '\0'; + slash2++; + address->address_ttl = strtol (slash, NULL, 10); + address->address_count = strtol (slash2, NULL, 10); + } + } + } + NEXT_LINE (*p); + } + else + { + return FSDPE_INVALID_CONNECTION; + } + } + return FSDPE_OK; +} + +static fsdp_error_t +fsdp_parse_b (const char **p, fsdp_bw_modifier_t ** bw_modifiers, + unsigned int *bw_modifiers_count) +{ + char fsdp_buf[MAXSHORTFIELDLEN]; + unsigned long int wuint; + unsigned int i = 0; + char *lp = (char *) *p; + + /* count b= lines */ + while (!strncmp (lp, "b=", 2)) + { + NEXT_LINE (lp); + i++; + } + *bw_modifiers = calloc (i, sizeof (fsdp_bw_modifier_t)); + *bw_modifiers_count = i; + + while (i > 0) + { + unsigned int index = *bw_modifiers_count - i; + if (2 == sscanf (*p, "b=%20[^:\r\n]:%lu", fsdp_buf, &wuint)) + { + if (!strncmp (fsdp_buf, "CT", 2)) + (*bw_modifiers)[index].b_mod_type = + FSDP_BW_MOD_TYPE_CONFERENCE_TOTAL; + else if (!strncmp (fsdp_buf, "AS", 2)) + (*bw_modifiers)[index].b_mod_type = + FSDP_BW_MOD_TYPE_APPLICATION_SPECIFIC; + else if (!strncmp (fsdp_buf, "RS", 2)) + (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_RTCP_SENDERS; + else if (!strncmp (fsdp_buf, "RR", 2)) + (*bw_modifiers)[index].b_mod_type = + FSDP_BW_MOD_TYPE_RTCP_RECEIVERS; + else + { + (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_UNKNOWN; + (*bw_modifiers)[index].b_unknown_bw_modt = + (char *) strdup (fsdp_buf); + } + (*bw_modifiers)[index].b_value = wuint; + NEXT_LINE (*p); + } + else + { + *bw_modifiers_count -= i; + return FSDPE_INVALID_BANDWIDTH; + } + i--; + } + return FSDPE_OK; +} + +static fsdp_error_t +fsdp_parse_k (const char **p, fsdp_encryption_method_t * method, + char **content) +{ + char fsdp_buf[MAXSHORTFIELDLEN]; + char longfsdp_buf[MAXLONGFIELDLEN]; + + if (!strncmp (*p, "k=", 2)) + { + if (sscanf (*p, "k=prompt")) + { + *method = FSDP_ENCRYPTION_METHOD_PROMPT; + *content = NULL; + NEXT_LINE (*p); + } + else + { + if (sscanf + (*p, "k=%6[^:\r\n]:%" MLFLENS "s", fsdp_buf, longfsdp_buf)) + { + if (!strncmp (fsdp_buf, "clear", 5)) + *method = FSDP_ENCRYPTION_METHOD_CLEAR; + else if (!strncmp (fsdp_buf, "base64", 6)) + *method = FSDP_ENCRYPTION_METHOD_BASE64; + else if (!strncmp (fsdp_buf, "uri", 3)) + *method = FSDP_ENCRYPTION_METHOD_URI; + else + return FSDPE_INVALID_ENCRYPTION_METHOD; + *content = strdup (longfsdp_buf); + NEXT_LINE (*p); + } + } + } + return FSDPE_OK; +} + +static fsdp_error_t +fsdp_parse_rtpmap (fsdp_rtpmap_t *** rtpmap, unsigned int *counter, + const char *value) +{ + fsdp_error_t result = FSDPE_OK; + + if (0 == *counter) + { + *counter = 0; + *rtpmap = calloc (MEDIA_RTPMAPS_MAX_COUNT, sizeof (fsdp_rtpmap_t *)); + } + if (*counter < MEDIA_RTPMAPS_MAX_COUNT) + { + unsigned int c = *counter; + fsdp_rtpmap_t **map = *rtpmap; + char fsdp_buf[MAXSHORTFIELDLEN]; + char longfsdp_buf[MAXLONGFIELDLEN]; + map[c] = calloc (1, sizeof (fsdp_rtpmap_t)); + + /* a=rtpmap: /[/ */ + if (2 == sscanf (value, "%s %s", fsdp_buf, longfsdp_buf)) + { + char *slash1; + map[c]->pt = strdup (fsdp_buf); + /* parse /[/] */ + slash1 = strchr (longfsdp_buf, '/'); + if (NULL == slash1) + { + result = FSDPE_INVALID_ATTRIBUTE_RTPMAP; + } + else + { + char *slash2; + *slash1 = '\0'; + slash1++; + map[c]->encoding_name = strdup (longfsdp_buf); + slash2 = strchr (slash1, '/'); + if (NULL != slash2) + { + *slash2 = '\0'; + slash2++; + map[c]->parameters = strdup (slash2); + } + map[c]->clock_rate = strtol (slash1, NULL, 10); + } + (*counter)++; + } + } + return result; +} + +static fsdp_error_t +fsdp_repeat_time_to_uint (const char *time, unsigned long int *seconds) +{ + const unsigned long SECONDS_PER_DAY = 86400; + const unsigned long SECONDS_PER_HOUR = 3600; + const unsigned long SECONDS_PER_MINUTE = 60; + char c; + unsigned long int wuint; + + if (sscanf (time, "%lu%c", &wuint, &c) == 2) + { + /* time with unit specification character */ + switch (c) + { + case 'd': + *seconds = wuint * SECONDS_PER_DAY; + break; + case 'h': + *seconds = wuint * SECONDS_PER_HOUR; + break; + case 'm': + *seconds = wuint * SECONDS_PER_MINUTE; + break; + case 's': + *seconds = wuint; + break; + default: + return FSDPE_INVALID_REPEAT; + break; + } + } + else if (sscanf (time, "%lu", &wuint) == 1) + { + /* time without unit specification character */ + *seconds = wuint; + } + else + { + return FSDPE_INVALID_REPEAT; + } + return FSDPE_OK; +} + +unsigned int +fsdp_get_version (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->version; +} + +const char * +fsdp_get_owner_username (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->o_username; +} + +const char * +fsdp_get_session_id (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->o_session_id; +} + +const char * +fsdp_get_announcement_version (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->o_announcement_version; +} + +fsdp_network_type_t +fsdp_get_owner_network_type (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_NETWORK_TYPE_UNDEFINED; + return dsc->o_network_type; +} + +fsdp_address_type_t +fsdp_get_owner_address_type (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_ADDRESS_TYPE_UNDEFINED; + return dsc->o_address_type; +} + +const char * +fsdp_get_owner_address (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->o_address; +} + +const char * +fsdp_get_name (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->s_name; +} + +const char * +fsdp_get_information (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->i_information; +} + +const char * +fsdp_get_uri (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->u_uri; +} + +unsigned int +fsdp_get_emails_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->emails_count; +} + +const char * +fsdp_get_email (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->emails_count)) + return NULL; + return dsc->emails[index]; +} + +unsigned int +fsdp_get_phones_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->phones_count; +} + +const char * +fsdp_get_phone (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->phones_count)) + return NULL; + return dsc->phones[index]; +} + +fsdp_network_type_t +fsdp_get_global_conn_network_type (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_NETWORK_TYPE_UNDEFINED; + return dsc->c_network_type; +} + +fsdp_address_type_t +fsdp_get_global_conn_address_type (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_ADDRESS_TYPE_UNDEFINED; + return dsc->c_address_type; +} + +const char * +fsdp_get_global_conn_address (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->c_address.address; +} + +unsigned int +fsdp_get_global_conn_address_ttl (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->c_address.address_ttl; +} + +unsigned int +fsdp_get_global_conn_address_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->c_address.address_count; +} + +unsigned int +fsdp_get_bw_modifier_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->bw_modifiers_count; +} + +fsdp_bw_modifier_type_t +fsdp_get_bw_modifier_type (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->bw_modifiers_count)) + return FSDP_BW_MOD_TYPE_UNDEFINED; + return dsc->bw_modifiers[index].b_mod_type; +} + +const char * +fsdp_get_bw_modifier_type_unknown (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->bw_modifiers_count) || + (dsc->bw_modifiers[index].b_mod_type != FSDP_BW_MOD_TYPE_UNKNOWN)) + return NULL; + return dsc->bw_modifiers[index].b_unknown_bw_modt; +} + +unsigned long int +fsdp_get_bw_value (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->bw_modifiers_count)) + return 0; + return dsc->bw_modifiers[index].b_value; +} + +time_t +fsdp_get_period_start (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return 0; + return dsc->time_periods[index]->start; +} + +time_t +fsdp_get_period_stop (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return 0; + return dsc->time_periods[index]->stop; +} + +unsigned int +fsdp_get_period_repeats_count (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return 0; + return dsc->time_periods[index]->repeats_count; +} + +unsigned long int +fsdp_get_period_repeat_interval (const fsdp_description_t * dsc, + unsigned int index, unsigned int rindex) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return 0; + return dsc->time_periods[index]->repeats[rindex]->interval; +} + +unsigned long int +fsdp_get_period_repeat_duration (const fsdp_description_t * dsc, + unsigned int index, unsigned int rindex) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return 0; + return dsc->time_periods[index]->repeats[rindex]->duration; +} + +const unsigned long int * +fsdp_get_period_repeat_offsets (const fsdp_description_t * dsc, + unsigned int index, unsigned int rindex) +{ + if ((!dsc) || (index >= dsc->time_periods_count)) + return NULL; + return dsc->time_periods[index]->repeats[rindex]->offsets; +} + +const char * +fsdp_get_timezone_adj (const fsdp_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->timezone_adj; +} + +unsigned int +fsdp_get_unidentified_attribute_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->unidentified_attributes_count; +} + +const char * +fsdp_get_unidentified_attribute (const fsdp_description_t * dsc, + unsigned int index) +{ + if (!dsc || (index < dsc->unidentified_attributes_count)) + return NULL; + return dsc->unidentified_attributes[index]; +} + +fsdp_encryption_method_t +fsdp_get_encryption_method (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_ENCRYPTION_METHOD_UNDEFINED; + return dsc->k_encryption_method; +} + +const char * +fsdp_get_encryption_content (const fsdp_description_t * dsc) +{ + if (!dsc || (dsc->k_encryption_method == FSDP_ENCRYPTION_METHOD_UNDEFINED)) + return NULL; + return dsc->k_encryption_content; +} + +unsigned int +fsdp_get_rtpmap_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_rtpmaps_count; +} + +const char * +fsdp_get_rtpmap_payload_type (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_rtpmaps_count)) + return NULL; + return dsc->a_rtpmaps[index]->pt; +} + +const char * +fsdp_get_rtpmap_encoding_name (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_rtpmaps_count)) + return NULL; + return dsc->a_rtpmaps[index]->encoding_name; +} + +unsigned int +fsdp_get_rtpmap_clock_rate (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_rtpmaps_count)) + return 0; + return dsc->a_rtpmaps[index]->clock_rate; +} + +const char * +fsdp_get_rtpmap_encoding_parameters (const fsdp_description_t * dsc, + unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_rtpmaps_count)) + return NULL; + return dsc->a_rtpmaps[index]->parameters; +} + +const char * +fsdp_get_str_att (const fsdp_description_t * dsc, fsdp_session_str_att_t att) +{ + /*TODO: change these individual attributes with a table, thus + avoiding this slow switch */ + char *result; + + if (!dsc) + return NULL; + + switch (att) + { + case FSDP_SESSION_STR_ATT_CATEGORY: + result = dsc->a_category; + break; + case FSDP_SESSION_STR_ATT_KEYWORDS: + result = dsc->a_keywords; + break; + case FSDP_SESSION_STR_ATT_TOOL: + result = dsc->a_tool; + break; + case FSDP_SESSION_STR_ATT_CHARSET: + result = dsc->a_charset; + break; + default: + result = NULL; + } + return result; +} + +unsigned int +fsdp_get_sdplang_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_sdplangs_count; +} + +const char * +fsdp_get_sdplang (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_sdplangs_count)) + return NULL; + return dsc->a_sdplangs[index]; +} + +unsigned int +fsdp_get_lang_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_langs_count; +} + +const char * +fsdp_get_lang (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_langs_count)) + return NULL; + return dsc->a_langs[index]; +} + +unsigned int +fsdp_get_control_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_controls_count; +} + +const char * +fsdp_get_control (const fsdp_description_t * dsc, unsigned int index) +{ + if ((!dsc) || (index >= dsc->a_controls_count)) + return NULL; + return dsc->a_controls[index]; +} + +const char * +fsdp_get_range (const fsdp_description_t * dsc) +{ + return dsc->a_range; +} + +fsdp_sendrecv_mode_t +fsdp_get_sendrecv_mode (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_SENDRECV_UNDEFINED; + return dsc->a_sendrecv_mode; +} + +fsdp_session_type_t +fsdp_get_session_type (const fsdp_description_t * dsc) +{ + if (!dsc) + return FSDP_SESSION_TYPE_UNDEFINED; + return dsc->a_type; +} + +unsigned int +fsdp_get_media_count (const fsdp_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->media_announcements_count; +} + +const fsdp_media_description_t * +fsdp_get_media (const fsdp_description_t * dsc, unsigned int index) +{ + if ((index >= dsc->media_announcements_count)) + return NULL; + return dsc->media_announcements[index]; +} + +fsdp_media_t +fsdp_get_media_type (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_MEDIA_UNDEFINED; + return dsc->media_type; +} + +unsigned int +fsdp_get_media_port (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->port; +} + +unsigned int +fsdp_get_media_port_count (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->port_count; +} + +fsdp_transport_protocol_t +fsdp_get_media_transport_protocol (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_TP_UNDEFINED; + return dsc->transport; +} + +unsigned int +fsdp_get_media_formats_count (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->formats_count; +} + +const char * +fsdp_get_media_format (const fsdp_media_description_t * dsc, + unsigned int index) +{ + if (!dsc && (index < dsc->formats_count)) + return NULL; + return dsc->formats[index]; +} + +const char * +fsdp_get_media_title (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->i_title; +} + +fsdp_network_type_t +fsdp_get_media_network_type (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_NETWORK_TYPE_UNDEFINED; + return dsc->c_network_type; +} + +fsdp_address_type_t +fsdp_get_media_address_type (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_ADDRESS_TYPE_UNDEFINED; + return dsc->c_address_type; +} + +const char * +fsdp_get_media_address (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->c_address.address; +} + +unsigned int +fsdp_get_media_address_ttl (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->c_address.address_ttl; +} + +unsigned int +fsdp_get_media_address_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->c_address.address_count; +} + +fsdp_bw_modifier_type_t +fsdp_get_media_bw_modifier_type (const fsdp_media_description_t * dsc, + unsigned int index) +{ + if (!dsc || (index >= dsc->bw_modifiers_count)) + return FSDP_BW_MOD_TYPE_UNDEFINED; + return dsc->bw_modifiers[index].b_mod_type; +} + +const char * +fsdp_get_media_bw_modifier_type_unknown (const fsdp_media_description_t * dsc, + unsigned int index) +{ + if (!dsc || (index >= dsc->bw_modifiers_count) || + (FSDP_BW_MOD_TYPE_UNKNOWN != dsc->bw_modifiers[index].b_mod_type)) + return NULL; + return dsc->bw_modifiers[index].b_unknown_bw_modt; +} + +unsigned long int +fsdp_get_media_bw_value (const fsdp_media_description_t * dsc, + unsigned int index) +{ + if (!dsc || (index >= dsc->bw_modifiers_count)) + return 0; + return dsc->bw_modifiers[index].b_value; +} + +fsdp_encryption_method_t +fsdp_get_media_encryption_method (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_ENCRYPTION_METHOD_UNDEFINED; + return dsc->k_encryption_method; +} + +const char * +fsdp_get_media_encryption_content (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->k_encryption_content; +} + +unsigned int +fsdp_get_media_ptime (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_ptime; +} + +unsigned int +fsdp_get_media_maxptime (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_maxptime; +} + +unsigned int +fsdp_get_media_rtpmap_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->a_rtpmaps_count; +} + +const char * +fsdp_get_media_rtpmap_payload_type (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_rtpmaps_count)) + return NULL; + return mdsc->a_rtpmaps[index]->pt; +} + +const char * +fsdp_get_media_rtpmap_encoding_name (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_rtpmaps_count)) + return NULL; + return mdsc->a_rtpmaps[index]->encoding_name; +} + +unsigned int +fsdp_get_media_rtpmap_clock_rate (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_rtpmaps_count)) + return 0; + return mdsc->a_rtpmaps[index]->clock_rate; +} + +const char * +fsdp_get_media_rtpmap_encoding_parameters (const fsdp_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_rtpmaps_count)) + return NULL; + return mdsc->a_rtpmaps[index]->parameters; +} + +unsigned int +fsdp_get_media_sdplang_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->a_sdplangs_count; +} + +const char * +fsdp_get_media_sdplang (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_sdplangs_count)) + return NULL; + return mdsc->a_sdplangs[index]; +} + +unsigned int +fsdp_get_media_lang_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->a_langs_count; +} + +const char * +fsdp_get_media_lang (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_langs_count)) + return NULL; + return mdsc->a_langs[index]; +} + +unsigned int +fsdp_get_media_control_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->a_controls_count; +} + +char * +fsdp_get_media_control (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_controls_count)) + return NULL; + return mdsc->a_controls[index]; +} + +char * +fsdp_get_media_range (const fsdp_media_description_t * mdsc) +{ + return mdsc->a_range; +} + +unsigned int +fsdp_get_media_fmtp_count (const fsdp_media_description_t * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->a_fmtps_count; +} + +const char * +fsdp_get_media_fmtp (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index >= mdsc->a_fmtps_count)) + return NULL; + return mdsc->a_fmtps[index]; +} + +fsdp_orient_t +fsdp_get_media_orient (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_ORIENT_UNDEFINED; + return dsc->a_orient; +} + +fsdp_sendrecv_mode_t +fsdp_get_media_sendrecv (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_SENDRECV_UNDEFINED; + return dsc->a_sendrecv_mode; +} + +float +fsdp_get_media_framerate (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_framerate; +} + +unsigned int +fsdp_get_media_quality (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_quality; +} + +unsigned int +fsdp_get_media_rtcp_port (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return 0; + return dsc->a_rtcp_port; +} + +fsdp_network_type_t +fsdp_get_media_rtcp_network_type (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_NETWORK_TYPE_UNDEFINED; + return dsc->a_rtcp_network_type; +} + +fsdp_address_type_t +fsdp_get_media_rtcp_address_type (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return FSDP_ADDRESS_TYPE_UNDEFINED; + return dsc->a_rtcp_address_type; +} + +const char * +fsdp_get_media_rtcp_address (const fsdp_media_description_t * dsc) +{ + if (!dsc) + return NULL; + return dsc->a_rtcp_address; +} + +unsigned int +fsdp_get_media_unidentified_attribute_count (const fsdp_media_description_t + * mdsc) +{ + if (!mdsc) + return 0; + return mdsc->unidentified_attributes_count; +} + +const char * +fsdp_get_media_unidentified_attribute (const fsdp_media_description_t * mdsc, + unsigned int index) +{ + if (!mdsc || (index < mdsc->unidentified_attributes_count)) + return NULL; + return mdsc->unidentified_attributes[index]; +} Index: libmpdemux/freesdp/common.h =================================================================== --- libmpdemux/freesdp/common.h (révision 0) +++ libmpdemux/freesdp/common.h (révision 0) @@ -0,0 +1,352 @@ +/* + This file is part of FreeSDP. + Copyright (C) 2001,2002,2003 Federico Montesino Pouzols + + FreeSDP is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + * @file common.h + * @ingroup common + * @short Public header common for both parsing and formatting modules. + **/ + +#ifndef FSDP_COMMON_H +#define FSDP_COMMON_H + +/* Macros to avoid name mangling when compiling with a C++ compiler */ +#ifdef __cplusplus +# define BEGIN_C_DECLS extern "C" { +# define END_C_DECLS } +#else /* !__cplusplus */ +# define BEGIN_C_DECLS +# define END_C_DECLS +#endif /* __cplusplus */ + +#include +#include + +BEGIN_C_DECLS +/** + * @defgroup common FreeSDP Common Facilities + * + * Data types and routines common for both parsing and formatting + * modules. + **/ +/** @addtogroup common */ +/*@{*/ +/** + * @enum fsdp_error_t freesdp/common.h + * @short Error codes in the FreeSDP library. + * + * There is a FSDPE_MISSING_XXXX for each mandatory line, as + * FSDPE_MISSING_OWNER. This kind of error is reported when a + * mandatory description line, such as the owner line, is not found + * where it should be in the SDP description. There are also several + * error codes like FSDPE_INVALID_XXXX. These are returned when there + * is a recognized line in the parsed description that violates the + * SDP syntax or gives wrong parameters, for instance "c=foo bar", + * which would cause a FSDPE_INVALID_CONNECTION error code to be + * returned. + **/ +typedef enum +{ + FSDPE_OK = 0, + FSDPE_ILLEGAL_CHARACTER, /**< Misplaced '\r', '\n' or '\0' */ + FSDPE_MISSING_VERSION, /**< The first line is not like + v=... */ + FSDPE_INVALID_VERSION, /**< Parse error in version line, + perhaps, the version specified in + v=... is not valid for FreeSDP */ + FSDPE_MISSING_OWNER, /**< No owner line found in its + place */ + FSDPE_INVALID_OWNER, /**< Parse error in owner line */ + FSDPE_MISSING_NAME, /**< No session name found in its + place */ + FSDPE_EMPTY_NAME, /**< Empty session name line */ + + FSDPE_INVALID_CONNECTION, /**< Syntax error in connection + line */ + + FSDPE_INVALID_CONNECTION_ADDRTYPE, /**< Unrecognized address type in + connection line */ + FSDPE_INVALID_CONNECTION_NETTYPE, /**< Unrecognized network type in + connection line */ + FSDPE_INVALID_BANDWIDTH, /**< Parse error in bandwidth + line */ + FSDPE_MISSING_TIME, /**< No time period has been given + for the session */ + FSDPE_INVALID_TIME, /**< Parse error in time line */ + FSDPE_INVALID_REPEAT, /**< Parse error in repeat time + line */ + FSDPE_INVALID_TIMEZONE, /**< Parse error in timezone line */ + FSDPE_INVALID_ENCRYPTION_METHOD, /**< Unknown encryption method */ + FSDPE_INVALID_ATTRIBUTE, /**< Syntax error in an attribute + line */ + + FSDPE_INVALID_ATTRIBUTE_RTPMAP,/**< Parse error in a=rtpmap:... line */ + FSDPE_INVALID_SESSION_TYPE, /**< An unknown session type has been + specified in a `type:' + session-level attribute */ + + FSDPE_INVALID_MEDIA, /**< Parse error in media line */ + FSDPE_UNKNOWN_MEDIA_TYPE, /**< Unknown media type in media + line */ + + FSDPE_UNKNOWN_MEDIA_TRANSPORT, /**< A media transport has been + specified that is unknown */ + + FSDPE_OVERFILLED, /**< extra unknown lines are at the + end of the description */ + FSDPE_INVALID_LINE, /**< a line unknown to FreeSDP has been + found */ + FSDPE_MISSING_CONNECTION_INFO, /**< No connection information has + been provided for the whole + session nor one or more media */ + FSDPE_INVALID_INDEX, + /* FSDPE_MAXSIZE, description does not fit requested maximun size */ + FSDPE_INTERNAL_ERROR, + + FSDPE_INVALID_PARAMETER, /**< Some parameter of the called + FreeSDP routine has been given an + invalid value. This includes + cases such as NULL pointers. */ + FSDPE_BUFFER_OVERFLOW +} fsdp_error_t; + +/** + * @short Type of network + * + * Initially, SDP defines "Internet". New network types may be + * registered with IANA. However, the number of types is expected to + * be small and rarely extended. In addition, every new network type + * requires at least one new address type. + **/ +typedef enum +{ + FSDP_NETWORK_TYPE_UNDEFINED, /**< Not provided */ + FSDP_NETWORK_TYPE_INET /**< Internet */ +} fsdp_network_type_t; + +/** + * @short Type of address + * + * Initially, IPv4 and IPv6 are defined for the network type + * Internet. New address types may be registered with IANA. + **/ +typedef enum +{ + FSDP_ADDRESS_TYPE_UNDEFINED, /**< Not provided */ + FSDP_ADDRESS_TYPE_IPV4, /**< IP version 4 */ + FSDP_ADDRESS_TYPE_IPV6 /**< IP version 6 */ +} fsdp_address_type_t; + +/** + * @short Type of bandwith modifiers + * + * Bandwidth modifiers specify the meaning of the bandwidth + * value. Initially "Conference Total" and "Application Specific" are + * defined. Both use kilobits as bandwidth unit. "Conference Total" + * specifies that the bandwidth value is a proposed upper limit to the + * session bandwidth. "Application Specific" specifies thath the + * bandwidth value is the application concept of maximum bandwidth. + **/ +typedef enum +{ + FSDP_BW_MOD_TYPE_UNDEFINED, /**< Not provided */ + FSDP_BW_MOD_TYPE_UNKNOWN, /**< Unknown bandwidth + modifier (FreeSDP + ignores it) */ + FSDP_BW_MOD_TYPE_CONFERENCE_TOTAL, /**< "CT - Conference Total" */ + FSDP_BW_MOD_TYPE_APPLICATION_SPECIFIC, /**< "AS - Application specific" */ + FSDP_BW_MOD_TYPE_RTCP_SENDERS, /**< "RS - RTCP bandwidth for + senders */ + FSDP_BW_MOD_TYPE_RTCP_RECEIVERS, /**< "RR - RTCP bandwidth for + receivers */ +} fsdp_bw_modifier_type_t; + +/** + * @short encryption method + * + * The encryption method specifies the way to get the encryption key. + **/ +typedef enum +{ + FSDP_ENCRYPTION_METHOD_UNDEFINED, /**< Not provided */ + FSDP_ENCRYPTION_METHOD_CLEAR, /**< The key field is the + untransformed key */ + FSDP_ENCRYPTION_METHOD_BASE64, /**< The key is base64 + encoded */ + FSDP_ENCRYPTION_METHOD_URI, /**< The key value provided is + a URI pointing to the actual + key */ + FSDP_ENCRYPTION_METHOD_PROMPT /**< The key is not provided + but should be got prompting + the user */ +} fsdp_encryption_method_t; + +/** + * @short Advised reception/transmission mode + * + * Depending on wheter sendrecv, recvonly, sendonly or inactive + * attribute is given, the tools used to participate in the session + * should be started in the corresponding transmission + * mode. FSDP_SENDRECV_SENDRECV is the default for sessions which are + * not of the conference type broadcast or H332. + **/ +typedef enum +{ + FSDP_SENDRECV_UNDEFINED, /**< Not specified */ + FSDP_SENDRECV_SENDRECV, /**< Send and receive */ + FSDP_SENDRECV_RECVONLY, /**< Receive only */ + FSDP_SENDRECV_SENDONLY, /**< Send only */ + FSDP_SENDRECV_INACTIVE /**< Do not send nor receive */ +} fsdp_sendrecv_mode_t; + +/** + * @short Values for `orient' media attribute. + * + * Normally used with whiteboard media, this attribute specifies the + * orientation of the whiteboard. + **/ +typedef enum +{ + FSDP_ORIENT_UNDEFINED, /**< Not specified */ + FSDP_ORIENT_PORTRAIT, /**< Portrait */ + FSDP_ORIENT_LANDSCAPE, /**< Landscape */ + FSDP_ORIENT_SEASCAPE /**< Upside down landscape */ +} fsdp_orient_t; + +/** + * @short Type of the conference + * + * The following types are initially defined: broadcast, meeting, + * moderated, test and H332. + **/ +typedef enum +{ + FSDP_SESSION_TYPE_UNDEFINED, /**< Not specified */ + FSDP_SESSION_TYPE_BROADCAST, /**< Broadcast session */ + FSDP_SESSION_TYPE_MEETING, /**< Meeting session */ + FSDP_SESSION_TYPE_MODERATED, /**< Moderated session */ + FSDP_SESSION_TYPE_TEST, /**< Test (do not display) */ + FSDP_SESSION_TYPE_H332 /**< H332 session */ +} fsdp_session_type_t; + +/** + * @short Media type + * + * The following types are defined initially: audio, video, + * application, data and control. + **/ +typedef enum +{ + FSDP_MEDIA_UNDEFINED, /**< Not specified */ + FSDP_MEDIA_VIDEO, /**< Video */ + FSDP_MEDIA_AUDIO, /**< Audio */ + FSDP_MEDIA_APPLICATION, /**< Application, such as whiteboard */ + FSDP_MEDIA_DATA, /**< bulk data */ + FSDP_MEDIA_CONTROL /**< Control channel */ +} fsdp_media_t; + +/** + * @short Transport protocol + * + * The transport protocol used depends on the address type. Initially, + * RTP over UDP Audio/Video Profile, and UDP are defined. + * + **/ +typedef enum +{ + FSDP_TP_UNDEFINED, /**< Not specified */ + FSDP_TP_RTP_AVP, /**< RTP Audio/Video Profile */ + FSDP_TP_UDP, /**< UDP */ + FSDP_TP_TCP, /**< TCP */ + FSDP_TP_UDPTL, /**< ITU-T T.38*/ + FSDP_TP_VAT, /**< old vat protocol (historic)*/ + FSDP_TP_OLD_RTP, /**< old rtp protocols (historic)*/ + FSDP_TP_H320 /**< TODO: add to the parser */ +} fsdp_transport_protocol_t; + +/** + * Session-level attributes whose value is specified as a character + * string in FreeSDP. These values are usually given to + * fsdp_get_strn_att() in order to get the corresponding value. + * + **/ +typedef enum +{ + FSDP_SESSION_STR_ATT_CATEGORY, + FSDP_SESSION_STR_ATT_KEYWORDS, + FSDP_SESSION_STR_ATT_TOOL, + FSDP_SESSION_STR_ATT_CHARSET, +} fsdp_session_str_att_t; + +/** + * @short FreeSDP SDP description media object. + * + * Object for media specific information in SDP descriptions. Each SDP + * description may include any number of media section. A + * fsdp_media_description_t object encapsulates the information in a + * media section, such as video, audio or whiteboard. + **/ +typedef struct fsdp_media_description_t_s fsdp_media_description_t; + +/** + * @short FreeSDP SDP session description object. + * + * Contains all the information extracted from a textual SDP + * description, including all the media announcements. + **/ +typedef struct fsdp_description_t_s fsdp_description_t; + +/** + * Allocates memory and initializes values for a new + * fsdp_description_t object. If you call this routine, do not forget + * about fsdp_description_delete() + * + * @return new fsdp_description_t object + **/ +fsdp_description_t *fsdp_description_new (void); + +/** + * Destroys a fsdp_description_t object. + * + * @param dsc pointer to the fsdp_description_t object to delete. + **/ +void fsdp_description_delete (fsdp_description_t * dsc); + +/** + * Calling this function over a description is equivalent to calling + * fsdp_description_delete and then fsdp_description_delete. This + * function is however more suitable and efficient for description + * processing loops. + * + * @param dsc pointer to the fsdp_description_t object to + * renew/recycle. + **/ +void fsdp_description_recycle (fsdp_description_t * dsc); + +/** + * * Returns a string correspondent to the error number. + * * + * * @param err_no error number. + * **/ +const char *fsdp_strerror (fsdp_error_t err_no); + + /*@}*//* closes addtogroup common */ + +END_C_DECLS +#endif /* FSDP_COMMON_H */ Index: libmpdemux/rtsp.c =================================================================== --- libmpdemux/rtsp.c (révision 0) +++ libmpdemux/rtsp.c (révision 0) @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2006 Benjamin Zores + * based on previous Real RTSP support from Roberto Togni and xine team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#ifndef HAVE_WINSOCK2 +#include +#include +#include +#define closesocket close +#else +#include +#include +#endif +#include + +#include "stream.h" +#include "librtsp/rtsp.h" +#include "librtsp/rtsp_session.h" + +extern int network_bandwidth; + +static int +rtsp_streaming_read (int fd, char *buffer, + int size, streaming_ctrl_t *stream_ctrl) +{ + return rtsp_session_read (stream_ctrl->data, buffer, size); +} + +static int +rtsp_streaming_start (stream_t *stream) +{ + struct rtsp_session_t *rtsp; + char *mrl; + char *file; + int port; + int redirected, temp; + int fd; + + if (!stream) + return -1; + + /* counter so we don't get caught in infinite redirections */ + temp = 5; + + do { + redirected = 0; + + fd = connect2Server (stream->streaming_ctrl->url->hostname, + port = (stream->streaming_ctrl->url->port ? + stream->streaming_ctrl->url->port : 554), 1); + + if (fd < 0 && !stream->streaming_ctrl->url->port) + fd = connect2Server (stream->streaming_ctrl->url->hostname, + port = 7070, 1); + + if (fd < 0) + return -1; + + file = stream->streaming_ctrl->url->file; + if (file[0] == '/') + file++; + + mrl = malloc (sizeof (char) + * (strlen (stream->streaming_ctrl->url->hostname) + + strlen (file) + 16)); + + sprintf (mrl, "rtsp://%s:%i/%s", + stream->streaming_ctrl->url->hostname, port, file); + + rtsp = rtsp_session_start (fd, &mrl, file, + stream->streaming_ctrl->url->hostname, + port, &redirected, + stream->streaming_ctrl->bandwidth); + + if (redirected == 1) + { + url_free (stream->streaming_ctrl->url); + stream->streaming_ctrl->url = url_new (mrl); + closesocket(fd); + } + + free (mrl); + temp--; + } while ((redirected != 0) && (temp > 0)); + + if (!rtsp) + return -1; + + stream->fd = fd; + stream->streaming_ctrl->data = rtsp; + + stream->streaming_ctrl->streaming_read = rtsp_streaming_read; + stream->streaming_ctrl->streaming_seek = NULL; + stream->streaming_ctrl->prebuffer_size = 16 * 1024; /* 80 KB */ + stream->streaming_ctrl->buffering = 1; + stream->streaming_ctrl->status = streaming_playing_e; + + return 0; +} + +static void +rtsp_streaming_close (struct stream_st *s) +{ + struct rtsp_session_t *rtsp = NULL; + + rtsp = s->streaming_ctrl->data; + if (rtsp) + rtsp_session_end (rtsp); +} + +static int +rtsp_streaming_open (stream_t *stream, int mode, void *opts, int *file_format) +{ + URL_t *url; + extern int index_mode; + + mp_msg (MSGT_OPEN, MSGL_INFO, "STREAM_RTSP, URL: %s\n", stream->url); + stream->streaming_ctrl = streaming_ctrl_new (); + if (!stream->streaming_ctrl) + return STREAM_ERROR; + + stream->streaming_ctrl->bandwidth = network_bandwidth; + url = url_new (stream->url); + stream->streaming_ctrl->url = check4proxies (url); + + stream->fd = -1; + index_mode = -1; /* prevent most RTSP streams from locking due to -idx */ + if (rtsp_streaming_start (stream) < 0) + { + streaming_ctrl_free (stream->streaming_ctrl); + stream->streaming_ctrl = NULL; + return STREAM_UNSUPORTED; + } + + fixup_network_stream_cache (stream); + stream->type = STREAMTYPE_STREAM; + stream->close = rtsp_streaming_close; + + return STREAM_OK; +} + +stream_info_t stream_info_rtsp = { + "RTSP streaming", + "rtsp", + "Benjamin Zores", + "", + rtsp_streaming_open, + {"rtsp", NULL}, + NULL, + 0 // Urls are an option string +}; + + Index: libmpdemux/Makefile =================================================================== --- libmpdemux/Makefile (révision 18695) +++ libmpdemux/Makefile (copie de travail) @@ -130,15 +130,22 @@ asf_mmst_streaming.c \ pnm.c \ rtp.c \ + rtsp.c \ SRCS += realrtsp/asmrp.c \ realrtsp/real.c \ realrtsp/rmff.c \ - realrtsp/rtsp.c \ - realrtsp/rtsp_session.c \ realrtsp/sdpplin.c \ realrtsp/xbuffer.c \ +SRCS += librtsp/rtsp.c \ + librtsp/rtsp_rtp.c \ + librtsp/rtsp_session.c \ + +SRCS += freesdp/common.c \ + freesdp/errorlist.c \ + freesdp/parser.c \ + ifeq ($(STREAMING_LIVE555),yes) CPLUSPLUSSRCS = demux_rtp.cpp demux_rtp_codec.cpp CPLUSPLUSINCLUDE = $(LIVE_INCLUDES) @@ -184,7 +191,10 @@ $(ALSA_LIB) $(VORBIS_LIB) $(CDPARANOIA_LIB) -lz -lpthread clean: - rm -f *.o *.a *~ realrtsp/*.o realrtsp/*.a realrtsp/*~ + rm -f *.o *.a *~ \ + realrtsp/*.o realrtsp/*.a realrtsp/*~ \ + librtsp/*.o librtsp/*.a librtsp/*~ \ + freesdp/*.o freesdp/*.a freesdp/*~ distclean: clean rm -f .depend test Index: libmpdemux/realrtsp/sdpplin.h =================================================================== --- libmpdemux/realrtsp/sdpplin.h (révision 18695) +++ libmpdemux/realrtsp/sdpplin.h (copie de travail) @@ -30,7 +30,7 @@ #define HAVE_SDPPLIN_H #include "rmff.h" -#include "rtsp.h" +#include "../librtsp/rtsp.h" typedef struct { Index: libmpdemux/realrtsp/rtsp_session.h =================================================================== --- libmpdemux/realrtsp/rtsp_session.h (révision 18695) +++ libmpdemux/realrtsp/rtsp_session.h (copie de travail) @@ -1,41 +0,0 @@ -/* - * This file was ported to MPlayer from xine CVS rtsp_session.h,v 1.4 2003/01/31 14:06:18 - */ - -/* - * Copyright (C) 2000-2002 the xine project - * - * This file is part of xine, a free video player. - * - * xine is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * xine is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - * - * - * high level interface to rtsp servers. - */ - -#ifndef HAVE_RTSP_SESSION_H -#define HAVE_RTSP_SESSION_H - -typedef struct rtsp_session_s rtsp_session_t; - -rtsp_session_t *rtsp_session_start(int fd, char **mrl, char *path, char *host, int port, int *redir, uint32_t bandwidth); - -int rtsp_session_read(rtsp_session_t *session, char *data, int len); - -int rtsp_session_peek_header(rtsp_session_t *this, char *buf, int maxsize); - -void rtsp_session_end(rtsp_session_t *session); - -#endif Index: libmpdemux/realrtsp/real.c =================================================================== --- libmpdemux/realrtsp/real.c (révision 18695) +++ libmpdemux/realrtsp/real.c (copie de travail) @@ -59,6 +59,8 @@ #define MAX(x,y) ((x>y) ? x : y) #endif +#define BUF_SIZE 4096 + #ifdef LOG static void hexdump (const char *buf, int length) { @@ -800,7 +802,7 @@ rtsp_schedule_field(rtsp_session, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play"); buf = xbuffer_ensure_size(buf, strlen(mrl) + 32); sprintf(buf, "%s/streamid=0", mrl); - rtsp_request_setup(rtsp_session,buf); + rtsp_request_setup(rtsp_session,buf,NULL); if (h->prop->num_streams > 1) { rtsp_schedule_field(rtsp_session, "Transport: x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play"); @@ -810,7 +812,7 @@ buf = xbuffer_ensure_size(buf, strlen(mrl) + 32); sprintf(buf, "%s/streamid=1", mrl); - rtsp_request_setup(rtsp_session,buf); + rtsp_request_setup(rtsp_session,buf,NULL); } /* set stream parameter (bandwidth) with our subscribe string */ rtsp_schedule_field(rtsp_session, subscribe); @@ -846,3 +848,24 @@ buf = xbuffer_free(buf); return h; } + +struct real_rtsp_session_t * +init_real_rtsp_session (void) +{ + struct real_rtsp_session_t *real_rtsp_session = NULL; + + real_rtsp_session = malloc (sizeof (struct real_rtsp_session_t)); + real_rtsp_session->recv = xbuffer_init(BUF_SIZE); + + return real_rtsp_session; +} + +void +free_real_rtsp_session (struct real_rtsp_session_t* real_session) +{ + if (!real_session) + return; + + xbuffer_free (real_session->recv); + free (real_session); +} Index: libmpdemux/realrtsp/real.h =================================================================== --- libmpdemux/realrtsp/real.h (révision 18695) +++ libmpdemux/realrtsp/real.h (copie de travail) @@ -31,8 +31,22 @@ #define HAVE_REAL_H #include "rmff.h" -#include "rtsp.h" +#include "../librtsp/rtsp.h" +#define HEADER_SIZE 4096 + +struct real_rtsp_session_t { + /* receive buffer */ + uint8_t *recv; + int recv_size; + int recv_read; + + /* header buffer */ + uint8_t header[HEADER_SIZE]; + int header_len; + int header_read; +}; + /* * calculates response and checksum of a given challenge * (RealChallenge1 in rtsp). See implementation for details. @@ -41,6 +55,8 @@ int real_get_rdt_chunk(rtsp_t *rtsp_session, char **buffer); rmff_header_t *real_parse_sdp(char *data, char **stream_rules, uint32_t bandwidth); rmff_header_t *real_setup_and_get_header(rtsp_t *rtsp_session, uint32_t bandwidth); +struct real_rtsp_session_t* init_real_rtsp_session(void); +void free_real_rtsp_session(struct real_rtsp_session_t* real_session); #endif Index: libmpdemux/realrtsp/rtsp.c =================================================================== --- libmpdemux/realrtsp/rtsp.c (révision 18695) +++ libmpdemux/realrtsp/rtsp.c (copie de travail) @@ -1,942 +0,0 @@ -/* - * This file was ported to MPlayer from xine CVS rtsp.c,v 1.9 2003/04/10 02:30:48 - */ - -/* - * Copyright (C) 2000-2002 the xine project - * - * This file is part of xine, a free video player. - * - * xine is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * xine is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - * - * - * a minimalistic implementation of rtsp protocol, - * *not* RFC 2326 compilant yet. - */ - -#include -#include -#include -#include "config.h" -#ifndef HAVE_WINSOCK2 -#define closesocket close -#include -#include -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rtsp.h" -#include "../stream.h" -#include "../demuxer.h" -#include "rtsp_session.h" -#include "osdep/timer.h" - -/* -#define LOG -*/ - -#define BUF_SIZE 4096 -#define HEADER_SIZE 1024 -#define MAX_FIELDS 256 - -extern int network_bandwidth; -struct rtsp_s { - - int s; - - char *host; - int port; - char *path; - char *param; - char *mrl; - char *user_agent; - - char *server; - unsigned int server_state; - uint32_t server_caps; - - unsigned int cseq; - char *session; - - char *answers[MAX_FIELDS]; /* data of last message */ - char *scheduled[MAX_FIELDS]; /* will be sent with next message */ -}; - -/* - * constants - */ - -const char rtsp_protocol_version[]="RTSP/1.0"; - -/* server states */ -#define RTSP_CONNECTED 1 -#define RTSP_INIT 2 -#define RTSP_READY 4 -#define RTSP_PLAYING 8 -#define RTSP_RECORDING 16 - -/* server capabilities */ -#define RTSP_OPTIONS 0x001 -#define RTSP_DESCRIBE 0x002 -#define RTSP_ANNOUNCE 0x004 -#define RTSP_SETUP 0x008 -#define RTSP_GET_PARAMETER 0x010 -#define RTSP_SET_PARAMETER 0x020 -#define RTSP_TEARDOWN 0x040 -#define RTSP_PLAY 0x080 -#define RTSP_RECORD 0x100 - -/* - * network utilities - */ - -static int host_connect_attempt(struct in_addr ia, int port) { - - int s; - struct sockaddr_in sin; - - s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (s == -1) { - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: socket(): %s\n", strerror(errno)); - return -1; - } - - sin.sin_family = AF_INET; - sin.sin_addr = ia; - sin.sin_port = htons(port); - - if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 -#ifndef HAVE_WINSOCK2 - && errno != EINPROGRESS) { -#else - && WSAGetLastError() == WSAEINPROGRESS) { -#endif - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: connect(): %s\n", strerror(errno)); - closesocket(s); - return -1; - } - - return s; -} - -static int host_connect(const char *host, int port) { - - struct hostent *h; - int i, s; - - h = gethostbyname(host); - if (h == NULL) { - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to resolve '%s'.\n", host); - return -1; - } - - for (i = 0; h->h_addr_list[i]; i++) { - struct in_addr ia; - - memcpy (&ia, h->h_addr_list[i], 4); - s = host_connect_attempt(ia, port); - if(s != -1) - return s; - } - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: unable to connect to '%s'.\n", host); - return -1; -} - -static int write_stream(int s, const char *buf, int len) { - int total, timeout; - - total = 0; timeout = 30; - while (total < len){ - int n; - - n = send (s, &buf[total], len - total, 0); - - if (n > 0) - total += n; - else if (n < 0) { -#ifndef HAVE_WINSOCK2 - if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) { -#else - if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) { -#endif - usec_sleep (1000000); timeout--; - } else - return -1; - } - } - - return total; -} - -static ssize_t read_stream(int fd, void *buf, size_t count) { - - ssize_t ret, total; - - total = 0; - - while (total < count) { - - ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0); - - if (ret<0) { - if(errno == EAGAIN) { - fd_set rset; - struct timeval timeout; - - FD_ZERO (&rset); - FD_SET (fd, &rset); - - timeout.tv_sec = 30; - timeout.tv_usec = 0; - - if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) { - return -1; - } - continue; - } - - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: read error.\n"); - return ret; - } else - total += ret; - - /* end of stream */ - if (!ret) break; - } - - return total; -} - -/* - * debugging utilities - */ -#if 0 -static void hexdump (char *buf, int length) { - - int i; - - mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: ascii>"); - for (i = 0; i < length; i++) { - unsigned char c = buf[i]; - - if ((c >= 32) && (c <= 128)) - mp_msg(MSGT_OPEN, MSGL_INFO, "%c", c); - else - mp_msg(MSGT_OPEN, MSGL_INFO, "."); - } - mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); - - mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: hexdump> "); - for (i = 0; i < length; i++) { - unsigned char c = buf[i]; - - mp_msg(MSGT_OPEN, MSGL_INFO, "%02x", c); - - if ((i % 16) == 15) - mp_msg(MSGT_OPEN, MSGL_INFO, "\nrtsp: "); - - if ((i % 2) == 1) - mp_msg(MSGT_OPEN, MSGL_INFO, " "); - - } - mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); -} -#endif - -/* - * rtsp_get gets a line from stream - * and returns a null terminated string. - */ - -static char *rtsp_get(rtsp_t *s) { - - int n=1; - char *buffer = malloc(BUF_SIZE); - char *string = NULL; - - read_stream(s->s, buffer, 1); - while (ns, &(buffer[n]), 1); - if ((buffer[n-1]==0x0d)&&(buffer[n]==0x0a)) break; - n++; - } - - if (n>=BUF_SIZE) { - mp_msg(MSGT_OPEN, MSGL_FATAL, "librtsp: buffer overflow in rtsp_get\n"); - exit(1); - } - string=malloc(sizeof(char)*n); - memcpy(string,buffer,n-1); - string[n-1]=0; - -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << '%s'\n", string); -#endif - - - free(buffer); - return string; -} - -/* - * rtsp_put puts a line on stream - */ - -static void rtsp_put(rtsp_t *s, const char *string) { - - int len=strlen(string); - char *buf=malloc(sizeof(char)*len+2); - -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: >> '%s'", string); -#endif - - memcpy(buf,string,len); - buf[len]=0x0d; - buf[len+1]=0x0a; - - write_stream(s->s, buf, len+2); - -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_INFO, " done.\n"); -#endif - - free(buf); -} - -/* - * extract server status code - */ - -static int rtsp_get_code(const char *string) { - - char buf[4]; - int code=0; - - if (!strncmp(string, rtsp_protocol_version, strlen(rtsp_protocol_version))) - { - memcpy(buf, string+strlen(rtsp_protocol_version)+1, 3); - buf[3]=0; - code=atoi(buf); - } else if (!strncmp(string, "SET_PARAMETER",8)) - { - return RTSP_STATUS_SET_PARAMETER; - } - - if(code != 200) mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: server responds: '%s'\n",string); - - return code; -} - -/* - * send a request - */ - -static void rtsp_send_request(rtsp_t *s, const char *type, const char *what) { - - char **payload=s->scheduled; - char *buf; - - buf = malloc(strlen(type)+strlen(what)+strlen(rtsp_protocol_version)+3); - - sprintf(buf,"%s %s %s",type, what, rtsp_protocol_version); - rtsp_put(s,buf); - free(buf); - if (payload) - while (*payload) { - rtsp_put(s,*payload); - payload++; - } - rtsp_put(s,""); - rtsp_unschedule_all(s); -} - -/* - * schedule standard fields - */ - -static void rtsp_schedule_standard(rtsp_t *s) { - - char tmp[16]; - - snprintf(tmp, 16, "Cseq: %u", s->cseq); - rtsp_schedule_field(s, tmp); - - if (s->session) { - char *buf; - buf = malloc(strlen(s->session)+15); - sprintf(buf, "Session: %s", s->session); - rtsp_schedule_field(s, buf); - free(buf); - } -} -/* - * get the answers, if server responses with something != 200, return NULL - */ - -static int rtsp_get_answers(rtsp_t *s) { - - char *answer=NULL; - unsigned int answer_seq; - char **answer_ptr=s->answers; - int code; - int ans_count = 0; - - answer=rtsp_get(s); - if (!answer) - return 0; - code=rtsp_get_code(answer); - free(answer); - - rtsp_free_answers(s); - - do { /* while we get answer lines */ - - answer=rtsp_get(s); - if (!answer) - return 0; - - if (!strncmp(answer,"Cseq:",5)) { - sscanf(answer,"Cseq: %u",&answer_seq); - if (s->cseq != answer_seq) { -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: Cseq mismatch. got %u, assumed %u", answer_seq, s->cseq); -#endif - s->cseq=answer_seq; - } - } - if (!strncmp(answer,"Server:",7)) { - char *buf = malloc(strlen(answer)); - sscanf(answer,"Server: %s",buf); - if (s->server) free(s->server); - s->server=strdup(buf); - free(buf); - } - if (!strncmp(answer,"Session:",8)) { - char *buf = calloc(1, strlen(answer)); - sscanf(answer,"Session: %s",buf); - if (s->session) { - if (strcmp(buf, s->session)) { - mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: setting NEW session: %s\n", buf); - free(s->session); - s->session=strdup(buf); - } - } else - { -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_INFO, "rtsp: setting session id to: %s\n", buf); -#endif - s->session=strdup(buf); - } - free(buf); - } - *answer_ptr=answer; - answer_ptr++; - } while ((strlen(answer)!=0) && (++ans_count < MAX_FIELDS)); - - s->cseq++; - - *answer_ptr=NULL; - rtsp_schedule_standard(s); - - return code; -} - -/* - * send an ok message - */ - -int rtsp_send_ok(rtsp_t *s) { - char cseq[16]; - - rtsp_put(s, "RTSP/1.0 200 OK"); - sprintf(cseq,"CSeq: %u", s->cseq); - rtsp_put(s, cseq); - rtsp_put(s, ""); - return 0; -} - -/* - * implementation of must-have rtsp requests; functions return - * server status code. - */ - -int rtsp_request_options(rtsp_t *s, const char *what) { - - char *buf; - - if (what) { - buf=strdup(what); - } else - { - buf=malloc(sizeof(char)*(strlen(s->host)+16)); - sprintf(buf,"rtsp://%s:%i", s->host, s->port); - } - rtsp_send_request(s,"OPTIONS",buf); - free(buf); - - return rtsp_get_answers(s); -} - -int rtsp_request_describe(rtsp_t *s, const char *what) { - - char *buf; - - if (what) { - buf=strdup(what); - } else - { - buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); - sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); - } - rtsp_send_request(s,"DESCRIBE",buf); - free(buf); - - return rtsp_get_answers(s); -} - -int rtsp_request_setup(rtsp_t *s, const char *what) { - - rtsp_send_request(s,"SETUP",what); - - return rtsp_get_answers(s); -} - -int rtsp_request_setparameter(rtsp_t *s, const char *what) { - - char *buf; - - if (what) { - buf=strdup(what); - } else - { - buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); - sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); - } - rtsp_send_request(s,"SET_PARAMETER",buf); - free(buf); - - return rtsp_get_answers(s); -} - -int rtsp_request_play(rtsp_t *s, const char *what) { - - char *buf; - - if (what) { - buf=strdup(what); - } else - { - buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); - sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); - } - rtsp_send_request(s,"PLAY",buf); - free(buf); - - return rtsp_get_answers(s); -} - -int rtsp_request_tearoff(rtsp_t *s, const char *what) { - - rtsp_send_request(s,"TEAROFF",what); - - return rtsp_get_answers(s); -} - -/* - * read opaque data from stream - */ - -int rtsp_read_data(rtsp_t *s, char *buffer, unsigned int size) { - - int i,seq; - - if (size>=4) { - i=read_stream(s->s, buffer, 4); - if (i<4) return i; - if (((buffer[0]=='S')&&(buffer[1]=='E')&&(buffer[2]=='T')&&(buffer[3]=='_')) || - ((buffer[0]=='O')&&(buffer[1]=='P')&&(buffer[2]=='T')&&(buffer[3]=='I'))) // OPTIONS - { - char *rest=rtsp_get(s); - if (!rest) - return -1; - - seq=-1; - do { - free(rest); - rest=rtsp_get(s); - if (!rest) - return -1; - if (!strncmp(rest,"CSeq:",5)) - sscanf(rest,"CSeq: %u",&seq); - } while (strlen(rest)!=0); - free(rest); - if (seq<0) { -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: cseq not recognized!\n"); -#endif - seq=1; - } - /* let's make the server happy */ - rtsp_put(s, "RTSP/1.0 451 Parameter Not Understood"); - rest=malloc(sizeof(char)*16); - sprintf(rest,"CSeq: %u", seq); - rtsp_put(s, rest); - rtsp_put(s, ""); - i=read_stream(s->s, buffer, size); - } else - { - i=read_stream(s->s, buffer+4, size-4); - i+=4; - } - } else - i=read_stream(s->s, buffer, size); -#ifdef LOG - mp_msg(MSGT_OPEN, MSGL_INFO, "librtsp: << %d of %d bytes\n", i, size); -#endif - - return i; -} - -/* - * connect to a rtsp server - */ - -//rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) { -rtsp_t *rtsp_connect(int fd, char* mrl, char *path, char *host, int port, char *user_agent) { - - rtsp_t *s=malloc(sizeof(rtsp_t)); - int i; - - for (i=0; ianswers[i]=NULL; - s->scheduled[i]=NULL; - } - - s->server=NULL; - s->server_state=0; - s->server_caps=0; - - s->cseq=1; - s->session=NULL; - - if (user_agent) - s->user_agent=strdup(user_agent); - else - s->user_agent=strdup("User-Agent: RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)"); - - s->mrl = strdup(mrl); - s->host = strdup(host); - s->port = port; - s->path = strdup(path); - while (*path == '/') - path++; - if ((s->param = strchr(s->path, '?')) != NULL) - s->param++; - //mp_msg(MSGT_OPEN, MSGL_INFO, "path=%s\n", s->path); - //mp_msg(MSGT_OPEN, MSGL_INFO, "param=%s\n", s->param ? s->param : "NULL"); - s->s = fd; - - if (s->s < 0) { - mp_msg(MSGT_OPEN, MSGL_ERR, "rtsp: failed to connect to '%s'\n", s->host); - rtsp_close(s); - return NULL; - } - - s->server_state=RTSP_CONNECTED; - - /* now let's send an options request. */ - rtsp_schedule_field(s, "CSeq: 1"); - rtsp_schedule_field(s, s->user_agent); - rtsp_schedule_field(s, "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7"); - rtsp_schedule_field(s, "PlayerStarttime: [28/03/2003:22:50:23 00:00]"); - rtsp_schedule_field(s, "CompanyID: KnKV4M4I/B2FjJ1TToLycw=="); - rtsp_schedule_field(s, "GUID: 00000000-0000-0000-0000-000000000000"); - rtsp_schedule_field(s, "RegionData: 0"); - rtsp_schedule_field(s, "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586"); - /*rtsp_schedule_field(s, "Pragma: initiate-session");*/ - rtsp_request_options(s, NULL); - - return s; -} - - -/* - * closes an rtsp connection - */ - -void rtsp_close(rtsp_t *s) { - - if (s->server_state) closesocket(s->s); /* TODO: send a TEAROFF */ - if (s->path) free(s->path); - if (s->host) free(s->host); - if (s->mrl) free(s->mrl); - if (s->session) free(s->session); - if (s->user_agent) free(s->user_agent); - rtsp_free_answers(s); - rtsp_unschedule_all(s); - free(s); -} - -/* - * search in answers for tags. returns a pointer to the content - * after the first matched tag. returns NULL if no match found. - */ - -char *rtsp_search_answers(rtsp_t *s, const char *tag) { - - char **answer; - char *ptr; - - if (!s->answers) return NULL; - answer=s->answers; - - while (*answer) { - if (!strncasecmp(*answer,tag,strlen(tag))) { - ptr=strchr(*answer,':'); - if (!ptr) return NULL; - ptr++; - while(*ptr==' ') ptr++; - return ptr; - } - answer++; - } - - return NULL; -} - -/* - * session id management - */ - -void rtsp_set_session(rtsp_t *s, const char *id) { - - if (s->session) free(s->session); - - s->session=strdup(id); - -} - -char *rtsp_get_session(rtsp_t *s) { - - return s->session; - -} - -char *rtsp_get_mrl(rtsp_t *s) { - - return s->mrl; - -} - -char *rtsp_get_param(rtsp_t *s, char *p) { - int len; - char *param; - if (!s->param) - return NULL; - if (!p) - return strdup(s->param); - len = strlen(p); - param = s->param; - while (param && *param) { - char *nparam = strchr(param, '&'); - if (strncmp(param, p, len) == 0 && param[len] == '=') { - param += len + 1; - len = nparam ? nparam - param : strlen(param); - nparam = malloc(len + 1); - memcpy(nparam, param, len); - nparam[len] = 0; - return nparam; - } - param = nparam ? nparam + 1 : NULL; - } - return NULL; -} - -/* - * schedules a field for transmission - */ - -void rtsp_schedule_field(rtsp_t *s, const char *string) { - - int i=0; - - if (!string) return; - - while(s->scheduled[i]) { - i++; - } - s->scheduled[i]=strdup(string); -} - -/* - * removes the first scheduled field which prefix matches string. - */ - -void rtsp_unschedule_field(rtsp_t *s, const char *string) { - - char **ptr=s->scheduled; - - if (!string) return; - - while(*ptr) { - if (!strncmp(*ptr, string, strlen(string))) - break; - } - if (*ptr) free(*ptr); - ptr++; - do { - *(ptr-1)=*ptr; - } while(*ptr); -} - -/* - * unschedule all fields - */ - -void rtsp_unschedule_all(rtsp_t *s) { - - char **ptr; - - if (!s->scheduled) return; - ptr=s->scheduled; - - while (*ptr) { - free(*ptr); - *ptr=NULL; - ptr++; - } -} -/* - * free answers - */ - -void rtsp_free_answers(rtsp_t *s) { - - char **answer; - - if (!s->answers) return; - answer=s->answers; - - while (*answer) { - free(*answer); - *answer=NULL; - answer++; - } -} - -static int realrtsp_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) { - return rtsp_session_read(stream_ctrl->data, buffer, size); -} - - -static int realrtsp_streaming_start( stream_t *stream ) { - int fd; - rtsp_session_t *rtsp; - char *mrl; - char *file; - int port; - int redirected, temp; - if( stream==NULL ) return -1; - - temp = 5; // counter so we don't get caught in infinite redirections (you never know) - - do { - redirected = 0; - - fd = connect2Server( stream->streaming_ctrl->url->hostname, - port = (stream->streaming_ctrl->url->port ? stream->streaming_ctrl->url->port : 554),1 ); - if(fd<0 && !stream->streaming_ctrl->url->port) - fd = connect2Server(stream->streaming_ctrl->url->hostname, port = 7070, 1); - if(fd<0) return -1; - - file = stream->streaming_ctrl->url->file; - if (file[0] == '/') - file++; - mrl = malloc(sizeof(char)*(strlen(stream->streaming_ctrl->url->hostname)+strlen(file)+16)); - sprintf(mrl,"rtsp://%s:%i/%s",stream->streaming_ctrl->url->hostname,port,file); - rtsp = rtsp_session_start(fd,&mrl, file, stream->streaming_ctrl->url->hostname, port, &redirected, stream->streaming_ctrl->bandwidth); - - if( redirected == 1) { - url_free(stream->streaming_ctrl->url); - stream->streaming_ctrl->url = url_new(mrl); - closesocket(fd); - } - - free(mrl); - temp--; - } while( (redirected != 0) && (temp > 0) ); - - if(!rtsp) return -1; - - stream->fd=fd; - stream->streaming_ctrl->data=rtsp; - - stream->streaming_ctrl->streaming_read = realrtsp_streaming_read; - //stream->streaming_ctrl->streaming_seek = nop_streaming_seek; - stream->streaming_ctrl->prebuffer_size = 128*1024; // 8 KBytes - stream->streaming_ctrl->buffering = 1; - stream->streaming_ctrl->status = streaming_playing_e; - return 0; -} - - -static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { - URL_t *url; - - mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_RTSP, URL: %s\n", stream->url); - stream->streaming_ctrl = streaming_ctrl_new(); - if( stream->streaming_ctrl==NULL ) - return STREAM_ERROR; - - stream->streaming_ctrl->bandwidth = network_bandwidth; - url = url_new(stream->url); - stream->streaming_ctrl->url = check4proxies(url); - //url_free(url); - - stream->fd = -1; - if(realrtsp_streaming_start( stream ) < 0) { - streaming_ctrl_free(stream->streaming_ctrl); - stream->streaming_ctrl = NULL; - return STREAM_UNSUPORTED; - } - - fixup_network_stream_cache(stream); - stream->type = STREAMTYPE_STREAM; - return STREAM_OK; -} - - -stream_info_t stream_info_rtsp = { - "RealNetworks rtsp streaming", - "realrtsp", - "Roberto Togni, xine team", - "ported from xine", - open_s, - {"rtsp", NULL}, - NULL, - 0 // Urls are an option string -}; Index: libmpdemux/realrtsp/sdpplin.c =================================================================== --- libmpdemux/realrtsp/sdpplin.c (révision 18695) +++ libmpdemux/realrtsp/sdpplin.c (copie de travail) @@ -28,7 +28,7 @@ #include "config.h" #include "rmff.h" -#include "rtsp.h" +#include "../librtsp/rtsp.h" #include "sdpplin.h" #include "xbuffer.h" #include "mp_msg.h" Index: libmpdemux/realrtsp/rtsp_session.c =================================================================== --- libmpdemux/realrtsp/rtsp_session.c (révision 18695) +++ libmpdemux/realrtsp/rtsp_session.c (copie de travail) @@ -1,210 +0,0 @@ -/* - * This file was ported to MPlayer from xine CVS rtsp_session.c,v 1.9 2003/02/11 16:20:40 - */ - -/* - * Copyright (C) 2000-2002 the xine project - * - * This file is part of xine, a free video player. - * - * xine is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * xine is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - * - * - * high level interface to rtsp servers. - */ - -#include -#include "config.h" -#ifndef HAVE_WINSOCK2 -#include -#include -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include - -#include "rtsp.h" -#include "rtsp_session.h" -#include "real.h" -#include "rmff.h" -#include "asmrp.h" -#include "xbuffer.h" - -/* -#define LOG -*/ - -#define BUF_SIZE 4096 -#define HEADER_SIZE 4096 - -struct rtsp_session_s { - - rtsp_t *s; - - /* receive buffer */ - uint8_t *recv; - int recv_size; - int recv_read; - - /* header buffer */ - uint8_t header[HEADER_SIZE]; - int header_len; - int header_read; - -}; - -//rtsp_session_t *rtsp_session_start(char *mrl) { -rtsp_session_t *rtsp_session_start(int fd, char **mrl, char *path, char *host, int port, int *redir, uint32_t bandwidth) { - - rtsp_session_t *rtsp_session=malloc(sizeof(rtsp_session_t)); - char *server; - char *mrl_line = NULL; - rmff_header_t *h; - - rtsp_session->recv = xbuffer_init(BUF_SIZE); - -//connect: - *redir = 0; - - /* connect to server */ - rtsp_session->s=rtsp_connect(fd,*mrl,path,host,port,NULL); - if (!rtsp_session->s) - { - printf("rtsp_session: failed to connect to server %s\n", path); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); - free(rtsp_session); - return NULL; - } - - /* looking for server type */ - if (rtsp_search_answers(rtsp_session->s,"Server")) - server=strdup(rtsp_search_answers(rtsp_session->s,"Server")); - else { - if (rtsp_search_answers(rtsp_session->s,"RealChallenge1")) - server=strdup("Real"); - else - server=strdup("unknown"); - } - if (strstr(server,"Real") || strstr(server,"Helix")) - { - /* we are talking to a real server ... */ - - h=real_setup_and_get_header(rtsp_session->s, bandwidth); - if (!h) { - /* got an redirect? */ - if (rtsp_search_answers(rtsp_session->s, "Location")) - { - free(mrl_line); - mrl_line=strdup(rtsp_search_answers(rtsp_session->s, "Location")); - printf("rtsp_session: redirected to %s\n", mrl_line); - rtsp_close(rtsp_session->s); - free(server); - free(*mrl); - free(rtsp_session); - /* tell the caller to redirect, return url to redirect to in mrl */ - *mrl = mrl_line; - *redir = 1; - return NULL; -// goto connect; /* *shudder* i made a design mistake somewhere */ - } else - { - printf("rtsp_session: session can not be established.\n"); - rtsp_close(rtsp_session->s); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); - free(rtsp_session); - return NULL; - } - } - - rtsp_session->header_len=rmff_dump_header(h,rtsp_session->header,1024); - - rtsp_session->recv = xbuffer_copyin(rtsp_session->recv, 0, rtsp_session->header, rtsp_session->header_len); - rtsp_session->recv_size = rtsp_session->header_len; - rtsp_session->recv_read = 0; - - } else - { - printf("rtsp_session: Not a Real server. Server type is '%s'.\n",server); - rtsp_close(rtsp_session->s); - free(server); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); - free(rtsp_session); - return NULL; - } - free(server); - - return rtsp_session; -} - -int rtsp_session_read (rtsp_session_t *this, char *data, int len) { - - int to_copy=len; - char *dest=data; - char *source=this->recv + this->recv_read; - int fill=this->recv_size - this->recv_read; - - if (len < 0) return 0; - while (to_copy > fill) { - - memcpy(dest, source, fill); - to_copy -= fill; - dest += fill; - this->recv_read = 0; - this->recv_size = real_get_rdt_chunk (this->s, (char **)&(this->recv)); - if (this->recv_size < 0) - return -1; - source = this->recv; - fill = this->recv_size; - - if (this->recv_size == 0) { -#ifdef LOG - printf ("librtsp: %d of %d bytes provided\n", len-to_copy, len); -#endif - return len-to_copy; - } - } - - memcpy(dest, source, to_copy); - this->recv_read += to_copy; - -#ifdef LOG - printf ("librtsp: %d bytes provided\n", len); -#endif - - return len; -} - -int rtsp_session_peek_header(rtsp_session_t *this, char *buf, int maxsize) { - - int len; - - len = (this->header_len < maxsize) ? this->header_len : maxsize; - - memcpy(buf, this->header, len); - return len; -} - -void rtsp_session_end(rtsp_session_t *session) { - - rtsp_close(session->s); - session->recv = xbuffer_free(session->recv); - free(session); -} Index: libmpdemux/realrtsp/rtsp.h =================================================================== --- libmpdemux/realrtsp/rtsp.h (révision 18695) +++ libmpdemux/realrtsp/rtsp.h (copie de travail) @@ -1,74 +0,0 @@ -/* - * This file was ported to MPlayer from xine CVS rtsp.h,v 1.2 2002/12/16 21:50:55 - */ - -/* - * Copyright (C) 2002 the xine project - * - * This file is part of xine, a free video player. - * - * xine is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * xine is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - * - * - * a minimalistic implementation of rtsp protocol, - * *not* RFC 2326 compilant yet. - */ - -#ifndef HAVE_RTSP_H -#define HAVE_RTSP_H - - -/* some codes returned by rtsp_request_* functions */ - -#define RTSP_STATUS_SET_PARAMETER 10 -#define RTSP_STATUS_OK 200 - -typedef struct rtsp_s rtsp_t; - -rtsp_t* rtsp_connect (int fd, char *mrl, char *path, char *host, int port, char *user_agent); - -int rtsp_request_options(rtsp_t *s, const char *what); -int rtsp_request_describe(rtsp_t *s, const char *what); -int rtsp_request_setup(rtsp_t *s, const char *what); -int rtsp_request_setparameter(rtsp_t *s, const char *what); -int rtsp_request_play(rtsp_t *s, const char *what); -int rtsp_request_tearoff(rtsp_t *s, const char *what); - -int rtsp_send_ok(rtsp_t *s); - -int rtsp_read_data(rtsp_t *s, char *buffer, unsigned int size); - -char* rtsp_search_answers(rtsp_t *s, const char *tag); -void rtsp_add_to_payload(char **payload, const char *string); - -void rtsp_free_answers(rtsp_t *this); - -int rtsp_read (rtsp_t *this, char *data, int len); -void rtsp_close (rtsp_t *this); - -void rtsp_set_session(rtsp_t *s, const char *id); -char *rtsp_get_session(rtsp_t *s); - -char *rtsp_get_mrl(rtsp_t *s); -char *rtsp_get_param(rtsp_t *s, char *param); - -/*int rtsp_peek_header (rtsp_t *this, char *data); */ - -void rtsp_schedule_field(rtsp_t *s, const char *string); -void rtsp_unschedule_field(rtsp_t *s, const char *string); -void rtsp_unschedule_all(rtsp_t *s); - -#endif - Index: libmpdemux/librtsp/rtsp_session.h =================================================================== --- libmpdemux/librtsp/rtsp_session.h (révision 0) +++ libmpdemux/librtsp/rtsp_session.h (copie de travail) @@ -23,6 +23,9 @@ * * * high level interface to rtsp servers. + * + * 2006, Benjamin Zores and Vincent Mussard + * Support for MPEG-TS streaming through RFC compliant RTSP servers */ #ifndef HAVE_RTSP_SESSION_H @@ -34,8 +37,6 @@ int rtsp_session_read(rtsp_session_t *session, char *data, int len); -int rtsp_session_peek_header(rtsp_session_t *this, char *buf, int maxsize); - void rtsp_session_end(rtsp_session_t *session); #endif Index: libmpdemux/librtsp/rtsp_rtp.h =================================================================== --- libmpdemux/librtsp/rtsp_rtp.h (révision 0) +++ libmpdemux/librtsp/rtsp_rtp.h (révision 0) @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 Benjamin Zores + * heavily base on the Freebox patch for xine by Vincent Mussard + * but with many enhancements for better RTSP RFC compliance. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _HAVE_RTSP_RTP_H_ +#define _HAVE_RTSP_RTP_H_ + +#include + +#include "rtsp.h" + +#define MAX_PREVIEW_SIZE 4096 + +struct rtp_rtsp_session_t { + int rtp_socket; + int rtcp_socket; + char *control_url; + int count; +}; + +struct rtp_rtsp_session_t *rtp_setup_and_play (rtsp_t* rtsp_session); +off_t rtp_read (struct rtp_rtsp_session_t* st, char *buf, off_t length); +void rtp_session_free (struct rtp_rtsp_session_t *st); +void rtcp_send_rr (rtsp_t *s, struct rtp_rtsp_session_t *st); + +#endif /* _HAVE_RTSP_RTP_H_ */ + Index: libmpdemux/librtsp/rtsp.c =================================================================== --- libmpdemux/librtsp/rtsp.c (révision 0) +++ libmpdemux/librtsp/rtsp.c (copie de travail) @@ -24,6 +24,9 @@ * * a minimalistic implementation of rtsp protocol, * *not* RFC 2326 compilant yet. + * + * 2006, Benjamin Zores and Vincent Mussard + * fixed a lot of RFC compliance issues. */ #include @@ -48,9 +51,8 @@ #include #include +#include "mp_msg.h" #include "rtsp.h" -#include "../stream.h" -#include "../demuxer.h" #include "rtsp_session.h" #include "osdep/timer.h" @@ -89,7 +91,7 @@ * constants */ -const char rtsp_protocol_version[]="RTSP/1.0"; +#define RTSP_PROTOCOL_VERSION "RTSP/1.0" /* server states */ #define RTSP_CONNECTED 1 @@ -197,7 +199,7 @@ total = 0; - while (total < count) { + while (total < (ssize_t) count) { ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0); @@ -336,9 +338,9 @@ char buf[4]; int code=0; - if (!strncmp(string, rtsp_protocol_version, strlen(rtsp_protocol_version))) + if (!strncmp(string, RTSP_PROTOCOL_VERSION, strlen(RTSP_PROTOCOL_VERSION))) { - memcpy(buf, string+strlen(rtsp_protocol_version)+1, 3); + memcpy(buf, string+strlen(RTSP_PROTOCOL_VERSION)+1, 3); buf[3]=0; code=atoi(buf); } else if (!strncmp(string, "SET_PARAMETER",8)) @@ -360,9 +362,9 @@ char **payload=s->scheduled; char *buf; - buf = malloc(strlen(type)+strlen(what)+strlen(rtsp_protocol_version)+3); + buf = malloc(strlen(type)+strlen(what)+strlen(RTSP_PROTOCOL_VERSION)+3); - sprintf(buf,"%s %s %s",type, what, rtsp_protocol_version); + sprintf(buf,"%s %s %s",type, what, RTSP_PROTOCOL_VERSION); rtsp_put(s,buf); free(buf); if (payload) @@ -382,7 +384,7 @@ char tmp[16]; - snprintf(tmp, 16, "Cseq: %u", s->cseq); + snprintf(tmp, 16, "CSeq: %u", s->cseq); rtsp_schedule_field(s, tmp); if (s->session) { @@ -419,11 +421,11 @@ if (!answer) return 0; - if (!strncmp(answer,"Cseq:",5)) { - sscanf(answer,"Cseq: %u",&answer_seq); + if (!strncmp(answer,"CSeq:",5)) { + sscanf(answer,"CSeq: %u",&answer_seq); if (s->cseq != answer_seq) { #ifdef LOG - mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: Cseq mismatch. got %u, assumed %u", answer_seq, s->cseq); + mp_msg(MSGT_OPEN, MSGL_WARN, "librtsp: warning: CSeq mismatch. got %u, assumed %u\n", answer_seq, s->cseq); #endif s->cseq=answer_seq; } @@ -495,7 +497,7 @@ buf=malloc(sizeof(char)*(strlen(s->host)+16)); sprintf(buf,"rtsp://%s:%i", s->host, s->port); } - rtsp_send_request(s,"OPTIONS",buf); + rtsp_send_request(s,RTSP_METHOD_OPTIONS,buf); free(buf); return rtsp_get_answers(s); @@ -512,19 +514,35 @@ buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); } - rtsp_send_request(s,"DESCRIBE",buf); + rtsp_send_request(s,RTSP_METHOD_DESCRIBE,buf); free(buf); return rtsp_get_answers(s); } -int rtsp_request_setup(rtsp_t *s, const char *what) { +int rtsp_request_setup (rtsp_t * s, const char *what, char *control) +{ + char *buf = NULL; - rtsp_send_request(s,"SETUP",what); - - return rtsp_get_answers(s); -} + if (what) + buf = strdup (what); + else + { + int len = strlen (s->host) + strlen (s->path) + 16; + if (control) + len += strlen (control) + 1; + buf = malloc (len * sizeof (char)); + sprintf (buf, "rtsp://%s:%i/%s%s%s", s->host, s->port, s->path, + control ? "/" : "", control ? control : ""); + } + + rtsp_send_request (s,RTSP_METHOD_SETUP, buf); + free (buf); + + return rtsp_get_answers (s); +} + int rtsp_request_setparameter(rtsp_t *s, const char *what) { char *buf; @@ -536,7 +554,7 @@ buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); } - rtsp_send_request(s,"SET_PARAMETER",buf); + rtsp_send_request(s,RTSP_METHOD_SET_PARAMETER,buf); free(buf); return rtsp_get_answers(s); @@ -545,7 +563,8 @@ int rtsp_request_play(rtsp_t *s, const char *what) { char *buf; - + int ret; + if (what) { buf=strdup(what); } else @@ -553,15 +572,30 @@ buf=malloc(sizeof(char)*(strlen(s->host)+strlen(s->path)+16)); sprintf(buf,"rtsp://%s:%i/%s", s->host, s->port, s->path); } - rtsp_send_request(s,"PLAY",buf); + rtsp_send_request(s,RTSP_METHOD_PLAY,buf); free(buf); + + ret = rtsp_get_answers (s); + + if (ret == 200) + s->server_state = RTSP_PLAYING; - return rtsp_get_answers(s); + return ret; } -int rtsp_request_tearoff(rtsp_t *s, const char *what) { +int rtsp_request_teardown(rtsp_t *s, const char *what){ + char *buf; - rtsp_send_request(s,"TEAROFF",what); + if (what) + buf = strdup (what); + else + { + buf = + malloc (sizeof (char) * (strlen (s->host) + strlen (s->path) + 16)); + sprintf (buf, "rtsp://%s:%i/%s", s->host, s->port, s->path); + } + rtsp_send_request (s,RTSP_METHOD_TEARDOWN,buf); + free (buf); return rtsp_get_answers(s); } @@ -596,7 +630,7 @@ free(rest); if (seq<0) { #ifdef LOG - mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: cseq not recognized!\n"); + mp_msg(MSGT_OPEN, MSGL_WARN, "rtsp: warning: CSeq not recognized!\n"); #endif seq=1; } @@ -625,7 +659,6 @@ * connect to a rtsp server */ -//rtsp_t *rtsp_connect(const char *mrl, const char *user_agent) { rtsp_t *rtsp_connect(int fd, char* mrl, char *path, char *host, int port, char *user_agent) { rtsp_t *s=malloc(sizeof(rtsp_t)); @@ -640,7 +673,7 @@ s->server_state=0; s->server_caps=0; - s->cseq=1; + s->cseq=0; s->session=NULL; if (user_agent) @@ -690,7 +723,13 @@ void rtsp_close(rtsp_t *s) { - if (s->server_state) closesocket(s->s); /* TODO: send a TEAROFF */ + if (s->server_state) + { + if (s->server_state == RTSP_PLAYING) + rtsp_request_teardown (s, NULL); + close (s->s); + } + if (s->path) free(s->path); if (s->host) free(s->host); if (s->mrl) free(s->mrl); @@ -752,6 +791,11 @@ } +char *rtsp_get_host (rtsp_t * s) +{ + return s->host; +} + char *rtsp_get_param(rtsp_t *s, char *p) { int len; char *param; @@ -805,6 +849,8 @@ while(*ptr) { if (!strncmp(*ptr, string, strlen(string))) break; + else + ptr++; } if (*ptr) free(*ptr); ptr++; @@ -847,96 +893,3 @@ answer++; } } - -static int realrtsp_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) { - return rtsp_session_read(stream_ctrl->data, buffer, size); -} - - -static int realrtsp_streaming_start( stream_t *stream ) { - int fd; - rtsp_session_t *rtsp; - char *mrl; - char *file; - int port; - int redirected, temp; - if( stream==NULL ) return -1; - - temp = 5; // counter so we don't get caught in infinite redirections (you never know) - - do { - redirected = 0; - - fd = connect2Server( stream->streaming_ctrl->url->hostname, - port = (stream->streaming_ctrl->url->port ? stream->streaming_ctrl->url->port : 554),1 ); - if(fd<0 && !stream->streaming_ctrl->url->port) - fd = connect2Server(stream->streaming_ctrl->url->hostname, port = 7070, 1); - if(fd<0) return -1; - - file = stream->streaming_ctrl->url->file; - if (file[0] == '/') - file++; - mrl = malloc(sizeof(char)*(strlen(stream->streaming_ctrl->url->hostname)+strlen(file)+16)); - sprintf(mrl,"rtsp://%s:%i/%s",stream->streaming_ctrl->url->hostname,port,file); - rtsp = rtsp_session_start(fd,&mrl, file, stream->streaming_ctrl->url->hostname, port, &redirected, stream->streaming_ctrl->bandwidth); - - if( redirected == 1) { - url_free(stream->streaming_ctrl->url); - stream->streaming_ctrl->url = url_new(mrl); - closesocket(fd); - } - - free(mrl); - temp--; - } while( (redirected != 0) && (temp > 0) ); - - if(!rtsp) return -1; - - stream->fd=fd; - stream->streaming_ctrl->data=rtsp; - - stream->streaming_ctrl->streaming_read = realrtsp_streaming_read; - //stream->streaming_ctrl->streaming_seek = nop_streaming_seek; - stream->streaming_ctrl->prebuffer_size = 128*1024; // 8 KBytes - stream->streaming_ctrl->buffering = 1; - stream->streaming_ctrl->status = streaming_playing_e; - return 0; -} - - -static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { - URL_t *url; - - mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_RTSP, URL: %s\n", stream->url); - stream->streaming_ctrl = streaming_ctrl_new(); - if( stream->streaming_ctrl==NULL ) - return STREAM_ERROR; - - stream->streaming_ctrl->bandwidth = network_bandwidth; - url = url_new(stream->url); - stream->streaming_ctrl->url = check4proxies(url); - //url_free(url); - - stream->fd = -1; - if(realrtsp_streaming_start( stream ) < 0) { - streaming_ctrl_free(stream->streaming_ctrl); - stream->streaming_ctrl = NULL; - return STREAM_UNSUPORTED; - } - - fixup_network_stream_cache(stream); - stream->type = STREAMTYPE_STREAM; - return STREAM_OK; -} - - -stream_info_t stream_info_rtsp = { - "RealNetworks rtsp streaming", - "realrtsp", - "Roberto Togni, xine team", - "ported from xine", - open_s, - {"rtsp", NULL}, - NULL, - 0 // Urls are an option string -}; Index: libmpdemux/librtsp/rtsp_session.c =================================================================== --- libmpdemux/librtsp/rtsp_session.c (révision 0) +++ libmpdemux/librtsp/rtsp_session.c (copie de travail) @@ -23,6 +23,9 @@ * * * high level interface to rtsp servers. + * + * 2006, Benjamin Zores and Vincent Mussard + * Support for MPEG-TS streaming through RFC compliant RTSP servers */ #include @@ -41,12 +44,15 @@ #include #include +#include "mp_msg.h" +#include "../rtp.h" #include "rtsp.h" +#include "rtsp_rtp.h" #include "rtsp_session.h" -#include "real.h" -#include "rmff.h" -#include "asmrp.h" -#include "xbuffer.h" +#include "../realrtsp/real.h" +#include "../realrtsp/rmff.h" +#include "../realrtsp/asmrp.h" +#include "../realrtsp/xbuffer.h" /* #define LOG @@ -55,23 +61,20 @@ #define BUF_SIZE 4096 #define HEADER_SIZE 4096 +#define RTSP_OPTIONS_PUBLIC "Public" +#define RTSP_OPTIONS_SERVER "Server" +#define RTSP_OPTIONS_LOCATION "Location" +#define RTSP_OPTIONS_REAL "RealChallenge1" +#define RTSP_SERVER_TYPE_REAL "Real" +#define RTSP_SERVER_TYPE_HELIX "Helix" +#define RTSP_SERVER_TYPE_UNKNOWN "unknown" + struct rtsp_session_s { - rtsp_t *s; - - /* receive buffer */ - uint8_t *recv; - int recv_size; - int recv_read; - - /* header buffer */ - uint8_t header[HEADER_SIZE]; - int header_len; - int header_read; - + struct real_rtsp_session_t* real_session; + struct rtp_rtsp_session_t* rtp_session; }; -//rtsp_session_t *rtsp_session_start(char *mrl) { rtsp_session_t *rtsp_session_start(int fd, char **mrl, char *path, char *host, int port, int *redir, uint32_t bandwidth) { rtsp_session_t *rtsp_session=malloc(sizeof(rtsp_session_t)); @@ -79,42 +82,47 @@ char *mrl_line = NULL; rmff_header_t *h; - rtsp_session->recv = xbuffer_init(BUF_SIZE); + rtsp_session = malloc (sizeof (rtsp_session_t)); + rtsp_session->s = NULL; + rtsp_session->real_session = NULL; + rtsp_session->rtp_session = NULL; -//connect: *redir = 0; /* connect to server */ rtsp_session->s=rtsp_connect(fd,*mrl,path,host,port,NULL); if (!rtsp_session->s) { - printf("rtsp_session: failed to connect to server %s\n", path); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); + mp_msg (MSGT_OPEN, MSGL_ERR, + "rtsp_session: failed to connect to server %s\n", path); free(rtsp_session); return NULL; } /* looking for server type */ - if (rtsp_search_answers(rtsp_session->s,"Server")) - server=strdup(rtsp_search_answers(rtsp_session->s,"Server")); + if (rtsp_search_answers(rtsp_session->s,RTSP_OPTIONS_SERVER)) + server=strdup(rtsp_search_answers(rtsp_session->s,RTSP_OPTIONS_SERVER)); else { - if (rtsp_search_answers(rtsp_session->s,"RealChallenge1")) - server=strdup("Real"); + if (rtsp_search_answers(rtsp_session->s,RTSP_OPTIONS_REAL)) + server=strdup(RTSP_SERVER_TYPE_REAL); else - server=strdup("unknown"); + server=strdup(RTSP_SERVER_TYPE_UNKNOWN); } - if (strstr(server,"Real") || strstr(server,"Helix")) + if (strstr(server,RTSP_SERVER_TYPE_REAL) + || strstr(server,RTSP_SERVER_TYPE_HELIX)) { /* we are talking to a real server ... */ h=real_setup_and_get_header(rtsp_session->s, bandwidth); if (!h) { /* got an redirect? */ - if (rtsp_search_answers(rtsp_session->s, "Location")) + if (rtsp_search_answers(rtsp_session->s, RTSP_OPTIONS_LOCATION)) { free(mrl_line); - mrl_line=strdup(rtsp_search_answers(rtsp_session->s, "Location")); - printf("rtsp_session: redirected to %s\n", mrl_line); + mrl_line=strdup(rtsp_search_answers(rtsp_session->s, + RTSP_OPTIONS_LOCATION)); + mp_msg (MSGT_OPEN, MSGL_INFO, + "rtsp_session: redirected to %s\n", mrl_line); rtsp_close(rtsp_session->s); free(server); free(*mrl); @@ -123,43 +131,89 @@ *mrl = mrl_line; *redir = 1; return NULL; -// goto connect; /* *shudder* i made a design mistake somewhere */ } else { - printf("rtsp_session: session can not be established.\n"); + mp_msg (MSGT_OPEN, MSGL_ERR, + "rtsp_session: session can not be established.\n"); rtsp_close(rtsp_session->s); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); + free (server); free(rtsp_session); return NULL; } } - rtsp_session->header_len=rmff_dump_header(h,rtsp_session->header,1024); + rtsp_session->real_session = init_real_rtsp_session (); + rtsp_session->real_session->header_len = + rmff_dump_header (h, (char *) rtsp_session->real_session->header, 1024); - rtsp_session->recv = xbuffer_copyin(rtsp_session->recv, 0, rtsp_session->header, rtsp_session->header_len); - rtsp_session->recv_size = rtsp_session->header_len; - rtsp_session->recv_read = 0; + rtsp_session->real_session->recv = + xbuffer_copyin (rtsp_session->real_session->recv, 0, + rtsp_session->real_session->header, + rtsp_session->real_session->header_len); + + rtsp_session->real_session->recv_size = + rtsp_session->real_session->header_len; + rtsp_session->real_session->recv_read = 0; + } + else /* not a Real server : try RTP instead */ + { + char *public = NULL; - } else - { - printf("rtsp_session: Not a Real server. Server type is '%s'.\n",server); - rtsp_close(rtsp_session->s); - free(server); - rtsp_session->recv = xbuffer_free(rtsp_session->recv); - free(rtsp_session); - return NULL; + /* look for the Public: field in response to RTSP OPTIONS */ + public = strdup (rtsp_search_answers (rtsp_session->s, + RTSP_OPTIONS_PUBLIC)); + if (!public) + { + rtsp_close (rtsp_session->s); + free (server); + free (mrl_line); + free (rtsp_session); + return NULL; + } + + if (!strstr (public, RTSP_METHOD_DESCRIBE) + || !strstr (public, RTSP_METHOD_SETUP) + || !strstr (public, RTSP_METHOD_PLAY) + || !strstr (public, RTSP_METHOD_TEARDOWN)) + { + free (public); + mp_msg (MSGT_OPEN, MSGL_ERR, + "Remote server does not meet minimal RTSP 1.0 compliance.\n"); + rtsp_close (rtsp_session->s); + free (server); + free (mrl_line); + free (rtsp_session); + return NULL; + } + + free (public); + rtsp_session->rtp_session = rtp_setup_and_play (rtsp_session->s); + + if (!rtsp_session->rtp_session) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "rtsp_session: unsupported RTSP server. "); + mp_msg (MSGT_OPEN, MSGL_ERR, "Server type is '%s'.\n", server); + rtsp_close (rtsp_session->s); + free (server); + free (mrl_line); + free (rtsp_session); + return NULL; + } } + free(server); return rtsp_session; } int rtsp_session_read (rtsp_session_t *this, char *data, int len) { - + + if (this->real_session) { int to_copy=len; char *dest=data; - char *source=this->recv + this->recv_read; - int fill=this->recv_size - this->recv_read; + char *source = + (char *) (this->real_session->recv + this->real_session->recv_read); + int fill = this->real_session->recv_size - this->real_session->recv_read; if (len < 0) return 0; while (to_copy > fill) { @@ -167,14 +221,14 @@ memcpy(dest, source, fill); to_copy -= fill; dest += fill; - this->recv_read = 0; - this->recv_size = real_get_rdt_chunk (this->s, (char **)&(this->recv)); - if (this->recv_size < 0) + this->real_session->recv_read = 0; + this->real_session->recv_size = real_get_rdt_chunk (this->s, (char **)&(this->real_session->recv)); + if (this->real_session->recv_size < 0) return -1; - source = this->recv; - fill = this->recv_size; + source = (char *) this->real_session->recv; + fill = this->real_session->recv_size; - if (this->recv_size == 0) { + if (this->real_session->recv_size == 0) { #ifdef LOG printf ("librtsp: %d of %d bytes provided\n", len-to_copy, len); #endif @@ -183,28 +237,36 @@ } memcpy(dest, source, to_copy); - this->recv_read += to_copy; + this->real_session->recv_read += to_copy; #ifdef LOG printf ("librtsp: %d bytes provided\n", len); #endif return len; -} + } + else if (this->rtp_session) + { + int l = 0; -int rtsp_session_peek_header(rtsp_session_t *this, char *buf, int maxsize) { + l = read_rtp_from_server (this->rtp_session->rtp_socket, data, len); + /* send RTSP and RTCP keepalive */ + rtcp_send_rr (this->s, this->rtp_session); - int len; + if (l == 0) + rtsp_session_end (this); + + return l; + } - len = (this->header_len < maxsize) ? this->header_len : maxsize; - - memcpy(buf, this->header, len); - return len; + return 0; } void rtsp_session_end(rtsp_session_t *session) { - - rtsp_close(session->s); - session->recv = xbuffer_free(session->recv); - free(session); + rtsp_close (session->s); + if (session->real_session) + free_real_rtsp_session (session->real_session); + else if (session->rtp_session) + rtp_session_free (session->rtp_session); + free (session); } Index: libmpdemux/librtsp/rtsp.h =================================================================== --- libmpdemux/librtsp/rtsp.h (révision 0) +++ libmpdemux/librtsp/rtsp.h (copie de travail) @@ -24,6 +24,9 @@ * * a minimalistic implementation of rtsp protocol, * *not* RFC 2326 compilant yet. + * + * 2006, Benjamin Zores and Vincent Mussard + * fixed a lot of RFC compliance issues. */ #ifndef HAVE_RTSP_H @@ -35,16 +38,23 @@ #define RTSP_STATUS_SET_PARAMETER 10 #define RTSP_STATUS_OK 200 +#define RTSP_METHOD_OPTIONS "OPTIONS" +#define RTSP_METHOD_DESCRIBE "DESCRIBE" +#define RTSP_METHOD_SETUP "SETUP" +#define RTSP_METHOD_PLAY "PLAY" +#define RTSP_METHOD_TEARDOWN "TEARDOWN" +#define RTSP_METHOD_SET_PARAMETER "SET_PARAMETER" + typedef struct rtsp_s rtsp_t; rtsp_t* rtsp_connect (int fd, char *mrl, char *path, char *host, int port, char *user_agent); int rtsp_request_options(rtsp_t *s, const char *what); int rtsp_request_describe(rtsp_t *s, const char *what); -int rtsp_request_setup(rtsp_t *s, const char *what); +int rtsp_request_setup(rtsp_t *s, const char *what, char *control); int rtsp_request_setparameter(rtsp_t *s, const char *what); int rtsp_request_play(rtsp_t *s, const char *what); -int rtsp_request_tearoff(rtsp_t *s, const char *what); +int rtsp_request_teardown(rtsp_t *s, const char *what); int rtsp_send_ok(rtsp_t *s); @@ -62,10 +72,9 @@ char *rtsp_get_session(rtsp_t *s); char *rtsp_get_mrl(rtsp_t *s); +char *rtsp_get_host(rtsp_t *s); char *rtsp_get_param(rtsp_t *s, char *param); -/*int rtsp_peek_header (rtsp_t *this, char *data); */ - void rtsp_schedule_field(rtsp_t *s, const char *string); void rtsp_unschedule_field(rtsp_t *s, const char *string); void rtsp_unschedule_all(rtsp_t *s); Index: libmpdemux/librtsp/rtsp_rtp.c =================================================================== --- libmpdemux/librtsp/rtsp_rtp.c (révision 0) +++ libmpdemux/librtsp/rtsp_rtp.c (révision 0) @@ -0,0 +1,655 @@ +/* + * Copyright (C) 2006 Benjamin Zores + * based on the Freebox patch for xine by Vincent Mussard + * but with many enhancements for better RTSP RFC compliance. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mp_msg.h" +#include "rtsp.h" +#include "rtsp_rtp.h" +#include "rtsp_session.h" +#include "../freesdp/common.h" +#include "../freesdp/parser.h" + +#define RTSP_DEFAULT_PORT 31336 +#define MAX_LENGTH 256 + +#define RTSP_ACCEPT_SDP "Accept: application/sdp" +#define RTSP_CONTENT_LENGTH "Content-length" +#define RTSP_CONTENT_TYPE "Content-Type" +#define RTSP_APPLICATION_SDP "application/sdp" +#define RTSP_RANGE "Range: " +#define RTSP_NPT_NOW "npt=now-" +#define RTSP_MEDIA_CONTAINER_MPEG_TS "33" +#define RTSP_TRANSPORT_REQUEST "Transport: RTP/AVP;%s;%s%i-%i;mode=\"PLAY\"" + +#define RTSP_TRANSPORT_MULTICAST "multicast" +#define RTSP_TRANSPORT_UNICAST "unicast" + +#define RTSP_MULTICAST_PORT "port=" +#define RTSP_UNICAST_CLIENT_PORT "client_port=" +#define RTSP_UNICAST_SERVER_PORT "server_port=" +#define RTSP_SETUP_DESTINATION "destination=" + +#define RTSP_SESSION "Session" +#define RTSP_TRANSPORT "Transport" + +/* hardcoded RTCP RR - this is _NOT_ RFC compliant */ +#define RTCP_RR_SIZE 32 +#define RTCP_RR "\201\311\0\7(.JD\31+\306\343\0\0\0\0\0\0/E\0\0\2&\0\0\0\0\0\0\0\0\201" +#define RTCP_SEND_FREQUENCY 1024 + +int rtsp_port = 0; + +void +rtcp_send_rr (rtsp_t *s, struct rtp_rtsp_session_t *st) +{ + if (st->rtcp_socket == -1) + return; + + /* send RTCP RR every RTCP_SEND_FREQUENCY packets + * FIXME : NOT CORRECT, HARDCODED, BUT MAKES SOME SERVERS HAPPY + * not rfc compliant + * http://www.faqs.org/rfcs/rfc1889.html chapter 6 for RTCP + */ + + if (st->count == RTCP_SEND_FREQUENCY) + { + char rtcp_content[RTCP_RR_SIZE]; + strcpy (rtcp_content, RTCP_RR); + send (st->rtcp_socket, rtcp_content, RTCP_RR_SIZE, 0); + + /* ping RTSP server to keep connection alive. + we use OPTIONS instead of PING as not all servers support it */ + rtsp_request_options (s, "*"); + st->count = 0; + } + else + st->count++; +} + +static struct rtp_rtsp_session_t * +rtp_session_new (void) +{ + struct rtp_rtsp_session_t *st = NULL; + + st = malloc (sizeof (struct rtp_rtsp_session_t)); + + st->rtp_socket = -1; + st->rtcp_socket = -1; + st->control_url = NULL; + st->count = 0; + + return st; +} + +void +rtp_session_free (struct rtp_rtsp_session_t *st) +{ + if (!st) + return; + + if (st->rtp_socket != -1) + close (st->rtp_socket); + if (st->rtcp_socket != -1) + close (st->rtcp_socket); + + if (st->control_url) + free (st->control_url); + free (st); +} + +static void +rtp_session_set_fd (struct rtp_rtsp_session_t *st, + int rtp_sock, int rtcp_sock) +{ + if (!st) + return; + + st->rtp_socket = rtp_sock; + st->rtcp_socket = rtcp_sock; +} + +static int +parse_port (const char *line, const char *param, + int *rtp_port, int *rtcp_port) +{ + char *parse1; + char *parse2; + char *parse3; + + char *line_copy = strdup (line); + + parse1 = strstr (line_copy, param); + + if (parse1) + { + parse2 = strstr (parse1, "-"); + + if (parse2) + { + parse3 = strstr (parse2, ";"); + + if (parse3) + parse3[0] = 0; + + parse2[0] = 0; + } + else + { + free (line_copy); + return 0; + } + } + else + { + free (line_copy); + return 0; + } + + *rtp_port = atoi (parse1 + strlen (param)); + *rtcp_port = atoi (parse2 + 1); + + free (line_copy); + + return 1; +} + +static char * +parse_destination (const char *line) +{ + char *parse1; + char *parse2; + + char *dest = NULL; + char *line_copy = strdup (line); + int len; + + parse1 = strstr (line_copy, RTSP_SETUP_DESTINATION); + if (!parse1) + { + free (line_copy); + return NULL; + } + + parse2 = strstr (parse1, ";"); + if (!parse2) + { + free (line_copy); + return NULL; + } + + len = strlen (parse1) - strlen (parse2) + - strlen (RTSP_SETUP_DESTINATION) + 1; + dest = (char *) malloc ((len + 1) * sizeof (char)); + snprintf (dest, len, parse1 + strlen (RTSP_SETUP_DESTINATION)); + free (line_copy); + + return dest; +} + +static int +rtcp_connect (int client_port, int server_port, const char* server_hostname) +{ + struct sockaddr_in sin; + struct hostent *hp; + int s; + + if (client_port <= 1023) + return -1; + + s = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) + return -1; + + hp = gethostbyname (server_hostname); + if (!hp) + { + close (s); + return -1; + } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons (client_port); + + if (bind (s, (struct sockaddr *) &sin, sizeof (sin))) + { + close (s); + return -1; + } + + sin.sin_family = AF_INET; + memcpy (&(sin.sin_addr.s_addr), hp->h_addr, sizeof (hp->h_addr)); + sin.sin_port = htons (server_port); + + /* datagram socket */ + if (connect (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) + { + close (s); + return -1; + } + + return s; +} + +static int +rtp_connect (char *hostname, int port) +{ + struct sockaddr_in sin; + struct timeval tv; + int err, err_len; + int rxsockbufsz; + int s; + fd_set set; + + if (port <= 1023) + return -1; + + s = socket (PF_INET, SOCK_DGRAM, 0); + if (s == -1) + return -1; + + sin.sin_family = AF_INET; + if (!hostname || !strcmp (hostname, "0.0.0.0")) + sin.sin_addr.s_addr = htonl (INADDR_ANY); + else + inet_pton (AF_INET, hostname, &sin.sin_addr); + sin.sin_port = htons (port); + + /* Increase the socket rx buffer size to maximum -- this is UDP */ + rxsockbufsz = 240 * 1024; + if (setsockopt (s, SOL_SOCKET, SO_RCVBUF, + &rxsockbufsz, sizeof (rxsockbufsz))) + mp_msg (MSGT_OPEN, MSGL_ERR, "Couldn't set receive socket buffer size\n"); + + /* if multicast address, add membership */ + if ((ntohl (sin.sin_addr.s_addr) >> 28) == 0xe) + { + struct ip_mreq mcast; + mcast.imr_multiaddr.s_addr = sin.sin_addr.s_addr; + mcast.imr_interface.s_addr = 0; + + if (setsockopt (s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof (mcast))) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "IP_ADD_MEMBERSHIP failed\n"); + close (s); + return -1; + } + } + + /* datagram socket */ + if (bind (s, (struct sockaddr *) &sin, sizeof (sin))) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "bind: %s\n", strerror (errno)); + close (s); + return -1; + } + + tv.tv_sec = 0; + tv.tv_usec = (1 * 1000000); /* 1 second timeout */ + + FD_ZERO (&set); + FD_SET (s, &set); + + err = select (s + 1, &set, NULL, NULL, &tv); + if (err < 0) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "Select failed: %s\n", strerror (errno)); + close (s); + return -1; + } + else if (err == 0) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "Timeout! No data from host %s\n", hostname); + close (s); + return -1; + } + + err_len = sizeof (err); + getsockopt (s, SOL_SOCKET, SO_ERROR, &err, (socklen_t *) &err_len); + if (err) + { + mp_msg (MSGT_OPEN, MSGL_ERR, "Socket error: %d\n", err); + close (s); + return -1; + } + + return s; +} + +static int +is_multicast_address (char *addr) +{ + struct sockaddr_in sin; + + if (!addr) + return -1; + + sin.sin_family = AF_INET; + inet_pton (AF_INET, addr, &sin.sin_addr); + + if ((ntohl (sin.sin_addr.s_addr) >> 28) == 0xe) + return 1; + + return 0; +} + +struct rtp_rtsp_session_t * +rtp_setup_and_play (rtsp_t *rtsp_session) +{ + struct rtp_rtsp_session_t* rtp_session = NULL; + const fsdp_media_description_t *med_dsc = NULL; + char temp_buf[MAX_LENGTH + 1]; + char npt[256]; + + char* answer; + char* sdp; + char *server_addr = NULL; + char *destination = NULL; + + int statut; + int content_length = 0; + int is_multicast = 0; + + fsdp_description_t *dsc = NULL; + fsdp_error_t result; + + int client_rtp_port = -1; + int client_rtcp_port = -1; + int server_rtp_port = -1; + int server_rtcp_port = -1; + int rtp_sock = -1; + int rtcp_sock = -1; + + /* 1. send a RTSP DESCRIBE request to server */ + rtsp_schedule_field (rtsp_session, RTSP_ACCEPT_SDP); + statut = rtsp_request_describe (rtsp_session, NULL); + if (statut < 200 || statut > 299) + return NULL; + + answer = rtsp_search_answers (rtsp_session, RTSP_CONTENT_LENGTH); + if (answer) + content_length = atoi (answer); + else + return NULL; + + answer = rtsp_search_answers (rtsp_session, RTSP_CONTENT_TYPE); + if (!answer || !strstr (answer, RTSP_APPLICATION_SDP)) + return NULL; + + /* 2. read SDP message from server */ + sdp = (char *) malloc (sizeof (char) * (content_length + 1)); + if (rtsp_read_data (rtsp_session, sdp, content_length) <= 0) + { + free (sdp); + return NULL; + } + sdp[content_length] = 0; + + /* 3. parse SDP message */ + dsc = fsdp_description_new (); + result = fsdp_parse (sdp, dsc); + if (result != FSDPE_OK) + { + free (sdp); + fsdp_description_delete (dsc); + return NULL; + } + mp_msg (MSGT_OPEN, MSGL_V, "SDP:\n%s\n", sdp); + free (sdp); + + /* 4. check for number of media streams: only one is supported */ + if (fsdp_get_media_count (dsc) != 1) + { + mp_msg (MSGT_OPEN, MSGL_ERR, + "A single media stream only is supported atm.\n"); + fsdp_description_delete (dsc); + return NULL; + } + + /* 5. set the Normal Play Time parameter + * use range provided by server in SDP or start now if empty */ + sprintf (npt, RTSP_RANGE); + if (fsdp_get_range (dsc)) + strcat (npt, fsdp_get_range (dsc)); + else + strcat (npt, RTSP_NPT_NOW); + + /* 5. check for a valid media stream */ + med_dsc = fsdp_get_media (dsc, 0); + if (!med_dsc) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* 6. parse the `m= ' line */ + + /* check for an A/V media */ + if (fsdp_get_media_type (med_dsc) != FSDP_MEDIA_VIDEO && + fsdp_get_media_type (med_dsc) != FSDP_MEDIA_AUDIO) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* only RTP/AVP transport method is supported right now */ + if (fsdp_get_media_transport_protocol (med_dsc) != FSDP_TP_RTP_AVP) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* only MPEG-TS is supported at the moment */ + if (!strstr (fsdp_get_media_format (med_dsc, 0), + RTSP_MEDIA_CONTAINER_MPEG_TS)) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* get client port (if any) advised by server */ + client_rtp_port = fsdp_get_media_port (med_dsc); + if (client_rtp_port == -1) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* if client_rtp_port = 0 => let client randomly pick one */ + if (client_rtp_port == 0) + { + /* TODO: we should check if the port is in use first */ + if (rtsp_port) + client_rtp_port = rtsp_port; + else + client_rtp_port = RTSP_DEFAULT_PORT; + } + + /* RTCP port generally is RTP port + 1 */ + client_rtcp_port = client_rtp_port + 1; + + mp_msg (MSGT_OPEN, MSGL_V, + "RTP Port from SDP appears to be: %d\n", client_rtp_port); + mp_msg (MSGT_OPEN, MSGL_V, + "RTCP Port from SDP appears to be: %d\n", client_rtcp_port); + + /* 7. parse the `c= ' line */ + + /* check for a valid media network type (inet) */ + if (fsdp_get_media_network_type (med_dsc) != FSDP_NETWORK_TYPE_INET) + { + /* no control for media: try global one instead */ + if (fsdp_get_global_conn_network_type (dsc) != FSDP_NETWORK_TYPE_INET) + { + fsdp_description_delete (dsc); + return NULL; + } + } + + /* only IPv4 is supported atm. */ + if (fsdp_get_media_address_type (med_dsc) != FSDP_ADDRESS_TYPE_IPV4) + { + /* no control for media: try global one instead */ + if (fsdp_get_global_conn_address_type (dsc) != FSDP_ADDRESS_TYPE_IPV4) + { + fsdp_description_delete (dsc); + return NULL; + } + } + + /* get the media server address to connect to */ + if (fsdp_get_media_address (med_dsc)) + server_addr = strdup (fsdp_get_media_address (med_dsc)); + else if (fsdp_get_global_conn_address (dsc)) + { + /* no control for media: try global one instead */ + server_addr = strdup (fsdp_get_global_conn_address (dsc)); + } + + if (!server_addr) + { + fsdp_description_delete (dsc); + return NULL; + } + + /* check for a UNICAST or MULTICAST address to connect to */ + is_multicast = is_multicast_address (server_addr); + + /* 8. initiate an RTP session */ + rtp_session = rtp_session_new (); + if (!rtp_session) + { + free (server_addr); + fsdp_description_delete (dsc); + return NULL; + } + + /* get the media control URL */ + if (fsdp_get_media_control (med_dsc, 0)) + rtp_session->control_url = strdup (fsdp_get_media_control (med_dsc, 0)); + fsdp_description_delete (dsc); + if (!rtp_session->control_url) + { + free (server_addr); + rtp_session_free (rtp_session); + return NULL; + } + + /* 9. create the payload for RTSP SETUP request */ + memset (temp_buf, '\0', MAX_LENGTH); + snprintf (temp_buf, MAX_LENGTH, + RTSP_TRANSPORT_REQUEST, + is_multicast ? RTSP_TRANSPORT_MULTICAST : RTSP_TRANSPORT_UNICAST, + is_multicast ? RTSP_MULTICAST_PORT : RTSP_UNICAST_CLIENT_PORT, + client_rtp_port, client_rtcp_port); + mp_msg (MSGT_OPEN, MSGL_V, "RTSP Transport: %s\n", temp_buf); + + rtsp_unschedule_field (rtsp_session, RTSP_SESSION); + rtsp_schedule_field (rtsp_session, temp_buf); + + /* 10. check for the media control URL type and initiate RTSP SETUP */ + if (!strncmp (rtp_session->control_url, "rtsp://", 7)) /* absolute URL */ + statut = rtsp_request_setup (rtsp_session, + rtp_session->control_url, NULL); + else /* relative URL */ + statut = rtsp_request_setup (rtsp_session, + NULL, rtp_session->control_url); + + if (statut < 200 || statut > 299) + { + free (server_addr); + rtp_session_free (rtp_session); + return NULL; + } + + /* 11. parse RTSP SETUP response: we need it to actually determine + * the real address and port to connect to */ + answer = rtsp_search_answers (rtsp_session, RTSP_TRANSPORT); + if (!answer) + { + free (server_addr); + rtp_session_free (rtp_session); + return NULL; + } + + /* check for RTP and RTCP ports to bind according to how request was done */ + is_multicast = 0; + if (strstr (answer, RTSP_TRANSPORT_MULTICAST)) + is_multicast = 1; + + if (is_multicast) + parse_port (answer, RTSP_MULTICAST_PORT, + &client_rtp_port, &client_rtcp_port); + else + { + parse_port (answer, RTSP_UNICAST_CLIENT_PORT, + &client_rtp_port, &client_rtcp_port); + parse_port (answer, RTSP_UNICAST_SERVER_PORT, + &server_rtp_port, &server_rtcp_port); + } + + /* now check network settings as determined by server */ + destination = parse_destination (answer); + if (!destination) + destination = strdup (server_addr); + free (server_addr); + + mp_msg (MSGT_OPEN, MSGL_V, "RTSP Destination: %s\n", destination); + mp_msg (MSGT_OPEN, MSGL_V, "Client RTP port : %d\n", client_rtp_port); + mp_msg (MSGT_OPEN, MSGL_V, "Client RTCP port : %d\n", client_rtcp_port); + mp_msg (MSGT_OPEN, MSGL_V, "Server RTP port : %d\n", server_rtp_port); + mp_msg (MSGT_OPEN, MSGL_V, "Server RTCP port : %d\n", server_rtcp_port); + + /* 12. performs RTSP PLAY request */ + rtsp_schedule_field (rtsp_session, npt); + statut = rtsp_request_play (rtsp_session, NULL); + if (statut < 200 || statut > 299) + { + rtp_session_free (rtp_session); + return NULL; + } + + /* 13. create RTP and RTCP connections */ + rtp_sock = rtp_connect (destination, client_rtp_port); + rtcp_sock = rtcp_connect (client_rtcp_port, server_rtcp_port, destination); + rtp_session_set_fd (rtp_session, rtp_sock, rtcp_sock); + free (destination); + + mp_msg (MSGT_OPEN, MSGL_V, "RTP Sock : %d\nRTCP Sock : %d\n", + rtp_session->rtp_socket, rtp_session->rtcp_socket); + + if (rtp_session->rtp_socket == -1) + { + rtp_session_free (rtp_session); + return NULL; + } + + return rtp_session; +}