/** * Copyright 2010 Voxeo Corporation Licensed under the Apache License, Version * 2.0 (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.voxeo.moho.sip; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import javax.media.mscontrol.MsControlException; import javax.media.mscontrol.networkconnection.NetworkConnection; import javax.media.mscontrol.networkconnection.SdpPortManagerEvent; import javax.sdp.SdpException; import javax.sdp.SessionDescription; import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipServletResponse; import org.apache.log4j.Logger; import com.voxeo.moho.NegotiateException; import com.voxeo.moho.sip.SIPCallImpl.HoldState; import com.voxeo.moho.util.SDPUtils; public class SIPCallMediaDelegate extends SIPCallDelegate { private static final Logger LOG = Logger.getLogger(SIPCallMediaDelegate.class); protected SipServletRequest _req; protected SipServletResponse _res; protected boolean _isWaiting; protected boolean _isUpdateWaiting; protected SIPCallMediaDelegate() { super(); } @Override protected void handleAck(final SIPCallImpl call, final SipServletRequest req) throws Exception { try { if (call.getHoldState() == HoldState.None) { call.processSDPAnswer(req); _isWaiting = false; call.notifyAll(); } } catch (final Exception e) { LOG.error("Exception", e); call.fail(e); } } @Override protected void handleReinvite(final SIPCallImpl call, final SipServletRequest req, final Map<String, String> headers) throws Exception { _req = req; if (call.getHoldState() == HoldState.None) { _isWaiting = true; call.processSDPOffer(req); } } @Override protected void handleReinviteResponse(SIPCallImpl call, SipServletResponse res, Map<String, String> headers) throws Exception { _res = res; if (res.getRequest().getAttribute(SIPCallDelegate.SIPCALL_HOLD_REQUEST) != null || res.getRequest().getAttribute(SIPCallDelegate.SIPCALL_UNHOLD_REQUEST) != null) { try { _res.createAck().send(); call.holdResp(); } catch (IOException e) { LOG.error("IOException when sending ACK", e); call.setHoldState(HoldState.None); call.notify(); call.fail(e); } } else if (res.getRequest().getAttribute(SIPCallDelegate.SIPCALL_MUTE_REQUEST) != null || res.getRequest().getAttribute(SIPCallDelegate.SIPCALL_UNMUTE_REQUEST) != null) { try { _res.createAck().send(); if (call.getMuteState() == HoldState.Muting) { call.setMuteState(HoldState.Muted); } else if (call.getMuteState() == HoldState.UnMuting) { call.setMuteState(HoldState.None); } } catch (IOException e) { LOG.error("IOException when sending ACK", e); call.setHoldState(HoldState.None); call.fail(e); } finally { call.notify(); } } else { call.processSDPOffer(res); } } @Override protected void handleUpdate(final SIPCallImpl call, final SipServletRequest req, final Map<String, String> headers) throws Exception { if(req.getRawContent() != null){ _isUpdateWaiting = true; _req = req; call.processSDPOffer(req); } else{ req.createResponse(SipServletResponse.SC_OK).send(); } } @Override protected void handleUpdateResponse(SIPCallImpl call, SipServletResponse res, Map<String, String> headers) throws Exception { if(SIPHelper.isSuccessResponse(res) && SIPHelper.getRawContentWOException(res) != null){ call.processSDPOffer(res); } } @Override protected void handleSdpEvent(final SIPCallImpl call, final SdpPortManagerEvent event) { if (event.getEventType().equals(SdpPortManagerEvent.OFFER_GENERATED) || event.getEventType().equals(SdpPortManagerEvent.ANSWER_GENERATED)) { if(_isUpdateWaiting){ if(event.isSuccessful()){ byte[] sdp = event.getMediaServerSdp(); call.setLocalSDP(sdp); try { final SipServletResponse res = _req.createResponse(SipServletResponse.SC_OK); res.setContent(SDPUtils.formulateSDP(call, sdp), "application/sdp"); res.send(); } catch (final Exception e) { LOG.error("Exception when sending back response for UPDATE", e); call.fail(e); } } else{ LOG.warn("Failed to process UPDATE request, got failure SdpPortManagerEvent:"+event); try { _req.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR, event.getErrorText()).send(); } catch (IOException e) { LOG.error("Exception when sending back error response for UPDATE", e); call.fail(e); } } _isUpdateWaiting = false; return; } else if(call.isSendingUpdate()){ // do nothing return; } if (call.isHoldingProcess()) { call.holdResp(); } else { if (event.isSuccessful()) { byte[] sdp = event.getMediaServerSdp(); SessionDescription sessionDescription = null; try { String remoteSdp = new String(call.getRemoteSdp(), "iso8859-1"); // TODO improve the parse. if (remoteSdp.indexOf("sendonly") > 0) { sessionDescription = createRecvonlySDP(call, sdp); sdp = sessionDescription.toString().getBytes("iso8859-1"); } } catch (UnsupportedEncodingException e1) { LOG.error("", e1); } catch (SdpException e) { LOG.error("", e); } call.setLocalSDP(sdp); try { final SipServletResponse res = _req.createResponse(SipServletResponse.SC_OK); res.setContent(SDPUtils.formulateSDP(call, sdp), "application/sdp"); res.send(); } catch (final Exception e) { LOG.error("Exception when sending back response", e); call.fail(e); } } else { SIPHelper.handleErrorSdpPortManagerEvent(event, _req); call.fail(new NegotiateException(event)); } } } } @Override protected void hold(SIPCallImpl call, boolean send) throws MsControlException, IOException, SdpException { ((NetworkConnection) call.getMediaObject()).getSdpPortManager().processSdpOffer( send ? createRecvonlySDP(call, call.getRemoteSdp()).toString().getBytes() : createSendonlySDP(call, call.getRemoteSdp()).toString().getBytes()); call.getMediaService(true); Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SIPCallDelegate.SIPCALL_HOLD_REQUEST, "true"); call.reInviteRemote(createSendonlySDP(call, call.getLocalSDP()), null, attributes); } @Override protected void mute(SIPCallImpl call) throws IOException, SdpException { call.getMediaService(true); Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SIPCallDelegate.SIPCALL_MUTE_REQUEST, "true"); call.reInviteRemote(createSendonlySDP(call, call.getLocalSDP()), null, attributes); } @Override protected void unhold(SIPCallImpl call) throws MsControlException, IOException, SdpException { ((NetworkConnection) call.getMediaObject()).getSdpPortManager().processSdpOffer( createSendrecvSDP(call, call.getRemoteSdp()).toString().getBytes()); call.getMediaService(true); Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SIPCallDelegate.SIPCALL_UNHOLD_REQUEST, "true"); call.reInviteRemote(createSendrecvSDP(call, call.getLocalSDP()), null, attributes); } @Override protected void unmute(SIPCallImpl call) throws IOException, SdpException { call.getMediaService(true); Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SIPCallDelegate.SIPCALL_UNMUTE_REQUEST, "true"); call.reInviteRemote(createSendrecvSDP(call, call.getLocalSDP()), null, attributes); } }