/* * Copyright (C) 2002-2003 Fhg Fokus * * This file is part of SEMS, a free SIP media server. * * SEMS 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 released under * the GPL with the additional exemption that compiling, linking, * and/or using OpenSSL is allowed. * * For a license to use the SEMS software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * SEMS 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 AmRtpStream.h */ #ifndef _RtpStream_h_ #define _RtpStream_h_ #include "AmSdp.h" #include "AmThread.h" #include "SampleArray.h" #include "AmRtpPacket.h" #include "AmEvent.h" #include "AmDtmfSender.h" #include #include #include #include #include using std::string; using std::auto_ptr; using std::pair; // return values of AmRtpStream::receive #define RTP_EMPTY 0 // no rtp packet available #define RTP_ERROR -1 // generic error #define RTP_PARSE_ERROR -2 // error while parsing rtp packet #define RTP_TIMEOUT -3 // last received packet is too old #define RTP_DTMF -4 // dtmf packet has been received #define RTP_BUFFER_SIZE -5 // buffer overrun #define RTP_UNKNOWN_PL -6 // unknown payload /** * Forward declarations */ class AmAudio; class AmSession; struct SdpPayload; struct amci_payload_t; class msg_logger; /** * This provides the memory for the receive buffer. */ struct PacketMem { #define MAX_PACKETS_BITS 5 #define MAX_PACKETS (1<> and &) void set(unsigned char payload_id) { if (payload_id < 128) bits[payload_id / 8] |= 1 << (payload_id % 8); } // set all flags to 'true' void set_all(); // invert all flags void invert(); // get given flag bool get(unsigned char payload_id) { if (payload_id > 127) { ERROR("BUG: payload_id out of range\n"); return false; } return (bits[payload_id / 8] & (1 << (payload_id % 8))); } PayloadMask() { clear(); } PayloadMask(bool _set_all) { if (_set_all) set_all(); else clear(); } PayloadMask(const PayloadMask &src); }; /** * \brief represents one admissible payload type * * */ struct Payload { unsigned char pt; string name; unsigned int clock_rate; unsigned int advertised_clock_rate; // differs for G722 int codec_id; string format_parameters; }; /** * \brief RTP implementation * * Rtp stream high level interface. */ class AmRtpStream : public AmObject { protected: // payload collection typedef std::vector PayloadCollection; // list of locally supported payloads PayloadCollection payloads; // current payload (index into @payloads) int payload; struct PayloadMapping { // remote payload type int8_t remote_pt; // index in payloads vector uint8_t index; }; typedef std::map ReceiveBuffer; typedef std::queue RtpEventQueue; typedef std::map PayloadMappingTable; // mapping from local payload type to PayloadMapping PayloadMappingTable pl_map; /** SDP media slot number (n-th media line) */ int sdp_media_index; /** RTP sequence number */ unsigned int sequence; /** Payload of last received packet. Usefull to detect talk spurt, looking for comfort noise packets. */ int last_payload; /** Remote host information */ string r_host; unsigned short r_port; unsigned short r_rtcp_port; /** * Local interface used for this stream * (index into @AmConfig::Ifs) */ int l_if; /** * Local and remote host addresses */ struct sockaddr_storage r_saddr; struct sockaddr_storage l_saddr; struct sockaddr_storage l_rtcp_saddr; /** Local port */ unsigned short l_port; /** Local socket */ int l_sd; /** Local RTCP port */ unsigned int l_rtcp_port; /** Local RTCP socket */ int l_rtcp_sd; /** Timestamp of the last received RTP packet */ struct timeval last_recv_time; /** Local and remote SSRC information */ unsigned int l_ssrc; unsigned int r_ssrc; bool r_ssrc_i; /** symmetric RTP & RTCP */ bool passive; bool passive_rtcp; /** mute && port == 0 */ bool hold; /** marker flag */ bool begin_talk; /** do check rtp timeout */ bool monitor_rtp_timeout; /** Payload type for telephone event */ auto_ptr remote_telephone_event_pt; auto_ptr local_telephone_event_pt; /** DTMF sender */ AmDtmfSender dtmf_sender; /** * Receive buffer, queue and mutex */ PacketMem mem; ReceiveBuffer receive_buf; RtpEventQueue rtp_ev_qu; AmMutex receive_mut; /** should we receive packets? if not -> drop */ bool receiving; /** if relay_stream is initialized, received RTP is relayed there */ bool relay_enabled; /** if true, packets are note parsed or checked */ bool relay_raw; /** pointer to relay stream. NOTE: This may only be accessed in initialization or by the AmRtpReceiver thread while relaying! */ AmRtpStream* relay_stream; /** control transparency for RTP seqno in RTP relay mode */ bool relay_transparent_seqno; /** control transparency for RTP ssrc in RTP relay mode */ bool relay_transparent_ssrc; /** filter RTP DTMF (2833 / 4733) in relaying */ bool relay_filter_dtmf; /** Session owning this stream */ AmSession* session; msg_logger *logger; /** Payload provider */ AmPayloadProvider* payload_provider; /** insert packet in DTMF queue if correct payload */ void recvDtmfPacket(AmRtpPacket* p); /** Insert an RTP packet to the buffer queue */ void bufferPacket(AmRtpPacket* p); /* Get next packet from the buffer queue */ int nextPacket(AmRtpPacket*& p); /** Try to reuse oldest buffered packet for newly coming packet */ AmRtpPacket *reuseBufferedPacket(); /** handle symmetric RTP/RTCP - if in passive mode, update raddr from rp */ void handleSymmetricRtp(struct sockaddr_storage* recv_addr, bool rtcp); void relay(AmRtpPacket* p); /** Sets generic parameters on SDP media */ void getSdp(SdpMedia& m); /** Clear RTP timeout at time recv_time */ void clearRTPTimeout(struct timeval* recv_time); PayloadMask relay_payloads; bool offer_answer_used; /** set to true if any data received */ bool active; /** * Select a compatible default payload * @return -1 if none available. */ int getDefaultPT(); public: /** * Set whether RTP stream will receive RTP packets internally (received packets will be dropped or not). */ void setReceiving(bool r); /** * Stops RTP stream receiving RTP packets internally (received packets will be dropped). */ void pause(); /** * Resume a paused RTP stream internally (received packets will be processed). */ void resume(); /** Mute */ bool mute; /** should we receive RFC-2833-style DTMF even when receiving is disabled? */ bool force_receive_dtmf; /** Allocates resources for future use of RTP. */ AmRtpStream(AmSession* _s, int _if); /** Stops the stream and frees all resources. */ virtual ~AmRtpStream(); int send( unsigned int ts, unsigned char* buffer, unsigned int size ); int send_raw( char* packet, unsigned int length ); int compile_and_send( const int payload, bool marker, unsigned int ts, unsigned char* buffer, unsigned int size ); int receive( unsigned char* buffer, unsigned int size, unsigned int& ts, int& payload ); void recvPacket(int fd); void recvRtcpPacket(); /** ping the remote side, to open NATs and enable symmetric RTP */ int ping(); /** returns the socket descriptor for local socket (initialized or not) */ int hasLocalSocket(); /** initializes and gets the socket descriptor for local socket */ int getLocalSocket(); /** * This function must be called before setLocalPort, because * setLocalPort will bind the socket and it will be not * possible to change the IP later */ void setLocalIP(const string& ip); /** * Initializes with a new random local port if 'p' is 0, * else binds the given port, and sets own attributes properly. */ void setLocalPort(unsigned short p = 0); /** * Gets RTP port number. If no RTP port in assigned, assigns a new one. * @return local RTP port. */ int getLocalPort(); /** * Gets RTCP port number. If no RTP/RTCP port in assigned, assigns a new one. * @return local RTCP port. */ int getLocalRtcpPort(); /** * Gets remote RTP port. * @return remote RTP port. */ int getRPort(); /** * Gets remote host IP. * @return remote host IP. */ string getRHost(); /** * Set remote IP & port. */ void setRAddr(const string& addr, unsigned short port, unsigned short rtcp_port = 0); /** Symmetric RTP & RTCP: passive mode ? */ void setPassiveMode(bool p); bool getPassiveMode() { return passive || passive_rtcp; } unsigned int get_ssrc() { return l_ssrc; } int getLocalTelephoneEventPT(); int getLocalTelephoneEventRate(); void setPayloadProvider(AmPayloadProvider* pl_prov); int getSdpMediaIndex() { return sdp_media_index; } void forceSdpMediaIndex(int idx) { sdp_media_index = idx; offer_answer_used = false; } int getPayloadType() { return payload; } int getLastPayload() { return last_payload; } string getPayloadName(int payload_type); /** * send a DTMF as RTP payload (RFC4733) * @param event event ID (e.g. key press), see rfc * @param duration_ms duration in milliseconds */ void sendDtmf(int event, unsigned int duration_ms); /** * Generate an SDP offer based on the stream capabilities. * @param index index of the SDP media within the SDP. * @param offer the local offer to be filled/completed. */ virtual void getSdpOffer(unsigned int index, SdpMedia& offer); /** * Generate an answer for the given SDP media based on the stream capabilities. * @param index index of the SDP media within the SDP. * @param offer the remote offer. * @param answer the local answer to be filled/completed. */ virtual void getSdpAnswer(unsigned int index, const SdpMedia& offer, SdpMedia& answer); /** * Enables RTP stream. * @param local the SDP message generated by the local UA. * @param remote the SDP message generated by the remote UA. * @warning It is necessary to call getSdpOffer/getSdpAnswer prior to init(...) * @warning so that the internal SDP media line index is set properly. */ virtual int init(const AmSdp& local, const AmSdp& remote, bool force_passive_mode = false); /** set the RTP stream on hold */ void setOnHold(bool on_hold); /** get whether RTP stream is on hold */ bool getOnHold(); /** setter for monitor_rtp_timeout */ void setMonitorRTPTimeout(bool m) { monitor_rtp_timeout = m; } /** getter for monitor_rtp_timeout */ bool getMonitorRTPTimeout() { return monitor_rtp_timeout; } /* * clear RTP timeout to current time */ void clearRTPTimeout(); /** set relay stream for RTP relaying */ void setRelayStream(AmRtpStream* stream); /** set relay payloads for RTP relaying */ void setRelayPayloads(const PayloadMask &_relay_payloads); /** ensable RTP relaying through relay stream */ void enableRtpRelay(); /** disable RTP relaying through relay stream */ void disableRtpRelay(); /** enable raw UDP relaying through relay stream */ void enableRawRelay(); /** disable raw UDP relaying through relay stream */ void disableRawRelay(); /** enable or disable transparent RTP seqno for relay */ void setRtpRelayTransparentSeqno(bool transparent); /** enable or disable transparent SSRC seqno for relay */ void setRtpRelayTransparentSSRC(bool transparent); /** enable or disable filtering of RTP DTMF for relay */ void setRtpRelayFilterRtpDtmf(bool filter); /** remove from RTP receiver */ void stopReceiving(); /** (re-)insert into RTP receiver */ void resumeReceiving(); /** Quick hack to assign existing stream to another session. The stream should * not be reinitialised implicitly (it might be used for media traffic * already). */ void changeSession(AmSession *_s) { session = _s; } /** set destination for logging all received/sent RTP and RTCP packets */ void setLogger(msg_logger *_logger); void debug(); }; #endif // Local Variables: // mode:C++ // End: