Commit 20569c21 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Rayo plugin - Added support for SIP handsets

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13776 b35dd754-fafc-0310-a699-88a17e54d16e
parent 51e60be8
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
var username = null; var username = null;
var password = null; var password = null;
var jid = window.location.hostname; var jid = window.location.hostname;
var sipHandset = null;
window.dialer = new Dialpad({ window.dialer = new Dialpad({
onPress: function (key) { onPress: function (key) {
...@@ -74,6 +75,7 @@ ...@@ -74,6 +75,7 @@
if (urlParam("domain")) domain = urlParam("domain"); if (urlParam("domain")) domain = urlParam("domain");
if (urlParam("username")) username = urlParam("username"); if (urlParam("username")) username = urlParam("username");
if (urlParam("password")) password = urlParam("password"); if (urlParam("password")) password = urlParam("password");
if (urlParam("handset")) sipHandset = urlParam("handset");
if (domain == "81.201.82.25" && prefix == "sip:") prefix = "sip:883510"; if (domain == "81.201.82.25" && prefix == "sip:") prefix = "sip:883510";
if (prefix == "tel:") domain = ""; if (prefix == "tel:") domain = "";
...@@ -132,6 +134,7 @@ ...@@ -132,6 +134,7 @@
{ {
codec_name: "PCMU", codec_name: "PCMU",
stereo_pan: "0", stereo_pan: "0",
sip_handset: sipHandset,
offHook: function() { offHook: function() {
$("#status").html("Off Hook"); $("#status").html("Off Hook");
...@@ -261,7 +264,6 @@ ...@@ -261,7 +264,6 @@
if (!username) groupname = null; if (!username) groupname = null;
window.connection.rayo.dial("xmpp:" + window.connection.jid, sipUri, { window.connection.rayo.dial("xmpp:" + window.connection.jid, sipUri, {
codec_name: "PCMU", codec_name: "PCMU",
group_name: groupname group_name: groupname
}); });
......
...@@ -178,7 +178,7 @@ Strophe.addConnectionPlugin('rayo', ...@@ -178,7 +178,7 @@ Strophe.addConnectionPlugin('rayo',
//console.log(iq.toString()); //console.log(iq.toString());
that._connection.sendIQ(iq, function(response) { that._connection.sendIQ(iq, function(response) {
$('ref', response).each(function() $('ref', response).each(function()
{ {
callId = $(this).attr('id'); callId = $(this).attr('id');
...@@ -200,6 +200,8 @@ Strophe.addConnectionPlugin('rayo', ...@@ -200,6 +200,8 @@ Strophe.addConnectionPlugin('rayo',
}); });
}, function(error){ }, function(error){
//console.log(error);
$('error', error).each(function() $('error', error).each(function()
{ {
...@@ -225,16 +227,47 @@ Strophe.addConnectionPlugin('rayo', ...@@ -225,16 +227,47 @@ Strophe.addConnectionPlugin('rayo',
//console.log(headers); //console.log(headers);
var that = this; var that = this;
var sipuri = (headers && headers.sip_handset) ? headers.sip_handset : (that.callbacks.sip_handset ? that.callbacks.sip_handset : null);
navigator.webkitGetUserMedia({audio:true, video:false}, function(stream) if (sipuri)
{ {
that.localStream = stream; var group = (headers && headers.group_name) ? headers.group_name : "";
that._offhook1(mixer, headers, action); var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
}, function(error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError(error);
}); var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
//console.log(iq.toString())
that._connection.sendIQ(iq, function(response)
{
//console.log(response)
$('ref', response).each(function()
{
that.handsetId = $(this).attr('id');
that.handsetUri = $(this).attr('uri');
if (action) action();
});
}, function (error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("offhook failure");
});
} else {
navigator.webkitGetUserMedia({audio:true, video:false}, function(stream)
{
that.localStream = stream;
that._offhook1(mixer, headers, action);
}, function(error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError(error);
});
}
}, },
_offhook1: function(mixer, headers, action) _offhook1: function(mixer, headers, action)
......
...@@ -24,32 +24,14 @@ import org.apache.commons.lang.builder.ToStringStyle; ...@@ -24,32 +24,14 @@ import org.apache.commons.lang.builder.ToStringStyle;
public class Handset extends BaseVerb { public class Handset extends BaseVerb {
public static final String MISSING_CRYPTO_SUITE = "Missing Crypto Suite"; public String cryptoSuite = null;
public static final String MISSING_LOCAL_CRYPTO = "Missing Local Crypto"; public String localCrypto = null;
public static final String MISSING_REMOTE_CRYPTO = "Missing Remote Crypto"; public String remoteCrypto = null;
public static final String MISSING_MIXER = "Missing Mixer"; public String codec = null;
public static final String MISSING_CODEC = "Missing Codec"; public String stereo = null;
public static final String MISSING_STEREO = "Missing Stereo"; public String mixer = null;
public String group = null;
@NotNull(message=Handset.MISSING_CRYPTO_SUITE) public String sipuri = null;
public String cryptoSuite;
@NotNull(message=Handset.MISSING_LOCAL_CRYPTO)
public String localCrypto;
@NotNull(message=Handset.MISSING_REMOTE_CRYPTO)
public String remoteCrypto;
@NotNull(message=Handset.MISSING_CODEC)
public String codec;
@NotNull(message=Handset.MISSING_STEREO)
public String stereo;
@NotNull(message=Handset.MISSING_MIXER)
public String mixer;
public String group;
public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo, String mixer) public Handset(String cryptoSuite, String localCrypto, String remoteCrypto, String codec, String stereo, String mixer)
{ {
...@@ -61,6 +43,13 @@ public class Handset extends BaseVerb { ...@@ -61,6 +43,13 @@ public class Handset extends BaseVerb {
this.mixer = mixer; this.mixer = mixer;
} }
public Handset(String sipuri, String mixer, String codec)
{
this.sipuri = sipuri;
this.mixer = mixer;
this.codec = codec;
}
@Override @Override
public String toString() { public String toString() {
...@@ -72,7 +61,8 @@ public class Handset extends BaseVerb { ...@@ -72,7 +61,8 @@ public class Handset extends BaseVerb {
.append("remoteCrypto",remoteCrypto) .append("remoteCrypto",remoteCrypto)
.append("codec",codec) .append("codec",codec)
.append("stereo",stereo) .append("stereo",stereo)
.append("stereo",mixer) .append("mixer",mixer)
.append("sipuri",sipuri)
.toString(); .toString();
} }
} }
...@@ -69,12 +69,22 @@ public class HandsetProvider extends BaseProvider { ...@@ -69,12 +69,22 @@ public class HandsetProvider extends BaseProvider {
private Object buildOffHookCommand(Element element) throws URISyntaxException { private Object buildOffHookCommand(Element element) throws URISyntaxException {
Handset handset = new Handset( element.attributeValue("cryptosuite"), Handset handset;
element.attributeValue("localcrypto"), String sipuri = element.attributeValue("sipuri");
element.attributeValue("remotecrypto"),
element.attributeValue("codec"), if (sipuri != null && "".equals(sipuri) == false)
element.attributeValue("stereo"), {
element.attributeValue("mixer")); handset = new Handset( element.attributeValue("sipuri"), element.attributeValue("mixer"), element.attributeValue("codec"));
} else {
handset = new Handset( element.attributeValue("cryptosuite"),
element.attributeValue("localcrypto"),
element.attributeValue("remotecrypto"),
element.attributeValue("codec"),
element.attributeValue("stereo"),
element.attributeValue("mixer"));
}
handset.group = element.attributeValue("group"); handset.group = element.attributeValue("group");
......
...@@ -26,9 +26,14 @@ public final class SIPAlertInfo extends ParametersHeader ...@@ -26,9 +26,14 @@ public final class SIPAlertInfo extends ParametersHeader
return stringbuffer.toString(); return stringbuffer.toString();
} }
protected StringBuilder encodeBody(StringBuilder builder) protected StringBuilder encodeBody(StringBuilder builder)
{ {
return null; builder.append(namePair);
if(!parameters.isEmpty())
builder.append(";").append(parameters.encode());
return builder;
} }
public void setNamePair(String namePair) public void setNamePair(String namePair)
......
...@@ -124,10 +124,12 @@ public class RayoComponent extends AbstractComponent ...@@ -124,10 +124,12 @@ public class RayoComponent extends AbstractComponent
Object object = handsetProvider.fromXML(element); Object object = handsetProvider.fromXML(element);
if (object instanceof OnHookCommand) { if (object instanceof OnHookCommand) {
reply = handleOnOffHookCommand((OnHookCommand) object, iq); OnHookCommand command = (OnHookCommand) object;
reply = handleOnOffHookCommand(command, iq);
} else if (object instanceof OffHookCommand) { } else if (object instanceof OffHookCommand) {
reply = handleOnOffHookCommand((OffHookCommand) object, iq); OffHookCommand command = (OffHookCommand) object;
reply = handleOnOffHookCommand(command, iq);
} }
return reply; return reply;
} }
...@@ -214,14 +216,15 @@ public class RayoComponent extends AbstractComponent ...@@ -214,14 +216,15 @@ public class RayoComponent extends AbstractComponent
Log.info("RayoComponent handleOnOffHookCommand"); Log.info("RayoComponent handleOnOffHookCommand");
IQ reply = IQ.createResultIQ(iq); IQ reply = IQ.createResultIQ(iq);
String callId = JID.escapeNode(iq.getFrom().toString());
if (object instanceof OnHookCommand) if (object instanceof OnHookCommand)
{ {
RelayChannel channel = plugin.getRelayChannel(JID.escapeNode(iq.getFrom().toString())); CallHandler handler = CallHandler.findCall(callId);
if (channel != null) if (handler != null)
{ {
handleOnOffHook(object, channel); handleOnOffHook(callId, object, plugin.getRelayChannel(callId));
} else { } else {
reply.setError(PacketError.Condition.item_not_found); reply.setError(PacketError.Condition.item_not_found);
...@@ -230,24 +233,39 @@ public class RayoComponent extends AbstractComponent ...@@ -230,24 +233,39 @@ public class RayoComponent extends AbstractComponent
} else { } else {
final Handset handset = ((OffHookCommand) object).getHandset(); final Handset handset = ((OffHookCommand) object).getHandset();
final RelayChannel channel = plugin.createRelayChannel(iq.getFrom(), handset);
if (channel != null) // rayo handset component can have only one call if (handset.sipuri == null) // webrtc handset
{ {
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1"); final RelayChannel channel = plugin.createRelayChannel(iq.getFrom(), handset);
childElement.addAttribute(HOST, LocalIPResolver.getLocalIP()); if (channel != null)
childElement.addAttribute(LOCAL_PORT, Integer.toString(channel.getPortA())); {
childElement.addAttribute(REMOTE_PORT, Integer.toString(channel.getPortB())); final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
childElement.addAttribute(ID, channel.getAttachment());
childElement.addAttribute(URI, "handset:" + channel.getAttachment() + "@rayo." + getDomain() + "/" + iq.getFrom().getNode());
Log.debug("Created handset channel {}:{}, {}:{}, {}:{}", new Object[]{HOST, LocalIPResolver.getLocalIP(), LOCAL_PORT, Integer.toString(channel.getPortA()), REMOTE_PORT, Integer.toString(channel.getPortB())}); childElement.addAttribute(HOST, LocalIPResolver.getLocalIP());
childElement.addAttribute(LOCAL_PORT, Integer.toString(channel.getPortA()));
childElement.addAttribute(REMOTE_PORT, Integer.toString(channel.getPortB()));
childElement.addAttribute(ID, channel.getAttachment());
childElement.addAttribute(URI, "handset:" + channel.getAttachment() + "@rayo." + getDomain() + "/" + iq.getFrom().getNode());
handleOnOffHook(object, channel); Log.debug("Created WebRTC handset channel {}:{}, {}:{}, {}:{}", new Object[]{HOST, LocalIPResolver.getLocalIP(), LOCAL_PORT, Integer.toString(channel.getPortA()), REMOTE_PORT, Integer.toString(channel.getPortB())});
} else { handleOnOffHook(callId, object, channel);
reply.setError(PacketError.Condition.internal_server_error);
} else {
reply.setError(PacketError.Condition.internal_server_error);
}
} else { // SIP handset
final Element childElement = reply.setChildElement("ref", "urn:xmpp:rayo:1");
childElement.addAttribute(ID, callId);
childElement.addAttribute(URI, "handset:" + callId + "@rayo." + getDomain() + "/" + iq.getFrom().getNode());
Log.info("Created SIP handset channel " + handset.sipuri);
handleOnOffHook(callId, object, null);
} }
} }
...@@ -255,19 +273,19 @@ public class RayoComponent extends AbstractComponent ...@@ -255,19 +273,19 @@ public class RayoComponent extends AbstractComponent
} }
private void handleOnOffHook(Object object, RelayChannel channel) private void handleOnOffHook(String callId, Object object, RelayChannel channel)
{ {
final boolean flag = object instanceof OnHookCommand; final boolean flag = object instanceof OnHookCommand;
Log.info("RayoComponent handleOnOffHook " + flag); Log.info("RayoComponent handleOnOffHook " + flag);
try { try {
OutgoingCallHandler callHandler = channel.getCallHandler(); CallHandler handler = CallHandler.findCall(callId);
if (callHandler != null) if (handler != null)
{ {
callHandler.cancelRequest("Reseting handset to " + (flag ? "on" : "off") + "hook"); handler.cancelRequest("Reseting handset to " + (flag ? "on" : "off") + "hook");
callHandler = null; handler = null;
} }
if (!flag) // offhook if (!flag) // offhook
...@@ -280,27 +298,41 @@ public class RayoComponent extends AbstractComponent ...@@ -280,27 +298,41 @@ public class RayoComponent extends AbstractComponent
mediaPreference = "PCM/48000/2"; mediaPreference = "PCM/48000/2";
CallParticipant cp = new CallParticipant(); CallParticipant cp = new CallParticipant();
cp.setCallId(channel.getAttachment()); cp.setCallId(callId);
cp.setProtocol("WebRtc");
cp.setConferenceId(handset.mixer); cp.setConferenceId(handset.mixer);
cp.setMediaPreference(mediaPreference); cp.setDisplayName("rayo-handset-" + System.currentTimeMillis());
cp.setRelayChannel(channel); cp.setName(cp.getDisplayName());
cp.setDisplayName(channel.getAttachment());
cp.setVoiceDetection(true); cp.setVoiceDetection(true);
cp.setCallOwner(channel.getFrom().toString()); cp.setCallOwner(JID.unescapeNode(callId));
if (handset.group != null && ! "".equals(handset.group)) if (handset.group != null && ! "".equals(handset.group))
{ {
// set for new or existing conference // set for new or existing conference
ConferenceManager.getConference(handset.mixer, channel.getMediaPreference(), handset.group, false); ConferenceManager.getConference(handset.mixer, mediaPreference, handset.group, false);
ConferenceManager.setDisplayName(handset.mixer, handset.group); ConferenceManager.setDisplayName(handset.mixer, handset.group);
} }
callHandler = new OutgoingCallHandler(this, cp); if (channel == null)
{
cp.setMediaPreference("PCMU/8000/1");
cp.setPhoneNumber(handset.sipuri);
cp.setAutoAnswer(true);
cp.setProtocol("SIP");
} else {
cp.setMediaPreference(mediaPreference);
cp.setRelayChannel(channel);
cp.setProtocol("WebRtc");
}
OutgoingCallHandler callHandler = new OutgoingCallHandler(this, cp);
callHandler.start(); callHandler.start();
channel.setCallHandler(callHandler); if (channel != null)
{
channel.setCallHandler(callHandler);
}
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -923,16 +955,15 @@ public class RayoComponent extends AbstractComponent ...@@ -923,16 +955,15 @@ public class RayoComponent extends AbstractComponent
{ {
Log.info("RayoComponent doPhoneAndPcCall " + handsetId); Log.info("RayoComponent doPhoneAndPcCall " + handsetId);
RelayChannel channel = plugin.getRelayChannel(handsetId); CallHandler handsetHandler = CallHandler.findCall(handsetId);
if (channel != null) if (handsetHandler != null)
{ {
try { try {
setMixer(channel, reply, cp); setMixer(handsetHandler, reply, cp);
OutgoingCallHandler outgoingCallHandler = new OutgoingCallHandler(this, cp); OutgoingCallHandler outgoingCallHandler = new OutgoingCallHandler(this, cp);
//OutgoingCallHandler handsetHandler = channel.getCallHandler();
//outgoingCallHandler.setOtherCall(handsetHandler); //outgoingCallHandler.setOtherCall(handsetHandler);
//handsetHandler.setOtherCall(outgoingCallHandler); //handsetHandler.setOtherCall(outgoingCallHandler);
...@@ -953,15 +984,16 @@ public class RayoComponent extends AbstractComponent ...@@ -953,15 +984,16 @@ public class RayoComponent extends AbstractComponent
return reply; return reply;
} }
private void setMixer(RelayChannel channel, IQ reply, CallParticipant cp) private void setMixer(CallHandler handsetHandler, IQ reply, CallParticipant cp)
{ {
String username = channel.getFrom().getNode(); CallParticipant hp = handsetHandler.getCallParticipant();
String mixer = channel.getHandset().mixer;
ConferenceManager conferenceManager = ConferenceManager.getConference( mixer,
channel.getMediaPreference(),
username, false);
try { try {
String mixer = hp.getConferenceId();
ConferenceManager conferenceManager = ConferenceManager.getConference( mixer,
hp.getMediaPreference(),
cp.getName(), false);
cp.setConferenceId(mixer); cp.setConferenceId(mixer);
cp.setCallId(mixer); cp.setCallId(mixer);
conferenceManager.setCallId(mixer); conferenceManager.setCallId(mixer);
...@@ -1026,8 +1058,12 @@ public class RayoComponent extends AbstractComponent ...@@ -1026,8 +1058,12 @@ public class RayoComponent extends AbstractComponent
if ("001 STATE CHANGED".equals(myEvent)) if ("001 STATE CHANGED".equals(myEvent))
{ {
if ("100 INVITED".equals(callState)) { if ("100 INVITED".equals(callState)) {
presence.getElement().add(rayoProvider.toXML(new RingingEvent(null, headers)));
sendPacket(presence); if (cp.isAutoAnswer() == false) // SIP handset, no ringing event
{
presence.getElement().add(rayoProvider.toXML(new RingingEvent(null, headers)));
sendPacket(presence);
}
} else if ("200 ESTABLISHED".equals(callState)) { } else if ("200 ESTABLISHED".equals(callState)) {
......
...@@ -294,9 +294,11 @@ public class RayoPlugin implements Plugin, SessionEventListener { ...@@ -294,9 +294,11 @@ public class RayoPlugin implements Plugin, SessionEventListener {
private void checkRecordingFolder(File pluginDirectory) private void checkRecordingFolder(File pluginDirectory)
{ {
String rayoHome = JiveGlobals.getHomeDirectory() + File.separator + "resources" + File.separator + "spank" + File.separator + "rayo";
try try
{ {
File rayoFolderPath = new File(JiveGlobals.getHomeDirectory() + File.separator + "rayo"); File rayoFolderPath = new File(rayoHome);
if(!rayoFolderPath.exists()) if(!rayoFolderPath.exists())
{ {
...@@ -304,7 +306,7 @@ public class RayoPlugin implements Plugin, SessionEventListener { ...@@ -304,7 +306,7 @@ public class RayoPlugin implements Plugin, SessionEventListener {
} }
File recordingFolderPath = new File(JiveGlobals.getHomeDirectory() + File.separator + "rayo" + File.separator + "recordings"); File recordingFolderPath = new File(rayoHome + File.separator + "recordings");
if(!recordingFolderPath.exists()) if(!recordingFolderPath.exists())
{ {
...@@ -312,7 +314,7 @@ public class RayoPlugin implements Plugin, SessionEventListener { ...@@ -312,7 +314,7 @@ public class RayoPlugin implements Plugin, SessionEventListener {
} }
File soundsFolderPath = new File(JiveGlobals.getHomeDirectory() + File.separator + "rayo" + File.separator + "sounds"); File soundsFolderPath = new File(rayoHome + File.separator + "sounds");
if(!soundsFolderPath.exists()) if(!soundsFolderPath.exists())
{ {
......
...@@ -46,7 +46,7 @@ public class Application implements CallEventListener { ...@@ -46,7 +46,7 @@ public class Application implements CallEventListener {
System.setProperty("com.sun.voip.server.PUBLIC_IP_ADDRESS", config.getPublicHost()); System.setProperty("com.sun.voip.server.PUBLIC_IP_ADDRESS", config.getPublicHost());
System.setProperty("com.sun.voip.server.PROTOCOL", config.getDefaultProtocol()); System.setProperty("com.sun.voip.server.PROTOCOL", config.getDefaultProtocol());
System.setProperty("com.sun.voip.server.SIP_PORT", config.getDefaultSIPPort()); System.setProperty("com.sun.voip.server.SIP_PORT", config.getDefaultSIPPort());
System.setProperty("com.sun.voip.server.Bridge.recordDirectory", pluginDirectory.getAbsolutePath() + File.separator + ".." + File.separator + ".." + File.separator + "rayo" + File.separator + "recordings"); System.setProperty("com.sun.voip.server.Bridge.recordDirectory", pluginDirectory.getAbsolutePath() + File.separator + ".." + File.separator + ".." + File.separator + "resources" + File.separator + "spank" + File.separator + "rayo" + File.separator + "recordings");
System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory"); System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory");
Properties properties = new Properties(); Properties properties = new Properties();
......
...@@ -178,7 +178,7 @@ Strophe.addConnectionPlugin('rayo', ...@@ -178,7 +178,7 @@ Strophe.addConnectionPlugin('rayo',
//console.log(iq.toString()); //console.log(iq.toString());
that._connection.sendIQ(iq, function(response) { that._connection.sendIQ(iq, function(response) {
$('ref', response).each(function() $('ref', response).each(function()
{ {
callId = $(this).attr('id'); callId = $(this).attr('id');
...@@ -200,6 +200,8 @@ Strophe.addConnectionPlugin('rayo', ...@@ -200,6 +200,8 @@ Strophe.addConnectionPlugin('rayo',
}); });
}, function(error){ }, function(error){
//console.log(error);
$('error', error).each(function() $('error', error).each(function()
{ {
...@@ -225,16 +227,47 @@ Strophe.addConnectionPlugin('rayo', ...@@ -225,16 +227,47 @@ Strophe.addConnectionPlugin('rayo',
//console.log(headers); //console.log(headers);
var that = this; var that = this;
var sipuri = (headers && headers.sip_handset) ? headers.sip_handset : (that.callbacks.sip_handset ? that.callbacks.sip_handset : null);
navigator.webkitGetUserMedia({audio:true, video:false}, function(stream) if (sipuri)
{ {
that.localStream = stream; var group = (headers && headers.group_name) ? headers.group_name : "";
that._offhook1(mixer, headers, action); var codec = (headers && headers.codec_name) ? headers.codec_name : (that.callbacks.codec_name ? that.callbacks.codec_name : "OPUS");
}, function(error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError(error);
}); var iq = $iq({to: "rayo." + that._connection.domain, from: that._connection.jid, type: "get"}).c("offhook", {xmlns: Strophe.NS.RAYO_HANDSET, sipuri: sipuri, mixer: mixer, group: group, codec: codec});
//console.log(iq.toString())
that._connection.sendIQ(iq, function(response)
{
//console.log(response)
$('ref', response).each(function()
{
that.handsetId = $(this).attr('id');
that.handsetUri = $(this).attr('uri');
if (action) action();
});
}, function (error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError("offhook failure");
});
} else {
navigator.webkitGetUserMedia({audio:true, video:false}, function(stream)
{
that.localStream = stream;
that._offhook1(mixer, headers, action);
}, function(error) {
if (that.callbacks && that.callbacks.onError) that.callbacks.onError(error);
});
}
}, },
_offhook1: function(mixer, headers, action) _offhook1: function(mixer, headers, action)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment