Initial video size setting, when creating video call.

Hide resize button if we can detect that its not supported, and limit the resolutions we have for setting according the remote party settings.
Fix changing resolution several times. Fix duplicate field for keeping framerate.
cusax-fix
Damian Minkov 15 years ago
parent 754f0397d2
commit 6064f8d33c

@ -892,6 +892,24 @@ public void callStateChanged(CallChangeEvent evt)
}
}
/**
* Checks whether the <tt>callPeer</tt> supports setting video
* quality presets. If quality controls is null, its not supported.
* @param callPeer the peer, which video quality we're checking
* @return whether call peer supports setting quality preset.
*/
public static boolean isVideoQualityPresetSupported(CallPeer callPeer)
{
ProtocolProviderService provider = callPeer.getProtocolProvider();
OperationSetVideoTelephony videoOpSet
= provider.getOperationSet(OperationSetVideoTelephony.class);
if (videoOpSet == null)
return false;
return videoOpSet.getQualityControls(callPeer) != null;
}
/**
* Sets the given quality preset for the video of the given call peer.
*
@ -899,15 +917,19 @@ public void callStateChanged(CallChangeEvent evt)
* @param qualityPreset the new quality settings
*/
public static void setVideoQualityPreset(final CallPeer callPeer,
final QualityPreset qualityPreset)
final QualityPresets qualityPreset)
{
ProtocolProviderService provider = callPeer.getProtocolProvider();
final OperationSetVideoTelephony videoOpSet
= (OperationSetVideoTelephony) provider
.getOperationSet(OperationSetVideoTelephony.class);
= provider.getOperationSet(OperationSetVideoTelephony.class);
if (videoOpSet == null)
return;
final QualityControls qualityControl =
videoOpSet.getQualityControls(callPeer);
if (videoOpSet != null)
if (qualityControl != null)
{
new Thread(new Runnable()
{
@ -915,8 +937,8 @@ public void run()
{
try
{
videoOpSet.setQualityPreset(
callPeer, qualityPreset);
qualityControl.setPreferredRemoteSendMaxPreset(
qualityPreset);
}
catch(OperationFailedException e)
{

@ -277,7 +277,6 @@ private void init()
recordButton = new RecordButton(call);
videoButton = new LocalVideoButton(call);
showHideVideoButton = new ShowHideVideoButton(call);
resizeVideoButton = new ResizeVideoButton(call);
showHideVideoButton.setPeerRenderer(((CallRenderer) callPanel)
.getCallPeerRenderer(call.getCallPeers().next()));
@ -1044,20 +1043,30 @@ public CallRenderer getCurrentCallRenderer()
/**
* Adds remote video specific components.
*/
public void addRemoteVideoSpecificComponents()
public void addRemoteVideoSpecificComponents(CallPeer callPeer)
{
settingsPanel.add(resizeVideoButton);
if(CallManager.isVideoQualityPresetSupported(callPeer))
{
if(resizeVideoButton == null)
resizeVideoButton = new ResizeVideoButton(call);
if(resizeVideoButton.countAvailableOptions() > 1)
settingsPanel.add(resizeVideoButton);
}
settingsPanel.add(fullScreenButton);
settingsPanel.revalidate();
settingsPanel.repaint();
}
/**
* Adds remote video specific components.
* Remove remote video specific components.
*/
public void removeRemoteVideoSpecificComponents()
{
settingsPanel.remove(resizeVideoButton);
if(resizeVideoButton != null)
settingsPanel.remove(resizeVideoButton);
settingsPanel.remove(fullScreenButton);
settingsPanel.revalidate();
settingsPanel.repaint();
@ -1103,7 +1112,10 @@ private void removeOneToOneSpecificComponents()
settingsPanel.remove(videoButton);
settingsPanel.remove(showHideVideoButton);
settingsPanel.remove(resizeVideoButton);
if(resizeVideoButton != null)
settingsPanel.remove(resizeVideoButton);
settingsPanel.remove(desktopSharingButton);
settingsPanel.remove(transferCallButton);
settingsPanel.remove(fullScreenButton);

@ -974,7 +974,7 @@ else if (isAncestor(videoContainer, visualComponent))
videoContainer.add(video, VideoLayout.CENTER_REMOTE, 0);
callRenderer.getCallContainer()
.addRemoteVideoSpecificComponents();
.addRemoteVideoSpecificComponents(callPeer);
}
else
{

@ -13,7 +13,7 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.neomedia.QualityPreset;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
/**
@ -24,6 +24,21 @@
public class ResizeVideoButton
extends AbstractCallToggleButton
{
/**
* Is low option present in menu when button is pressed.
*/
private boolean loOptionPresent = false;
/**
* Is SD option present in menu when button is pressed.
*/
private boolean sdOptionPresent = false;
/**
* Is HD option present in menu when button is pressed.
*/
private boolean hdOptionPresent = false;
/**
* Initializes a new <tt>DesktopSharingButton</tt> instance which is meant
* to be used to initiate a desktop sharing during a call.
@ -49,11 +64,42 @@ public ResizeVideoButton(Call call)
*/
public ResizeVideoButton(Call call, boolean fullScreen, boolean selected)
{
super( call,
super(call,
fullScreen,
selected,
ImageLoader.SD_VIDEO_BUTTON,
"service.gui.CHANGE_VIDEO_QUALITY");
// catch everything, make sure we don't interrupt gui creation
// if anything strange happens
try
{
CallPeer peer = call.getCallPeers().next();
OperationSetVideoTelephony videoOpSet = peer.getProtocolProvider()
.getOperationSet(OperationSetVideoTelephony.class);
QualityControls qualityControls = videoOpSet.getQualityControls(peer);
QualityPresets maxRemotePreset =
qualityControls.getRemoteSendMaxPreset();
// if there is a setting for max preset lets look at it
// and add only those presets that are remotely supported
if( maxRemotePreset == null ||
maxRemotePreset.compareTo(QualityPresets.HD_QUALITY) >= 0)
hdOptionPresent = true;
if( maxRemotePreset == null ||
maxRemotePreset.compareTo(QualityPresets.SD_QUALITY) >= 0)
sdOptionPresent = true;
if( maxRemotePreset == null ||
maxRemotePreset.compareTo(QualityPresets.LO_QUALITY) >= 0)
loOptionPresent = true;
}
catch(Throwable t)
{
// do nothing
}
}
/**
@ -93,9 +139,9 @@ private JPopupMenu createResizeVideoMenu()
popupMenu.setInvoker(this);
popupMenu.setFocusable(true);
Dimension loDimension = QualityPreset.LO_QUALITY.getResolution();
Dimension sdDimension = QualityPreset.SD_QUALITY.getResolution();
Dimension hdDimension = QualityPreset.HD_QUALITY.getResolution();
Dimension loDimension = QualityPresets.LO_QUALITY.getResolution();
Dimension sdDimension = QualityPresets.SD_QUALITY.getResolution();
Dimension hdDimension = QualityPresets.HD_QUALITY.getResolution();
JMenuItem lowQuality = new JMenuItem(
GuiActivator.getResources()
@ -124,9 +170,15 @@ private JPopupMenu createResizeVideoMenu()
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
popupMenu.add(titleLabel);
popupMenu.addSeparator();
popupMenu.add(hdQuality);
popupMenu.add(normalQuality);
popupMenu.add(lowQuality);
if(hdOptionPresent)
popupMenu.add(hdQuality);
if(sdOptionPresent)
popupMenu.add(normalQuality);
if(loOptionPresent)
popupMenu.add(lowQuality);
lowQuality.addActionListener(new ActionListener()
{
@ -138,7 +190,7 @@ public void actionPerformed(ActionEvent e)
CallManager.setVideoQualityPreset(
call.getCallPeers().next(),
QualityPreset.LO_QUALITY);
QualityPresets.LO_QUALITY);
}
});
@ -151,7 +203,7 @@ public void actionPerformed(ActionEvent e)
setIconImageID(ImageLoader.SD_VIDEO_BUTTON);
CallManager.setVideoQualityPreset(
call.getCallPeers().next(), QualityPreset.SD_QUALITY);
call.getCallPeers().next(), QualityPresets.SD_QUALITY);
}
});
@ -164,7 +216,7 @@ public void actionPerformed(ActionEvent e)
setIconImageID(ImageLoader.HD_VIDEO_BUTTON);
CallManager.setVideoQualityPreset(
call.getCallPeers().next(), QualityPreset.HD_QUALITY);
call.getCallPeers().next(), QualityPresets.HD_QUALITY);
}
});
@ -181,4 +233,19 @@ private String getFormattedDimension(Dimension d)
{
return " (" + (int) d.getWidth() + "x" + (int) d.getHeight() + ")";
}
/**
* Check the available options that will be shown
* when button is pressed.
* @return the number of options.
*/
public int countAvailableOptions()
{
int count = 0;
if(loOptionPresent) count++;
if(sdOptionPresent) count++;
if(hdOptionPresent) count++;
return count;
}
}

@ -616,11 +616,17 @@ public static String createImageAttr(java.awt.Dimension sendSize,
if(sendSize != null)
{
/* single value => send [x=width,y=height] */
img.append("send [x=");
/*img.append("send [x=");
img.append((int)sendSize.getWidth());
img.append(",y=");
img.append((int)sendSize.getHeight());
img.append("]");
img.append("]");*/
/* send [x=[min-max],y=[min-max]] */
img.append("send [x=[0-");
img.append((int)sendSize.getWidth());
img.append("],y=[0-");
img.append((int)sendSize.getHeight());
img.append("]]");
/*
else
{

@ -22,6 +22,7 @@
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
@ -54,6 +55,11 @@ public class VideoMediaStreamImpl
*/
private boolean usePLI = false;
/**
* This stream quality control.
*/
private QualityControlsImpl qualityControls = new QualityControlsImpl();
/**
* Selects the <tt>VideoFormat</tt> from the list of supported formats of a
* specific video <tt>DataSource</tt> which has a size as close as possible
@ -584,6 +590,9 @@ else if(key.equals("imageattr"))
if(res != null)
{
outputSize = res[1];
qualityControls.setRemoteSendMaxPreset(new QualityPresets(res[0]));
qualityControls.setRemoteReceiveResolution(outputSize);
((VideoMediaDeviceSession)getDeviceSession()).
setOutputSize(outputSize);
}
@ -807,4 +816,136 @@ protected int getPriority()
{
return 5;
}
/**
* Returns the quality control for this stream.
* @return
*/
public QualityControls getQualityControls()
{
return qualityControls;
}
/**
* The quality control implementation used for
* this video stream quality control.
*/
private class QualityControlsImpl
implements QualityControls
{
/**
* The current used preset.
*/
private QualityPresets preset;
/**
* The minimum values for resolution, framerate ...
*/
private QualityPresets minPreset;
/**
* The maximum values for resolution, framerate ...
*/
private QualityPresets maxPreset;
/**
* This is the local settings from the config panel.
*/
private QualityPresets localSettingsPreset;
/**
* Sets the preset.
* @param preset the desired video settings
* @throws OperationFailedException
*/
private void setRemoteReceivePreset(QualityPresets preset)
throws
OperationFailedException
{
if(preset.compareTo(getPreferredSendPreset()) > 0)
this.preset = getPreferredSendPreset();
else
this.preset = preset;
}
/**
* The current preset.
* @return
*/
public QualityPresets getRemoteReceivePreset()
{
return preset;
}
/**
* The minimum resolution values for remote part.
* @return
*/
public QualityPresets getRemoteSendMinPreset()
{
return minPreset;
}
/**
* The max resolution values for remote part.
* @return
*/
public QualityPresets getRemoteSendMaxPreset()
{
return maxPreset;
}
/**
* Does nothing specific locally.
*
* @param preset the max preset
* @throws OperationFailedException not thrown.
*/
public void setPreferredRemoteSendMaxPreset(QualityPresets preset)
throws OperationFailedException
{
setRemoteSendMaxPreset(preset);
}
/**
* Changes remote send preset, the one we will receive.
* @param preset
*/
public void setRemoteSendMaxPreset(QualityPresets preset)
{
this.maxPreset = preset;
}
/**
* The local setting of capture.
* @return
*/
private QualityPresets getPreferredSendPreset()
{
if(localSettingsPreset == null)
{
DeviceConfiguration deviceConfiguration =
NeomediaActivator.getMediaServiceImpl()
.getDeviceConfiguration();
localSettingsPreset = new QualityPresets(
deviceConfiguration.getVideoSize(),
deviceConfiguration.getFrameRate());
}
return localSettingsPreset;
}
/**
* Sets maximum resolution.
* @param res
*/
public void setRemoteReceiveResolution(Dimension res)
{
try
{
this.setRemoteReceivePreset(new QualityPresets(res));
}
catch(OperationFailedException e){}
}
}
}

@ -415,11 +415,12 @@ public List<RTPExtension> getSupportedExtensions()
/**
* Gets a list of <tt>MediaFormat</tt>s supported by this
* <tt>MediaDevice</tt>. Currently does nothing.
* @param preset does nothing for audio.
* @param sendPreset does nothing for audio.
* @return the list of <tt>MediaFormat</tt>s supported by this device
* @see MediaDevice#getSupportedFormats()
*/
public List<MediaFormat> getSupportedFormats(QualityPreset preset)
public List<MediaFormat> getSupportedFormats(QualityPresets sendPreset,
QualityPresets receivePreset)
{
return device.getSupportedFormats();
}
@ -432,7 +433,7 @@ public List<MediaFormat> getSupportedFormats(QualityPreset preset)
*/
public List<MediaFormat> getSupportedFormats()
{
return this.getSupportedFormats(null);
return this.getSupportedFormats(null, null);
}
/**

@ -6,8 +6,10 @@
*/
package net.java.sip.communicator.impl.neomedia.device;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.control.*;
@ -323,17 +325,18 @@ public MediaType getMediaType()
*/
public List<MediaFormat> getSupportedFormats()
{
return this.getSupportedFormats(null);
return this.getSupportedFormats(null, null);
}
/**
* Gets a list of <tt>MediaFormat</tt>s supported by this
* <tt>MediaDevice</tt>.
* @param preset the preset used to set some of the format parameters,
* @param sendPreset the preset used to set some of the format parameters,
* used for video and settings.
* @return the list of <tt>MediaFormat</tt>s supported by this device
* @see MediaDevice#getSupportedFormats()
*/
public List<MediaFormat> getSupportedFormats(QualityPreset preset)
public List<MediaFormat> getSupportedFormats(QualityPresets sendPreset,
QualityPresets receivePreset)
{
EncodingConfiguration encodingConfiguration
= NeomediaActivator
@ -348,7 +351,6 @@ public List<MediaFormat> getSupportedFormats(QualityPreset preset)
// if there is preset check and set the format attributes
// where needed
if(preset != null)
{
MediaFormat customFormat = null;
MediaFormat toRemove = null;
@ -361,9 +363,37 @@ public List<MediaFormat> getSupportedFormats(QualityPreset preset)
if(h264AdvancedAttributes == null)
h264AdvancedAttributes = new HashMap<String, String>();
Dimension sendSize = null;
Dimension receiveSize;
// change send size only for video calls
if(!captureDeviceInfo.getLocator().getProtocol().equals(
ImageStreamingAuto.LOCATOR_PROTOCOL))
{
if(sendPreset != null)
sendSize = sendPreset.getResolution();
else
sendSize = NeomediaActivator.getMediaServiceImpl()
.getDeviceConfiguration().getVideoSize();
}
// if there is specified preset, send its settings
if(receivePreset != null)
receiveSize = receivePreset.getResolution();
else
{
// or just send the max video resolution of the PC
// as we do by default
ScreenDevice screen
= NeomediaActivator.getMediaServiceImpl()
.getDefaultScreenDevice();
receiveSize = (screen == null) ?
null : screen.getSize();
}
h264AdvancedAttributes.put("imageattr",
MediaUtils.createImageAttr(null,
preset.getResolution()));
MediaUtils.createImageAttr(sendSize,
receiveSize));
customFormat = NeomediaActivator.getMediaServiceImpl()
.getFormatFactory().createMediaFormat(

@ -35,11 +35,6 @@ public class VideoMediaFormatImpl
*/
private final double clockRate;
/**
* The frame rate of this <tt>VideoMediaFormat</tt>.
*/
private float frameRate;
/**
* Initializes a new <tt>VideoMediaFormatImpl</tt> instance with a specific
* encoding.
@ -117,10 +112,15 @@ public class VideoMediaFormatImpl
Map<String, String> formatParameters,
Map<String, String> advancedParameters)
{
super(format, formatParameters, advancedParameters);
super(new VideoFormat(
format.getEncoding(),
format.getSize(),
format.getMaxDataLength(),
format.getDataType(),
frameRate
), formatParameters, advancedParameters);
this.clockRate = clockRate;
this.frameRate = frameRate;
}
/**
@ -260,7 +260,7 @@ public double getClockRate()
*/
public float getFrameRate()
{
return frameRate;
return format.getFrameRate();
}
/**

@ -133,7 +133,7 @@ public class ProtocolProviderServiceIcqImpl
*/
public RegistrationState getRegistrationState()
{
if(getAimConnection() == null)
if(getAimConnection() == null || lastRegistrationState == null)
return RegistrationState.UNREGISTERED;
return lastRegistrationState;

@ -65,6 +65,17 @@ public class CallPeerMediaHandlerSipImpl
*/
private final TransportManagerSipImpl transportManager;
/**
* Whether other party is able to change video quality settings.
* Normally its whether we have detected existence of imageattr in sdp.
*/
boolean supportQualityControls;
/**
* The current quality controls for this peer media handler if any.
*/
private QualityControlsWrapper qualityControls;
/**
* Creates a new handler that will be managing media streams for
* <tt>peer</tt>.
@ -77,6 +88,7 @@ public CallPeerMediaHandlerSipImpl(CallPeerSipImpl peer)
super(peer, peer);
transportManager = new TransportManagerSipImpl(peer);
qualityControls = new QualityControlsWrapper(peer);
}
/**
@ -150,6 +162,19 @@ private Vector<MediaDescription> createMediaDescriptions()
//Audio Media Description
Vector<MediaDescription> mediaDescs = new Vector<MediaDescription>();
QualityPresets sendQualityPreset = null;
QualityPresets receiveQualityPreset = null;
if(qualityControls != null)
{
// the one we will send is the one the other part has announced
// as receive
sendQualityPreset = qualityControls.getRemoteReceivePreset();
// the one we want to receive is the setting that remote
// can send
receiveQualityPreset = qualityControls.getRemoteSendMaxPreset();
}
for (MediaType mediaType : MediaType.values())
{
MediaDevice dev = getDefaultDevice(mediaType);
@ -166,7 +191,9 @@ private Vector<MediaDescription> createMediaDescriptions()
{
MediaDescription md =
createMediaDescription(
dev.getSupportedFormats(videoQualityPreset),
dev.getSupportedFormats(
sendQualityPreset,
receiveQualityPreset),
getTransportManager().getStreamConnector(mediaType),
direction,
dev.getSupportedExtensions());
@ -175,10 +202,12 @@ private Vector<MediaDescription> createMediaDescriptions()
{
// if we have setting for video preset lets
// send info for the desired framerate
if(videoQualityPreset != null)
if(receiveQualityPreset != null
&& receiveQualityPreset.getFameRate() > 0)
md.setAttribute("framerate",
// doing only int frame rate for now
String.valueOf(
videoQualityPreset.getFameRate()));
(int)receiveQualityPreset.getFameRate()));
}
catch(SdpException e)
{
@ -451,6 +480,52 @@ private Vector<MediaDescription> createMediaDescriptionsForAnswer(
mutuallySupportedFormats.get(0));
initStream(connector, dev, fmt, target, direction, rtpExtensions);
// check for options from remote party and set them locally
if(mediaType.equals(MediaType.VIDEO))
{
QualityPresets sendQualityPreset = null;
QualityPresets receiveQualityPreset = null;
if(qualityControls != null)
{
// the one we will send is the other party receive
sendQualityPreset =
qualityControls.getRemoteReceivePreset();
// the one we want to receive
receiveQualityPreset =
qualityControls.getRemoteSendMaxPreset();
mutuallySupportedFormats
= (dev == null)
? null
: intersectFormats(
mutuallySupportedFormats,
dev.getSupportedFormats(
sendQualityPreset, receiveQualityPreset));
}
supportQualityControls =
SdpUtils.containsAttribute(mediaDescription, "imageattr");
float frameRate = -1;
// check for frame rate setting
try
{
String frStr = mediaDescription.getAttribute("framerate");
if(frStr != null)
frameRate = Integer.parseInt(frStr);
}
catch(SdpParseException e)
{
// do nothing
}
if(frameRate > 0)
{
qualityControls.setMaxFrameRate(frameRate);
}
}
MediaDescription md = createMediaDescription(
mutuallySupportedFormats, connector, direction, rtpExtensions);
@ -771,4 +846,32 @@ protected TransportManagerSipImpl getTransportManager()
{
return transportManager;
}
/**
* Returns the quality control for video calls if any.
* @return the implemented quality control.
*/
public QualityControls getQualityControls()
{
if(supportQualityControls)
{
return qualityControls;
}
else
{
// we have detected that its not supported and return null
// and control ui won't be visible
return null;
}
}
/**
* Sometimes as initing a call with custom preset can set and we force
* that quality controls is supported.
* @param value whether quality controls is supported..
*/
public void setSupportQualityControls(boolean value)
{
this.supportQualityControls = value;
}
}

@ -1356,29 +1356,4 @@ private void setDisconnectedState(boolean failed, String reason)
else
setState(CallPeerState.DISCONNECTED, reason);
}
/**
* Changes video quality preset.
* @param preset the preset to use.
*/
public void setVideoQualityPreset(QualityPreset preset)
throws OperationFailedException
{
CallPeerMediaHandlerSipImpl mediaHandler = getMediaHandler();
// set the new preset
mediaHandler.setVideoQualityPreset(preset);
try
{
// re-invites the peer with the new settings
sendReInvite(mediaHandler.createOffer());
}
catch (Exception ex)
{
ProtocolProviderServiceSipImpl.throwOperationFailedException(
"Failed to create SDP offer to hold.",
OperationFailedException.INTERNAL_ERROR, ex, logger);
}
}
}

@ -42,6 +42,12 @@ public class CallSipImpl
*/
private final SipMessageFactory messageFactory;
/**
* When starting call we may have quality preferences we must use
* for the call.
*/
private QualityPresets initialQualityPreferences;
/**
* Crates a CallSipImpl instance belonging to <tt>sourceProvider</tt> and
* initiated by <tt>CallCreator</tt>.
@ -174,6 +180,16 @@ public CallPeerSipImpl invite(Address calleeAddress,
callPeer.getMediaHandler().setLocalVideoTransmissionEnabled(
localVideoAllowed);
if(initialQualityPreferences != null)
{
// we are in situation where we init the call and we cannot
// determine whether the other party supports changing quality
// so we force it
callPeer.getMediaHandler().setSupportQualityControls(true);
callPeer.getMediaHandler().getQualityControls()
.setRemoteSendMaxPreset(initialQualityPreferences);
}
try
{
callPeer.invite();
@ -378,4 +394,13 @@ public CallPeerSipImpl processInvite(SipProvider jainSipProvider,
return peer;
}
/**
* Set a quality preferences we may use when we start the call.
* @param qualityPreferences the initial quality preferences.
*/
public void setInitialQualityPreferences(QualityPresets qualityPreferences)
{
this.initialQualityPreferences = qualityPreferences;
}
}

@ -98,11 +98,56 @@ public void setLocalVideoAllowed(Call call, boolean allowed)
*/
public Call createVideoCall(String uri)
throws OperationFailedException, ParseException
{
return createVideoCall(uri, null);
}
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param callee the address of the callee that we should invite to a new
* call.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(Contact callee)
throws OperationFailedException
{
return createVideoCall(callee, null);
}
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param uri the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
* @throws ParseException if <tt>callee</tt> is not a valid sip address
* string.
*/
public Call createVideoCall(String uri, QualityPresets qualityPreferences)
throws OperationFailedException, ParseException
{
Address toAddress = parentProvider.parseAddressString(uri);
CallSipImpl call = basicTelephony.createOutgoingCall();
call.setLocalVideoAllowed(true, getMediaUseCase());
call.setInitialQualityPreferences(qualityPreferences);
call.invite(toAddress, null);
return call;
@ -113,6 +158,10 @@ public Call createVideoCall(String uri)
*
* @param callee the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
@ -121,7 +170,9 @@ public Call createVideoCall(String uri)
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(Contact callee) throws OperationFailedException
public Call createVideoCall(Contact callee,
QualityPresets qualityPreferences)
throws OperationFailedException
{
Address toAddress;
@ -138,6 +189,7 @@ public Call createVideoCall(Contact callee) throws OperationFailedException
CallSipImpl call = basicTelephony.createOutgoingCall();
call.setLocalVideoAllowed(true, getMediaUseCase());
call.setInitialQualityPreferences(qualityPreferences);
call.invite(toAddress, null);
return call;
@ -162,19 +214,12 @@ public void answerVideoCallPeer(CallPeer peer)
}
/**
* Changes the current video settings for the peer with the desired
* quality settings and inform the peer to stream the video
* with those settings.
*
* @param peer the peer that is sending us the video
* @param preset the desired video settings
* @throws OperationFailedException
* Returns the quality control for video calls if any.
* @param peer the peer which this control operates on.
* @return the implemented quality control.
*/
public void setQualityPreset(CallPeer peer,
QualityPreset preset)
throws OperationFailedException
public QualityControls getQualityControls(CallPeer peer)
{
CallPeerSipImpl sipPeer = (CallPeerSipImpl) peer;
sipPeer.setVideoQualityPreset(preset);
return ((CallPeerSipImpl) peer).getMediaHandler().getQualityControls();
}
}

@ -0,0 +1,180 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.sip;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* A wrapper of media quality control.
* @author Damian Minkov
*/
public class QualityControlsWrapper
implements QualityControls
{
/**
* Our class logger.
*/
private static final Logger logger
= Logger.getLogger(QualityControlsWrapper.class);
/**
* The peer we are controlling.
*/
private CallPeerSipImpl peer;
/**
* The media quality control.
*/
private QualityControls qualityControls;
/**
* The currently used video quality preset.
*/
protected QualityPresets remoteSendMaxPreset = null;
/**
* The frame rate.
*/
private float maxFrameRate = -1;
/**
* Creates quality control for peer.
* @param peer
*/
QualityControlsWrapper(CallPeerSipImpl peer)
{
this.peer = peer;
}
/**
* Checks and obtains quality control from media stream.
* @return
*/
private QualityControls getMediaQualityControls()
{
if(qualityControls != null)
return qualityControls;
MediaStream stream = peer.getMediaHandler().getStream(MediaType.VIDEO);
if(stream != null && stream instanceof VideoMediaStream)
qualityControls = ((VideoMediaStream)stream).getQualityControls();
return qualityControls;
}
/**
* The currently used quality preset announced as receive by remote party.
* @return the current quality preset.
*/
public QualityPresets getRemoteReceivePreset()
{
QualityControls qControls = getMediaQualityControls();
if(qControls == null)
{
return null;
}
return qControls.getRemoteReceivePreset();
}
/**
* The minimum preset that the remote party is sending and we are receiving.
* Not Used.
* @return the minimum remote preset.
*/
public QualityPresets getRemoteSendMinPreset()
{
QualityControls qControls = getMediaQualityControls();
if(qControls == null)
return null;
return qControls.getRemoteSendMinPreset();
}
/**
* The maximum preset that the remote party is sending and we are receiving.
* @return the maximum preset announced from remote party as send.
*/
public QualityPresets getRemoteSendMaxPreset()
{
QualityControls qControls = getMediaQualityControls();
if(qControls == null)
return remoteSendMaxPreset;
QualityPresets qp = qControls.getRemoteSendMaxPreset();
// there is info about max frame rate
if(qp != null && maxFrameRate > 0)
qp = new QualityPresets(qp.getResolution(), (int)maxFrameRate);
return qp;
}
/**
* Changes local value of frame rate, the one we have received from
* remote party.
* @param f new frame rate.
*/
public void setMaxFrameRate(float f)
{
this.maxFrameRate = f;
}
/**
* Changes remote send preset. This doesn't have impact of current stream.
* But will have on next media changes.
* With this we can try to change the resolution that the remote part
* is sending.
* @param preset the new preset value.
*/
public void setRemoteSendMaxPreset(QualityPresets preset)
{
QualityControls qControls = getMediaQualityControls();
if(qControls != null)
qControls.setRemoteSendMaxPreset(preset);
else
remoteSendMaxPreset = preset;
}
/**
* Changes the current video settings for the peer with the desired
* quality settings and inform the peer to stream the video
* with those settings.
*
* @param preset the desired video settings
* @throws OperationFailedException
*/
public void setPreferredRemoteSendMaxPreset(QualityPresets preset)
throws OperationFailedException
{
QualityControls qControls = getMediaQualityControls();
if(qControls != null)
{
qControls.setRemoteSendMaxPreset(preset);
try
{
// re-invites the peer with the new settings
peer.sendReInvite();
}
catch (Throwable ex)
{
ProtocolProviderServiceSipImpl.throwOperationFailedException(
"Failed to re-invite for video quality change.",
OperationFailedException.INTERNAL_ERROR, ex, logger);
}
}
}
}

@ -372,7 +372,7 @@ public static List<MediaFormat> extractFormats(
{
String frStr = mediaDesc.getAttribute("framerate");
if(frStr != null)
frameRate = Integer.parseInt(frStr);
frameRate = Float.parseFloat(frStr);
}
catch(SdpParseException e)
{
@ -1531,6 +1531,42 @@ public static MediaType getMediaType(MediaDescription description)
}
}
/**
* Returns the media type (e.g. audio or video) for the specified media
* whether it contains the specified <tt>attributeName</tt>.
*
* @param description the <tt>MediaDescription</tt> whose media type we'd
* like to extract.
*
* @return the media type (e.g. audio or video) for the specified media
* <tt>description</tt>.
*
* @throws IllegalArgumentException if <tt>description</tt> does not
* contain a known media type.
*/
public static boolean containsAttribute(MediaDescription description,
String attributeName)
throws IllegalArgumentException
{
try
{
Vector<Attribute> atts = description.getAttributes(false);
for(Attribute a : atts)
if(a.getName().equals(attributeName))
return true;
return false;
}
catch (SdpException exc)
{
// impossible to happen for reasons mentioned many times here :)
if (logger.isDebugEnabled())
logger.debug("Invalid media type in a= line: " + description, exc);
throw new IllegalArgumentException(
"Invalid media type in a= line: " + description, exc);
}
}
/**
* Creates and returns a <tt>MediaDescription</tt> in answer of the
* specified <tt>offer</tt> that disables the corresponding stream by

@ -0,0 +1,53 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.neomedia;
import net.java.sip.communicator.service.protocol.*;
/**
* The quality controls we use to control other party video presets.
* @author Damian Minkov
*/
public interface QualityControls
{
/**
* The currently used quality preset announced as receive by remote party.
* @return the current quality preset.
*/
public QualityPresets getRemoteReceivePreset();
/**
* The minimum preset that the remote party is sending and we are receiving.
* @return the minimum remote preset.
*/
public QualityPresets getRemoteSendMinPreset();
/**
* The maximum preset that the remote party is sending and we are receiving.
* @return the maximum preset announced from remote party as send.
*/
public QualityPresets getRemoteSendMaxPreset();
/**
* Changes remote send preset. This doesn't have impact of current stream.
* But will have on next media changes.
* With this we can try to change the resolution that the remote part
* is sending.
* @param preset the new preset value.
*/
public void setRemoteSendMaxPreset(QualityPresets preset);
/**
* Changes remote send preset and protocols who can handle the changes
* will implement this for re-inviting the other party or just sending that
* media has changed.
* @param preset the new preset.
* @throws OperationFailedException
*/
public void setPreferredRemoteSendMaxPreset(QualityPresets preset)
throws OperationFailedException;
}

@ -1,76 +0,0 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.neomedia;
import java.awt.*;
/**
* Predefined Quality presets used to specify some video settings during
* call or when starting call.
* @author Damian Minkov
*/
public class QualityPreset
{
/**
* 720p HD
*/
public final static QualityPreset HD_QUALITY =
new QualityPreset(new Dimension(1280, 720), 30);
/**
* SD
*/
public final static QualityPreset SD_QUALITY =
new QualityPreset(new Dimension(640, 480), 20);
/**
* Low
*/
public final static QualityPreset LO_QUALITY =
new QualityPreset(new Dimension(320, 240), 15);
/**
* The frame rate to use.
*/
private int frameRate;
/**
* The resolution to use.
*/
private Dimension resolution;
/**
* Creates preset with <tt>resolution</tt> and <tt>frameRate</tt>.
* Predefined presets can be created only here.
*
* @param resolution the resolution.
* @param frameRate the frame rate.
*/
private QualityPreset(Dimension resolution, int frameRate)
{
this.frameRate = frameRate;
this.resolution = resolution;
}
/**
* Returns this preset frame rate.
* @return the frame rate.
*/
public int getFameRate()
{
return this.frameRate;
}
/**
* Returns this preset resolution.
* @return the resolution.
*/
public Dimension getResolution()
{
return this.resolution;
}
}

@ -0,0 +1,106 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.neomedia;
import java.awt.*;
/**
* Predefined Quality presets used to specify some video settings during
* call or when starting call.
* @author Damian Minkov
*/
public class QualityPresets
implements Comparable<QualityPresets>
{
/**
* 720p HD
*/
public final static QualityPresets HD_QUALITY =
new QualityPresets(new Dimension(1280, 720), 30);
/**
* SD
*/
public final static QualityPresets SD_QUALITY =
new QualityPresets(new Dimension(640, 480), 20);
/**
* Low
*/
public final static QualityPresets LO_QUALITY =
new QualityPresets(new Dimension(320, 240), 15);
/**
* The frame rate to use.
*/
private float frameRate;
/**
* The resolution to use.
*/
private Dimension resolution;
/**
* Creates preset with <tt>resolution</tt> and <tt>frameRate</tt>.
* Predefined presets can be created only here.
*
* @param resolution the resolution.
* @param frameRate the frame rate.
*/
public QualityPresets(Dimension resolution, float frameRate)
{
this.frameRate = frameRate;
this.resolution = resolution;
}
/**
* Creates preset with <tt>resolution</tt> and <tt>frameRate</tt>.
* Predefined presets can be created only here.
*
* @param resolution the resolution.
*/
public QualityPresets(Dimension resolution)
{
// unspecified frame rate
this(resolution, -1);
}
/**
* Returns this preset frame rate.
* @return the frame rate.
*/
public float getFameRate()
{
return this.frameRate;
}
/**
* Returns this preset resolution.
* @return the resolution.
*/
public Dimension getResolution()
{
return this.resolution;
}
/**
* Compares to presets and its dimensions.
* @param o object to compare to.
* @return a negative integer, zero, or a positive integer as this object is
* less than, equal to, or greater than the specified object.
*/
public int compareTo(QualityPresets o)
{
if(this.resolution.equals(o.resolution))
return 0;
else if(this.resolution.height < o.resolution.height &&
this.resolution.width < o.resolution.width)
return -1;
else
return 1;
}
}

@ -73,4 +73,10 @@ public interface VideoMediaStream
* <tt>VideoMediaStream</tt>
*/
public void removeVideoListener(VideoListener listener);
/**
* The quality control that can be used with this stream.
* @return the quality control.
*/
public QualityControls getQualityControls();
}

@ -32,11 +32,15 @@ public interface MediaDevice
* Returns a list of <tt>MediaFormat</tt> instances representing the media
* formats supported by this <tt>MediaDevice</tt>.
*
* @param preset the preset used to set some of the format parameters,
* @param localPreset the preset used to set the send format parameters,
* used for video and settings.
* @param remotePreset the preset used to set the receive format parameters,
* used for video and settings.
*
* @return the list of <tt>MediaFormat</tt>s supported by this device.
*/
public List<MediaFormat> getSupportedFormats(QualityPreset preset);
public List<MediaFormat> getSupportedFormats(QualityPresets localPreset,
QualityPresets remotePreset);
/**
* Returns the <tt>List</tt> of <tt>RTPExtension</tt>s that this device

@ -215,6 +215,49 @@ public Call createVideoCall(String uri)
public Call createVideoCall(Contact callee)
throws OperationFailedException;
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param uri the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
* @throws ParseException if <tt>callee</tt> is not a valid sip address
* string.
*/
public Call createVideoCall(String uri, QualityPresets qualityPreferences)
throws OperationFailedException, ParseException;
/**
* Create a new video call and invite the specified CallPeer to it.
*
* @param callee the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(Contact callee,
QualityPresets qualityPreferences)
throws OperationFailedException;
/**
* Indicates a user request to answer an incoming call with video enabled
* from the specified CallPeer.
@ -226,15 +269,10 @@ public void answerVideoCallPeer(CallPeer peer)
throws OperationFailedException;
/**
* Changes the current video settings for the peer with the desired
* quality settings and inform the peer to stream the video
* with those settings.
*
* @param peer the peer that is sending us the video
* @param preset the desired video settings
* @throws OperationFailedException
* Returns the quality control for video calls if any. It can be null if we
* were able to successfully determine that other party does not support it.
* @param peer the peer which this control operates on.
* @return the implemented quality control.
*/
public void setQualityPreset(CallPeer peer,
QualityPreset preset)
throws OperationFailedException;
public QualityControls getQualityControls(CallPeer peer);
}

@ -8,6 +8,7 @@
import java.awt.*;
import java.beans.*;
import java.text.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
@ -260,18 +261,67 @@ public MediaUseCase getMediaUseCase()
}
/**
* Changes the current video settings for the peer with the desired
* quality settings and inform the peer to stream the video
* with those settings.
* Returns the quality control for video calls if any.
* Return null so protocols who supports it to override it.
* @param peer the peer which this control operates on.
* @return the implemented quality control.
*/
public QualityControls getQualityControls(CallPeer peer)
{
return null;
}
/**
* Create a new video call and invite the specified CallPeer to it with
* initial video setting.
*
* @param peer the peer that is sending us the video
* @param preset the desired video settings
* @throws OperationFailedException
* @param uri the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
* @throws java.text.ParseException if <tt>callee</tt> is not a valid sip address
* string.
*/
public void setQualityPreset(CallPeer peer,
QualityPreset preset)
public Call createVideoCall(String uri, QualityPresets qualityPreferences)
throws OperationFailedException,
ParseException
{
return createVideoCall(uri);
}
/**
* Create a new video call and invite the specified CallPeer to it with
* initial video setting.
*
* @param callee the address of the callee that we should invite to a new
* call.
* @param qualityPreferences the quality preset we will use establishing
* the video call, and we will expect from the other side. When establishing
* call we don't have any indications whether remote part supports quality
* presets, so this setting can be ignored.
* @return CallPeer the CallPeer that will represented by the
* specified uri. All following state change events will be delivered
* through that call peer. The Call that this peer is a member
* of could be retrieved from the CallParticipatn instance with the use
* of the corresponding method.
* @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
public Call createVideoCall(Contact callee,
QualityPresets qualityPreferences)
throws OperationFailedException
{}
{
return createVideoCall(callee);
}
/**
* Represents a <tt>VideoListener</tt> which forwards notifications to a

@ -203,11 +203,6 @@ public abstract class CallPeerMediaHandler<
private final List<VideoListener> videoListeners
= new LinkedList<VideoListener>();
/**
* The currently used video quality preset.
*/
protected QualityPreset videoQualityPreset = null;
/**
* The <tt>PropertyChangeListener</tt> which listens to changes in the
* values of the properties of {@link #audioStream} and
@ -1611,15 +1606,6 @@ public T getPeer()
return peer;
}
/**
* Changes video quality preset.
* @param preset the preset to use.
*/
public void setVideoQualityPreset(QualityPreset preset)
{
this.videoQualityPreset = preset;
}
/**
* Lets the underlying implementation take note of this error and only
* then throws it to the using bundles.

Loading…
Cancel
Save