package nxm.sys.net; //not needed for non-nxm use

// Import Java encryption libraries - Since 3.5.4 for Bug 2921
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
import java.util.zip.Inflater;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import nxm.sys.lib.Convert;
import nxm.sys.lib.ICEPacket;
import nxm.sys.lib.Shell;

/**
  DatagramData - reads data inputs (Raw, RTP, SAP) from Multicast or unicast
                 streams. Modified on 22FEB2006 to allow reading of all streams
                 on a given port.
  @author MSZ / JPH
  @deprecated Use {@link nxm.net.lib.DatagramData} instead - since 3.7.2
*/
@Deprecated
public class DatagramData {
  /** This defines a interface for a class that accepts a call-back with the
      time from an ICE packet and two doubles in the user data area.
   */
  public static interface IceTimeCallBack {
    /** Specifies the time for the datagram.
        @param jTime  The Julian time (<code>wsec+fsec</code> from the packet).
        @param xStart The x start     (<code>   x2    </code> from the packet).
        @param xDelta The x delta     (<code>   x1    </code> from the packet).
     */
    public void dgramTime(double jTime, double xStart, double xDelta);
  }
  /** This defines a interface for a class that accepts a call-back with the
      time from an ICE packet and four floats in the user data area.
      @since NeXtMidas 2.7.4
   */
  public static interface IceTimeFloatDataCallBack {
    /** Specifies the time for the datagram.
        @param jTime  The Julian time (<code>wsec+fsec</code> from the packet).
        @param p1     the first float data from the packet
        @param p2     the second float data from the packet
        @param p3     the third float data from the packet
        @param p4     the fourth float data from the packet
     */
    public void dgramTimeFloatData(double jTime,float p1,float p2,float p3,float p4);
  }
  /** This defines a interface for a class that accepts a call-back with the
      time from an SDDS packet.
      @since NeXtMidas 2.7.4
   */
  public static interface SDDSTimeDataCallBack {
    /** Specifies the time for the datagram.
        @param jTime  The Julian time calculated from the packet.
    */
    public void dgramTime(double jTime);
  }

  /** This defines an interface for a class that accepts a call-back when an RTCP packet is received.
      Each method call deals with different RTCP type packet.
      @since NeXtMidas 3.1.3
   */
  public static interface RTCPCallBack {
    /** SR - Sender Report 200
     *  @param buf the buffer
     */
    public void RTCP_SR(byte[] buf);
    /** RR - Receiver Report 201
     *  @param buf the buffer
     */
    public void RTCP_RR(byte[] buf);
    /** SDES - Source Description - 202
     *  @param buf the buffer
     */
    public void RTCP_SDES(byte[] buf);
    /** BYE  - Goodbye with optional reason - 203
     *  @param reason the optional reason
     */
    public void RTCP_BYE(String reason);
    /** APP  - APPlication - 204, this assumes text appdata
     *  @param name the name of the application
     *  @param buf  the buffer
     */
    public void RTCP_APP(String name,byte[] buf);
    /** Others like XR,RSI...
     *  @param type the packet type
     *  @param buf  the buffer
     */
    public void RTCP_Other(byte type,byte[] buf);
  }

  //settable by accessor methods
  private int timeOut=200;
  private boolean verbose = false;
  private boolean packetDebug = false;
  private boolean packetDataDebug = false;
  private boolean multicast = false;
  private boolean rtp = true;
  private boolean checkhdr = false;
  private boolean sap = false;
  private boolean fill = false;
  private boolean alladdr = false;
  private boolean isOpen = false;
  /** show socket errors */
  private boolean socketerror = false;
  /** output the ICE Header in the output pipe, else is ignored or uses callbacks */
  private boolean icehdrinpipe = false;
  /** store in "all" icehdr fields so can retrieve via method calls */
  private boolean geticedata = false;
  private String host=new String("127.0.0.1");
  private int port=18800;
  /** MSZ work-around for windows */
  private String osname = (System.getProperty("os.name")).toUpperCase();
  private boolean windows = (osname.startsWith("WINDOWS"));
  private boolean ignore_rtcp = true;
  /** whole sec */
  private double wsec;
  /** fractional sec */
  private double fsec;
  /** timecode offset
      @since NeXtMidas 3.2.2
   */
  private double tcoff;
  private double dp1,dp2;
  private float fp1,fp2,fp3,fp4;
  String ifrmt="";
  int isize=0,ibpa=0;
  ICEPacket lastICE=null;

  //private variables
  private static final int MAXPKTBUF=32768;
  private static int pktBufferSize = MAXPKTBUF;
  private InetAddress addr;
  private DatagramSocket dsock;
  private MulticastSocket msock;
  private int len,offset,ci,boff,ccs,extendflag,xtype,xlen,sublen,temp,ipver,state,encrypt,compress,authlen,datapos,pos,i;
  private int lastseq=-1,llseq=-1,seq=-1,hi,lo,fillnum=0,lasthdr=-1,llhdr=-1;
  /** @since 3.5.4 for Bug 2921 */
  private int timepacktype=-1,timepackpos=0,timepacklen=0,complen=0;
  /** Used to get only RTP packets that have a specific payload number */
  private int rtpfiltertype=-1;
  /** number of packet skips to auto reset the fill on; zero (0) is off */
  private int seqreset=0; //number of packet skips to auto reset the fill on: zero (0) is off

  /** The recipient of ICE time/data call-back.     */ private IceTimeCallBack timeCB = null;
  /** The recipient of ICE time/data call-back.     */ private IceTimeFloatDataCallBack timeDCB = null;
  /** The recipient of SDDS time/data call-back.    */ private SDDSTimeDataCallBack timeSCB = null;
  /** The recipient of RTCP data call back.         */ private RTCPCallBack rtcpCB = null;
  /** Packet start position (in bytes).             */ private int packetStart = 0;
  /** ICE/SDDS packet position in buffer (in bytes).*/ private int PacketPos = -1; //-1=not set

  // Packet Constants Package Public Access - since 3.7.2 for NXM-3048
  /** ICE packet type for a compound packet.  */ public static final int COMPOUND_PACKET = 255;
  /** ICE packet type for type 1 ICE packet.  */ public static final int ICE1_PACKET     =   1;
  /** ICE packet type for type 2 ICE packet.  */ public static final int ICE2_PACKET     =   8;
  /** ICE packet type for type 3 ICE packet.  */ public static final int ICE3_PACKET     =  16;
  /** SDDS packet type                        */ public static final int SDDS_PACKET     =  17;
  /** XSTREAM packet type                     */ public static final int XSTREAM_PACKET  =   4;
  /** MAC packet type                         */ public static final int MAC_PACKET      =  18;

  // encryption related fields - Since 3.5.4 for Bug 2921
  private Inflater decompressor = null;
  private Cipher cipher = null;
  private Key cipherkey = null;
  private PBEKeySpec pbeKeySpec=null;
  private AlgorithmParameterSpec pbeParamSpec = null;
  private String ciphertype="PBEWithSHA1AndDESede"; //PBEWithMD5AndDES,PBEWithMD5AndTripleDES,PBEWithSHA1AndRC2

