/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.service.protocol;
import java.util.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.util.event.*;
/**
* Provides the default implementation of the <tt>ConferenceMember</tt>
* interface.
*
* @author Lyubomir Marinov
* @author Yana Stamcheva
* @author Emil Ivov
*/
public class AbstractConferenceMember
extends PropertyChangeNotifier
implements ConferenceMember
{
/**
* A Public Switched Telephone Network (PSTN) ALERTING or SIP 180 Ringing
* was returned for the outbound call; endpoint is being alerted.
*/
public static final String ALERTING = "alerting";
/**
* The endpoint is a participant in the conference. Depending on the media
* policies, he/she can send and receive media to and from other
* participants.
*/
public static final String CONNECTED = "connected";
/**
* Endpoint is dialing into the conference, not yet in the roster (probably
* being authenticated).
*/
public static final String DIALING_IN = "dialing-in";
/**
* Focus has dialed out to connect the endpoint to the conference, but the
* endpoint is not yet in the roster (probably being authenticated).
*/
public static final String DIALING_OUT = "dialing-out";
/**
* The endpoint is not a participant in the conference, and no active dialog
* exists between the endpoint and the focus.
*/
public static final String DISCONNECTED = "disconnected";
/**
* Active signaling dialog exists between an endpoint and a focus, but
* endpoint is "on-hold" for this conference, i.e., he/she is neither
* "hearing" the conference mix nor is his/her media being mixed in the
* conference.
*/
public static final String ON_HOLD = "on-hold";
/**
* Endpoint is not yet in the session, but it is anticipated that he/she
* will join in the near future.
*/
public static final String PENDING = "pending";
/**
* The protocol address of this <tt>ConferenceMember</tt>.
*/
private final String address;
/**
* The audio SSRC value if transmitted by the focus of the conference.
*/
private long audioSsrc = -1;
/**
* The status in both directions of the audio RTP stream from the point of
* view of this <tt>ConferenceMember</tt>.
*/
private MediaDirection audioStatus = MediaDirection.INACTIVE;
/**
* The <tt>CallPeer</tt> which is the conference focus of this
* <tt>ConferenceMember</tt>.
*/
private final CallPeer conferenceFocusCallPeer;
/**
* The user-friendly display name of this <tt>ConferenceMember</tt> in
* the conference.
*/
private String displayName;
/**
* The state of the device and signaling session of this
* <tt>ConferenceMember</tt> in the conference.
*/
private ConferenceMemberState state = ConferenceMemberState.UNKNOWN;
/**
* The video SSRC value if transmitted by the focus of the conference.
*/
private long videoSsrc = -1;
/**
* The status in both directions of the video RTP stream from the point of
* view of this <tt>ConferenceMember</tt>.
*/
private MediaDirection videoStatus = MediaDirection.INACTIVE;
/**
* Creates an instance of <tt>AbstractConferenceMember</tt> by specifying
* the corresponding <tt>conferenceFocusCallPeer</tt>, to which this member
* is connected.
*
* @param conferenceFocusCallPeer the <tt>CallPeer</tt> to which this member
* is connected
* @param address the protocol address of this <tt>ConferenceMember</tt>
* @throws NullPointerException if <tt>conferenceFocusCallPeer</tt> or
* <tt>address</tt> is <tt>null</tt>
*/
public AbstractConferenceMember(
CallPeer conferenceFocusCallPeer,
String address)
{
if (conferenceFocusCallPeer == null)
throw new NullPointerException("conferenceFocusCallPeer");
if (address == null)
throw new NullPointerException("address");
this.conferenceFocusCallPeer = conferenceFocusCallPeer;
this.address = address;
}
/**
* Returns the protocol address of this <tt>ConferenceMember</tt>.
*
* @return the protocol address of this <tt>ConferenceMember</tt>
*/
public String getAddress()
{
return address;
}
/**
* Returns the SSRC value associated with this participant;
*
* @return the audio ssrc id
*/
public long getAudioSsrc()
{
return audioSsrc;
}
/**
* {@inheritDoc}
*/
public MediaDirection getAudioStatus()
{
return audioStatus;
}
/**
* {@inheritDoc}
*
* Implements {@link ConferenceMember#getConferenceFocusCallPeer()}.
*/
public CallPeer getConferenceFocusCallPeer()
{
return conferenceFocusCallPeer;
}
/**
* Returns the display name of this conference member. Implements
* <tt>ConferenceMember#getDisplayName()</tt>.
* @return the display name of this conference member
*/
public String getDisplayName()
{
String displayName = this.displayName;
if ((displayName == null) || (displayName.length() < 1))
{
String address = getAddress();
if ((address != null) && (address.length() > 0))
return address;
}
return displayName;
}
/**
* Returns the state of this conference member. Implements
* <tt>ConferenceMember#getState()</tt>.
* @return the state of this conference member
*/
public ConferenceMemberState getState()
{
return state;
}
/**
* Returns the SSRC value associated with this participant;
*
* @return the video ssrc id
*/
public long getVideoSsrc()
{
return videoSsrc;
}
/**
* {@inheritDoc}
*/
public MediaDirection getVideoStatus()
{
return videoStatus;
}
private static long parseMediaSSRC(Object value)
{
long ssrc;
if (value == null)
ssrc = -1;
else if (value instanceof Long)
ssrc = ((Long) value).longValue();
else
{
String str = value.toString();
if ((str == null) || (str.length() == 0))
ssrc = -1;
else
ssrc = Long.parseLong(str);
}
return ssrc;
}
private static MediaDirection parseMediaStatus(Object value)
{
MediaDirection status;
if (value == null)
status = MediaDirection.INACTIVE;
else if (value instanceof MediaDirection)
status = (MediaDirection) value;
else
{
String str = value.toString();
if ((str == null) || (str.length() == 0))
status = MediaDirection.INACTIVE;
else
status = MediaDirection.parseString(str);
}
return status;
}
/**
* Sets the audio SSRC identifier of this member.
*
* @param ssrc the audio SSRC ID to set for this member.
*/
public void setAudioSsrc(long ssrc)
{
if (this.audioSsrc != ssrc)
{
long oldValue = this.audioSsrc;
this.audioSsrc = ssrc;
firePropertyChange(
AUDIO_SSRC_PROPERTY_NAME,
oldValue, this.audioSsrc);
}
}
/**
* Sets the status in both directions of the audio RTP stream from the point
* of view of this <tt>ConferenceMember</tt>.
*
* @param status the status in both directions of the audio RTP stream from
* the point of view of this <tt>ConferenceMember</tt>. If <tt>null</tt>,
* the method executes as if {@link MediaDirection#INACTIVE}. was specified.
*/
public void setAudioStatus(MediaDirection status)
{
if (status == null)
status = MediaDirection.INACTIVE;
if (this.audioStatus != status)
{
MediaDirection oldValue = this.audioStatus;
this.audioStatus = status;
firePropertyChange(
AUDIO_STATUS_PROPERTY_NAME,
oldValue, this.audioStatus);
}
}
/**
* Sets the user-friendly display name of this <tt>ConferenceMember</tt>
* in the conference and fires a new <tt>PropertyChangeEvent</tt> for
* the property <tt>#DISPLAY_NAME_PROPERTY_NAME</tt>.
*
* @param displayName
* the user-friendly display name of this
* <tt>ConferenceMember</tt> in the conference
*/
public void setDisplayName(String displayName)
{
if (((this.displayName == null) && (displayName != null))
|| ((this.displayName != null)
&& !this.displayName.equals(displayName)))
{
String oldValue = this.displayName;
this.displayName = displayName;
firePropertyChange(
DISPLAY_NAME_PROPERTY_NAME,
oldValue, this.displayName);
}
}
/**
* Sets the <tt>state</tt> property of this <tt>ConferenceMember</tt> by
* translating it from its conference-info XML endpoint status.
*
* @param endpointStatus the conference-info XML endpoint status of this
* <tt>ConferenceMember</tt> indicated by its
* <tt>conferenceFocusCallPeer</tt>
*/
public void setEndpointStatus(String endpointStatus)
{
ConferenceMemberState state;
if (ALERTING.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.ALERTING;
else if (CONNECTED.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.CONNECTED;
else if (DIALING_IN.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.DIALING_IN;
else if (DIALING_OUT.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.DIALING_OUT;
else if (DISCONNECTED.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.DISCONNECTED;
else if (ON_HOLD.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.ON_HOLD;
else if (PENDING.equalsIgnoreCase(endpointStatus))
state = ConferenceMemberState.PENDING;
else
state = ConferenceMemberState.UNKNOWN;
setState(state);
}
public boolean setProperties(Map<String, Object> properties)
{
boolean changed = false;
for (Map.Entry<String, Object> entry : properties.entrySet())
{
String key = entry.getKey();
Object value = entry.getValue();
if (AUDIO_SSRC_PROPERTY_NAME.equals(key))
{
long ssrc = parseMediaSSRC(value);
if (getAudioSsrc() != ssrc)
{
setAudioSsrc(ssrc);
changed = true;
}
}
else if (AUDIO_STATUS_PROPERTY_NAME.equals(key))
{
MediaDirection status = parseMediaStatus(value);
if (!getAudioStatus().equals(status))
{
setAudioStatus(status);
changed = true;
}
}
else if (VIDEO_SSRC_PROPERTY_NAME.equals(key))
{
long ssrc = parseMediaSSRC(value);
if (getVideoSsrc() != ssrc)
{
setVideoSsrc(ssrc);
changed = true;
}
}
else if (VIDEO_STATUS_PROPERTY_NAME.equals(key))
{
MediaDirection status = parseMediaStatus(value);
if (!getVideoStatus().equals(status))
{
setVideoStatus(status);
changed = true;
}
}
}
return changed;
}
/**
* Sets the state of the device and signaling session of this
* <tt>ConferenceMember</tt> in the conference and fires a new
* <tt>PropertyChangeEvent</tt> for the property
* <tt>#STATE_PROPERTY_NAME</tt>.
*
* @param state
* the state of the device and signaling session of this
* <tt>ConferenceMember</tt> in the conference
*/
public void setState(ConferenceMemberState state)
{
if (this.state != state)
{
ConferenceMemberState oldValue = this.state;
this.state = state;
firePropertyChange(STATE_PROPERTY_NAME, oldValue, this.state);
}
}
/**
* Sets the video SSRC identifier of this member.
*
* @param ssrc the video SSRC ID to set for this member.
*/
public void setVideoSsrc(long ssrc)
{
if (this.videoSsrc != ssrc)
{
long oldValue = this.videoSsrc;
this.videoSsrc = ssrc;
firePropertyChange(
VIDEO_SSRC_PROPERTY_NAME,
oldValue, this.videoSsrc);
}
}
/**
* Sets the status in both directions of the video RTP stream from the point
* of view of this <tt>ConferenceMember</tt>.
*
* @param status the status in both directions of the video RTP stream from
* the point of view of this <tt>ConferenceMember</tt>. If <tt>null</tt>,
* the method executes as if {@link MediaDirection#INACTIVE}. was specified.
*/
public void setVideoStatus(MediaDirection status)
{
if (status == null)
status = MediaDirection.INACTIVE;
if (this.videoStatus != status)
{
MediaDirection oldValue = this.videoStatus;
this.videoStatus = status;
firePropertyChange(
VIDEO_STATUS_PROPERTY_NAME,
oldValue, this.videoStatus);
}
}
}