AVTransmit2.java和AVReceive2.java

 
http://www.cs.odu.edu/~cs778/jmfexamples/
————————————————————————————————————————————————————————————————————————————————————————————————
                     Transmitting Audio and Video over RTP
 
 

The AVTransmit2 class is similar to VideoTransmit except that it attempts to
transmit each of the tracks within the given input media. To do that, an RTP
session is created for each media track. For that purpose, the RTP Session
API instead of the DataSink API is used for flexibility.
The default (first available) RTP format is set for each track. For video, special
attention is taken to ensure that the input sizes are usable for RTP
transmission. Real-time scaling is applied when necessary.

Note that due to limitations of the JMF 2.1 implementation, audio and video are
not in tight synchronization.

The AVTransmit2 class takes three parameters in the constructor -

  • the source locator,
  • the destination IP address and
  • a base destination port number for all the media tracks.

  • The first media track will be transmitted from the base port number.
    The next track will   go to base port number plus 2 and so on.


The sample program can be used as a utility class as well as standalone. There
is a main method that takes three command line parameters. If all goes well,
the media will be sent out for 60 seconds and then the application will exit.
This 60 seconds limit is because there is no GUI that allows you to specify
when to stop the transmission.
 

How to run this sample

        1. Run AVTransmit2 with the required 3 command line parameters
            For example,
              Use Multicast:
                java  AVTransmit2  file:clip01.mpg  224.122.122.122   8800

            OR
             Use Unicast:
                 java  AVTransmit2  file:clip01.mpg   128.82.4.7   8800

             Where 128.82.4.7 is the receicver address.
             If the sender address is 128.82.4.9 it will use port 8800
             to send out data. In this case the receiver (e.g., JMStudio)
             should specify the sender as:  128.82.4.9 8800.
             Therefore to use unicast you should have two machines since
              you can not use the same port for both sender and receiver.

        2. Since the media tracks are transmitted in multiple sessions, you'll need to
            use one Player per track on the receive side. Using JMStudio, you can
            start multiple Players from the "File" menu using the "New Window"
            item. Then use the "Open URL..." item to open one RTP session per
            track. The URL to use is:

                          rtp://<sourceIP>:<port>/media

            Where <sourceIP> is the IP address of the RTP session and the port
            number is the same one that is used on the transmitting side.

Source Code
                        AVTransmit2.java
 


/*
 * @(#)AVTransmit2.java    1.4 01/03/13
 *
 * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 
*/

import java.awt.*;
import java.io.*;
import java.net.InetAddress;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.protocol.DataSource;
import javax.media.format.*;
import javax.media.control.TrackControl;
import javax.media.control.QualityControl;
import javax.media.rtp.*;
import javax.media.rtp.rtcp.*;
import com.sun.media.rtp.*;

public class AVTransmit2 {

    // Input MediaLocator
    
// Can be a file or http or capture source
    private MediaLocator locator;
    private String ipAddress;
    private int portBase;

    private Processor processor = null;
    private RTPManager rtpMgrs[];
    private DataSource dataOutput = null;
    
    public AVTransmit2(MediaLocator locator,
             String ipAddress,
             String pb,
             Format format) {
    
    this.locator = locator;
    this.ipAddress = ipAddress;
    Integer integer = Integer.valueOf(pb);
    if (integer != null)
        this.portBase = integer.intValue();
    }

    /**
     * Starts the transmission. Returns null if transmission started ok.
     * Otherwise it returns a string with the reason why the setup failed.
     
*/
    public synchronized String start() {
    String result;

    // Create a processor for the specified media locator
    
// and program it to output JPEG/RTP
    result = createProcessor();
    if (result != null)
        return result;

    // Create an RTP session to transmit the output of the
    
// processor to the specified IP address and port no.
    result = createTransmitter();
    if (result != null) {
        processor.close();
        processor = null;
        return result;
    }

    // Start the transmission
    processor.start();
    
    return null;
    }

