/*
* 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.media.codec;
import java.io.*;
import java.util.*;
import javax.media.*;
import javax.sdp.*;
import net.java.sip.communicator.impl.media.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.util.*;
/**
* Simple configuration of encoding priorities.
*
* @author Damian Minkov
*/
public class EncodingConfiguration
{
private final Logger logger = Logger.getLogger(EncodingConfiguration.class);
private static final String PROP_SDP_PREFERENCE =
"net.java.sip.communicator.impl.media.sdppref";
/**
* SDP Codes of all video formats that JMF supports.
*/
private final String[] availableVideoEncodings = new String[]
{ Integer.toString(Constants.H264_RTP_SDP),
// javax.media.format.VideoFormat.H263_RTP
Integer.toString(SdpConstants.H263),
// javax.media.format.VideoFormat.JPEG_RTP
Integer.toString(SdpConstants.JPEG),
// javax.media.format.VideoFormat.H261_RTP
Integer.toString(SdpConstants.H261) };
/**
* SDP Codes of all audio formats that JMF supports.
*/
private final String[] availableAudioEncodings = new String[]
{
// ILBC
Integer.toString(97),
// javax.media.format.AudioFormat.G723_RTP
Integer.toString(SdpConstants.G723),
// javax.media.format.AudioFormat.GSM_RTP;
Integer.toString(SdpConstants.GSM),
// javax.media.format.AudioFormat.ULAW_RTP;
Integer.toString(SdpConstants.PCMU),
// javax.media.format.AudioFormat.DVI_RTP;
Integer.toString(SdpConstants.DVI4_8000),
// javax.media.format.AudioFormat.DVI_RTP;
Integer.toString(SdpConstants.DVI4_16000),
// javax.media.format.AudioFormat.ALAW;
Integer.toString(SdpConstants.PCMA), Integer.toString(110),
// javax.media.format.AudioFormat.G728_RTP;
Integer.toString(SdpConstants.G728)
// javax.media.format.AudioFormat.G729_RTP
// g729 is not suppported by JMF
// Integer.toString(SdpConstants.G729)
};
private final Set<String> supportedVideoEncodings =
new TreeSet<String>(new EncodingComparator());
private final Set<String> supportedAudioEncodings =
new TreeSet<String>(new EncodingComparator());
/**
* That's where we keep format preferences matching SDP formats to integers.
* We keep preferences for both audio and video formats here in case we'd
* ever need to compare them to one another. In most cases however both
* would be decorelated and other components (such as the UI) should present
* them separately.
*/
private final Map<String, Integer> encodingPreferences =
new Hashtable<String, Integer>();
private static final String[] customCodecs =
new String[]
{
FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.alaw.Encoder"
: "net.java.sip.communicator.impl.media.codec.audio.alaw.JavaEncoder",
FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.alaw.DePacketizer"
: "net.java.sip.communicator.impl.media.codec.audio.alaw.DePacketizer",
FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.alaw.Packetizer"
: "net.java.sip.communicator.impl.media.codec.audio.alaw.Packetizer",
FMJConditionals.FMJ_CODECS ? "net.sf.fmj.media.codec.audio.ulaw.Packetizer"
: "net.java.sip.communicator.impl.media.codec.audio.ulaw.Packetizer",
"net.java.sip.communicator.impl.media.codec.video.h264.JNIEncoder",
"net.java.sip.communicator.impl.media.codec.video.h264.Packetizer",
"net.java.sip.communicator.impl.media.codec.video.h264.JNIDecoder",
"net.java.sip.communicator.impl.media.codec.video.ImageScaler",
"net.java.sip.communicator.impl.media.codec.audio.speex.JavaEncoder",
"net.java.sip.communicator.impl.media.codec.audio.speex.JavaDecoder",
"net.java.sip.communicator.impl.media.codec.audio.ilbc.JavaEncoder",
"net.java.sip.communicator.impl.media.codec.audio.ilbc.JavaDecoder" };
/**
* Custom Packages provided by Sip-Communicator
*/
private static final String[] customPackages = new String[]
{ // datasource for low latency ALSA input
"net.java.sip.communicator.impl", "net.sf.fmj" };
/**
* Default constructor.
*/
public EncodingConfiguration()
{
}
/**
* Retrieves (from the configuration service) preferences specified for
* various formats and assigns default ones to those that haven't been
* mentioned.
*/
public void initializeFormatPreferences()
{
// first init default preferences
// video
setEncodingPreference(Constants.H264_RTP_SDP, 1100);
setEncodingPreference(SdpConstants.H263, 1000);
setEncodingPreference(SdpConstants.JPEG, 950);
setEncodingPreference(SdpConstants.H261, 800);
// audio
setEncodingPreference(SdpConstants.PCMU, 650);
setEncodingPreference(SdpConstants.PCMA, 600);
setEncodingPreference(97, 500);
setEncodingPreference(SdpConstants.GSM, 450);
setEncodingPreference(110, 350);
setEncodingPreference(SdpConstants.DVI4_8000, 300);
setEncodingPreference(SdpConstants.DVI4_16000, 250);
setEncodingPreference(SdpConstants.G723, 150);
setEncodingPreference(SdpConstants.G728, 100);
// now override with those that are specified by the user.
ConfigurationService confService =
MediaActivator.getConfigurationService();
List<String> sdpPreferences =
confService.getPropertyNamesByPrefix(PROP_SDP_PREFERENCE, false);
for (String pName : sdpPreferences)
{
String prefStr = confService.getString(pName);
String fmtName =
pName.substring(pName.lastIndexOf('.') + 1).replaceAll("sdp",
"");
int preference = -1;
int fmt = -1;
try
{
preference = Integer.parseInt(prefStr);
fmt = Integer.parseInt(fmtName);
}
catch (NumberFormatException exc)
{
logger.warn("Failed to parse format (" + fmtName
+ ") or preference(" + prefStr + ").", exc);
continue;
}
setEncodingPreference(fmt, preference);
}
// now update the arrays so that they are returned by order of
// preference.
updateSupportedEncodings();
}
/**
* Updates the codecs in the supported sets according preferences in
* encodingPreferences. If value is "0" the codec is disabled.
*/
private void updateSupportedEncodings()
{
for (String ac : availableAudioEncodings)
{
Integer pref1 = encodingPreferences.get(ac);
int pref1IntValue = (pref1 == null) ? 0 : pref1;
if (pref1IntValue > 0)
supportedAudioEncodings.add(ac);
else
supportedAudioEncodings.remove(ac);
}
for (String ac : availableVideoEncodings)
{
Integer pref1 = encodingPreferences.get(ac);
int pref1IntValue = (pref1 == null) ? 0 : pref1;
if (pref1IntValue > 0)
supportedVideoEncodings.add(ac);
else
supportedVideoEncodings.remove(ac);
}
}
/**
* Updates the codecs in the set according preferences in
* encodingPreferences. If value is "0" the codec is disabled.
*/
public String[] updateEncodings(List<String> encs)
{
Set<String> result = new TreeSet<String>(new EncodingComparator());
for (String c : encs)
{
Integer pref1 = encodingPreferences.get(c);
int pref1IntValue = (pref1 == null) ? 0 : pref1;
if (pref1IntValue > 0)
result.add(c);
}
return result.toArray(new String[result.size()]);
}
/**
* Sets <tt>pref</tt> as the preference associated with <tt>encoding</tt>.
* Use this method for both audio and video encodings and don't worry if
* preferences are equal since we rarely need to compare prefs of video
* encodings to those of audio encodings.
*
* @param encoding the SDP int of the encoding whose pref we're setting.
* @param pref a positive int indicating the preference for that encoding.
*/
private void setEncodingPreference(int encoding, int pref)
{
this.encodingPreferences.put(Integer.toString(encoding), pref);
}
/**
* Sets <tt>pref</tt> as the preference associated with <tt>encoding</tt>.
* Use this method for both audio and video encodings and don't worry if
* preferences are equal since we rarely need to compare prefs of video
* encodings to those of audio encodings.
*
* @param encoding a string containing the SDP int of the encoding whose
* pref we're setting.
* @param priority a positive int indicating the preference for that encoding.
*/
public void setPriority(String encoding, int priority)
{
this.encodingPreferences.put(encoding, priority);
// save the settings
MediaActivator.getConfigurationService().setProperty(
PROP_SDP_PREFERENCE + ".sdp" + encoding, priority);
updateSupportedEncodings();
}
public int getPriority(String encoding)
{
return encodingPreferences.get(encoding);
}
/**
* Register in JMF the custom codecs we provide
*/
public void registerCustomCodecs()
{
// use a set to check if the codecs are already
// registered in jmf.properties
Set<String> registeredPlugins = new HashSet<String>();
registeredPlugins.addAll(PlugInManager.getPlugInList(null, null,
PlugInManager.CODEC));
for (String className : customCodecs)
{
if (registeredPlugins.contains(className))
{
logger.debug("Codec : " + className + " is already registered");
}
else
{
try
{
Object instance = Class.forName(className).newInstance();
boolean result =
PlugInManager.addPlugIn(className, ((Codec) instance)
.getSupportedInputFormats(), ((Codec) instance)
.getSupportedOutputFormats(null),
PlugInManager.CODEC);
logger.debug("Codec : " + className
+ " is successfully registered : " + result);
}
catch (Throwable ex)
{
logger.debug("Codec : " + className
+ " is NOT succsefully registered", ex);
}
}
}
try
{
PlugInManager.commit();
}
catch (IOException ex)
{
logger.error("Cannot commit to PlugInManager", ex);
}
// Register the custom codec formats with the RTP manager once at
// initialization. This is needed for the Sun JMF implementation. It
// causes the registration of the formats with the static FormatInfo
// instance of com.sun.media.rtp.RTPSessionMgr, which in turn makes the
// formats available when the supported encodings arrays are generated
// in initProcessor(). In other JMF implementations this might not be
// needed, but should do no harm.
// Commented as it fails to load alaw codec
// RTPManager rtpManager = RTPManager.newInstance();
// CallSessionImpl.registerCustomCodecFormats(rtpManager);
// rtpManager.dispose();
}
/**
* Register in JMF the custom packages we provide
*/
public void registerCustomPackages()
{
Vector<String> currentPackagePrefix =
PackageManager.getProtocolPrefixList();
for (String className : customPackages)
{
// linear search in a loop, but it doesn't have to scale since the
// list is always short
if (!currentPackagePrefix.contains(className))
{
currentPackagePrefix.add(className);
logger.debug("Adding package : " + className);
}
}
PackageManager.setProtocolPrefixList(currentPackagePrefix);
PackageManager.commitProtocolPrefixList();
logger.debug("Registering new protocol prefix list : "
+ currentPackagePrefix);
}
public String[] getAvailableVideoEncodings()
{
return availableVideoEncodings;
}
public String[] getAvailableAudioEncodings()
{
return availableAudioEncodings;
}
public String[] getSupportedVideoEncodings()
{
return supportedVideoEncodings
.toArray(new String[supportedVideoEncodings.size()]);
}
public String[] getSupportedAudioEncodings()
{
return supportedAudioEncodings
.toArray(new String[supportedAudioEncodings.size()]);
}
/**
* Compares the two formats for order. Returns a negative integer, zero, or
* a positive integer as the first format has been assigned a preference
* higher, equal to, or greater than the one of the second.
* <p>
*
* @param enc1 the first format to compare for preference.
* @param enc2 the second format to compare for preference.
*
* @return a negative integer, zero, or a positive integer as the first
* format has been assigned a preference higher, equal to, or
* greater than the one of the second.
*/
private int compareEncodingPreferences(String enc1, String enc2)
{
Integer pref1 = encodingPreferences.get(enc1);
int pref1IntValue = (pref1 == null) ? 0 : pref1;
Integer pref2 = encodingPreferences.get(enc2);
int pref2IntValue = (pref2 == null) ? 0 : pref2;
return pref2IntValue - pref1IntValue;
}
/**
* Comaparator sorting the sets according the settings in
* encodingPreferences.
*/
private class EncodingComparator
implements Comparator<String>
{
public int compare(String s1, String s2)
{
return compareEncodingPreferences(s1, s2);
}
}
}