  private Mac mac = null;
  private int ITERATIONS = 1000;
  private SecretKeySpec signingkey = null;
  private String hmactype="HmacSHA1"; //HmacMD5(128bit),HmacSHA1(160bit),HmacSHA256,HmacSHA384,HmacSHA512
  private byte[] signature = new byte[20];
  private byte[] fakesignature="00000000000000000000".getBytes();
  /** pull from buffer and then put back */
  private byte[] bufholder=new byte[20];
  private boolean sigpass;

  private byte[] tcbuf = new byte[8];
  private static final long ticsPerSecond = 4000000000L;
  long tics,lwsec,lfsec;

  //here to save time rather than in read
  private byte[] buf = new byte[pktBufferSize];
  private byte[] rtcpbuf;
  private DatagramPacket dpacki = new DatagramPacket(buf,buf.length);

  /** rtp packet header byte offsets */
  private static final byte RTPVPXCC=0,RTPMPT=1,RTPSQH=2,RTPSQL=3,RTPTS1=4,RTPTS2=5,RTPTS3=6,RTPTS4=7,
                            RTPSSRC1=8,RTPSSRC2=9,RTPSSRC3=10,RTPSSRC4=11,RTPDATA=12;
     //RTPDATA=12 for case of 0 CSRC, 1 would be->RTPCSRC1=12,RTPCSRC2=13,RTPCSRC3=14,RTPCSRC4=15,RTPDATA=16;

     //  The RTP header has the following format:
     // 0                   1                   2                   3
     // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|V=2|P|X|  CC   |M|     PT      |       sequence number         |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|                           timestamp                           |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|           synchronization source (SSRC) identifier            |
     //+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
     //|            contributing source (CSRC) identifiers             |
     //|                             ....                              |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //The CC in RTPVPXCC tells how many CSRC's (0-15) follow the fixed header
     //If the X bit in the RTP header is one, a variable-length header
     //extension is appended to the RTP header, following the CSRC list if
     //present. The header extension contains a 16-bit length field that
     //counts the number of 32-bit words in the extension, excluding the
     //four-octet extension header (therefore zero is a valid length).
     // 0                   1                   2                   3
     // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|      defined by profile       |           length              |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|                        header extension                       |
     //|                             ....                              |

   //Should be sending out RTCP with ReceiverReport (RR)
   // RTCP has similar format to RTP except 1) there is no X bit so CC is either a count or a subtype and is 1 bit larger
   //                                       2) there is not M bit so the PT is 1 bit larger and
   //                                       3) compound (aka multiple) packets exist

  /** sap packet header byte offsets */
  private static final byte SAPVARTEC=0,SAPAUTHLEN=1,SAPMSGHASHH=2,SAPMSHHASHL=3,SAPORIG1=4,SAPORIG2=5,
                            SAPORIG3=6,SAPORIG4=7,SAPDATA=8;
     //SAPDATA=8 for case of 0 AUTHLEN and IP4, else add authlen 8bit words and/or 96bits for IP6

     //  The SAP header has the following format:
     // 0                   1                   2                   3
     // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //| V=1 |A|R|T|E|C|   auth len    |         msg id hash           |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|                                                               |
     //:                originating source (32 or 128 bits)            :
     //:                                                               :
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //|                    optional authentication data               |
     //:                              ....                             :
     //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
     //|                      optional payload type                    |
     //+                                         +-+- - - - - - - - - -+
     //|                                         |0|                   |
     //+ - - - - - - - - - - - - - - - - - - - - +-+                   |
     //|                                                               |
     //:                            payload                            :
     //|                                                               |
     //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     // The A=0 for IP4,1 for IP6, R=0,T=0 for start, 1 for deletion,
     // E=1 for encrypt,C=1 for zlib compression (compression before encrypt),
     //auth len is # 32bit words of authentication data, msg hash - globally unique id
     //originating source is IP in network byte order
     //payload type is MIME content type followed by ASCII NUL (byte of 0) but
     //if format is application/sdp the format and 0 can be ignored

  /** DatagramData Default Constructor */
  public DatagramData() { }
  /** DatagramData Constructor
   *  @param phost the host address
   *  @param pport the port
   */
  public DatagramData(String phost,int pport) {
    host=phost;
    port=pport;
  }

  /** Register a class to receive call backs with time and data information from the ICE packets.
      This will replace any class that was previously receiving the call-backs.
      @param cb The class to receive callbacks.
   */
  public void registerTimeCB(IceTimeCallBack cb){ timeCB = cb; }
  /** Register a class to receive call backs with time and data information from the ICE packets.
      This will replace any class that was previously receiving the call-backs.
      @param cb The class to receive callbacks.
      @since NeXtMidas 2.7.4
   */
  public void registerTimeFloatDataCB(IceTimeFloatDataCallBack cb){ timeDCB = cb; }
  /** Register a class to receive call backs with time information from the SDDS packets.
      This will replace any class that was previously receiving the call-backs.
      @param cb The class to receive callbacks.
      @since NeXtMidas 2.7.4
   */
  public void registerSDDSTimeDataCB(SDDSTimeDataCallBack cb){ timeSCB = cb; }
  /** Register a class to receive call backs with RTCP Packet information
      This will replace any class that was previously receiving call-backs.
      NOTE: Must also enable processing of RTCP packets via {@link #setRTCPIgnore(boolean)}.
      @param cb The class to receive callbacks.
      @since NeXtMidas 3.1.3
   */
  public void registerRTCPCB(RTCPCallBack cb) { rtcpCB = cb;}