    /**
     * Stops the transmission if already started
     
*/
    public void stop() {
    synchronized (this) {
        if (processor != null) {
        processor.stop();
        processor.close();
        processor = null;
        for (int i = 0; i < rtpMgrs.length; i++) {
            rtpMgrs[i].removeTargets( "Session ended.");
            rtpMgrs[i].dispose();
        }
        }
    }
    }

    private String createProcessor() {
    if (locator == null)
        return "Locator is null";

    DataSource ds;
    DataSource clone;

    try {
        ds = javax.media.Manager.createDataSource(locator);
    } catch (Exception e) {
        return "Couldn't create DataSource";
    }

    // Try to create a processor to handle the input media locator
    try {
        processor = javax.media.Manager.createProcessor(ds);
    } catch (NoProcessorException npe) {
        return "Couldn't create processor";
    } catch (IOException ioe) {
        return "IOException creating processor";
    } 

    // Wait for it to configure
    boolean result = waitForState(processor, Processor.Configured);
    if (result == false)
        return "Couldn't configure processor";

    // Get the tracks from the processor
    TrackControl [] tracks = processor.getTrackControls();

    // Do we have atleast one track?
    if (tracks == null || tracks.length < 1)
        return "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);
    processor.setContentDescriptor(cd);

    Format supported[];
    Format chosen;
    boolean atLeastOneTrack = false;

    // Program the tracks.
    for (int i = 0; i < tracks.length; i++) {
        Format format = tracks[i].getFormat();
        if (tracks[i].isEnabled()) {

        supported = tracks[i].getSupportedFormats();

        // We've set the output content to the RAW_RTP.
        
// So all the supported formats should work with RTP.
        
// We'll just pick the first one.

        if (supported.length > 0) {
            if (supported[0] instanceof VideoFormat) {
            // For video formats, we should double check the
            
// sizes since not all formats work in all sizes.
            chosen = checkForVideoSizes(tracks[i].getFormat(), 
                            supported[0]);
            } else
            chosen = supported[0];
            tracks[i].setFormat(chosen);
            System.err.println("Track " + i + " is set to transmit as:");
            System.err.println("  " + chosen);
            atLeastOneTrack = true;
        } else
            tracks[i].setEnabled(false);
        } else
        tracks[i].setEnabled(false);
    }

    if (!atLeastOneTrack)
        return "Couldn't set any of the tracks to a valid RTP format";

    // Realize the processor. This will internally create a flow
    
// graph and attempt to create an output datasource for JPEG/RTP
    
// audio frames.
    result = waitForState(processor, Controller.Realized);
    if (result == false)
        return "Couldn't realize processor";

    // Set the JPEG quality to .5.
    setJPEGQuality(processor, 0.5f);

    // Get the output data source of the processor
    dataOutput = processor.getDataOutput();

    return null;
    }


    /**
     * Use the RTPManager API to create sessions for each media 
     * track of the processor.
     
*/
    private String createTransmitter() {

    // Cheated.  Should have checked the type.
    PushBufferDataSource pbds = (PushBufferDataSource)dataOutput;
    PushBufferStream pbss[] = pbds.getStreams();

    rtpMgrs = new RTPManager[pbss.length];
    SessionAddress localAddr, destAddr;
    InetAddress ipAddr;
    SendStream sendStream;
    int port;
    SourceDescription srcDesList[];

    for (int i = 0; i < pbss.length; i++) {
        try {
        rtpMgrs[i] = RTPManager.newInstance();        

        // The local session address will be created on the
        
// same port as the the target port. This is necessary
        
// if you use AVTransmit2 in conjunction with JMStudio.
        
// JMStudio assumes -  in a unicast session - that the
        
// transmitter transmits from the same port it is receiving
        
// on and sends RTCP Receiver Reports back to this port of
        
// the transmitting host.
        
        port = portBase + 2*i;
        ipAddr = InetAddress.getByName(ipAddress);

        localAddr = new SessionAddress( InetAddress.getLocalHost(),
                        port);
        
        destAddr = new SessionAddress( ipAddr, port);

        rtpMgrs[i].initialize( localAddr);
        
        rtpMgrs[i].addTarget( destAddr);
        
        System.err.println( "Created RTP session: " + ipAddress + " " + port);
        
        sendStream = rtpMgrs[i].createSendStream(dataOutput, i);        
        sendStream.start();
        } catch (Exception  e) {
        return e.getMessage();
        }
    }

    return null;
    }


