📄 rtpsession.c
字号:
/* The oRTP LinPhone RTP library intends to provide basics for a RTP stack. Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include "ortp.h"#include "rtpmod.h"#ifdef TARGET_IS_HPUXKERNEL#else#include <fcntl.h>#include <sys/types.h>#include <errno.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <stdlib.h>//#include <string.h>#endifvoidrtp_session_init (RtpSession * session, gint mode){ memset (session, 0, sizeof (RtpSession)); session->rtp.max_rq_size = RTP_MAX_RQ_SIZE; session->rtp.jitt_comp_time = RTP_DEFAULT_JITTER; session->mode = mode; if ((mode == RTP_SESSION_RECVONLY) || (mode == RTP_SESSION_SENDRECV)) { rtp_session_set_flag (session, RTP_SESSION_RECV_SYNC); rtp_session_set_flag (session, RTP_SESSION_RECV_NOT_STARTED); } if ((mode == RTP_SESSION_SENDONLY) || (mode == RTP_SESSION_SENDRECV)) { rtp_session_set_flag (session, RTP_SESSION_SEND_NOT_STARTED); rtp_session_set_flag (session, RTP_SESSION_SEND_SYNC); } session->telephone_events_pt=-1; /* not defined a priori */ rtp_session_set_profile (session, &av_profile);#ifndef TARGET_IS_HPUXKERNEL session->rtp.rq = &session->rtp._rq; session->rtp.wq = &session->rtp._wq;#endif session->lock = g_mutex_new (); /* init signal tables */ rtp_signal_table_init (&session->on_ssrc_changed, session); rtp_signal_table_init (&session->on_payload_type_changed, session); rtp_signal_table_init (&session->on_telephone_event, session); rtp_signal_table_init (&session->on_telephone_event_packet, session);#ifdef BUILD_SCHEDULER session->rtp.wait_for_packet_to_be_sent_mutex = g_mutex_new (); session->rtp.wait_for_packet_to_be_sent_cond = g_cond_new (); session->rtp.wait_for_packet_to_be_recv_mutex = g_mutex_new (); session->rtp.wait_for_packet_to_be_recv_cond = g_cond_new ();#endif session->max_buf_size = UDP_MAX_SIZE;}/** *rtp_session_new: *@mode: One of the #RtpSessionMode flags. * * Creates a new rtp session. * *Returns: the newly created rtp session.**/RtpSession *rtp_session_new (gint mode){ RtpSession *session = g_malloc (sizeof (RtpSession)); rtp_session_init (session, mode); return session;}/** *rtp_session_set_scheduling_mode: *@session: a rtp session. *@yesno: a boolean to indicate the scheduling mode. * * Sets the scheduling mode of the rtp session. If @yesno is 1, the rtp session is in * the scheduled mode: this means that packet input/output for that session * is done by a thread that is in charge of getting and sending packet at regular time * interval. This is very usefull for outgoing packets, that have to be sent at a time that * matches their timestamp. * If @yesno is zero, then the session is not scheduled. All recv and send operation will * occur when calling respectively rtp_session_recv_with_ts() and rtp_session_send_with_ts(). ***/voidrtp_session_set_scheduling_mode (RtpSession * session, gint yesno){ if (yesno) {#ifdef BUILD_SCHEDULER RtpScheduler *sched; sched = ortp_get_scheduler (); if (sched != NULL) { rtp_session_set_flag (session, RTP_SESSION_SCHEDULED); session->sched = sched; rtp_scheduler_add_session (sched, session); } else g_warning ("rtp_session_set_scheduling_mode: Cannot use scheduled mode because the " "scheduler is not started. Use ortp_scheduler_init() before.");#else g_warning ("rtp_session_set_scheduling_mode: Cannot use scheduled mode because the " "scheduler is not compiled within this oRTP stack.");#endif } else rtp_session_unset_flag (session, RTP_SESSION_SCHEDULED);}/** *rtp_session_set_blocking_mode: *@session: a rtp session *@yesno: a boolean * * This function defines the behaviour of the rtp_session_recv_with_ts() and * rtp_session_send_with_ts() functions. If @yesno is 1, rtp_session_recv_with_ts() * will block until it is time for the packet to be received, according to the timestamp * passed to the function. After this time, the function returns. * For rtp_session_send_with_ts(), it will block until it is time for the packet to be sent. * If @yesno is 0, then the two functions will return immediately. ***/voidrtp_session_set_blocking_mode (RtpSession * session, gint yesno){ if (yesno) rtp_session_set_flag (session, RTP_SESSION_BLOCKING_MODE); else rtp_session_unset_flag (session, RTP_SESSION_BLOCKING_MODE);}/** *rtp_session_set_profile: *@session: a rtp session *@profile: a rtp profile * * Set the RTP profile to be used for the session. By default, all session are created by * rtp_session_new() are initialized with the AV profile, as defined in RFC 1890. The application * can set any other profile instead using that function. * ***/voidrtp_session_set_profile (RtpSession * session, RtpProfile * profile){ session->profile = profile; rtp_session_telephone_events_supported(session);}/** *rtp_session_signal_connect: *@session: a rtp session *@signal: the name of a signal *@cb: a #RtpCallback *@user_data: a pointer to any data to be passed when invoking the callback. * * This function provides the way for an application to be informed of various events that * may occur during a rtp session. @signal is a string identifying the event, and @cb is * a user supplied function in charge of processing it. The application can register * several callbacks for the same signal, in the limit of #RTP_CALLBACK_TABLE_MAX_ENTRIES. * Here are name and meaning of supported signals types: * * "ssrc_changed" : the SSRC of the incoming stream has changed. * * "payload_type_changed" : the payload type of the incoming stream has changed. * * "telephone-event_packet" : a telephone-event rtp packet (RFC1833) is received. * * "telephone-event" : a telephone event has occured. This is a shortcut for "telephone-event_packet". * * Returns: 0 on success, -EOPNOTSUPP if the signal does not exists, -1 if no more callbacks * can be assigned to the signal type.**/intrtp_session_signal_connect (RtpSession * session, char *signal, RtpCallback cb, gpointer user_data){ if (strcmp (signal, "ssrc_changed") == 0) { return rtp_signal_table_add (&session->on_ssrc_changed, cb, user_data); } else if (strcmp (signal, "payload_type_changed") == 0) { return rtp_signal_table_add (&session-> on_payload_type_changed, cb, user_data); } else if (strcmp (signal, "telephone-event")==0) { return rtp_signal_table_add (&session->on_telephone_event,cb,user_data); } else if (strcmp (signal, "telephone-event_packet")==0) { return rtp_signal_table_add (&session->on_telephone_event_packet,cb,user_data); } g_warning ("rtp_session_signal_connect: inexistant signal."); return -EOPNOTSUPP;}/** *rtp_session_signal_disconnect_by_callback: *@session: a rtp session *@signal: a signal name *@cb: a callback function. * * Removes callback function @cb to the list of callbacks for signal @signal. * *Returns: 0 on success, -ENOENT if the callbacks was not found.**/intrtp_session_signal_disconnect_by_callback (RtpSession * session, char *signal, RtpCallback cb){ if (strcmp (signal, "ssrc_changed") == 0) { return rtp_signal_table_remove_by_callback (&session-> on_ssrc_changed, cb); } else if (strcmp (signal, "payload_type_changed") == 0) { return rtp_signal_table_remove_by_callback (&session-> on_payload_type_changed, cb); } else if (strcmp (signal,"telephone-event")==0){ return rtp_signal_table_remove_by_callback(&session->on_telephone_event,cb); } else if (strcmp (signal,"telephone-event_packet")==0){ return rtp_signal_table_remove_by_callback(&session->on_telephone_event_packet,cb); } g_warning ("rtp_session_signal_disconnect_by_callback: callback not found."); return -ENOENT;}/** *rtp_session_set_local_addr: *@session: a rtp session freshly created. *@addr: a local IP address in the xxx.xxx.xxx.xxx form. *@port: a local port. * * Specify the local addr to be use to listen for rtp packets or to send rtp packet from. * In case where the rtp session is send-only, then it is not required to call this function: * when calling rtp_session_set_remote_addr(), if no local address has been set, then the * default INADRR_ANY (0.0.0.0) IP address with a random port will be used. Calling * rtp_sesession_set_local_addr() is mandatory when the session is send-only or duplex. * * Returns: 0 on success.**/#ifdef TARGET_IS_HPUXKERNELgintrtp_session_set_local_addr (RtpSession * session, gchar * addr, gint port){ return EOPNOTSUPP;}#elsegintrtp_session_set_local_addr (RtpSession * session, gchar * addr, gint port){ gint err; gint optval = 1; session->rtp.loc_addr.sin_family = AF_INET; err = inet_aton (addr, &session->rtp.loc_addr.sin_addr); if (err < 0) { g_warning ("Error in socket address:%s.", strerror (errno)); return err; } session->rtp.loc_addr.sin_port = htons (port); session->rtp.socket = socket (PF_INET, SOCK_DGRAM, 0); g_return_val_if_fail (session->rtp.socket > 0, -1); /* set non blocking mode */ fcntl (session->rtp.socket, F_SETFL, O_NONBLOCK); err = bind (session->rtp.socket, (struct sockaddr *) &session->rtp.loc_addr, sizeof (struct sockaddr_in)); if (err != 0) { g_warning ("Fail to bind rtp socket to port %i: %s.", port, strerror (errno)); close (session->rtp.socket); return -1; } /* set the address reusable */ err = setsockopt (session->rtp.socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval)); if (err < 0) { g_warning ("Fail to set rtp address reusable: %s.", strerror (errno)); } memcpy (&session->rtcp.loc_addr, &session->rtp.loc_addr, sizeof (struct sockaddr_in)); session->rtcp.loc_addr.sin_port = htons (port + 1); session->rtcp.socket = socket (PF_INET, SOCK_DGRAM, 0); g_return_val_if_fail (session->rtcp.socket > 0, -1); err = bind (session->rtcp.socket, (struct sockaddr *) &session->rtcp.loc_addr, sizeof (struct sockaddr_in)); if (err != 0) { g_warning ("Fail to bind rtcp socket to port %i: %s.", port + 1, strerror (errno)); close (session->rtp.socket); close (session->rtcp.socket); return -1; } optval = 1; err = setsockopt (session->rtcp.socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval)); if (err < 0) { g_warning ("Fail to set rtcp address reusable: %s.", strerror (errno)); } FD_ZERO (&session->scanfd); FD_SET (session->rtp.socket, &session->scanfd); FD_SET (session->rtcp.socket, &session->scanfd); if (session->rtcp.socket > session->rtp.socket) session->highest_fd = session->rtcp.socket + 1; else session->highest_fd = session->rtp.socket + 1; return 0;}#endif/** *rtp_session_set_remote_addr: *@session: a rtp session freshly created. *@addr: a local IP address in the xxx.xxx.xxx.xxx form. *@port: a local port. * * Sets the remote address of the rtp session, ie the destination address where rtp packet * are sent. If the session is recv-only or duplex, it sets also the origin of incoming RTP * packets. Rtp packets that don't come from addr:port are discarded. * * Returns: 0 on success.**/#ifdef TARGET_IS_HPUXKERNELgint rtp_session_set_remote_addr(RtpSession *session, struct sockaddr_in *dest){ mblk_t *mproto; struct T_unitdata_req *req; /* make a M_PROTO message to be linked with every outgoing rtp packet */ mproto=allocb(sizeof(struct T_unitdata_req)+sizeof(struct sockaddr_in),BPRI_MED); if (mproto==NULL) return -1; mproto->b_datap->db_type=M_PROTO; req=(struct T_unitdata_req*)mproto->b_wptr; req->PRIM_type=T_UNITDATA_REQ; req->DEST_length=sizeof(struct sockaddr_in); req->DEST_offset=sizeof(struct T_unitdata_req); req->OPT_length=0; req->OPT_offset=0; mproto->b_wptr+=sizeof(struct T_unitdata_req); memcpy((void*)mproto->b_wptr,(void*)dest,sizeof(struct sockaddr_in)); mproto->b_wptr+=sizeof(struct sockaddr_in); rtp_session_lock(session); if (session->dest_mproto!=NULL){ freemsg(session->dest_mproto); } session->dest_mproto=mproto; rtp_session_unlock(session); return 0;}#elsegintrtp_session_set_remote_addr (RtpSession * session, gchar * addr, gint port){ gint err; if (session->rtp.socket == 0) { int retry = 0; /* the session has not its socket bound, do it */ g_message ("Setting random local addresses."); while (retry < 10) { int localport; do { localport = (rand () + 5000) & 0xfffe; } while ((localport < 5000) || (localport > 0xffff)); err = rtp_session_set_local_addr (session, "0.0.0.0", localport); if (err == 0) break; } if (retry == 10) g_warning ("rtp_session_set_remote_addr: Could not find a random local address for socket !"); } session->rtp.rem_addr.sin_family = AF_INET; err = inet_aton (addr, &session->rtp.rem_addr.sin_addr); if (err < 0) { g_warning ("Error in socket address:%s.", strerror (errno)); return err; } session->rtp.rem_addr.sin_port = htons (port); memcpy (&session->rtcp.rem_addr, &session->rtp.rem_addr, sizeof (struct sockaddr_in)); session->rtcp.rem_addr.sin_port = htons (port + 1);#ifndef NOCONNECT if (session->mode == RTP_SESSION_SENDONLY) { err = connect (session->rtp.socket, (struct sockaddr *) &session->rtp.rem_addr, sizeof (struct sockaddr_in)); if (err != 0) { g_message ("Can't connect rtp socket: %s.", strerror (errno)); return err; } err = connect (session->rtcp.socket, (struct sockaddr *) &session->rtcp.rem_addr, sizeof (struct sockaddr_in)); if (err != 0) { g_message ("Can't connect rtp socket: %s.", strerror (errno)); return err; } }#endif return 0;}#endif#ifdef TARGET_IS_HPUXKERNEL#ifdef WORDS_BIGENDIAN#if 0#define rtp_send(_session,_m) \ do{\ mblk_t *_destmp;\ if ((_session)->dest_mproto!=NULL){\ _destmp=dupb((_session)->dest_mproto);\ _destmp->b_cont=(_m);\ streams_put(putnext,(_session)->rtp.wq,(_destmp),(void*)(_session)->rtp.wq);\ } else {\ g_warning("rtp_send: ERROR - there is no destination addreess !");\ freemsg(_m);\
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -