diff --git a/apps/conference/Conference.cpp b/apps/conference/Conference.cpp index ae7f8ffc..dd95a937 100644 --- a/apps/conference/Conference.cpp +++ b/apps/conference/Conference.cpp @@ -31,6 +31,9 @@ #include "AmConferenceStatus.h" #include "AmConfig.h" +#include "AmSessionContainer.h" +#include "AmSessionScheduler.h" + #include "sems.h" #include "log.h" @@ -46,6 +49,7 @@ ConferenceFactory::ConferenceFactory(const string& _app_name) string ConferenceFactory::LonelyUserFile; string ConferenceFactory::JoinSound; string ConferenceFactory::DropSound; +string ConferenceFactory::DialoutSuffix; int ConferenceFactory::onLoad() { @@ -66,6 +70,12 @@ int ConferenceFactory::onLoad() JoinSound = cfg.getParameter("join_sound"); DropSound = cfg.getParameter("drop_sound"); + DialoutSuffix = cfg.getParameter("dialout_suffix"); + if(DialoutSuffix.empty()){ + WARN("No dialout_suffix has been configured in the conference plug-in:\n"); + WARN("\t -> dial out will not be available\n"); + } + return 0; } @@ -74,13 +84,20 @@ AmSession* ConferenceFactory::onInvite(const AmSipRequest& req) return new ConferenceDialog(req.user); } -ConferenceDialog::ConferenceDialog(const string& conf_id) - : conf_id(conf_id), channel(0), play_list(this) +ConferenceDialog::ConferenceDialog(const string& conf_id, + AmConferenceChannel* dialout_channel) + : conf_id(conf_id), + channel(0), + play_list(this), + dialout_channel(dialout_channel), + state(CS_normal) { + dialedout = this->dialout_channel.get() != 0; } ConferenceDialog::~ConferenceDialog() { + DBG("ConferenceDialog::~ConferenceDialog()\n"); } void ConferenceDialog::onStart() @@ -89,6 +106,16 @@ void ConferenceDialog::onStart() } void ConferenceDialog::onSessionStart(const AmSipRequest& req) +{ + setupAudio(); +} + +void ConferenceDialog::onSessionStart(const AmSipReply& reply) +{ + setupAudio(); +} + +void ConferenceDialog::setupAudio() { if(!ConferenceFactory::JoinSound.empty()) { @@ -106,29 +133,39 @@ void ConferenceDialog::onSessionStart(const AmSipRequest& req) DropSound.reset(0); } - channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag())); + play_list.close();// !!! + + if(dialout_channel.get()){ + + play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), + dialout_channel.get())); + } + else { + + channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag())); - play_list.addToPlaylist(new AmPlaylistItem(channel.get(),channel.get())); + play_list.addToPlaylist(new AmPlaylistItem(channel.get(), + channel.get())); + } setInOut(&play_list,&play_list); setCallgroup(conf_id); - - //setInOut(channel.get(),channel.get()); } void ConferenceDialog::onBye(const AmSipRequest& req) { - play_list.close(); - setInOut(NULL,NULL); - channel.reset(NULL); + if(dialout_channel.get()) + disconnectDialout(); + + closeChannels(); setStopped(); } void ConferenceDialog::process(AmEvent* ev) { ConferenceEvent* ce = dynamic_cast(ev); - if(ce){ + if(ce && (conf_id == ce->conf_id)){ switch(ce->event_id){ case ConfNewParticipant: @@ -172,5 +209,257 @@ void ConferenceDialog::process(AmEvent* ev) return; } + DialoutConfEvent* do_ev = dynamic_cast(ev); + if(do_ev){ + + if(dialedout){ + + switch(do_ev->event_id){ + + case DoConfConnect: + + connectMainChannel(); + break; + + case DoConfDisconnect: + + dlg.bye(); + closeChannels(); + setStopped(); + break; + + default: + break; + } + } + else { + + switch(do_ev->event_id){ + + case DoConfDisconnect: + + connectMainChannel(); + break; + + case DoConfConnect: + + state = CS_dialout_connected; + break; + } + } + + return; + } + AmSession::process(ev); } + +string dtmf2str(int event) +{ + switch(event){ + case 0: case 1: case 2: + case 3: case 4: case 5: + case 6: case 7: case 8: + case 9: + return int2str(event); + + case 10: return "*"; + case 11: return "#"; + default: return ""; + } +} + + +void ConferenceDialog::onDtmf(int event, int duration) +{ + DBG("ConferenceDialog::onDtmf\n"); + if(dialedout) + return; + + switch(state){ + + case CS_normal: + dtmf_seq += dtmf2str(event); + + if(dtmf_seq.length() == 2){ + if(dtmf_seq == "#*") + state = CS_dialing_out; + dtmf_seq = ""; + } + break; + + case CS_dialing_out:{ + string digit = dtmf2str(event); + + if(digit == "*"){ + + if(!dtmf_seq.empty()){ + createDialoutParticipant("sip:" + dtmf_seq + + ConferenceFactory::DialoutSuffix); + state = CS_dialed_out; + } + else { + state = CS_normal; + } + + dtmf_seq = ""; + } + else + dtmf_seq += digit; + + } break; + + + case CS_dialout_connected: + if(event == 10){ // '*' + + AmSessionContainer::instance() + ->postEvent(dialout_id, + new DialoutConfEvent(DoConfConnect, + getLocalTag())); + + connectMainChannel(); + } + break; + + case CS_dialed_out: + if(event == 11){ // '#' + disconnectDialout(); + } + break; + + } +} + +void ConferenceDialog::createDialoutParticipant(const string& uri) +{ + dialout_channel.reset(AmConferenceStatus::getChannel(getLocalTag(),getLocalTag())); + + dialout_id = AmSession::getNewId(); + + ConferenceDialog* dialout_session = + new ConferenceDialog(conf_id, + AmConferenceStatus::getChannel(getLocalTag(), + dialout_id)); + + AmSipDialog& dialout_dlg = dialout_session->dlg; + + dialout_dlg.local_tag = dialout_id; + dialout_dlg.callid = AmSession::getNewId() + "@" + AmConfig::LocalIP; + + dialout_dlg.local_party = dlg.local_party; + dialout_dlg.remote_party = uri; + dialout_dlg.remote_uri = uri; + + string body; + int local_port = dialout_session->rtp_str.getLocalPort(); + dialout_session->sdp.genRequest(AmConfig::LocalIP,local_port,body); + dialout_dlg.sendRequest("INVITE","application/sdp",body,""); + + + play_list.close(); // !!! + play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), + dialout_channel.get())); + + dialout_session->start(); + + AmSessionContainer* sess_cont = AmSessionContainer::instance(); + sess_cont->addSession(dialout_id,dialout_session); +} + +void ConferenceDialog::disconnectDialout() +{ + if(dialedout){ + + if(dialout_channel.get()){ + + AmSessionContainer::instance() + ->postEvent(dialout_channel->getConfID(), + new DialoutConfEvent(DoConfDisconnect, + dialout_channel->getConfID())); + } + } + else { + + AmSessionContainer::instance() + ->postEvent(dialout_id, + new DialoutConfEvent(DoConfDisconnect, + getLocalTag())); + + connectMainChannel(); + } +} + +void ConferenceDialog::connectMainChannel() +{ + dialout_id = ""; + dialout_channel.reset(NULL); + + play_list.close(); + + if(!channel.get()) + channel.reset(AmConferenceStatus + ::getChannel(conf_id, + getLocalTag())); + + play_list.addToPlaylist(new AmPlaylistItem(channel.get(), + channel.get())); +} + +void ConferenceDialog::closeChannels() +{ + play_list.close(); + setInOut(NULL,NULL); + channel.reset(NULL); + dialout_channel.reset(NULL); +} + +void ConferenceDialog::onSipReply(const AmSipReply& reply) +{ + int status = dlg.getStatus(); + AmSession::onSipReply(reply); + + if(status < AmSipDialog::Connected){ + + switch(dlg.getStatus()){ + + case AmSipDialog::Connected: + + // connected! + if(acceptAudio(reply.body,reply.hdrs)){ + ERROR("could not connect audio!!!\n"); + dlg.bye(); + setStopped(); + } + else { + if(getDetached() && !getStopped()){ + + onSessionStart(reply); + + if(getInput() || getOutput()) + AmSessionScheduler::instance()->addSession(this, + getCallgroup()); + else { + ERROR("missing audio input and/or ouput.\n"); + } + + // send connect event + AmSessionContainer::instance() + ->postEvent(dialout_channel->getConfID(), + new DialoutConfEvent(DoConfConnect, + dialout_channel->getConfID())); + } + } + break; + + case AmSipDialog::Pending: + + switch(reply.code){ + case 180: break;//TODO: local ring tone. + case 183: break;//TODO: remote ring tone. + default: break;// continue waiting. + } + } + } +} + diff --git a/apps/conference/Conference.h b/apps/conference/Conference.h index 6c18980b..c815c1e6 100644 --- a/apps/conference/Conference.h +++ b/apps/conference/Conference.h @@ -42,12 +42,33 @@ using std::string; class ConferenceStatus; class ConferenceStatusContainer; + +enum { CS_normal=0, + CS_dialing_out, + CS_dialed_out, + CS_dialout_connected }; + +enum { DoConfConnect = 100, + DoConfDisconnect }; + +struct DialoutConfEvent : public AmEvent { + + string conf_id; + + DialoutConfEvent(int event_id, + const string& conf_id) + : AmEvent(event_id), + conf_id(conf_id) + {} +}; + class ConferenceFactory : public AmSessionFactory { public: static string LonelyUserFile; static string JoinSound; static string DropSound; + static string DialoutSuffix; ConferenceFactory(const string& _app_name); virtual AmSession* onInvite(const AmSipRequest&); @@ -62,17 +83,37 @@ class ConferenceDialog : public AmSession auto_ptr JoinSound; auto_ptr DropSound; + string conf_id; auto_ptr channel; + int state; + string dtmf_seq; + bool dialedout; + string dialout_id; + auto_ptr dialout_channel; + + + void createDialoutParticipant(const string& uri); + void disconnectDialout(); + void connectMainChannel(); + void closeChannels(); + void setupAudio(); + public: - ConferenceDialog(const string& conf_id); + ConferenceDialog(const string& conf_id, + AmConferenceChannel* dialout_channel=0); + ~ConferenceDialog(); void process(AmEvent* ev); void onStart(); + void onDtmf(int event, int duration); + void onSessionStart(const AmSipReply& reply); void onSessionStart(const AmSipRequest& req); void onBye(const AmSipRequest& req); + + void onSipReply(const AmSipReply& reply); }; #endif diff --git a/apps/early_announce/EarlyAnnounce.cpp b/apps/early_announce/EarlyAnnounce.cpp index 1e30692a..1b9e62e0 100644 --- a/apps/early_announce/EarlyAnnounce.cpp +++ b/apps/early_announce/EarlyAnnounce.cpp @@ -75,7 +75,7 @@ int EarlyAnnounceFactory::onLoad() void EarlyAnnounceDialog::onInvite(const AmSipRequest& req) { string sdp_reply; - if(acceptAudio(req,sdp_reply)!=0) + if(acceptAudio(req.body,req.hdrs,&sdp_reply)!=0) return; if(dlg.reply(req,183,"Session Progress", diff --git a/apps/voicemail/default.template b/apps/voicemail/default.template index 104e38c9..2a22bd67 100644 --- a/apps/voicemail/default.template +++ b/apps/voicemail/default.template @@ -1,5 +1,5 @@ subject: Voice message from: %from% -from: voicemail@%domain% +from:raf@samson to: %email% Hello %user%@%domain%, diff --git a/core/AmConferenceChannel.h b/core/AmConferenceChannel.h index bad95c13..cbb30577 100644 --- a/core/AmConferenceChannel.h +++ b/core/AmConferenceChannel.h @@ -27,6 +27,7 @@ public: ~AmConferenceChannel(); + string getConfID() { return conf_id; } }; diff --git a/core/AmConferenceStatus.cpp b/core/AmConferenceStatus.cpp index ead9243e..bd76342b 100644 --- a/core/AmConferenceStatus.cpp +++ b/core/AmConferenceStatus.cpp @@ -97,6 +97,7 @@ AmConferenceStatus::AmConferenceStatus(const string& conference_id) AmConferenceStatus::~AmConferenceStatus() { + DBG("AmConferenceStatus::~AmConferenceStatus(): conf_id = %s\n",conf_id.c_str()); } AmConferenceChannel* AmConferenceStatus::getChannel(const string& sess_id) @@ -119,14 +120,15 @@ AmConferenceChannel* AmConferenceStatus::getChannel(const string& sess_id) AmSessionContainer::instance()->postEvent( it->first, new ConferenceEvent(ConfNewParticipant, - participants) + participants,conf_id) ); } } else { // The First participant gets its own NewParticipant message AmSessionContainer::instance()->postEvent( - sess_id, new ConferenceEvent(ConfNewParticipant,1)); + sess_id, new ConferenceEvent(ConfNewParticipant,1, + conf_id)); } unsigned int ch_id = mixer.addChannel(); @@ -165,7 +167,8 @@ int AmConferenceStatus::releaseChannel(unsigned int ch_id) AmSessionContainer::instance()->postEvent( s_it->first, new ConferenceEvent(ConfParticipantLeft, - participants)); + participants, + conf_id)); } } diff --git a/core/AmConferenceStatus.h b/core/AmConferenceStatus.h index 840f70a1..5c003b15 100644 --- a/core/AmConferenceStatus.h +++ b/core/AmConferenceStatus.h @@ -46,10 +46,14 @@ enum { ConfNewParticipant = 1, struct ConferenceEvent: public AmEvent { unsigned int participants; + string conf_id; - ConferenceEvent(int event_id, unsigned int participants) + ConferenceEvent(int event_id, + unsigned int participants, + const string& conf_id) : AmEvent(event_id), - participants(participants) + participants(participants), + conf_id(conf_id) {} }; diff --git a/core/AmRtpStream.cpp b/core/AmRtpStream.cpp index c096e76f..931e1cdc 100644 --- a/core/AmRtpStream.cpp +++ b/core/AmRtpStream.cpp @@ -197,6 +197,7 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size, { AmRtpPacket rp; int err = nextPacket(rp); + if(err <= 0) return err; @@ -318,7 +319,6 @@ AmRtpStream::~AmRtpStream() { if(l_sd){ AmRtpReceiver::instance()->removeStream(l_sd); - //AmIcmpWatcher::instance()->removeStream(l_port); close(l_sd); } } diff --git a/core/AmSdp.h b/core/AmSdp.h index 438018a1..ffcded1f 100644 --- a/core/AmSdp.h +++ b/core/AmSdp.h @@ -174,7 +174,7 @@ class AmSdp * Generate an SDP offer. * @return !=0 if error encountered. */ - int genRequest(const string& dstip,int localport, string& out_buf); + int genRequest(const string& localip,int localport, string& out_buf); /** * Get a compatible payload from SDP offer/response. diff --git a/core/AmSession.cpp b/core/AmSession.cpp index a34e5a07..56138f72 100644 --- a/core/AmSession.cpp +++ b/core/AmSession.cpp @@ -215,7 +215,7 @@ int AmSession::getRPort() void AmSession::negotiate(const string& sdp_body, bool force_symmetric_rtp, - string& sdp_reply) + string* sdp_reply) { string r_host = ""; int r_port = 0; @@ -266,7 +266,8 @@ void AmSession::negotiate(const string& sdp_body, passive_mode = true; } - sdp.genResponse(AmConfig::LocalIP,rtp_str.getLocalPort(),sdp_reply); + if(sdp_reply) + sdp.genResponse(AmConfig::LocalIP,rtp_str.getLocalPort(),*sdp_reply); lockAudio(); rtp_str.setLocalIP(AmConfig::LocalIP); @@ -514,7 +515,7 @@ void AmSession::onSipReply(const AmSipReply& reply) void AmSession::onInvite(const AmSipRequest& req) { string sdp_reply; - if(acceptAudio(req,sdp_reply)!=0) + if(acceptAudio(req.body,req.hdrs,&sdp_reply)!=0) return; if(dlg.reply(req,200,"OK", @@ -530,20 +531,21 @@ void AmSession::onBye(const AmSipRequest& req) setStopped(); } -int AmSession::acceptAudio(const AmSipRequest& req, - string& sdp_reply) +int AmSession::acceptAudio(const string& body, + const string& hdrs, + string* sdp_reply) { try { try { // handle codec and send reply - string str_msg_flags = getHeader(req.hdrs,"P-MsgFlags"); + string str_msg_flags = getHeader(hdrs,"P-MsgFlags"); unsigned int msg_flags = 0; if(reverse_hex2int(str_msg_flags,msg_flags)){ ERROR("while parsing 'P-MsgFlags' header\n"); msg_flags = 0; } - negotiate( req.body, + negotiate( body, msg_flags & FL_FORCE_ACTIVE, sdp_reply); @@ -568,9 +570,9 @@ int AmSession::acceptAudio(const AmSipRequest& req, } catch(const AmSession::Exception& e){ ERROR("%i %s\n",e.code,e.reason.c_str()); - if(dlg.reply(req,e.code,e.reason, "")){ - dlg.bye(); - } +// if(dlg.reply(req,e.code,e.reason, "")){ +// dlg.bye(); +// } setStopped(); } diff --git a/core/AmSession.h b/core/AmSession.h index e1feed42..098f6c2e 100644 --- a/core/AmSession.h +++ b/core/AmSession.h @@ -177,13 +177,15 @@ public: * the session to the scheduler! */ void setCallgroup(const string& cg); + string getCallgroup() { return callgroup; } /** - * Accept the INVITE proposal + * Accept the SDP proposal * thus setting up audio stream */ - int acceptAudio(const AmSipRequest& req, - string& sdp_reply); + int acceptAudio(const string& body, + const string& hdrs = "", + string* sdp_reply=0); /** * Lock and unlock audio input & output @@ -236,7 +238,7 @@ public: /** handle SDP negotiation: only for INVITEs & re-INVITEs */ virtual void negotiate(const string& sdp_body, bool force_symmetric_rtp, - string& sdp_reply); + string* sdp_reply); void sendUpdate(); void sendReinvite(); @@ -261,6 +263,8 @@ public: */ bool getStopped() { return sess_stopped.get(); } + bool getDetached() { return detached.get(); } + /** * Creates a new Id which can be used within sessions. */