/* ==================================================================== * 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.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.media.ControllerClosedEvent; import javax.media.ControllerErrorEvent; import javax.media.ControllerEvent; import javax.media.ControllerListener; import javax.media.EndOfMediaEvent; import javax.media.Format; import javax.media.Manager; import javax.media.Player; import javax.media.Processor; import javax.media.RealizeCompleteEvent; import javax.media.StartEvent; import javax.media.control.BufferControl; import javax.media.control.TrackControl; import javax.media.protocol.DataSource; import javax.media.protocol.FileTypeDescriptor; import javax.media.rtp.InvalidSessionAddressException; import javax.media.rtp.Participant; import javax.media.rtp.RTPControl; import javax.media.rtp.RTPManager; import javax.media.rtp.ReceiveStream; import javax.media.rtp.ReceiveStreamListener; import javax.media.rtp.SendStreamListener; import javax.media.rtp.SessionAddress; import javax.media.rtp.SessionListener; import javax.media.rtp.event.ByeEvent; import javax.media.rtp.event.NewParticipantEvent; import javax.media.rtp.event.NewReceiveStreamEvent; import javax.media.rtp.event.ReceiveStreamEvent; import javax.media.rtp.event.SendStreamEvent; import javax.media.rtp.event.SessionEvent; import javax.media.rtp.event.StreamMappedEvent; import net.sourceforge.gjtapi.raw.sipprovider.common.Console; /** * AVReceiver to receive RTP transmission using the new RTP API. * * <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> * @author Emil Ivov (http://www.emcho.com) * @version 1.1 */ class AVReceiver implements ReceiveStreamListener, SessionListener, ControllerListener, SendStreamListener { private static Console console = Console.getConsole(AVReceiver.class); private net.sourceforge.gjtapi.raw.sipprovider.media.MediaManager mediaManager; private String sessions[] = null; private RTPManager mgrs[] = null; boolean dataReceived = false; //private Object dataSync = new Object(); private int bindRetries = 3; private final Properties sipProp; private Processor processor; public DataSource ds; /** Utility to wait for processor state changes. */ private final ProcessorUtility procUtility = new ProcessorUtility("AVReceiver"); public AVReceiver(String sessions[],Properties sipProp) { this.sessions = sessions; this.sipProp = new Properties() ; this.sipProp.putAll(sipProp); String retries = null; if((retries = sipProp.getProperty("net.java.sip.communicator.media.RECEIVER_BIND_RETRIES")) != null) try { bindRetries = Integer.valueOf(retries).intValue(); } catch (NumberFormatException ex) { console.error(retries + " is not a valid number. ignoring property", ex); } } void setMediaManager(MediaManager mManager) { this.mediaManager = mManager; } protected boolean initialize() { try { console.logEntry(); InetAddress ipAddr; SessionAddress localAddr = new SessionAddress(); SessionAddress destAddr; mgrs = new RTPManager[sessions.length]; SessionLabel session; // Open the RTP sessions. for (int i = 0; i < sessions.length; i++) { // Parse the session addresses. try { session = new SessionLabel(sessions[i]); } catch (IllegalArgumentException e) { console.error( "Failed to parse the session address given: " + sessions[i]); console.logExit(); return false; } if (console.isDebugEnabled()) { console.debug( " Start listening for RTP @ addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl); } //rtpmanager avec localost + port local mgrs[i] = mediaManager.getRtpManager(new SessionAddress(mediaManager.getLocalHost(),session.port)); if(mgrs[i] == null) { mgrs[i] = RTPManager.newInstance(); mediaManager.putRtpManager(new SessionAddress(mediaManager.getLocalHost(),session.port), mgrs[i]); } mgrs[i].addSessionListener(this); mgrs[i].addReceiveStreamListener(this); mgrs[i].addSendStreamListener(this); ipAddr = InetAddress.getByName(session.addr); int tries = 0; while (tries++ < bindRetries) { if (ipAddr.isMulticastAddress()) { // local and remote address pairs are identical: localAddr = new SessionAddress(ipAddr, session.port, session.ttl); destAddr = new SessionAddress(ipAddr, session.port, session.ttl); } else { localAddr = new SessionAddress(mediaManager.getLocalHost(),session.port); destAddr = new SessionAddress(ipAddr, session.port); } try { mgrs[i].initialize(localAddr); } catch (Exception exc) { if (tries < bindRetries) { continue; } console.error( "Could not initialize rtp manager!",exc); return false; } // You can try out some other buffer size to see // if you can get better smoothness. BufferControl bc = (BufferControl) mgrs[i].getControl("javax.media.control.BufferControl"); if (bc != null) { bc.setBufferLength(350); } mgrs[i].addTarget(destAddr);//ajout de l'address break; //port retries } //port retries } } catch (Exception e) { console.error("Cannot create the RTP Session: ", e); console.logExit(); return false; } console.logExit(); return true; } /** * * @param remoteAddr String * @param ports ArrayList * @throws MediaException */ protected void initialize2(List<Integer> ports) throws MediaException { console.logEntry(); mgrs = new RTPManager[sessions.length]; for (int i = 0; i < sessions.length; i++) { SessionLabel session = new SessionLabel(sessions[0]); final int localPort = session.port; final String remoteAddress = session.addr; final Integer port = ports.get(0); final int remotePort = port.intValue(); if (console.isDebugEnabled()) { console.debug("IP localHostAVRECV: " + mediaManager.getLocalHost() + " localPort: " + localPort); console.debug("IP remoteHostAVRECV: " + remoteAddress + " remotePort: " + remotePort); } InetSocketAddress dialogLocalAddr = new InetSocketAddress( mediaManager.getLocalHost(), localPort); InetAddress rtpLocalAddress = dialogLocalAddr.getAddress(); SessionAddress local = new SessionAddress(rtpLocalAddress, localPort); final SessionAddress address = new SessionAddress( mediaManager.getLocalHost(), localPort); mgrs[i] = mediaManager.getRtpManager(address); if (mgrs[i] == null) { mgrs[i] = RTPManager.newInstance(); mediaManager.putRtpManager(new SessionAddress(mediaManager. getLocalHost(), localPort), mgrs[i]); } mgrs[i].addSessionListener(this); mgrs[i].addReceiveStreamListener(this); try { mgrs[i].initialize(local); } catch (IOException ex) { throw new MediaException("Error initializing the RTP manager!", ex); } catch (InvalidSessionAddressException ex) { throw new MediaException("Error initializing the RTP manager!", ex); } // Now get the real local ports from the manager //RTPSessionMgr smgr = (RTPSessionMgr) mgrs[i]; //int _controlPort = smgr.getLocalSessionAddress().getControlPort(); //int _dataPort = smgr.getLocalSessionAddress().getDataPort(); InetSocketAddress dialogRemoteAddr = new InetSocketAddress( remoteAddress, remotePort); InetAddress bogusAddress = dialogRemoteAddr.getAddress(); InetAddress rtpRemoteAddress = null; try { rtpRemoteAddress = InetAddress.getByAddress(InetAddress. getLocalHost().getHostName(), bogusAddress.getAddress()); } catch (UnknownHostException ex) { throw new MediaException(ex.getMessage(), ex); } SessionAddress sessionAddress = new SessionAddress(rtpRemoteAddress, remotePort); //set buffer parameters BufferControl bc = (BufferControl) mgrs[i].getControl( "javax.media.control.BufferControl"); if (bc != null) { bc.setBufferLength(4098); } //add target to manager try { mgrs[i].addTarget(sessionAddress); } catch (IOException ex) { throw new MediaException( "Error adding the target to the RTP Manager!", ex); } catch (InvalidSessionAddressException ex) { throw new MediaException( "Error adding the target to the RTP Manager!", ex); } console.logExit(); } } public boolean isDone() { return false; } /** * Close the players and the session managers. */ protected void close(String LocalAddress) { try { console.logEntry(); // close the RTP session. for (int i = 0; i < mgrs.length; i++) { if (mgrs[i] != null) { if (console.isDebugEnabled()) { console.debug("Stopped mgr " + (i + 1)); } try { InetAddress inetAdd = InetAddress.getByName(LocalAddress); SessionAddress sessionAddress = new SessionAddress(inetAdd, mediaManager.getAudioPort()); mgrs[i].removeTarget(sessionAddress ,"bye"); mgrs[i].dispose(); mgrs[i] = null; } catch (java.net.UnknownHostException ex) { console.warn(ex.toString(), ex); } catch (javax.media.rtp.InvalidSessionAddressException ex) { console.warn(ex.toString(), ex); } } } } finally { console.logExit(); } } protected void close() { try { console.logEntry(); // close the RTP session. for (int i = 0; i < mgrs.length; i++) { if (mgrs[i] != null) { if (console.isDebugEnabled()) { console.debug("Stopped mgr " + (i + 1)); } mgrs[i].removeTargets("Closing session from AVReceiver"); mgrs[i].dispose(); mgrs[i] = null; } } } finally { console.logExit(); } } /** * SessionListener. */ public synchronized void update(SessionEvent evt) { try { console.logEntry(); if (evt instanceof NewParticipantEvent) { Participant p = ( (NewParticipantEvent) evt).getParticipant(); if (console.isDebugEnabled()) { console.debug("A new participant had just joined: " + p.getCNAME()); } } else { if (console.isDebugEnabled()) { console.debug( "Received a the following JMF Session event - " + "evt.getClass().getName()"); } } } finally { console.logExit(); } } /** * ReceiveStreamListener */ public synchronized void update(ReceiveStreamEvent evt) { try { console.logEntry(); Participant participant = evt.getParticipant(); // could be null. ReceiveStream stream = evt.getReceiveStream(); // could be null. if (evt instanceof NewReceiveStreamEvent) { try { stream = evt.getReceiveStream(); ds = stream.getDataSource(); // Find out the formats. RTPControl ctl = (RTPControl) ds.getControl("javax.media.rtp.RTPControl"); if (console.isDebugEnabled()) { if (ctl != null) { console.debug("Recevied new RTP stream: " + ctl.getFormat()); } else { console.debug("Recevied new RTP stream"); } } processor = Manager.createProcessor(ds); this.configureProcessor(processor); processor.realize(); } catch (Exception e) { console.error("NewReceiveStreamEvent exception ", e); return; } } else if (evt instanceof StreamMappedEvent) { if (stream != null && stream.getDataSource() != null) { ds = stream.getDataSource(); // Find out the formats. RTPControl ctl = (RTPControl) ds.getControl( "javax.media.rtp.RTPControl"); if (console.isDebugEnabled()) { String msg = "The previously unidentified stream "; if (ctl != null) { msg += ctl.getFormat(); } msg += " had now been identified as sent by: " + participant.getCNAME(); console.debug(msg); } } } else if (evt instanceof ByeEvent) { console.debug("Got \"bye\" from: " + participant.getCNAME()); } } finally { console.logExit(); } } /** * ControllerListener for the Players. */ public synchronized void controllerUpdate(ControllerEvent ce) { try { console.logEntry(); Player p = (Player) ce.getSourceController(); if (p == null) { return; } // Get this when the internal players are realized. if (ce instanceof RealizeCompleteEvent) { console.debug("A player was realized and will be started."); p.start(); } if (ce instanceof StartEvent) { console.debug("Received a StartEvent"); mediaManager.firePlayerStarting(p); } if (ce instanceof ControllerErrorEvent) { console.error( "The following error was reported while starting a player" + ce); } if (ce instanceof ControllerClosedEvent) { console.debug("Received a ControllerClosedEvent"); mediaManager.firePlayerStopped(); } } finally { console.logExit(); } } /** * A utility class to parse the session addresses. */ class SessionLabel { public String addr = null; public int port; public int ttl = 1; private final Console console = Console.getConsole(SessionLabel.class); SessionLabel(String session) throws IllegalArgumentException { try { console.logEntry(); int off; String portStr = null, ttlStr = null; if (session != null && session.length() > 0) { while (session.length() > 1 && session.charAt(0) == '/') { session = session.substring(1); // Now see if there's a addr specified. } off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) { addr = session; } } else { addr = session.substring(0, off); session = session.substring(off + 1); // Now see if there's a port specified off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) { portStr = session; } } else { portStr = session.substring(0, off); session = session.substring(off + 1); // Now see if there's a ttl specified off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) { ttlStr = session; } } else { ttlStr = session.substring(0, off); } } } } if (addr == null) { throw new IllegalArgumentException(); } if (portStr != null) { Integer integer = Integer.valueOf(portStr); if (integer != null) { port = integer.intValue(); } } else { throw new IllegalArgumentException(); } if (ttlStr != null) { try { Integer integer = Integer.valueOf(ttlStr); if (integer != null) { ttl = integer.intValue(); } } catch (Throwable t) { throw new IllegalArgumentException(); } } } finally { console.logExit(); } } } public void update(SendStreamEvent event) { console.debug( "received the following JMF Session event - " + event.getClass().getName()); } public Processor getProcessor() { return processor; } protected void configureProcessor(Processor p) throws MediaException { processor = p; try { console.logEntry(); if (processor == null) { console.error("Processor is null."); throw new MediaException("Processor is null."); } // Wait for the processor to configure // processor.addControllerListener(new StateListener()); boolean result = procUtility.waitForState(processor, Processor.Configured); if (result == false) { console.error("Couldn't configure processor"); throw new MediaException("Couldn't configure processor"); } // Get the tracks from the processor TrackControl[] tracks = processor.getTrackControls(); if (console.isDebugEnabled()) { console.debug("-----" + tracks[0].getFormat()); } int t = tracks[0].getSupportedFormats().length; if (console.isDebugEnabled()) { for(int i = 0; i < t; i++) { console.debug("supported format: " + tracks[0].getSupportedFormats()[i]); } } // Do we have at least one track? if (tracks == null || tracks.length < 1) { console.error("Couldn't find tracks in processor"); throw new MediaException("Couldn't find tracks in processor"); } // Set the output content descriptor to RAW_RTP // This will limit the supported formats reported from // Track.getSupportedFormats to only valid RTP formats. // ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); //kk FileTypeDescriptor fd = new FileTypeDescriptor(FileTypeDescriptor.BASIC_AUDIO); processor.setContentDescriptor(fd); // // processor.setContentDescriptor(new AudioFileFormat(AudioFileFormat.Type.WAVE)); } finally { console.logExit(); } } protected int findFirstMatchingFormat(Format[] hayStack, ArrayList needles) { try { console.logEntry(); if (hayStack == null || needles == null) { return -1; } for (int j = 0; j < needles.size(); j++) { ArrayList currentSet = (ArrayList) needles.get(j); for (int k = 0; k < currentSet.size(); k++) { for (int i = 0; i < hayStack.length; i++) { if (hayStack[i].getEncoding().equals( currentSet.get(k))) { return i; } } } } return -1; } finally { console.logExit(); } } /**************************************************************** * Inner Classes ****************************************************************/ class StateListener implements ControllerListener { public void controllerUpdate(ControllerEvent ce) { try { console.logEntry(); if (console.isDebugEnabled()) { console.debug(ce.toString()); } //stop the stream at the end of the audio file if (ce instanceof EndOfMediaEvent) { try { processor.stop(); }catch(Exception ex) { console.debug(ex.toString()); } } } finally { console.logExit(); } } } }