/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * Large portions of this software are based upon public domain software * https://sip-communicator.dev.java.net/ * */ package net.sourceforge.gjtapi.raw.sipprovider.media; import java.io.IOException; import java.io.Serializable; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Vector; import javax.media.CaptureDeviceInfo; import javax.media.DataSink; import javax.media.Format; import javax.media.IncompatibleSourceException; import javax.media.Manager; import javax.media.MediaLocator; import javax.media.NoDataSinkException; import javax.media.NoDataSourceException; import javax.media.NoProcessorException; import javax.media.Player; import javax.media.Processor; import javax.media.control.TrackControl; import javax.media.format.AudioFormat; import javax.media.format.VideoFormat; import javax.media.protocol.ContentDescriptor; import javax.media.protocol.DataSource; import javax.media.rtp.RTPManager; import javax.media.rtp.SessionAddress; import javax.sdp.Connection; import javax.sdp.Media; import javax.sdp.MediaDescription; import javax.sdp.Origin; import javax.sdp.SdpConstants; import javax.sdp.SdpException; import javax.sdp.SdpFactory; import javax.sdp.SdpParseException; import javax.sdp.SessionDescription; import javax.sdp.SessionName; import javax.sdp.TimeDescription; import javax.sdp.Version; import net.sourceforge.gjtapi.raw.sipprovider.common.Console; import net.sourceforge.gjtapi.raw.sipprovider.common.NetworkAddressManager; import net.sourceforge.gjtapi.raw.sipprovider.media.event.MediaErrorEvent; import net.sourceforge.gjtapi.raw.sipprovider.media.event.MediaEvent; import net.sourceforge.gjtapi.raw.sipprovider.media.event.MediaListener; /** * <p>Title: SIP COMMUNICATOR</p> * <p>Description:JAIN-SIP Audio/Video phone application</p> * <p>Copyright: Copyright (c) 2003</p> * <p>Organisation: LSIIT laboratory (http://lsiit.u-strasbg.fr) </p> * <p>Network Research Team (http://www-r2.u-strasbg.fr))</p> * <p>Louis Pasteur University - Strasbourg - France</p> * <p>Division Chief: Thomas Noel </p> * @author Emil Ivov (http://www.emcho.com) * @version 1.1 * */ public class MediaManager implements Serializable { static final long serialVersionUID = 0L; // never serialized protected static Console console = Console.getConsole(MediaManager.class); protected Collection<MediaListener> listeners = new java.util.ArrayList<MediaListener>(); protected SdpFactory sdpFactory; protected ProcessorUtility procUtility = new ProcessorUtility("MediaManager"); //media devices protected CaptureDeviceInfo audioDevice; //Sdp Codes of all formats supported for //transmission by the selected datasource protected ArrayList transmittableAudioFormats = new ArrayList(); //Sdp Codes of all formats that we can receive //i.e. all formats supported by JMF protected String[] receivableVideoFormats = new String[] { //sdp format // corresponding JMF Format Integer.toString(SdpConstants. H263), // javax.media.format.VideoFormat.H263_RTP Integer.toString(SdpConstants. JPEG), // javax.media.format.VideoFormat.JPEG_RTP Integer.toString(SdpConstants. H261) // javax.media.format.VideoFormat.H261_RTP }; protected String[] receivableAudioFormats = new String[] { //sdp format Integer.toString(SdpConstants. GSM), // javax.media.format.AudioFormat.GSM_RTP;// corresponding JMF Format Integer.toString(SdpConstants. G723), // javax.media.format.AudioFormat.G723_RTP Integer.toString(SdpConstants. PCMU), // javax.media.format.AudioFormat.ULAW_RTP; Integer.toString(SdpConstants. DVI4_8000), // javax.media.format.AudioFormat.DVI_RTP; Integer.toString(SdpConstants. DVI4_16000), // javax.media.format.AudioFormat.DVI_RTP; Integer.toString(SdpConstants. PCMA), // javax.media.format.AudioFormat.ALAW; Integer.toString(SdpConstants. G728), //, // javax.media.format.AudioFormat.G728_RTP; //g729 is not suppported by JMF Integer.toString(SdpConstants. G729) // javax.media.format.AudioFormat.G729_RTP }; /** * A list of currently active RTPManagers mapped against Local session addresses. * The list is used by transmitters and receivers so that receiving and transmitting * from the same port simultaneousl is possible */ protected Map activeRtpManagers = new Hashtable(); protected Map sessions = new Hashtable(); protected RTPManager rtpManager; private final String mediaSource; /** The data source. */ protected DataSource source; /** The processor to use for sending the data. */ protected Processor sndProcessor; /** The processor to use for receiving the data. */ protected Processor rcvProcessor; protected boolean isStarted = false; protected Properties sipProp; private final int audioPort; protected Collection<AVTransmitter> transmitters = new java.util.ArrayList<AVTransmitter>(); protected Collection<AVReceiver> receivers = new java.util.ArrayList<AVReceiver>(); /** The data sink. */ protected DataSink sink; /** The data sink. */ private MediaLocator dest; /** Reference to the address manager. */ private final NetworkAddressManager addressManager; public MediaManager(Properties sipProp, NetworkAddressManager manager) { String port = sipProp.getProperty( "net.java.sip.communicator.media.AUDIO_PORT"); audioPort = Integer.parseInt(port); console.debug("using audio port " + audioPort); mediaSource = sipProp.getProperty( "net.java.sip.communicator.media.MEDIA_SOURCE"); console.debug("using media source '" + mediaSource + "'"); this.sipProp = new Properties(); this.sipProp.putAll(sipProp); addressManager = manager; } /** * Reads the audio from the given URL and publishes it to all transmitters. * @param url the URL pointing to an audio data source * @throws MediaException * Error playing the audio. */ public void play(final String url) throws MediaException { console.logEntry(); if (console.isDebugEnabled()) { console.debug("playing '" + url + "'..."); } final MediaLocator locator = new MediaLocator(url); try { source = createDataSource(locator); } catch (IOException e) { throw new MediaException(e.getMessage(), e); } initSndProcessor(source); for (AVTransmitter transmitter : transmitters) { if (!transmitter.isStarted()) { console.debug("Starting transmission."); transmitter.start(sndProcessor); } transmitter.play(sndProcessor); } console.logExit(); } /** * Stops all transmitters. */ public void stopPlaying() { console.logEntry(); for (AVTransmitter transmitter : transmitters) { try { transmitter.stopPlaying(); } catch (IOException ex) { console.warn(ex.toString(), ex); } } console.logExit(); } public void record(String url) throws MediaException { console.logEntry(); try { // DataSource mergeDs = this.getDataSource(); // append "file:/" to URL if it is not already there String fullUrl = (url.indexOf("file:") == 0) ? url : "file:/" + url; dest = new MediaLocator(fullUrl); final DataSource ds = createDataSource(dest); initRcvProcessor(ds); sink = Manager.createDataSink(ds, dest); sink.open(); sink.start(); for (AVReceiver receiver : receivers) { final Processor pro = receiver.getProcessor(); pro.start(); } } catch (IOException ex) { throw new MediaException(ex.getMessage(), ex); } catch (NoDataSinkException ex) { throw new MediaException(ex.getMessage(), ex); } console.logExit(); } /** * Gets the datasource for a Sip session. * @return */ public DataSource getDataSource() throws IncompatibleSourceException, IOException { DataSource dsTab[] = new DataSource[receivers.size()]; DataSource ds = null; int i = 0; for (AVReceiver receiver : receivers) { Processor pro = receiver.getProcessor(); ds = pro.getDataOutput(); dsTab[i] = ds; ++i; } DataSource mergeDs = Manager.createMergingDataSource(dsTab); mergeDs.connect(); mergeDs.start(); return mergeDs; } public void stopRecording() { try { sink.stop(); sink.close(); } catch (Exception ex) { console.debug(ex.toString()); } } public void start() throws MediaException { try { console.logEntry(); sdpFactory = SdpFactory.getInstance(); isStarted = true; } catch (Throwable ex) { // also handles SdpException, which is thrown by some SIP implementations // (such as older nist-sdp-1.0) and not by others (such as later versions of nist-sdp-1.0 -- not sure why the version number didn't change) throw new MediaException(ex); } finally { console.logExit(); } } /** * Creates the {@link DataSource} for the given {@link MediaLocator}. * @param locator the medi loactor * @return created data source. * @exception IOException * error creating the data source */ protected DataSource createDataSource(MediaLocator locator) throws IOException { try { console.logEntry(); try { if (console.isDebugEnabled()) { console.debug("Creating datasource for:" + locator != null ? locator.toExternalForm() : "null"); } return Manager.createDataSource(locator); } catch (NoDataSourceException ex) { throw new IOException("Error creating the data source", ex); } } finally { console.logExit(); } } public void openMediaStreams(String sdpData) throws MediaException { try { console.logEntry(); if (console.isDebugEnabled()) { console.debug("sdpData arg - " + sdpData); } checkIfStarted(); final SessionDescription sessionDescription; if (sdpData == null) { console.error("The SDP data was null! Cannot open " + "a stream withour an SDP Description!"); throw new MediaException( "The SDP data was null! Cannot open " + "a stream withour an SDP Description!"); } try { sessionDescription = sdpFactory.createSessionDescription( sdpData); } catch (SdpParseException ex) { console.error("Incorrect SDP data!", ex); throw new MediaException("Incorrect SDP data!", ex); } final Collection<MediaDescription> mediaDescriptions; try { mediaDescriptions = sessionDescription. getMediaDescriptions(true); } catch (SdpException ex) { console.error( "Failed to extract media descriptions from provided session description!", ex); throw new MediaException( "Failed to extract media descriptions from provided session description!", ex); } final Connection connection = sessionDescription.getConnection(); if (connection == null) { console.error( "A connection parameter was not present in provided session description"); throw new MediaException( "A connection parameter was not present in provided session description"); } final String remoteAddress; try { remoteAddress = connection.getAddress(); } catch (SdpParseException ex) { console.error( "Failed to extract the connection address parameter" + "from privided session description", ex); throw new MediaException( "Failed to extract the connection address parameter" + "from privided session description", ex); } int mediaPort = -1; boolean atLeastOneTransmitterStarted = false; List<Integer> ports = new java.util.ArrayList<Integer>(); ArrayList formatSets = new ArrayList(); for (MediaDescription mediaDescription : mediaDescriptions) { final Media media = mediaDescription.getMedia(); //Media Type final String mediaType; try { mediaType = media.getMediaType(); } catch (SdpParseException ex) { console.error( "Failed to extract the media type for one of the provided media descriptions!\n" + "Ignoring description!", ex); fireNonFatalMediaError(new MediaException( "Failed to extract the media type for one of the provided media descriptions!\n" + "Ignoring description!", ex )); continue; } //Find ports try { mediaPort = media.getMediaPort(); } catch (SdpParseException ex) { console.error("Failed to extract port for media type [" + mediaType + "]. Ignoring description!", ex); fireNonFatalMediaError(new MediaException( "Failed to extract port for media type [" + mediaType + "]. Ignoring description!", ex )); continue; } //Find formats Collection<String> sdpFormats; try { sdpFormats = media.getMediaFormats(true); } catch (SdpParseException ex) { console.error( "Failed to extract media formats for media type [" + mediaType + "]. Ignoring description!", ex); fireNonFatalMediaError(new MediaException( "Failed to extract media formats for media type [" + mediaType + "]. Ignoring description!", ex )); continue; } //START TRANSMISSION try { if (isMediaTransmittable(mediaType)) { ports.add(new Integer(mediaPort)); formatSets.add(extractTransmittableJmfFormats( sdpFormats)); } else { //nothing to transmit here so skip setting the flag //bug report and fix - Gary M. Levin - Telecordia continue; } } catch (MediaException ex) { console.error( "Could not start a transmitter for media type [" + mediaType + "]\nIgnoring media [" + mediaType + "]!", ex ); fireNonFatalMediaError(new MediaException( "Could not start a transmitter for media type [" + mediaType + "]\nIgnoring media [" + mediaType + "]!", ex )); continue; } atLeastOneTransmitterStarted = true; } //startReceiver(remoteAddress); //open corrects ports for RTP Session createReceiver(remoteAddress, ports); softStartReceiver(); if (!atLeastOneTransmitterStarted) { console.error( "Apparently all media descriptions failed to initialise!\n" + "SIP COMMUNICATOR won't be able to open a media stream!"); throw new MediaException( "Apparently all media descriptions failed to initialise!\n" + "SIP COMMUNICATOR won't be able to open a media stream!"); } else { createTransmitter(remoteAddress, ports, formatSets); } } finally { console.logExit(); } } protected void closeProcessor() { try { console.logEntry(); if (sndProcessor != null) { sndProcessor.stop(); sndProcessor.close(); sndProcessor = null; } if (source != null) { source.disconnect(); source = null; } if (rcvProcessor != null) { rcvProcessor.stop(); rcvProcessor.close(); rcvProcessor = null; } if (sink != null) { sink.close(); sink = null; } } finally { console.logExit(); } } public void stop() throws MediaException { try { console.logEntry(); // closeStreams(); closeProcessor(); } finally { console.logExit(); } } public void closeStreams(String sdpData) throws MediaException { SessionDescription sessionDescription = null; int mediaPort = -1; //remote port String remoteAddress = null; //remote address try { sessionDescription = sdpFactory.createSessionDescription(sdpData); } catch (SdpParseException ex) { console.error("Incorrect SDP data!", ex); } Vector mediaDescriptions = null; try { mediaDescriptions = sessionDescription.getMediaDescriptions(true); } catch (SdpException ex) { console.error( "Failed to extract media descriptions from provided session description!", ex); } final Connection connection = sessionDescription.getConnection(); if (connection == null) { console.error( "A connection parameter was not present in provided session description"); throw new MediaException( "A connection parameter was not present in provided session description"); } try { remoteAddress = connection.getAddress(); } catch (SdpParseException ex) { console.error( "Failed to extract the connection address parameter" + "from privided session description", ex); throw new MediaException( "Failed to extract the connection address parameter" + "from privided session description", ex); } //boolean atLeastOneTransmitterStarted = false; //ArrayList ports = new ArrayList(); //ArrayList formatSets = new ArrayList(); for (int i = 0; i < mediaDescriptions.size(); i++) { Media media = ((MediaDescription) mediaDescriptions.get(i)). getMedia(); //Media Type String mediaType = null; try { mediaType = media.getMediaType(); } catch (SdpParseException ex) { console.error( "Failed to extract the media type for one of the provided media descriptions!\n" + "Ignoring description!", ex); fireNonFatalMediaError(new MediaException( "Failed to extract the media type for one of the provided media descriptions!\n" + "Ignoring description!", ex )); continue; } //Find ports try { mediaPort = media.getMediaPort(); } catch (SdpParseException ex) { console.error("Failed to extract port for media type [" + mediaType + "]. Ignoring description!", ex); fireNonFatalMediaError(new MediaException( "Failed to extract port for media type [" + mediaType + "]. Ignoring description!", ex )); continue; } } //====================== try { //removeAllRtpManagers(); console.logEntry(); SessionAddress addToStop = new SessionAddress(InetAddress.getByName( remoteAddress), mediaPort); stopTransmitters(addToStop); if (transmitters.size() == 0) { stopReceiver("localhost"); } firePlayerStopped(); } catch (java.net.UnknownHostException ex) { console.debug(ex.toString()); } finally { console.logExit(); } } protected void createTransmitter(String destHost, List<Integer> ports, ArrayList formatSets) throws MediaException { try { console.logEntry(); final AVTransmitter transmitter = new AVTransmitter(sndProcessor, destHost, ports, formatSets); transmitter.setMediaManagerCallback(this); transmitters.add(transmitter); } finally { console.logExit(); } } protected void stopTransmitters(SessionAddress addToStop) { try { console.logEntry(); for (AVTransmitter transmitter : transmitters) { try { transmitter.stop(addToStop); } //Catch everything that comes out as we wouldn't want //Some null pointer prevent us from closing a device and thus //render it unusable catch (Exception exc) { console.error("Could not close transmitter " + transmitter, exc); } } transmitters.clear(); } finally { console.logExit(); } } protected void createReceiver(String remoteAddress, List<Integer> ports) throws MediaException { try { console.logEntry(); final AVReceiver receiver = new AVReceiver(new String[] { remoteAddress + "/" + getAudioPort() + "/1"}, sipProp); receiver.setMediaManager(this); receiver.initialize2(ports); receivers.add(receiver); } finally { console.logExit(); } } protected void stopReceiver(String localAddress) { try { console.logEntry(); for (AVReceiver receiver : receivers) { try { receiver.close(localAddress); firePlayerStopped(); } //Catch everything that comes out as we wouldn't want //Some null pointer prevent us from closing a device and thus //render it unusable catch (Exception exc) { console.warn("Could not close receiver " + receiver, exc); } } receivers.clear(); } finally { console.logExit(); } } protected void stopReceiver() { try { console.logEntry(); for (AVReceiver receiver : receivers) { try { receiver.close(); firePlayerStopped(); } //Catch everything that comes out as we wouldn't want //Some null pointer prevent us from closing a device and thus //render it unusable catch (Exception exc) { console.warn("Could not close receiver " + receiver, exc); } } receivers.clear(); } finally { console.logExit(); } } /** * Only stops the receiver without deleting it. After calling this method * one can call softStartReceiver to relauch reception. */ public void softStopReceiver() { try { console.logEntry(); for (AVReceiver receiver : receivers) { try { receiver.close(); firePlayerStopped(); } //Catch everything that comes out as we wouldn't want //Some null pointer prevent us from closing a device and thus //render it unusable catch (Exception exc) { console.warn("Could not close receiver " + receiver, exc); } } } finally { console.logExit(); } } /** * Starts a receiver that has been stopped using softStopReceiver(). */ public void softStartReceiver() { try { console.logEntry(); for (AVReceiver receiver : receivers) { try { receiver.initialize(); } //Catch everything that comes out as we wouldn't want //Some null pointer prevent us from closing a device and thus //render it unusable catch (Exception exc) { console.warn("Could not close receiver " + receiver, exc); } } } finally { console.logExit(); } } void firePlayerStarting(Player player) { try { console.logEntry(); final MediaEvent evt = new MediaEvent(player); for (MediaListener listener : listeners) { listener.playerStarting(evt); } } finally { console.logExit(); } } void firePlayerStopped() { try { console.logEntry(); for (MediaListener listener : listeners) { listener.playerStopped(); } } finally { console.logExit(); } } void fireNonFatalMediaError(Throwable cause) { try { console.logEntry(); final MediaErrorEvent evt = new MediaErrorEvent(cause); for (MediaListener listener : listeners) { listener.nonFatalMediaErrorOccurred(evt); } } finally { console.logExit(); } } public void addMediaListener(MediaListener listener) { try { console.logEntry(); listeners.add(listener); } finally { console.logExit(); } } InetAddress getLocalHost() throws MediaException { try { final String hostAddress = sipProp.getProperty( "net.java.sip.communicator.media.IP_ADDRESS"); if (console.isDebugEnabled()) { console.debug(hostAddress); } return InetAddress.getByName(hostAddress); } catch (Exception ex) { throw new MediaException(ex.getMessage(), ex); } } public String generateSdpDescription() throws MediaException { try { console.logEntry(); checkIfStarted(); try { SessionDescription sessDescr = sdpFactory. createSessionDescription(); //"v=0" Version v = sdpFactory.createVersion(0); InetSocketAddress publicAudioAddress = addressManager. getPublicAddressFor(getAudioPort()); InetAddress publicIpAddress = publicAudioAddress.getAddress(); String addrType = publicIpAddress instanceof Inet6Address ? "IP6" : "IP4"; //spaces in the user name mess everything up. //bug report - Alessandro Melzi Origin o = sdpFactory.createOrigin( System.getProperty("user.name").replace(' ', '_'), 0, 0, "IN", addrType, publicIpAddress.getHostAddress()); //"s=-" SessionName s = sdpFactory.createSessionName("-"); //c= Connection c = sdpFactory.createConnection("IN", addrType, publicIpAddress.getHostAddress()); //"t=0 0" TimeDescription t = sdpFactory.createTimeDescription(); Vector timeDescs = new Vector(); timeDescs.add(t); //--------Audio media description //make sure preferred formats come first surfacePreferredEncodings(getReceivableAudioFormats()); String[] formats = getReceivableAudioFormats(); MediaDescription am = sdpFactory.createMediaDescription("audio", publicAudioAddress.getPort(), 1, "RTP/AVP", formats); if (!isAudioTransmissionSupported()) { am.setAttribute("recvonly", null); //--------Video media description } surfacePreferredEncodings(getReceivableVideoFormats()); //"m=video 22222 RTP/AVP 34"; //String[] vformats = getReceivableVideoFormats(); Vector mediaDescs = new Vector(); mediaDescs.add(am); sessDescr.setVersion(v); sessDescr.setOrigin(o); sessDescr.setConnection(c); sessDescr.setSessionName(s); sessDescr.setTimeDescriptions(timeDescs); if (mediaDescs.size() > 0) { sessDescr.setMediaDescriptions(mediaDescs); } if (console.isDebugEnabled()) { console.debug("Generated SDP - " + sessDescr.toString()); } return sessDescr.toString(); } catch (SdpException exc) { console.error( "An SDP exception occurred while generating local sdp description", exc); throw new MediaException( "An SDP exception occurred while generating local sdp description", exc); } } finally { console.logExit(); } } public int getAudioPort() { return audioPort; } @Override protected void finalize() { try { console.logEntry(); try { if (source != null) { source.disconnect(); } } catch (Exception exc) { console.error("Failed to disconnect data source:" + exc.getMessage()); } } finally { console.logExit(); } } public boolean isStarted() { return isStarted; } protected void checkIfStarted() throws MediaException { if (!isStarted()) { console.error("The MediaManager has not been properly started! " + "Impossible to continue"); throw new MediaException( "The MediaManager had not been properly started! " + "Impossible to continue"); } } protected boolean isAudioTransmissionSupported() { return transmittableAudioFormats.size() > 0; } protected boolean isMediaTransmittable(String media) { if (media.equalsIgnoreCase("audio") /*&& isAudioTransmissionSupported()*/) { return true; } else { return false; } } protected String[] getReceivableAudioFormats() { return receivableAudioFormats; } protected String[] getReceivableVideoFormats() { return receivableVideoFormats; } protected String findCorrespondingJmfFormat(String sdpFormatStr) { int sdpFormat = -1; try { sdpFormat = Integer.parseInt(sdpFormatStr); } catch (NumberFormatException ex) { return null; } switch (sdpFormat) { case SdpConstants.PCMU: return AudioFormat.ULAW_RTP; case SdpConstants.GSM: return AudioFormat.GSM_RTP; case SdpConstants.G723: return AudioFormat.G723_RTP; case SdpConstants.DVI4_8000: return AudioFormat.DVI_RTP; case SdpConstants.DVI4_16000: return AudioFormat.DVI_RTP; case SdpConstants.PCMA: return AudioFormat.ALAW; case SdpConstants.G728: return AudioFormat.G728_RTP; case SdpConstants.G729: return AudioFormat.G729_RTP; case SdpConstants.H263: return VideoFormat.H263_RTP; case SdpConstants.JPEG: return VideoFormat.JPEG_RTP; case SdpConstants.H261: return VideoFormat.H261_RTP; default: return null; } } protected String findCorrespondingSdpFormat(String jmfFormat) { if (jmfFormat == null) { return null; } else if (jmfFormat.equals(AudioFormat.ULAW_RTP)) { return Integer.toString(SdpConstants.PCMU); } else if (jmfFormat.equals(AudioFormat.GSM_RTP)) { return Integer.toString(SdpConstants.GSM); } else if (jmfFormat.equals(AudioFormat.G723_RTP)) { return Integer.toString(SdpConstants.G723); } else if (jmfFormat.equals(AudioFormat.DVI_RTP)) { return Integer.toString(SdpConstants.DVI4_8000); } else if (jmfFormat.equals(AudioFormat.DVI_RTP)) { return Integer.toString(SdpConstants.DVI4_16000); } else if (jmfFormat.equals(AudioFormat.ALAW)) { return Integer.toString(SdpConstants.PCMA); } else if (jmfFormat.equals(AudioFormat.G728_RTP)) { return Integer.toString(SdpConstants.G728); } else if (jmfFormat.equals(AudioFormat.G729_RTP)) { return Integer.toString(SdpConstants.G729); } else if (jmfFormat.equals(VideoFormat.H263_RTP)) { return Integer.toString(SdpConstants.H263); } else if (jmfFormat.equals(VideoFormat.JPEG_RTP)) { return Integer.toString(SdpConstants.JPEG); } else if (jmfFormat.equals(VideoFormat.H261_RTP)) { return Integer.toString(SdpConstants.H261); } else { return null; } } /** * @param sdpFormats * @return * @throws MediaException */ protected Collection<String> extractTransmittableJmfFormats( Collection<String> sdpFormats) throws MediaException { try { console.logEntry(); Collection<String> jmfFormats = new java.util.ArrayList<String>(); for (String sdpFormat : sdpFormats) { final String jmfFormat = findCorrespondingJmfFormat(sdpFormat); if (jmfFormat != null) { jmfFormats.add(jmfFormat); } } if (jmfFormats.size() == 0) { throw new MediaException( "None of the supplied sdp formats for is supported by SIP COMMUNICATOR"); } return jmfFormats; } finally { console.logExit(); } } //This is the data source that we'll be using to transmit //let's see what can it do protected void initSndProcessor(DataSource ds) throws MediaException { try { console.logEntry(); try { try { ds.connect(); } catch (NullPointerException ex) { //Thrown when operation is not supported by the OS console.error( "An internal error occurred while" + " trying to connec to to datasource!", ex); throw new MediaException( "An internal error occurred while" + " trying to connec to to datasource!", ex); } sndProcessor = Manager.createProcessor(ds); sndProcessor.configure(); boolean success = procUtility.waitForState(sndProcessor, Processor.Configured); if (!success) { throw new MediaException( "Media manager could not create a processor\n" + "for the specified data source"); } } catch (NoProcessorException ex) { console.error( "Media manager could not create a processor\n" + "for the specified data source", ex ); throw new MediaException( "Media manager could not create a processor\n" + "for the specified data source", ex); } catch (IOException ex) { console.error( "Media manager could not connect " + "to the specified data source", ex); throw new MediaException("Media manager could not connect " + "to the specified data source", ex); } sndProcessor.setContentDescriptor(new ContentDescriptor( ContentDescriptor.RAW_RTP)); TrackControl[] trackControls = sndProcessor.getTrackControls(); if (console.isDebugEnabled()) { console.debug("We will be able to transmit in:"); } for (int i = 0; i < trackControls.length; i++) { Format[] formats = trackControls[i].getSupportedFormats(); for (int j = 0; j < formats.length; j++) { Format format = formats[j]; String encoding = format.getEncoding(); if (format instanceof AudioFormat) { String sdp = findCorrespondingSdpFormat(encoding); if (sdp != null && !transmittableAudioFormats.contains(sdp)) { if (console.isDebugEnabled()) { console.debug("Audio[" + (j + 1) + "]=" + encoding + "; sdp=" + sdp); } transmittableAudioFormats.add(sdp); } } } } } finally { console.logExit(); } } protected void initRcvProcessor(DataSource ds) throws MediaException { try { console.logEntry(); try { try { ds.connect(); } catch (NullPointerException ex) { //Thrown when operation is not supported by the OS console.error( "An internal error occurred while" + " trying to connec to to datasource!", ex); throw new MediaException( "An internal error occurred while" + " trying to connec to to datasource!", ex); } rcvProcessor = Manager.createProcessor(ds); rcvProcessor.configure(); boolean success = procUtility.waitForState(rcvProcessor, Processor.Configured); if (!success) { throw new MediaException( "Media manager could not create a processor\n" + "for the specified data source"); } } catch (NoProcessorException ex) { console.error( "Media manager could not create a processor\n" + "for the specified data source", ex ); throw new MediaException( "Media manager could not create a processor\n" + "for the specified data source", ex); } catch (IOException ex) { console.error( "Media manager could not connect " + "to the specified data source", ex); throw new MediaException("Media manager could not connect " + "to the specified data source", ex); } rcvProcessor.setContentDescriptor(new ContentDescriptor( ContentDescriptor.RAW_RTP)); TrackControl[] trackControls = sndProcessor.getTrackControls(); if (console.isDebugEnabled()) { console.debug("We will be able to receive in:"); } for (int i = 0; i < trackControls.length; i++) { Format[] formats = trackControls[i].getSupportedFormats(); for (int j = 0; j < formats.length; j++) { Format format = formats[j]; String encoding = format.getEncoding(); if (format instanceof AudioFormat) { String sdp = findCorrespondingSdpFormat(encoding); if (sdp != null && !transmittableAudioFormats.contains(sdp)) { if (console.isDebugEnabled()) { console.debug("Audio[" + (j + 1) + "]=" + encoding + "; sdp=" + sdp); } // transmittableAudioFormats.add(sdp); } } } } } finally { console.logExit(); } } /** * Returns a cached instance of an RtpManager bound on the specified local * address. If no such instance exists null is returned. * @param localAddress the address where the rtp manager must be bound locally. * @return an rtp manager bound on the specified address or null if such an * instance was not found. */ synchronized RTPManager getRtpManager(SessionAddress localAddress) { return (RTPManager) activeRtpManagers.get(localAddress); } /** * Maps the specified rtp manager against the specified local address so * that it may be later retrieved in case someone wants to operate * (transmit/receive) on the same port. * @param localAddress the address where the rtp manager is bound * @param rtpManager the rtp manager itself */ synchronized void putRtpManager(SessionAddress localAddress, RTPManager rtpManager) { activeRtpManagers.put(localAddress, rtpManager); } /** * Removes all rtp managers from the rtp manager cache. */ synchronized void removeAllRtpManagers() { activeRtpManagers.clear(); } /** * Moves formats with the specified encoding to the top of the array list * so that they are the ones chosen for transmission (if supported by the * remote party) (feature request by Vince Fourcade) */ protected void surfacePreferredEncodings(String[] formats) { try { console.logEntry(); String preferredAudioEncoding = sipProp.getProperty( "net.java.sip.communicator.media.PREFERRED_AUDIO_ENCODING"); String preferredVideoEncoding = sipProp.getProperty( "sipProp.java.sip.communicator.media.PREFERRED_VIDEO_ENCODING"); if (preferredAudioEncoding == null && preferredVideoEncoding == null) { return; } for (int i = 0; i < formats.length; i++) { String encoding = formats[i]; if ((preferredAudioEncoding != null && encoding.equalsIgnoreCase(preferredAudioEncoding)) || (preferredVideoEncoding != null && encoding.equalsIgnoreCase(preferredVideoEncoding))) { formats[i] = formats[0]; formats[0] = encoding; if (console.isDebugEnabled()) { console.debug("Encoding [" + findCorrespondingJmfFormat(encoding) + "] is set as preferred."); } break; } } } finally { console.logExit(); } } }