Transmit.java
Upload User: liulanlin
Upload Date: 2017-12-08
Package Size: 1274k
Code Size: 22k
Category:

VOIP program

Development Platform:

Java

  1. /*
  2.  * Transmit.java
  3.  *
  4.  * Created on November 19, 2003, 10:38 AM
  5.  */
  6. package gov.nist.applet.phone.media.transmitter;
  7. import java.awt.*;
  8. import java.io.*;
  9. import java.net.InetAddress;
  10. import java.net.Socket;
  11. import javax.media.*;
  12. import java.util.*;
  13. import javax.media.protocol.*;
  14. import javax.media.protocol.DataSource;
  15. import javax.media.format.*;
  16. import javax.media.control.TrackControl;
  17. import javax.media.control.QualityControl;
  18. import javax.media.rtp.*;
  19. import javax.media.rtp.rtcp.*;
  20. import gov.nist.applet.phone.media.util.*;
  21. import gov.nist.applet.phone.media.receiver.*;
  22. import gov.nist.applet.phone.media.protocol.transport.*;
  23. /**
  24.  * Class to send RTP using the JMF RTP API.
  25.  * @author DERUELLE Jean
  26.  */
  27. public class Transmit {
  28.     private Processor processor = null;
  29.     private RTPManager rtpMgrs[] = null;
  30.     private DataSource dataOutput = null;
  31.     private StateListener stateListener=null;
  32.     private SessionDescription sessionDescription =null;
  33.     private String session=null;
  34.     private MediaLocator videoLocator=null;
  35.     private MediaLocator audioLocator=null;
  36.     private RTPManager rtpManager = null;
  37.     private SessionAddress remoteAddress =null;
  38.     private Receiver receiver=null;
  39.     private Socket socketRTPTransmit=null;
  40.     private Socket socketRTCPTransmit=null;
  41.     /**
  42.      * Constructor for Transmitter 
  43.      * @param session - the concatened parameters of the session stored in a string
  44.      */
  45.     public Transmit(String session) throws IllegalArgumentException{
  46.         this.session=session;
  47.         stateListener=new StateListener();
  48.         //the session Label containing the address, the port and the Time To Live
  49.         try {
  50.             //create a session label on the session given in argument
  51.             // and parse the session address.
  52.             sessionDescription =new SessionDescription(session);
  53.             sessionDescription.setAudioFormat("6");
  54.             sessionDescription.setVideoFormat("h263/rtp");
  55.             sessionDescription.setTransportProtocol("udp");
  56.         } catch (IllegalArgumentException e) {
  57.             System.err.println("Failed to parse the session address given: " + session);
  58.             throw e;
  59.         }
  60.         if(!initialize()){
  61.             System.out.println("Bad Session intialization");
  62.             //System.exit(0);
  63.         }
  64.     }
  65.     /**
  66.      * Constructor for Transmitter 
  67.      * @param session - the session Description containing the address, the port, the Time To Live
  68.      * the video format, the audio format and the transport protocol
  69.      */
  70.     public Transmit(SessionDescription session,Receiver receiver) {
  71.         this.receiver=receiver;
  72.         this.sessionDescription=session;
  73.         stateListener=new StateListener();
  74.         if(!initialize()){
  75.             System.out.println("Bad Session intialization");
  76.             //System.exit(0);
  77.         }
  78.     }
  79.     /**
  80.      * get the devices for the capture and print their formats
  81.      * @return true if all has been initialized correctly
  82.      */
  83.     protected boolean initialize() {
  84.         CaptureDeviceInfo videoCDI=null;
  85.         CaptureDeviceInfo audioCDI=null;
  86.         Vector captureDevices=null;
  87.         captureDevices= CaptureDeviceManager.getDeviceList(null);
  88.         System.out.println("- number of capture devices: "+captureDevices.size() );
  89.         CaptureDeviceInfo cdi=null;
  90.         for (int i = 0; i < captureDevices.size(); i++) {
  91.         cdi = (CaptureDeviceInfo) captureDevices.elementAt(i);
  92.         //System.out.println("    - name of the capture device: "+cdi.getName() );
  93.             Format[] formatArray=cdi.getFormats();
  94.             for (int j = 0; j < formatArray.length; j++) {
  95.                 Format format=formatArray[j];
  96.                 if (format instanceof VideoFormat) {
  97.                     //System.out.println("         - format accepted by this VIDEO device: "+
  98.                     //format.toString().trim());
  99.                     if (videoCDI ==null) {
  100.                         videoCDI=cdi;
  101.                     }
  102.                }
  103.                else if (format instanceof AudioFormat) {
  104.                     //System.out.println("         - format accepted by this AUDIO device: "+
  105.                     //format.toString().trim());
  106.                     if (audioCDI == null) {
  107.                         audioCDI=cdi;
  108.                     }
  109.                }
  110.                //else
  111.                    //System.out.println("         - format of type UNKNOWN");
  112.             }
  113.         }
  114.         if(videoCDI!=null)
  115.             videoLocator=videoCDI.getLocator();
  116.         if(audioCDI!=null)
  117.             audioLocator=audioCDI.getLocator();
  118.         return true;
  119.     }
  120.     /**
  121.      * Starts the transmission. Returns null if transmission started ok.
  122.      * Otherwise it returns a string with the reason why the setup failed.
  123.      */
  124.     public synchronized String start(String localIpAddress) {
  125.         String result;
  126.         // Create a processor for the specified media locator
  127.         // and program it to output JPEG/RTP
  128.         result = createProcessor();
  129.         if (result != null)
  130.             return result;
  131.         // Create an RTP session to transmit the output of the
  132.         // processor to the specified IP address and port no.
  133.         result = createTransmitter(localIpAddress);
  134.         if (result != null) {
  135.             processor.close();
  136.             processor = null;
  137.             return result;
  138.         }
  139.         // Start the transmission
  140.         processor.start();
  141.         return null;
  142.     }
  143.     /**
  144.      * Stops the transmission if already started
  145.      */
  146.     public void stop() {
  147.         synchronized (this) {
  148.             if (processor != null) {
  149.                 processor.stop();
  150.                 processor.close();
  151.                 processor = null;                
  152.             }
  153.             if(rtpMgrs!=null){            
  154. for (int i = 0; i < rtpMgrs.length; i++) {
  155. if(rtpMgrs[i]!=null){
  156. rtpMgrs[i].removeTargets( "Session ended.");
  157. rtpMgrs[i].dispose();
  158. rtpMgrs[i]=null;
  159. }
  160. }
  161.             }
  162.         }
  163.     }
  164.     /**
  165.      * Creates the processor
  166.      */
  167.     private String createProcessor() {
  168.         DataSource audioDS=null;
  169.         DataSource videoDS=null;
  170.         DataSource mergeDS=null;
  171.         StateListener stateListener=new StateListener();
  172.         //create the DataSource
  173.         //it can be a 'video' DataSource, an 'audio' DataSource
  174.         //or a combination of audio and video by merging both
  175.         if (videoLocator == null && audioLocator == null)
  176.             return "Locator is null";
  177.         if (audioLocator != null){
  178.             try {
  179.                 //create the 'audio' DataSource
  180.                 audioDS= javax.media.Manager.createDataSource(audioLocator);
  181.             } catch (Exception e) {
  182.                 System.out.println("-> Couldn't connect to audio capture device");
  183.             }
  184.         }
  185.         if (videoLocator != null){
  186.             try {
  187.                 //create the 'video' DataSource
  188.                 videoDS = javax.media.Manager.createDataSource(videoLocator);
  189.             } catch (Exception e) {
  190.                 System.out.println("-> Couldn't connect to video capture device");
  191.             }
  192.         }
  193.         if(videoDS!=null && audioDS!=null){
  194.             try {
  195.                 //create the 'audio' and 'video' DataSource
  196.                 mergeDS = javax.media.Manager.createMergingDataSource(new DataSource [] {audioDS, videoDS});
  197.             } catch (Exception e) {
  198.                 System.out.println("-> Couldn't connect to audio or video capture device");
  199.             }
  200.             try{
  201.                 //Create the processor from the merging DataSource
  202.                 processor = javax.media.Manager.createProcessor(mergeDS);
  203.             }
  204.             catch (NoProcessorException npe) {
  205.                 return "Couldn't create processor";
  206.             } catch (IOException ioe) {
  207.                 return "IOException creating processor";
  208.             } 
  209.         }
  210.         //if the processor has not been created from the merging DataSource
  211.         if(processor==null){
  212.             try {
  213.                 if(audioDS!=null)
  214.                     //Create the processor from the 'audio' DataSource
  215.                     processor = javax.media.Manager.createProcessor(audioDS);
  216.                 else
  217.                     //Create the processor from the 'video' DataSource
  218.                     processor = javax.media.Manager.createProcessor(videoDS);
  219.             } catch (NoProcessorException npe) {
  220.                 return "Couldn't create processor";
  221.             } catch (IOException ioe) {
  222.                 return "IOException creating processor";
  223.             } 
  224.         }
  225.         // Wait for it to configure
  226.         boolean result = stateListener.waitForState(processor, Processor.Configured);
  227.         if (result == false)
  228.             return "Couldn't configure processor";
  229.         // Get the tracks from the processor
  230.         TrackControl [] tracks = processor.getTrackControls();
  231.         // Do we have atleast one track?
  232.         if (tracks == null || tracks.length < 1)
  233.             return "Couldn't find tracks in processor";
  234.         // Set the output content descriptor to RAW_RTP
  235.         // This will limit the supported formats reported from
  236.         // Track.getSupportedFormats to only valid RTP formats.
  237.         ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
  238.         processor.setContentDescriptor(cd);
  239.         Format supported[];
  240.         Format chosen=null;
  241.         boolean atLeastOneTrack = false;
  242.         // Program the tracks.
  243.         for (int i = 0; i < tracks.length; i++) {
  244.             Format format = tracks[i].getFormat();
  245.             if (tracks[i].isEnabled()) {
  246.                 /*if (tracks[i] instanceof VideoFormat) 
  247.                     System.out.println("Supported Video Formats :");
  248.                 else
  249.                     System.out.println("Supported Audio Formats :");*/
  250.                 supported = tracks[i].getSupportedFormats();
  251.                 /*System.out.println("track : "+ i);
  252.                 for(int j=0;j<supported.length;j++)
  253.                 System.out.println("Supported format : "+supported[j].getEncoding());*/
  254.                 // We've set the output content to the RAW_RTP.
  255.                 // So all the supported formats should work with RTP.            
  256.                 if (supported.length > 0) {
  257.                     for(int j=0;j<supported.length;j++){
  258.                         //System.out.println("Supported format : "+supported[j].toString().toLowerCase());
  259.                         if (supported[j] instanceof VideoFormat) {
  260.                             // For video formats, we should double check the
  261.                             // sizes since not all formats work in all sizes.
  262.                             if(sessionDescription.getVideoFormat()!=null)
  263.                                 if(supported[j].toString().toLowerCase().indexOf(
  264.                                     sessionDescription.getVideoFormat().toLowerCase())!=-1)
  265.                                     chosen = checkForVideoSizes(tracks[i].getFormat(), 
  266.                                                                 supported[j]);
  267.                         } else {
  268.                             if(sessionDescription.getAudioFormat()!=null)
  269.                                 if(supported[j].toString().toLowerCase().indexOf(
  270.                                 sessionDescription.getAudioFormat().toLowerCase())!=-1)
  271.                                     chosen = supported[j];  
  272.                         }
  273.                     }
  274.                     if(chosen!=null){
  275.                         tracks[i].setFormat(chosen);                
  276.                         System.err.println("Track " + i + " is set to transmit as:");
  277.                         System.err.println("  " + chosen);
  278.                         atLeastOneTrack = true;
  279.                     }
  280.                 } else
  281.                     tracks[i].setEnabled(false);
  282.             } else
  283.                 tracks[i].setEnabled(false);
  284.         }
  285.         if (!atLeastOneTrack)
  286.             return "Couldn't set any of the tracks to a valid RTP format";
  287.         // Realize the processor. This will internally create a flow
  288.         // graph and attempt to create an output datasource for JPEG/RTP
  289.         // audio frames.
  290.         result = stateListener.waitForState(processor, Controller.Realized);
  291.         if (result == false)
  292.             return "Couldn't realize processor";
  293.         // Set the JPEG quality to .5.
  294.         setJPEGQuality(processor, 0.25f);
  295.         // Get the output data source of the processor
  296.         dataOutput = processor.getDataOutput();
  297.         return null;
  298.     }
  299.     /**
  300.      * Use the RTPManager API to create sessions for each media 
  301.      * track of the processor.
  302.      */
  303.     private String createTransmitter(String localIpAddress) {
  304.         // Cheated.  Should have checked the type.
  305.         PushBufferDataSource pbds = (PushBufferDataSource)dataOutput;
  306.         PushBufferStream pbss[] = pbds.getStreams();
  307.         rtpMgrs = new RTPManager[pbss.length];
  308.         SessionAddress localAddr, destAddr;
  309.         InetAddress ipAddr;
  310.         SendStream sendStream;
  311.         int localPort;
  312.         int destPort;
  313.         SourceDescription srcDesList[];
  314.         for (int i = 0; i < pbss.length; i++) {
  315.             try {
  316.                 //New instance of RTPManager
  317.                 //to handle the RTP and RTCP transmission
  318.                 rtpMgrs[i] = RTPManager.newInstance();                     
  319.                 destPort = sessionDescription.getDestinationPort() + 2*i;
  320.                 ipAddr = InetAddress.getByName(sessionDescription.getAddress());
  321.                 destAddr = new SessionAddress( ipAddr, destPort);
  322.                 localPort = sessionDescription.getLocalPort() + 2*i;
  323.                 localAddr = new SessionAddress( 
  324.                  InetAddress.getByName(localIpAddress),
  325.                     localPort);
  326.                 //Establishing the connection with the remote host 
  327.                 //with the chosen underlying protocol to RTP (either UDP or TCP)
  328.                 if(sessionDescription.getTransportProtocol().toLowerCase().equals("tcp")){
  329.                     if(receiver==null){
  330.                         boolean connected=false;
  331.                         System.out.println("Trying to connect to "+ipAddr+"/"+destPort);
  332.                         //Trying to connect to the TCP Port of the remote host to establish a RTP connection
  333.                         while(!connected){
  334.                             try{
  335.                                 socketRTPTransmit = new Socket(ipAddr,destPort);
  336.                                 System.out.println("Socket connected to "+ipAddr+
  337.                                                     " on port "+destPort);
  338.                                 connected=true;
  339.                             }
  340.                             catch(IOException ioe){
  341.                             }
  342.                         }
  343.                         connected=false;
  344.                         int rtcpDestPort=destPort+1;
  345.                         int rtcpLocalPort=localPort+1;
  346.                         System.out.println("Trying to connect to "+ipAddr+"/"+rtcpDestPort);
  347.                         //Trying to connect to the TCP Port of the remote host to establish a RTCP connection
  348.                         while(!connected){
  349.                             try{
  350.                                 socketRTCPTransmit = new Socket(ipAddr, rtcpDestPort);     
  351.                                 System.out.println("Control Socket connected to "+ipAddr+
  352.                                                     " on port "+rtcpDestPort);
  353.                                 connected=true;
  354.                             }
  355.                             catch(IOException ioe){
  356.                             }
  357.                         }                             
  358.                     }
  359.                     else{
  360.                         socketRTPTransmit=receiver.getSocketRTPReceiver();
  361.                         socketRTCPTransmit=receiver.getSocketRTCPReceiver();
  362.                     }
  363.                     rtpMgrs[i].initialize( new TCPSendAdapter(socketRTPTransmit,socketRTCPTransmit));
  364.                 }
  365.                 else{
  366. SessionAddress localAddress = new SessionAddress(
  367. InetAddress.getByName(localIpAddress),
  368. destPort);
  369. rtpMgrs[i].initialize(localAddress);
  370. SessionAddress destAddress = new SessionAddress(
  371. InetAddress.getByName(sessionDescription.getAddress()), 
  372. destPort);                
  373. rtpMgrs[i].addTarget(destAddress);                 
  374.                 }
  375.                 System.err.println( 
  376. "Created RTP session: " + 
  377. sessionDescription.getAddress()+ 
  378. " dest "+ 
  379. destPort);
  380.                 //Start the transmission with the remote host
  381.                 sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
  382.                 sendStream.start();
  383.             } catch (Exception  e) {
  384.                 e.printStackTrace();
  385.             }
  386.         }
  387.         return null;
  388.     }
  389.     /**
  390.      * For JPEG and H263, we know that they only work for particular
  391.      * sizes.  So we'll perform extra checking here to make sure they
  392.      * are of the right sizes.
  393.      */
  394.     private Format checkForVideoSizes(Format original, Format supported) {
  395.         int width, height;
  396.         Dimension size = ((VideoFormat)original).getSize();
  397.         Format jpegFmt = new Format(VideoFormat.JPEG_RTP);
  398.         Format h263Fmt = new Format(VideoFormat.H263_RTP);
  399.         if (supported.matches(jpegFmt)) {
  400.             // For JPEG, make sure width and height are divisible by 8.
  401.             width = (size.width % 8 == 0 ? size.width :
  402.                             (int)(size.width / 8) * 8);
  403.             height = (size.height % 8 == 0 ? size.height :
  404.                             (int)(size.height / 8) * 8);
  405.         } else if (supported.matches(h263Fmt)) {
  406.             // For H.263, we only support some specific sizes.
  407.             if (size.width < 128) {
  408.             width = 128;
  409.             height = 96;
  410.             } else if (size.width < 176) {
  411.             width = 176;
  412.             height = 144;
  413.             } else {
  414.             width = 352;
  415.             height = 288;
  416.             }
  417.         } else {
  418.             // We don't know this particular format.  We'll just
  419.             // leave it alone then.
  420.             return supported;
  421.         }
  422.         return (new VideoFormat(null, 
  423.                                 new Dimension(width, height), 
  424.                                 Format.NOT_SPECIFIED,
  425.                                 null,
  426.                                 Format.NOT_SPECIFIED)).intersects(supported);
  427.     }
  428.     /**
  429.      * Setting the encoding quality to the specified value on the JPEG encoder.
  430.      * 0.5 is a good default.
  431.      */
  432.     void setJPEGQuality(Player p, float val) {
  433.         Control cs[] = p.getControls();
  434.         QualityControl qc = null;
  435.         VideoFormat jpegFmt = new VideoFormat(VideoFormat.JPEG);
  436.         // Loop through the controls to find the Quality control for
  437.         // the JPEG encoder.
  438.         for (int i = 0; i < cs.length; i++) {
  439.             if (cs[i] instanceof QualityControl && cs[i] instanceof Owned) {
  440.                 Object owner = ((Owned)cs[i]).getOwner();
  441.                 // Check to see if the owner is a Codec.
  442.                 // Then check for the output format.
  443.                 if (owner instanceof Codec) {
  444.                     Format fmts[] = ((Codec)owner).getSupportedOutputFormats(null);
  445.                     for (int j = 0; j < fmts.length; j++) {
  446.                     if (fmts[j].matches(jpegFmt)) {
  447.                         qc = (QualityControl)cs[i];
  448.                         qc.setQuality(val);
  449.                         System.err.println("- Setting quality to " + 
  450.                                 val + " on " + qc);
  451.                         break;
  452.                     }
  453.                 }
  454.             }
  455.             if (qc != null)
  456.                 break;
  457.             }
  458.         }
  459.     }
  460.     /**
  461.      * Start the RTP transmission
  462.      */
  463.     public void transmit(String localIpAddress){
  464.         Format fmt = null;
  465.         int i = 0;
  466.         // Start the transmission
  467.         String result = this.start(localIpAddress);
  468.         // result will be non-null if there was an error. The return
  469.         // value is a String describing the possible error. Print it.
  470.         if (result != null) {
  471.             System.err.println("Error : " + result);
  472.             //System.exit(0);
  473.         }
  474.         System.err.println("Start transmission... ");
  475.     }       
  476.     
  477.     public Socket getSocketRTPTransmit(){
  478.         return socketRTPTransmit;
  479.     }
  480.     
  481.     public Socket getSocketRTCPTransmit(){
  482.         return socketRTCPTransmit;
  483.     }
  484.     /****************************************************************
  485.      * Sample Usage for AVTransmit class                            *
  486.      ****************************************************************/
  487.     public static void main(String [] args) {
  488.         if (args.length < 1) {
  489.             prUsage();
  490.         }
  491.         // Create a transmit object with the specified params.
  492.         Transmit transmit = new Transmit(args[0]);
  493.         // Start the media transmission
  494.         String result = transmit.start("127.0.0.1");
  495.         // result will be non-null if there was an error. The return
  496.         // value is a String describing the possible error. Print it.
  497.         if (result != null) {
  498.             System.err.println("Error : " + result);
  499.             //System.exit(0);
  500.         }
  501.         System.err.println("Start transmission for 60 seconds...");
  502.         // Transmit for 60 seconds and then close the processor
  503.         // This is a safeguard when using a capture data source
  504.         // so that the capture device will be properly released
  505.         // before quitting.
  506.         // The right thing to do would be to have a GUI with a
  507.         // "Stop" button that would call stop on AVTransmit2
  508.         try {
  509.             Thread.sleep(60000);
  510.         } catch (InterruptedException ie) {
  511.             ie.printStackTrace();
  512.         }
  513.         // Stop the transmission
  514.         transmit.stop();
  515.         System.err.println("...transmission ended.");
  516.     }
  517.     static void prUsage() {
  518.         System.err.println("Usage: AVTransmit <IPAddress>/<LocalPort>/<DestPort>");
  519.         //System.exit(0);
  520.     }
  521. }