    /**
     * For JPEG and H263, we know that they only work for particular
     * sizes.  So we'll perform extra checking here to make sure they
     * are of the right sizes.
     
*/
    Format checkForVideoSizes(Format original, Format supported) {

    int width, height;
    Dimension size = ((VideoFormat)original).getSize();
    Format jpegFmt = new Format(VideoFormat.JPEG_RTP);
    Format h263Fmt = new Format(VideoFormat.H263_RTP);

    if (supported.matches(jpegFmt)) {
        // For JPEG, make sure width and height are divisible by 8.
        width = (size.width % 8 == 0 ? size.width :
                (int)(size.width / 8) * 8);
        height = (size.height % 8 == 0 ? size.height :
                (int)(size.height / 8) * 8);
    } else if (supported.matches(h263Fmt)) {
        // For H.263, we only support some specific sizes.
        if (size.width < 128) {
        width = 128;
        height = 96;
        } else if (size.width < 176) {
        width = 176;
        height = 144;
        } else {
        width = 352;
        height = 288;
        }
    } else {
        // We don't know this particular format.  We'll just
        
// leave it alone then.
        return supported;
    }

    return (new VideoFormat(null
                new Dimension(width, height), 
                Format.NOT_SPECIFIED,
                null,
                Format.NOT_SPECIFIED)).intersects(supported);
    }


    /**
     * Setting the encoding quality to the specified value on the JPEG encoder.
     * 0.5 is a good default.
     
*/
    void setJPEGQuality(Player p, float val) {

    Control cs[] = p.getControls();
    QualityControl qc = null;
    VideoFormat jpegFmt = new VideoFormat(VideoFormat.JPEG);

    // Loop through the controls to find the Quality control for
     
// the JPEG encoder.
    for (int i = 0; i < cs.length; i++) {

        if (cs[i] instanceof QualityControl &&
        cs[i] instanceof Owned) {
        Object owner = ((Owned)cs[i]).getOwner();

        // Check to see if the owner is a Codec.
        
// Then check for the output format.
        if (owner instanceof Codec) {
            Format fmts[] = ((Codec)owner).getSupportedOutputFormats(null);
            for (int j = 0; j < fmts.length; j++) {
            if (fmts[j].matches(jpegFmt)) {
                qc = (QualityControl)cs[i];
                    qc.setQuality(val);
                System.err.println("- Setting quality to " + 
                    val + " on " + qc);
                break;
            }
            }
        }
        if (qc != null)
            break;
        }
    }
    }


    /****************************************************************
     * Convenience methods to handle processor's state changes.
     ***************************************************************
*/
    
    private Integer stateLock = new Integer(0);
    private boolean failed = false;
    
    Integer getStateLock() {
    return stateLock;
    }

    void setFailed() {
    failed = true;
    }
    
    private synchronized boolean waitForState(Processor p, int state) {
    p.addControllerListener(new StateListener());
    failed = false;

    // Call the required method on the processor
    if (state == Processor.Configured) {
        p.configure();
    } else if (state == Processor.Realized) {
        p.realize();
    }
    
    // Wait until we get an event that confirms the
    
// success of the method, or a failure event.
    
// See StateListener inner class
    while (p.getState() < state && !failed) {
        synchronized (getStateLock()) {
        try {
            getStateLock().wait();
        } catch (InterruptedException ie) {
            return false;
        }
        }
    }

    if (failed)
        return false;
    else
        return true;
    }

    /****************************************************************
     * Inner Classes
     ***************************************************************
*/

    class StateListener implements ControllerListener {

    public void controllerUpdate(ControllerEvent ce) {

        // If there was an error during configure or
        
// realize, the processor will be closed
        if (ce instanceof ControllerClosedEvent)
        setFailed();

        // All controller events, send a notification
        
// to the waiting thread in waitForState method.
        if (ce instanceof ControllerEvent) {
        synchronized (getStateLock()) {
            getStateLock().notifyAll();
        }
        }
    }
    }