  /** Get the timeout to use for the Datagrams
   *  @return the timeout (in milliseconds)
   */
  public int getTimeOut() { return timeOut; }
  /** Set the timeout to use for the Datagrams
   *  @param pTimeOut the timeout (in milliseconds)
   */
  public void setTimeOut(int pTimeOut) { timeOut=pTimeOut;
                         if (verbose) System.out.println("DatagramData: setting TimeOut "+pTimeOut); }
  /** Get the state of Midas verbose output
   *  @return the state of Midas verbose output
   */
  public boolean getVerbose() { return verbose; }
  /** Set the state of Midas verbose output
   *  @param pVerbose the state of Midas verbose output
   */
  public void setVerbose(boolean pVerbose) { verbose=pVerbose;
                         if (verbose) System.out.println("DatagramData: setting Verbose "+pVerbose); }
  /** Get the state of debugging Datagram packets
   *  @return the state of debugging Datagram packets
   */
  public boolean getPacketDebug() { return packetDebug; }
  /** Set the state of debugging Datagram packets
   *  @param pPacketDebug the state of debugging Datagram packets
   */
  public void setPacketDebug(boolean pPacketDebug) { packetDebug=pPacketDebug;
                         if (verbose) System.out.println("DatagramData: setting PacketDebug "+pPacketDebug); }
  /** Get the state of debugging packet data
   *  @return the state of debugging packet data
   */
  public boolean getPacketDataDebug() { return packetDataDebug; }
  /** Set the state of debugging packet data
   *  @param pPacketDataDebug the state of debugging packet data
   */
  public void setPacketDataDebug(boolean pPacketDataDebug) { packetDataDebug=pPacketDataDebug;
                         if (verbose) System.out.println("DatagramData: setting PacketDataDebug "+pPacketDataDebug); }
  /** Get the host address
   *  @return the host address
   */
  public String getHost() { return host; }
  /** Set the host address
   *  @param phost the host address
   */
  public void setHost(String phost) { host=phost;
                         if (verbose) System.out.println("DatagramData: setting host "+phost); }
  /** Get the port
   *  @return the port
   */
  public int getPort() { return port; }
  /** set the port
   *  @param pport the port
   */
  public void setPort(int pport) { port=pport;
                         if (verbose) System.out.println("DatagramData: setting port "+pport); }
  /** Get the state of reading RTP packets
   *  @return the state of reading RTP packets
   */
  public boolean getRTP() { return rtp; }
  /** Set the state of reading RTP packets
   *  @param prtp the state of reading RTP packets
   */
  public void setRTP(boolean prtp) { rtp=prtp;
                         if (verbose) System.out.println("DatagramData: setting RTP "+prtp); }
  /** Get filter on specific RTP type - used to pull a "channel" out of stream based on type.
      @return -1 if no RTP filter is set, otherwise RTP type that being filtered
   */
  public int getRTPFilter() { return rtpfiltertype; }
  /** Set filter on specific RTP type - used to pull a "channel" out of stream based on type.
      @param ptemp the filter (-1 means no RTP filter is set, otherwise RTP type that being filtered)
   */
  public void setRTPFilter(int ptemp) { rtpfiltertype=ptemp;
                         if (verbose) System.out.println("DatagramData: setting RTP Filter to handle only "+ptemp); }
  /** Are RTCP packets being ignored?
      @return true if RTCP packets are ignored
      @since NeXtMidas 3.1.3
   */
  public boolean getRTCPIgnore() { return ignore_rtcp; }
  /** Set to ignore or allow RTCP packets to be processed.
      NOTE: Must also register callback via {@link #registerRTCPCB(RTCPCallBack)}.
      @param pri true to ignor RTCP
      @since NeXtMidas 3.1.3
   */
  public void setRTCPIgnore(boolean pri) { ignore_rtcp=pri;
                         if (verbose) System.out.println("DatagramData: setting RTCP Ignore "+pri); }
  /** Fill in missing packets?
   *  @return whether or not to fill in missing packets
   */
  public boolean getFill() { return fill; }
  /** Set the state of whether or not to fill in missing packets
   *  @param pfill the state of whether or not to fill in missing packets
   */
  public void setFill(boolean pfill) { fill=pfill;
                         if (verbose) System.out.println("DatagramData: setting fill "+fill);}
  /** Are SAP packets being read?
   *  @return true if reading SAP packets
   */
  public boolean getSAP() { return sap; }
  /** Sets the state of whether or not to read SAP packets
   *  @param psap the state of whether or not to read SAP packets
   */
  public void setSAP(boolean psap) { sap=psap; if (sap) { host="224.2.127.254"; port=9875; }
                         if (verbose) System.out.println("DatagramData: setting sap "+sap+".host="+host+".port="+port);}
  /** Does the header get checked?
   *  @return Whether or not the header gets checked
   */
  public boolean getCheck() { return checkhdr; }
  /** Sets the state of whether or not the header gets checked
   *  @param pcheck the state of whether or not the header gets checked
   */
  public void setCheck(boolean pcheck) { checkhdr=pcheck;
                         if (verbose) System.out.println("DatagramData: setting check "+checkhdr);}
  /** Is there an ICE header in the pipe?
   *  @return whether or not there is an ICE header in the pipe
   */
  public boolean getIceHdrInPipe() { return icehdrinpipe; }
  /** Set the state of whether or not there is an ICE header in the pipe
   *  @param parm the state of whether or not there is an ICE header in the pipe
   */
  public void setIceHdrInPipe(boolean parm) { icehdrinpipe=parm;
                         if (verbose) System.out.println("DatagramData: setting ice hdr in pipe "+icehdrinpipe);}
  /** Set number of packet skips to auto reset the fill on; zero (0) is off
   *  @param srm number of packet skips to auto reset the fill on; zero (0) is off
   *  @since NeXtMidas 3.0.0
   */
  public void setAutoSeqResetMAX(int srm) { seqreset=srm; if (seqreset<0) seqreset=-seqreset; }
  /** Get the number of packet skips to auto reset the fill on; zero (0) is off
   *  @return the number of packet skips to auto reset the fill on
   *  @since NeXtMidas 3.0.0
   */
  public int getAutoSeqResetMAX() { return seqreset; }
  /** store in "all" ICE Header fields.
      @return true if storing all ICE Header fiels
      @since NeXtMidas 2.7.4
   */
  public boolean getIceData() { return geticedata; }
  /** set to store in "all" ICE Haeader fields.
      @param parm true to store all ICE Header fields
      @since NeXtMidas 2.7.4
   */
  public void setIceData(boolean parm) { geticedata=parm;
                         if (verbose) System.out.println("DatagramData: setting icedata available "+geticedata);}

  /** Get Packet Buffer Size
   *  @return packet buffer size */
  public static int getPktBufferSize () { return pktBufferSize; }
  /** Set Packet Buffer Size
   *  @param pktBufferSize packet buffer size */
  public static void setPktBufferSize (int pktBufferSize) { DatagramData.pktBufferSize=pktBufferSize;}

///////////////////////////////////////////////////////////////////////////////////////////
//Deal with compressed,encrypted data and signatures - since 3.5.4 for Bug 2921
  /** Is there an active decompressor for decompressing data?
   *  @return Whether or not there is an active decompressor for decompressing data
   */
  public boolean getDecompress() { if (decompressor!=null) return true; else return false; }
  /** Toggle the state of the decompressor
   *  @param pdecompress true if activating a new decompressor; false if deactivating the current one
   */
  public void setDecompress(boolean pdecompress) {
    if (pdecompress) {
      decompressor=new Inflater();
    } else {
      decompressor=null;
    }
    if (verbose) System.out.println("DatagramData: setting decompress "+pdecompress+","+decompressor);
  }
  /** Is a message authentication code (MAC) being used?
   *  @return whether or not a message authentication code being used
   */
  public boolean getMac() { if (mac!=null) return true; else return false; }
  /** Calls {@link #setMac(boolean, String)} to set or unset a message authentication code (MAC)
   *  @param dpmac state for whether to set or unset the MAC as an int
   *  @param key   the MAC key
   */
  public void setMac(int dpmac,String key) {
    if (dpmac==1) setMac(false,key);
    else setMac(true,key);
  }
  /** Sets or unsets a message authentication code (MAC)
   *  @param pmac state for whether to set or unset the MAC as a boolean
   *  @param key  the MAC key
   */
  public void setMac(boolean pmac,String key) {
    if (verbose) System.out.println("DatagramData: setting mac "+pmac);
    if (!pmac) {
      mac=null;
      return;
    }
    try {
      mac=Mac.getInstance(hmactype);
      signingkey=new SecretKeySpec(key.getBytes(),hmactype);
      mac.init(signingkey);
    } catch (Exception e) {
      e.printStackTrace();
      mac=null;
    }
  }
  /** Get if an encryption cipher being used
   *  @return true if an encryption cipher is being used
   */
  public boolean getEncrypt() { if (cipher!=null) return true; else return false; }
  /** Calls {@link #setEncrypt(boolean, String, String)} to set or unset an encryption cipher
   *  @param dpcipher the state for whether to set or unset the encryption cipher as an int
   *  @param key      the cipher key
   *  @param salt     the cipher salt data
   */
  public void setEncrypt(int dpcipher,String key,String salt) {
    if (dpcipher==1) setEncrypt(false,key,salt);
    else setEncrypt(true,key,salt);
  }
  /** Sets or unsets an encryption cipher
   *  @param pcipher the state for whether to set or unset the encryption cipher as a boolean
   *  @param key      the cipher key
   *  @param salt     the cipher salt data
   */
  public void setEncrypt(boolean pcipher,String key,String salt) {
    if (verbose) System.out.println("DatagramData: setting cipher "+pcipher);
    if (!pcipher) {
      cipher=null;
      return;
    }
    try {
      if (verbose) System.out.println("DatagramData: setEncrypt with "+key+" and "+salt);
      pbeParamSpec=new PBEParameterSpec(salt.getBytes(),ITERATIONS);
      pbeKeySpec=new PBEKeySpec(key.toCharArray());
      cipherkey=SecretKeyFactory.getInstance(ciphertype).generateSecret(pbeKeySpec);
      cipher=Cipher.getInstance(cipherkey.getAlgorithm());
      cipher.init(Cipher.DECRYPT_MODE,cipherkey,pbeParamSpec);
      if (verbose) System.out.println("DatagramData: Done setEncrypt");
    } catch (Exception e) {
      e.printStackTrace();
      cipher=null;
    }
  }
// end since 3.5.4 for Bug 2921///////////////////////////////////////////////////