    /****************************************************************
     * Sample Usage for AVTransmit2 class
     ***************************************************************
*/
    
    public static void main(String [] args) {
    // We need three parameters to do the transmission
    
// For example,
    
//   java AVTransmit2 file:/C:/media/test.mov  129.130.131.132 42050
    
    if (args.length < 3) {
        prUsage();
    }

    Format fmt = null;
    int i = 0;

    // Create a audio transmit object with the specified params.
    AVTransmit2 at = new AVTransmit2(new MediaLocator(args[i]),
                         args[i+1], args[i+2], fmt);
    // Start the transmission
    String result = at.start();

    // result will be non-null if there was an error. The return
    
// value is a String describing the possible error. Print it.
    if (result != null) {
        System.err.println("Error : " + result);
        System.exit(0);
    }
    
    System.err.println("Start transmission for 60 seconds...");

    // Transmit for 60 seconds and then close the processor
    
// This is a safeguard when using a capture data source
    
// so that the capture device will be properly released
    
// before quitting.
    
// The right thing to do would be to have a GUI with a
    
// "Stop" button that would call stop on AVTransmit2
    try {
        Thread.currentThread().sleep(60000);
    } catch (InterruptedException ie) {
    }

    // Stop the transmission
    at.stop();
    
    System.err.println("...transmission ended.");

    System.exit(0);
    }


    static void prUsage() {
    System.err.println("Usage: AVTransmit2 <sourceURL> <destIP> <destPortBase>");
    System.err.println("     <sourceURL>: input URL or file name");
    System.err.println("     <destIP>: multicast, broadcast or unicast IP address for the transmission");
    System.err.println("     <destPortBase>: network port numbers for the transmission.");
    System.err.println("                     The first track will use the destPortBase.");
    System.err.println("                     The next track will use destPortBase + 2 and so on.\n");
    System.exit(0);
    }
}
 

                Receiving Audio and Video using RTP
 

AVReceive2 uses the RTPManager API to receive RTP transmissions.
AVReceive2 performs the following tasks:

  •  Open one RTP session per session address given.
  •  Listen for the NewReceiveStreamEvent from the ReceiveStreamListener.
  •  Create a JMF Player for each stream received for playback.
This sample program can be used in conjunction with JMStudio,
the AVTransmit sample or any other RTP compliant transmitter.
  • The IP address should be the address of the computer which transmits the data;

  • or the multicast address if multicast is being used for the transmission.
  • The ports should be the same as what's being used by the  transmitter.
 
How to run this sample

           Run AVReceive2 and specify the RTP session addresses to receive from.

           For example,

           > java AVReceive2  224.122.122.122/8800  224.122.122.122/8802

          to simultaneously receive 2 different RTP sessions.

Source Code
                          AVReceive2.java

/*
 * @(#)AVReceive2.java    1.3 01/03/13
 *
 * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 
*/

import java.io.*;
import java.awt.*;
import java.net.*;
import java.awt.event.*;
import java.util.Vector;

import javax.media.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.rtp.rtcp.*;
import javax.media.protocol.*;
import javax.media.protocol.DataSource;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;
import javax.media.Format;
import javax.media.format.FormatChangeEvent;
import javax.media.control.BufferControl;


/**
 * AVReceive2 to receive RTP transmission using the new RTP API.
 
*/
public class AVReceive2 implements ReceiveStreamListener, SessionListener, 
    ControllerListener
{
    String sessions[] = null;
    RTPManager mgrs[] = null;
    Vector playerWindows = null;

    boolean dataReceived = false;
    Object dataSync = new Object();


    public AVReceive2(String sessions[]) {
    this.sessions = sessions;
    }

    protected boolean initialize() {

        try {
        InetAddress ipAddr;
        SessionAddress localAddr = new SessionAddress();
        SessionAddress destAddr;

        mgrs = new RTPManager[sessions.length];
        playerWindows = new Vector();

        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) {
            System.err.println("Failed to parse the session address given: " + sessions[i]);
            return false;
        }

        System.err.println("  - Open RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl);

        mgrs[i] = (RTPManager) RTPManager.newInstance();
        mgrs[i].addSessionListener(this);
        mgrs[i].addReceiveStreamListener(this);

        ipAddr = InetAddress.getByName(session.addr);

        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( InetAddress.getLocalHost(),
                                 session.port);
                    destAddr = new SessionAddress( ipAddr, session.port);
        }
            
        mgrs[i].initialize( localAddr);

        // 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);
        }

        } catch (Exception e){
            System.err.println("Cannot create the RTP Session: " + e.getMessage());
            return false;
        }

    // Wait for data to arrive before moving on.

    long then = System.currentTimeMillis();
    long waitingPeriod = 30000;  // wait for a maximum of 30 secs.

    try{
        synchronized (dataSync) {
        while (!dataReceived && 
            System.currentTimeMillis() - then < waitingPeriod) {
            if (!dataReceived)
            System.err.println("  - Waiting for RTP data to arrive...");
            dataSync.wait(1000);
        }
        }
    } catch (Exception e) {}

    if (!dataReceived) {
        System.err.println("No RTP data was received.");
        close();
        return false;
    }

        return true;
    }


    public boolean isDone() {
    return playerWindows.size() == 0;
    }


    /**
     * Close the players and the session managers.
     
*/
    protected void close() {

    for (int i = 0; i < playerWindows.size(); i++) {
        try {
        ((PlayerWindow)playerWindows.elementAt(i)).close();
        } catch (Exception e) {}
    }

    playerWindows.removeAllElements();

    // close the RTP session.
    for (int i = 0; i < mgrs.length; i++) {
        if (mgrs[i] != null) {
                mgrs[i].removeTargets( "Closing session from AVReceive2");
                mgrs[i].dispose();
        mgrs[i] = null;
        }
    }
    }


    PlayerWindow find(Player p) {
    for (int i = 0; i < playerWindows.size(); i++) {
        PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
        if (pw.player == p)
        return pw;
    }
    return null;
    }


    PlayerWindow find(ReceiveStream strm) {
    for (int i = 0; i < playerWindows.size(); i++) {
        PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
        if (pw.stream == strm)
        return pw;
    }
    return null;
    }


    /**
     * SessionListener.
     
*/
    public synchronized void update(SessionEvent evt) {
    if (evt instanceof NewParticipantEvent) {
        Participant p = ((NewParticipantEvent)evt).getParticipant();
        System.err.println("  - A new participant had just joined: " + p.getCNAME());
    }
    }


    /**
     * ReceiveStreamListener
     
*/
    public synchronized void update( ReceiveStreamEvent evt) {

    RTPManager mgr = (RTPManager)evt.getSource();
    Participant participant = evt.getParticipant();    // could be null.
    ReceiveStream stream = evt.getReceiveStream();  // could be null.

    if (evt instanceof RemotePayloadChangeEvent) {
     
        System.err.println("  - Received an RTP PayloadChangeEvent.");
        System.err.println("Sorry, cannot handle payload change.");
        System.exit(0);

    }
    
    else if (evt instanceof NewReceiveStreamEvent) {

        try {
        stream = ((NewReceiveStreamEvent)evt).getReceiveStream();
        DataSource ds = stream.getDataSource();

        // Find out the formats.
        RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");
        if (ctl != null){
            System.err.println("  - Recevied new RTP stream: " + ctl.getFormat());
        } else
            System.err.println("  - Recevied new RTP stream");

        if (participant == null)
            System.err.println("      The sender of this stream had yet to be identified.");
        else {
            System.err.println("      The stream comes from: " + participant.getCNAME()); 
        }

        // create a player by passing datasource to the Media Manager
        Player p = javax.media.Manager.createPlayer(ds);
        if (p == null)
            return;

        p.addControllerListener(this);
        p.realize();
        PlayerWindow pw = new PlayerWindow(p, stream);
        playerWindows.addElement(pw);

        // Notify intialize() that a new stream had arrived.
        synchronized (dataSync) {
            dataReceived = true;
            dataSync.notifyAll();
        }

        } catch (Exception e) {
        System.err.println("NewReceiveStreamEvent exception " + e.getMessage());
        return;
        }
        
    }

    else if (evt instanceof StreamMappedEvent) {

         if (stream != null && stream.getDataSource() != null) {
        DataSource ds = stream.getDataSource();
        // Find out the formats.
        RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");
        System.err.println("  - The previously unidentified stream ");
        if (ctl != null)
            System.err.println("      " + ctl.getFormat());
        System.err.println("      had now been identified as sent by: " + participant.getCNAME());
         }
    }

    else if (evt instanceof ByeEvent) {

         System.err.println("  - Got \"bye\" from: " + participant.getCNAME());
         PlayerWindow pw = find(stream);
         if (pw != null) {
        pw.close();
        playerWindows.removeElement(pw);
         }
    }

    }


    /**
     * ControllerListener for the Players.
     
*/
    public synchronized void controllerUpdate(ControllerEvent ce) {

    Player p = (Player)ce.getSourceController();

    if (p == null)
        return;

    // Get this when the internal players are realized.
    if (ce instanceof RealizeCompleteEvent) {
        PlayerWindow pw = find(p);
        if (pw == null) {
        // Some strange happened.
        System.err.println("Internal error!");
        System.exit(-1);
        }
        pw.initialize();
        pw.setVisible(true);
        p.start();
    }

    if (ce instanceof ControllerErrorEvent) {
        p.removeControllerListener(this);
        PlayerWindow pw = find(p);
        if (pw != null) {
        pw.close();    
        playerWindows.removeElement(pw);
        }
        System.err.println("AVReceive2 internal error: " + ce);
    }

    }


    /**
     * A utility class to parse the session addresses.
     
*/
    class SessionLabel {

    public String addr = null;
    public int port;
    public int ttl = 1;

    SessionLabel(String session) throws IllegalArgumentException {

        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) {
        try {
            Integer integer = Integer.valueOf(portStr);
            if (integer != null)
            port = integer.intValue();
        } catch (Throwable t) {
            throw new IllegalArgumentException();
        }
        } 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();
        }
        }
    }
    }


    /**
     * GUI classes for the Player.
     
*/
    class PlayerWindow extends Frame {

    Player player;
    ReceiveStream stream;

    PlayerWindow(Player p, ReceiveStream strm) {
        player = p;
        stream = strm;
    }

    public void initialize() {
        add(new PlayerPanel(player));
    }

    public void close() {
        player.close();
        setVisible(false);
        dispose();
    }

    public void addNotify() {
        super.addNotify();
        pack();
    }
    }


    /**
     * GUI classes for the Player.
     
*/
    class PlayerPanel extends Panel {

    Component vc, cc;

    PlayerPanel(Player p) {
        setLayout(new BorderLayout());
        if ((vc = p.getVisualComponent()) != null)
        add("Center", vc);
        if ((cc = p.getControlPanelComponent()) != null)
        add("South", cc);
    }

    public Dimension getPreferredSize() {
        int w = 0, h = 0;
        if (vc != null) {
        Dimension size = vc.getPreferredSize();
        w = size.width;
        h = size.height;
        }
        if (cc != null) {
        Dimension size = cc.getPreferredSize();
        if (w == 0)
            w = size.width;
        h += size.height;
        }
        if (w < 160)
        w = 160;
        return new Dimension(w, h);
    }
    }


    public static void main(String argv[]) {
    if (argv.length == 0)
        prUsage();

    AVReceive2 avReceive = new AVReceive2(argv);
    if (!avReceive.initialize()) {
        System.err.println("Failed to initialize the sessions.");
        System.exit(-1);
    }

    // Check to see if AVReceive2 is done.
    try {
        while (!avReceive.isDone())
        Thread.sleep(1000);
    } catch (Exception e) {}

    System.err.println("Exiting AVReceive2");
    }


    static void prUsage() {
    System.err.println("Usage: AVReceive2 <session> <session> ...");
    System.err.println("     <session>: <address>/<port>/<ttl>");
    System.exit(0);
    }

}// end of AVReceive2
原文地址:https://www.cnblogs.com/cuizhf/p/2219282.html