  /** Gets the number of missing packets
   *  @return the number of missing packets
   */
  public int getFillNum() { return fillnum; }
  /** Gets the last header
   *  @return the last header
   */
  public int getLastHdrSeq() { return llhdr; }
  /** Get current header
   *  @return the current header
   */
  public int getCurHdrSeq() { return lasthdr; }
  /** Get current sequence
   *  @return the current sequence
   */
  public int getSeq() { return seq; }
  /** Get last sequence
   *  @return the last sequence
   */
  public int getLastSeq() { return llseq; }
  /** Set previous sequence
   *  @param pseq the previous sequence
   */
  public void setLastSeq(int pseq) { lastseq=pseq; }

//these pretty much match the ICEPacket.java stuff (since NeXtMidas 2.7.4)
  /** Get the total ICE time in seconds (whole + fractional)
   *  @return the total ICE time in seconds (whole + fractional)
   */
  public double getICETime() { return wsec+fsec; }
  /** Get the whole seconds
   *  @return the whole seconds
   */
  public double getWSEC() { return wsec; }
  /** Get the fractional seconds
   *  @return the fractional seconds
   */
  public double getFSEC() { return fsec; }
  /** Get the ICE time with a TC offset
   *  @param xdelta x-axis delta value
   *  @return the ICE time with a TC offset
   *  @since NeXtMidas 3.2.2
   */
  public double getICETimeTCOFF (double xdelta) { return wsec+fsec-(tcoff*xdelta); }
  /** Get the first element of user data as a double
   *  @return the first element of user data as a double
   */
  public double getDU1() { if (lastICE==null) return 0.0d; return lastICE.getUserDataD(0); }
  /** Get the second element of user data as a double
   *  @return the second element of user data as a double
   */
  public double getDU2() { if (lastICE==null) return 0.0d; return lastICE.getUserDataD(1); }
  /** Get the first element of user data as a float
   *  @return the first element of user data as a float
   */
  public double getFU1() { if (lastICE==null) return 0.0f; return lastICE.getUserDataF(0); }
  /** Get the second element of user data as a float
   *  @return the second element of user data as a float
   */
  public double getFU2() { if (lastICE==null) return 0.0f; return lastICE.getUserDataF(1); }
  /** Get the third element of user data as a float
   *  @return the third element of user data as a float
   */
  public double getFU3() { if (lastICE==null) return 0.0f; return lastICE.getUserDataF(2); }
  /** Get the fourth element of user data as a float
   *  @return the fourth element of user data as a float
   */
  public double getFU4() { if (lastICE==null) return 0.0f; return lastICE.getUserDataF(3); }
  /** is current seq = lasthdr - aka, current header
   *  @return is header case
   */
  public boolean isIceHdrPacket() { return (seq==lasthdr); }
  /** get the entire ICEPacket
   *  @return the entire ICEPacket */
  public ICEPacket getICE() { return lastICE; }
  /** get the current Midas digraph format, byte mode,byte type
   *  @return String with current Midas digraph format, byte mode,byte type */
  public String getFormat() { return ifrmt; }
  /** Get Size of packet data in elements, integer*4 elem ! data elements in packet
   *  @return size of packet data in elements */
  public int getSize() { return isize; }
  /** Get bytes per atom
   *  @return bytes per atom */
  public int getBPA() { return ibpa; }
  /** Get size of packet data in bytes
   * @return size of packet data in bytes */
  public int getBytes () { return ibpa*isize; }

  /** Sets the address for all hosts
   *  @param palladdr true if use local, false if use unique */
  public void setAllHosts (boolean palladdr) {
    alladdr = palladdr;
    if (verbose) System.out.println("DatagramData: setting ALL hosts "+alladdr+".port="+port);
  }
  /** Gets the address for all hosts
   *  @return the address for all hosts
   */
  public boolean getAllHosts () { return alladdr; }
  /** Gets whether or not the Datagram is open
   *  @return whether or not the Datagram is open
   */
  public boolean isOpen () { return isOpen; }
  /** Get Datagram type
   *  @return the Datagram type
   */
  public int getType () { return buf[RTPMPT]&127; }

  /** Calls {@link #open()} to open a Datagram across a host
   *  @param phost the host address
   *  @param pport the port
   *  @return whether or not the Datagram was opened
   */
  public boolean open (String phost,int pport) {
    host=phost;
    port=pport;
    return open();
  }

  /** Opens a Datagram across a host
   *  @return whether or not the Datagram was opened
   */
  public boolean open () {
    isOpen = false;
    socketerror=false;
    lastseq=-1;
    llseq=-1;
    seq=-1;
    lasthdr=-1;
    llhdr=-1;
    fillnum=0;

    if (host==null && !alladdr) { System.err.println("DatagramData: No Host to open"); return isOpen; }
    try {
      if (alladdr) {
        multicast = false;
      } else {
        addr = InetAddress.getByName(host);
        //224.0.0.0 through 239.255.255.255
        multicast=addr.isMulticastAddress();
      }
      if (verbose) {
        System.out.println("DatagramData: HOST:"+host+". PORT:"+port);
        if (multicast) System.out.println("DatagramData: Multicast");
      }

      try {// connect to socket ports
        if (multicast) {
          // MSZ: work-around for Java Bug 4701650 on Windows (09JAN06), NeXtMidas Bug 299
          //        InetSocketAddress isa = new InetSocketAddress(host, port);
          //        msock = new MulticastSocket(isa);
          if (windows) { msock = new MulticastSocket(port); }
          else { msock = new MulticastSocket(new InetSocketAddress(host,port)); }
          msock.joinGroup(addr);
          dsock = (DatagramSocket)msock;
        } else {
          if (alladdr) { dsock = new DatagramSocket(port);      }
          else         { dsock = new DatagramSocket(port,addr); }
        }
        dsock.setSoTimeout(timeOut);   // in milliseconds
        if (verbose) System.out.println("DatagramData: Connected to Socket. Host:"+host+". Port:"+port);
      } catch (Exception e) {
        System.err.println("DatagramData: Open Error: "+e+". For IP="+host+". Port="+port);
        if (e instanceof BindException && "Cannot assign requested address".equals(e.getMessage())) { // nice info msg for Bug 1864
          System.err.println("DatagramData: On RHEL4+ (system with dual IPv4 and IPv6 stacks), try defining\n"
                           + "              java.net.preferIPv4Stack=true system property to use IPv4 stack.");
        }
        e.printStackTrace();
        return isOpen;
      }
    } catch (Exception e) {
       System.err.println("DatagramData: Problem with stream address:"+host+":"+port+" ->"+e);
       e.printStackTrace();
       return isOpen;
    }
    if (verbose) System.out.println("DatagramData: Done HOST:"+host+". PORT:"+port);
    return(isOpen = true);
  }

  /** Leave DatagramData Group */
  public void leave () {
    if (verbose) System.out.println("DatagramData: Leave Group");
    try {
      if (multicast) msock.leaveGroup(addr);
      if (dsock!=null) dsock.close();
      isOpen = false;
      socketerror=false;
    } catch (Exception e) { }
    if (verbose) System.out.println("DatagramData: Done Leave Group");
  }

  /** Close Datagram connection */
  public void close () {
    if (verbose) System.out.println("DatagramData: Closing");
    isOpen = false;
    socketerror=false;
    try {
      if (multicast) { msock.leaveGroup(addr); }
      if (dsock!=null) dsock.close();
    } catch (Exception e) { }
  }
  ////////////////////////////////////////////////////////////////////////////////
  /** Calls {@link #read(byte[], int)} to read in packet and process headers (if applicable)
   *  @param tbuf The temp buf to be populated.
   *  @return The length of valid information in the buffer (data, or data + header(s))
   */
  public int read (byte[] tbuf) { return read(tbuf,0);}

  /** Read in packet and process headers (if applicable)
   *  @param tbuf The temp buf to be populated.
   *  @param poffset The offset into the buf.
   *  @return The length of valid information in the buffer (data, or data + header(s))
   */
  public int read (byte[] tbuf,int poffset) {
    len=0;
    offset=0;
    try {
      if (dpacki==null || dsock==null) {
        if (!socketerror) { System.err.println("DatagramData: Null Socket or buffer for host "+host); socketerror=true; }
        return -1;
      }
      //should throw new Exception();
      dpacki.setLength(buf.length); // reset length as receive changes it
      try {
         dsock.receive(dpacki);
      } catch (SocketTimeoutException ste) {
         if (packetDebug) System.out.println("SocketTimeOutException" + ste); //was verbose
         return 0;
      }
      len=dpacki.getLength();
      if (rtp) { //read the rtp header data
        if (packetDebug) {  // since 3.5.4 for Bug 2921
          for (i=0;i<len;i++) {
            System.out.println("BUF["+i+"]="+buf[i]);
          }
        }
        if (buf[RTPMPT]>=194 && buf[RTPMPT]<=210) { //check for usual RTCP cases
          if (ignore_rtcp) return 0;
          if (rtcpCB!=null) {
            //all RTCP packets are supposed to be compound starting with either 200 or 201 - SR, RR
            if (buf[RTPMPT]==200 || buf[RTPMPT]==201) parseRTCP(buf,0,len);
            return 0;
          }
        }
        if (rtpfiltertype>=0 && (buf[RTPMPT]&127)!=rtpfiltertype) return 0;
        if (fill) {
          hi=buf[RTPSQH];
          if (hi<0) hi+=256;
          lo=buf[RTPSQL];
          if (lo<0) lo+=256;
          seq=hi*256+lo;
          fillnum=0;
          if (lastseq>-1) {
            if (lastseq-seq>32767) { //rollover condition or a LOT of packet drops!!!
              fillnum=(65536-lastseq)+seq-1;
              if (verbose) System.out.println("Rollover condition "+lastseq+" to "+seq+" for host "+host+". determined fill num of "+fillnum);
            } else if (lastseq>=seq) { //packet either a duplicate or earlier - out of order and a fill packet generated
              if (verbose) System.out.println("Skip Out of order or duplicate packet "+lastseq+" to "+seq+" for host "+host);
              if (seqreset>0 && lastseq>=seq-seqreset) lastseq=seq;
              return 0;
            } else if (lastseq+1!=seq) {
              fillnum=seq-lastseq-1;
              if (verbose) System.out.println("Missed packets from "+lastseq+" to "+seq+" for host "+host+" so fill "+fillnum);
            } //else this is a good packet
          }
          llseq=lastseq;
          lastseq=seq;
        }
        ccs = buf[RTPVPXCC] & 15;
        extendflag = buf[RTPVPXCC] & 16;
        datapos = RTPDATA+(ccs*4);
        PacketPos = -1;
        if (extendflag>0) {
          //first 2 bytes are "type"
          //the next 2 bytes are extended length is in 4 byte words
          xtype=unpackINet(buf,datapos);
          xlen=unpackINet(buf,datapos+2);
          packetStart = datapos + 4; //actual RTP Data, not header data
          datapos+=4+(xlen<<2); //*4 for complete data
          if (xtype==COMPOUND_PACKET) {  // updated in 3.5.4 for Bug 2921
            timepacktype=0;
            timepacklen=0;
            timepackpos=-1;
            while (packetStart<datapos) { //at least 4 bytes in header structure
              xtype=unpackINet(buf,packetStart);
              sublen=unpackINet(buf,packetStart+2)*4;
              if (xtype==SDDS_PACKET || xtype==ICE3_PACKET) {
                timepacktype=xtype;
                timepacklen=sublen;
                timepackpos=packetStart+4;
              } else if (xtype==MAC_PACKET && mac!=null) {
                if (verbose) System.out.println("Checking packet signature "+bufholder.length);
                if (sublen > bufholder.length) sublen=bufholder.length;
                System.arraycopy(buf,packetStart+4,bufholder,0,sublen);
                System.arraycopy(fakesignature,0,buf,packetStart+4,sublen);
                mac.update(buf,0,len);
                signature=mac.doFinal();
                sigpass=true;
                for (i=0;i<sublen;i++) {
                  if (signature[i]!=bufholder[i]) {
                    sigpass=false;
                    break;
                  }
                }
                if (!sigpass) {
                  if (verbose) System.err.println("DatagramData: Packet did not pass signature check");
                  return 0;
                }
              }
              packetStart=packetStart+4+sublen;
            }
            if (timepackpos>=0) {
              packetStart=timepackpos;
              xtype=timepacktype;
              sublen=timepacklen;
            }
          }

          if (xtype==ICE3_PACKET || xtype==ICE2_PACKET || xtype==ICE1_PACKET) {
            llhdr=lasthdr;
            if (fill) {
              lasthdr=seq;
            } else {
              hi=buf[RTPSQH];
              if (hi<0) hi+=256;
              lo=buf[RTPSQL];
              if (lo<0) lo+=256;
              lasthdr=hi*256+lo;
            }
            /* Check for a valid ICE packet. */
            if ( ICEPacket.hasSync(buf, packetStart) ) {
              PacketPos=packetStart;
              byte rep = buf[PacketPos+18];
              if (timeCB!=null || timeDCB!=null || geticedata) {
                wsec = Convert.unpackD(buf,PacketPos+32,rep);
                fsec = Convert.unpackD(buf,PacketPos+40,rep);
                ibpa=(int)buf[PacketPos+19];
                tcoff = Convert.unpackD(buf,PacketPos+24,rep);  // tcoff added in 3.2.2 for Bug 2789
                if (verbose) Shell.info("DGData: Time set "+wsec+","+fsec+","+tcoff);
                lastICE = new ICEPacket(buf,PacketPos);
                isize=lastICE.getSize();
                ifrmt=lastICE.getFormat();
                if (timeCB != null){
                  dp1  = Convert.unpackD(buf,PacketPos+48,rep);
                  dp2  = Convert.unpackD(buf,PacketPos+56,rep);
                  try{
                    timeCB.dgramTime(wsec+fsec,dp2,dp1);
                  } catch(Exception ex) {
                    System.err.println("DatagramData: Error while making ICE time call back to '"+timeCB+"'.");
                  }
                } else if (timeDCB!=null) {
                  fp1 =Convert.unpackF(buf,PacketPos+48,rep);
                  fp2 =Convert.unpackF(buf,PacketPos+52,rep);
                  fp3 =Convert.unpackF(buf,PacketPos+56,rep);
                  fp4 =Convert.unpackF(buf,PacketPos+60,rep);
                  try{
                    timeDCB.dgramTimeFloatData(wsec+fsec,fp1,fp2,fp3,fp4);
                  } catch(Exception ex) {
                    System.err.println("DatagramData: Error while making ICE time call back to '"+timeDCB+"'.");
                  }
                }
              }
            }
          } else if (xtype==SDDS_PACKET) {
            llhdr=lasthdr;
            if (fill) {
              lasthdr=seq;
            } else {
              hi=buf[RTPSQH];
              if (hi<0) hi+=256;
              lo=buf[RTPSQL];
              if (lo<0) lo+=256;
              lasthdr=hi*256+lo;
            }
            PacketPos=packetStart;
            if (timeSCB!=null) {
              if ((buf[PacketPos+12]&0xC0)!=0) { //no timecode case
                System.arraycopy(buf,PacketPos+16,tcbuf,0, 8);
                Convert.swap8(tcbuf,0,1);
                tics = Convert.unpackX(tcbuf,0);
                lwsec = tics / ticsPerSecond;
                lfsec = tics - (lwsec*ticsPerSecond);
                timeSCB.dgramTime(((double)lwsec)+(((double)lfsec)*250e-12));
              }
            }
          } else if (xtype==XSTREAM_PACKET) {
          } else if (xtype==MAC_PACKET) {  // Since 3.5.4 for Bug 2921
            if (mac!=null) {
              if (verbose) System.out.println("Checking packet signature");
              sublen=xlen*4;
              if (sublen>bufholder.length) sublen=bufholder.length;
              System.arraycopy(buf,packetStart,bufholder,0,sublen);
              System.arraycopy(fakesignature,0,buf,packetStart,sublen);
              mac.update(buf,0,len);
              signature=mac.doFinal();
              sigpass=true;
              for (i=0;i<sublen;i++) {
                if (signature[i]!=bufholder[i]) {
                  sigpass=false;
                  break;
                }
              }
              if (!sigpass) {
                if (verbose) System.err.println("DatagramData: Packet did not pass signature check");
                return 0;
              }
            }
          } else {
            System.err.println("DatagramData: Unknown packet xtype (" + xtype + "), "+
                               "expected COMPOUND, ICE1(1), ICE2(8), ICE3(16), SDDS(17), or MAC(18) packet.");
          }
        } else {
          xtype=0;
          xlen=0;
        }

        if (packetDebug) {
          System.out.println("DatagramData: **RTP Version="+((buf[RTPVPXCC]&192)>>6));
          System.out.println("DatagramData: Len="+len);
          System.out.println("DatagramData: Padding="+(buf[RTPVPXCC]&32));
          System.out.println("DatagramData: CCRCs="+ccs);
          System.out.println("DatagramData: Extended Header="+(extendflag>>4));
          if (xlen>0) {
            System.out.println("DatagramData: Header Type="+xtype);
            System.out.println("DatagramData: Header Len (4 byte words)="+xlen);
            if (PacketPos>0) {
              System.out.println("DatagramData: ICE/SDDS Header starting at "+PacketPos);
              lastICE = new ICEPacket(buf,PacketPos);
              System.out.println("TC="+lastICE.getTC());
              System.out.println("BPA="+lastICE.getBPA());
              System.out.println("Format="+lastICE.getFormat());
              System.out.println("Size="+lastICE.getSize());
              System.out.println("Count="+lastICE.getCount());
              System.out.println("Data1="+lastICE.getUserDataD(0));
              System.out.println("Data2="+lastICE.getUserDataD(1));
            }
          }
          System.out.println("DatagramData: Marked="+(buf[RTPMPT]&128));
          System.out.println("DatagramData: Payload="+(buf[RTPMPT]&127));
          temp=0;
          if (buf[RTPSQH]<0) temp=(buf[RTPSQH]+256)*256;
          else temp=buf[RTPSQH]*256;
          if (buf[RTPSQL]<0) temp+=(buf[RTPSQL]+256);
          else temp+=buf[RTPSQL];
          //System.out.println("Sequence="+(buf[RTPSQH]*256+buf[RTPSQL]));
          System.out.println("DatagramData: Sequence="+temp);
          temp=0;
          if (buf[RTPTS1]<0) temp=(buf[RTPTS1]+256)*16777216;
          else temp=buf[RTPTS1]*16777216;
          if (buf[RTPTS2]<0) temp+=(buf[RTPTS2]+256)*65536;
          else temp+=buf[RTPTS2]*65536;
          if (buf[RTPTS3]<0) temp+=(buf[RTPTS3]+256)*256;
          else temp+=buf[RTPTS3]*256;
          if (buf[RTPTS4]<0) temp+=(buf[RTPTS4]+256);
          else temp+=buf[RTPTS4];
          System.out.println("DatagramData: Timestamp="+temp);
          //System.out.println("Timestamp="+(buf[RTPTS1]*16777216+buf[RTPTS2]*65536+buf[RTPTS3]*256+buf[RTPTS4]));
          if (buf[RTPSSRC1]<0) temp=(buf[RTPSSRC1]+256)*16777216;
          else temp=buf[RTPSSRC1]*16777216;
          if (buf[RTPSSRC2]<0) temp+=(buf[RTPSSRC2]+256)*65536;
          else temp+=buf[RTPSSRC2]*65536;
          if (buf[RTPSSRC3]<0) temp+=(buf[RTPSSRC3]+256)*256;
          else temp+=buf[RTPSSRC3]*256;
          if (buf[RTPSSRC4]<0) temp+=(buf[RTPSSRC4]+256);
          else temp+=buf[RTPSSRC4];
          System.out.println("DatagramData: SSRC="+Integer.toHexString(temp));
          //System.out.println("SSRC="+buf[RTPSSRC1]+"."+buf[RTPSSRC2]+"."+buf[RTPSSRC3]+"."+buf[RTPSSRC4]);
          boff=RTPDATA;
          for (ci=0;ci<ccs;ci++) {
            System.out.println("DatagramData: CCRC Num="+(ci+1));
            System.out.println("DatagramData: CSRC="+buf[boff]+"."+buf[boff+1]+"."+buf[boff+2]+"."+buf[boff+3]);
            boff+=4;
          }
        }
        offset=datapos+poffset;
        if (offset>len) offset=len;
        if (len>datapos) len=len-offset;
        else len=0;
      } else if (sap) { //read the sap header
        ipver = buf[SAPVARTEC] & 16;
        state = buf[SAPVARTEC] & 4;
        encrypt = buf[SAPVARTEC] & 2;
        compress = buf[SAPVARTEC] & 1;
        authlen = buf[SAPAUTHLEN];
        datapos = SAPDATA;
        if (ipver>0) datapos+=(authlen*4)+12;
        else datapos+=(authlen*4);

        if (packetDebug) {
          System.out.println("DatagramData: **SAP Version="+((buf[SAPVARTEC]&224)>>5));
          System.out.println("DatagramData: IP Version="+(ipver>>4));
          System.out.println("DatagramData: Start/Stop="+(state>>2));
          System.out.println("DatagramData: Encryption="+(encrypt>>1));
          System.out.println("DatagramData: Compressed="+compress);
          System.out.println("DatagramData: Authentication Length="+authlen);

          temp=0;
          if (buf[SAPMSGHASHH]<0) temp=(buf[SAPMSGHASHH]+256)*256;
          else temp=buf[SAPMSGHASHH]*256;
          if (buf[SAPMSHHASHL]<0) temp+=(buf[SAPMSHHASHL]+256);
          else temp+=buf[SAPMSHHASHL];
          //System.out.println("MSG ID Hash="+(buf[SAPMSGHASHH]*256+buf[SAPMSHHASHL]));
          System.out.println("DatagramData: Msg ID hash="+temp);

          if (ipver>0) {
            System.out.println("DatagramData: IP6.1="+buf[SAPORIG1]+"-"+buf[SAPORIG2]+"-"+buf[SAPORIG3]+"-"+buf[SAPORIG4]);
            System.out.println("DatagramData: IP6.2="+buf[SAPORIG1+4]+"-"+buf[SAPORIG2+5]+"-"+buf[SAPORIG3+6]+"-"+buf[SAPORIG4+7]);
            System.out.println("DatagramData: IP6.3="+buf[SAPORIG1+8]+"-"+buf[SAPORIG2+9]+"-"+buf[SAPORIG3+10]+"-"+buf[SAPORIG4+11]);
            System.out.println("DatagramData: IP6.4="+buf[SAPORIG1+12]+"-"+buf[SAPORIG2+13]+"-"+buf[SAPORIG3+14]+"-"+buf[SAPORIG4+15]);
          } else System.out.println("DatagramData: IP4="+buf[SAPORIG1]+"-"+buf[SAPORIG2]+"-"+buf[SAPORIG3]+"-"+buf[SAPORIG4]);
          if (authlen>0) {
            pos = 0;
            for (i=0;i<authlen;i++) {
              System.out.println("DatagramData: Authdata "+i+"="+buf[SAPDATA+pos]+"-"+buf[SAPDATA+pos+1]+"-"+buf[SAPDATA+pos+2]+"-"+
                                                                 buf[SAPDATA+pos+3]);
              pos+=4;
            }
          }
          byte xbuf[] = new byte[len-poffset];
          System.arraycopy(buf,datapos,xbuf,0,len-datapos-poffset);
          String sdp = new String(xbuf);
          System.out.println("DatagramData: SAP Data="+sdp);
        }
        //originally thought to send all data (offset=0) but added STOP=1 or STOP=0 at start
        offset=datapos+poffset;
        if (datapos>7) {
          buf[datapos-7]='S';
          buf[datapos-6]='T';
          buf[datapos-5]='O';
          buf[datapos-4]='P';
          buf[datapos-3]='=';
          if (state>0) buf[datapos-2]='1';
          else buf[datapos-2]='0';
          buf[datapos-1]='\n';
          datapos-=7;
        }
        if (len>datapos) len=len-offset;
        else len=0;
      } else {
        offset=poffset;
        if (packetDebug) {
          String tmp=new String(buf,0,len);
          System.out.println("received from:"+dpacki.getAddress()+" "+len+" bytes ");
          System.out.println("DatagramData: Data="+tmp);
        }
        len-=offset;
      }
      if (tbuf!=null && len>0) { // section updated in 3.5.4 for Bug 2913
        if (icehdrinpipe && PacketPos>-1) {
          System.arraycopy(buf,PacketPos,tbuf,0,ICEPacket.headerLength);
          if (cipher!=null) {
            if (verbose) System.out.println("Do Decrypt "+offset+","+len+","+ICEPacket.headerLength);
            len=cipher.doFinal(buf,offset,len,tbuf,ICEPacket.headerLength);
            if (verbose) System.out.println("Done Decrypt");
          }
          if (decompressor==null) {
            if (cipher==null) System.arraycopy(buf,offset,tbuf,ICEPacket.headerLength,len);
          } else { //fix, will be in wrong buffer
            decompressor.setInput(buf,offset,len);
            try {
              complen=decompressor.inflate(tbuf,ICEPacket.headerLength,len);
              len=complen;
            } catch (java.util.zip.DataFormatException dfe) {
              if (verbose) System.out.println("No Decompress, just pass");
              System.arraycopy(buf,offset,tbuf,ICEPacket.headerLength,len);
            }
            decompressor.reset();
            if (packetDataDebug) {
              String tmp=new String(buf,offset,len);
              System.out.println("DatagramData: Data="+tmp);
            }
          }
          return len+ICEPacket.headerLength;
        } else {
          if (decompressor==null) {
            if (cipher==null) System.arraycopy(buf,offset,tbuf,0,len);
          } else {
            decompressor.setInput(buf,offset,len);
            try {
              complen=decompressor.inflate(tbuf);
              len=complen;
            } catch (java.util.zip.DataFormatException dfe) {
              if (verbose) System.out.println("No Decompress, just pass");
              System.arraycopy(buf,offset,tbuf,0,len);
            }
            decompressor.reset();
          }
        }
      }
      if (packetDataDebug) { // section updated in 3.5.4 for Bug 2913
        if (cipher!=null) {
          byte[] dcbuf=new byte[pktBufferSize];
          System.out.println("Do Decrypt "+offset+","+len);
          System.arraycopy(buf,offset,dcbuf,0,len);
          len=cipher.doFinal(dcbuf,0,len,buf,offset);
          System.out.println("Done Decrypt");
        }
        if (decompressor==null) {
          String tmp=new String(buf,offset,len);
          System.out.println("DatagramData: Nobuffer="+tmp);
        } else {
          byte[] dcbuf=new byte[pktBufferSize];
          decompressor.setInput(buf,offset,len);
          try {
            complen = decompressor.inflate(dcbuf);
            len=complen;
          } catch (java.util.zip.DataFormatException dfe) {
            System.out.println("No Decompress, just pass");
            System.arraycopy(buf,offset,dcbuf,0,len);
          }
          decompressor.reset();
          String tmp=new String(dcbuf);
          System.out.println("DatagramData: Data="+tmp);
        }
      }
      return len;
    } catch (Exception e) {//receive can throw IO, PortUnreachable and IllegalBlockingMode Exceptions
      if (e instanceof java.io.InterruptedIOException) {
        if (verbose) System.out.println("DatagramData: Interrupted:"+e);
      } else if (e instanceof java.net.SocketException) {
        if (verbose) System.out.println("DatagramData: Socket Closed:"+e);
      } else {
        System.err.println("DatagramData: Exception: "+e);
        System.err.println("DatagramData: values: len="+len+"offset="+offset);
        e.printStackTrace();
      }
    }
    return len;
  }
  ////////////////////////////////////////////////////////////////////////////////
  /** Parse RTCP packets and invoke the appropriate callback method for each RTCP type.
      @see RTCPCallBack
      @param buf     Buffer to be parsed
      @param poffset Offet into the buffer to parse
      @param len     length
      @since NeXtMidas 3.1.3
   */
  public void parseRTCP (byte[] buf, int poffset, int len) {
    xlen=0;
    pos=poffset;
    if (pos+4>len) return;

    do {
      //calc length of first RTCP packet
      hi=buf[pos+RTPSQH];
      if (hi<0) hi+=256;
      lo=buf[pos+RTPSQL];
      if (lo<0) lo+=256;
      xlen=hi*256+lo;
      xlen++; //xlen is 0 based and includes header to real length is plus 1
      xlen=xlen*4; //xlen is in words
      if (xlen>len-pos) xlen=len-pos;

      xtype=buf[pos+RTPMPT];

      if (xtype!=203) { //203 is BYE type and is better parsed
        rtcpbuf = new byte[xlen];
        System.arraycopy(buf, 0, rtcpbuf, 0, xlen);
      }
      if (xtype==200) {
        rtcpCB.RTCP_SR(rtcpbuf);
      } else if (xtype==201) {
        rtcpCB.RTCP_RR(rtcpbuf);
      } else if (xtype==202) {
        rtcpCB.RTCP_SDES(rtcpbuf);
      } else if (xtype==203) {
        ccs=buf[pos+RTPVPXCC] & 31; //# of csrc/ssrc
        if (ccs*4+4<xlen) { //contains optional BYE reason
          //bye reason has 1 byte length followed by reason
          int npos=pos+ccs*4+4;
          int rlen=buf[npos];
          try {
            rtcpCB.RTCP_BYE(new String(buf,npos,rlen,"US-ASCII")); //need to get reason yet
          } catch (Exception e) {} //fails when data is not a string
        } else {
          rtcpCB.RTCP_BYE("DONE");
        }
      } else if (xtype==204) {
        rtcpCB.RTCP_APP("APP", rtcpbuf); //need to grab data
      } else {
        rtcpCB.RTCP_Other(buf[RTPMPT], rtcpbuf);
      }
      pos+=xlen; //skip to new section
    } while (pos+4<=len); //need to find all parts
  }
  ////////////////////////////////////////////////////////////////////////////////
  /** Calls {@link #read(byte[], int)} to read in packet and process headers (if applicable)
   *  @param poffset The offset into the buf.
   *  @return the buffer
   */
  public byte[] read(int poffset) {
    temp = read(null,poffset);
    if (temp<1) return null;
    byte[] tbuf = new byte[temp];
    System.arraycopy(buf,offset,tbuf,0,temp); //offset calculated inside main read
    return tbuf;
  }
  ////////////////////////////////////////////////////////////////////////////////
  /** Calls {@link #read(byte[], int)} to read in packet and process headers (if applicable)
   *  @return the buffer
   */
  public byte[] read() {
    temp = read(null,0);
    if (temp<1) return null;
    byte[] tbuf = new byte[temp];
    System.arraycopy(buf,offset,tbuf,0,temp);
    return tbuf;
  }
  ////////////////////////////////////////////////////////////////////////////////
  /** Gets a packet xtype
   *  @param buf the buffer to be read from
   *  @param off the buffer offset
   *  @return the packet xtype
   */
  public static int unpackINet(byte[] buf, int off) {
    int xtype;
    if (buf[off]<0) xtype=(buf[off]+256)<<8;
    else xtype=buf[off]<<8;
    if (buf[off+1]<0) xtype+=(buf[off+1]+256); //3
    else xtype+=buf[off+1];
    return xtype;
  }
/**************************************************************
// Test method for development of MixerGUI Thread
static int counter = -1;
static java.util.HashMap testStrings = new java.util.HashMap();

static {
String a = "c=IN IP4 238.4.0.1/255\nm=audio 18800 a=xml:<XML><INFO MSN=\"RF1111\" RX=\"11\" START=\"";
long nowTime = new java.util.Date().getTime();
nowTime -= 30000;
a = a + nowTime + "\"/></XML>";
String b = "c=IN IP4 238.4.0.14/255\nm=audio 18800 a=xml:<XML><INFO MSN=\"RF1111\" RX=\"16\" START=\"";
b = b + nowTime + "\"/></XML>";
String c = "c=IN IP4 238.4.0.27/255\nm=audio 18800 a=xml:<XML><INFO MSN=\"RF2222\" RX=\"1\" START=\"";
c = c + nowTime + "\"/></XML>";
String d = "c=IN IP4 238.4.0.31/255\nm=audio 18800 a=xml:<XML><INFO MSN=\"RF2222\" RX=\"21\" START=\"";
d = d + nowTime + "\"/></XML>";
String e = "c=IN IP4 238.4.0.40/255\nm=audio 18800 a=xml:<XML><INFO MSN=\"RF2222\" RX=\"24\" START=\"";
e = e + nowTime + "\"/></XML>";

testStrings.put(new Integer(0), a);
testStrings.put(new Integer(1), b);
testStrings.put(new Integer(2), c);
testStrings.put(new Integer(3), d);
testStrings.put(new Integer(4), e);
}
public static byte[] readSAPTestData() {
 if (counter == 4) counter = 0;
 else counter++;
 return ((String)testStrings.get(new Integer(counter))).getBytes();
}

**************************************************************/
}
