You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openmeetings.apache.org by co...@google.com on 2013/02/19 20:33:33 UTC

[red5phone] r94 committed - Video support: initial code is commited

Revision: 94
Author:   solomax666@gmail.com
Date:     Tue Feb 19 11:33:08 2013
Log:      Video support: initial code is commited
http://code.google.com/p/red5phone/source/detail?r=94

Added:
  /branches/red5sip/src/java/org/red5/codecs/SIPCodecH264.java
  /branches/red5sip/src/java/org/red5/sip/app/RTPStreamVideoReceiver.java
  /branches/red5sip/src/java/org/red5/sip/app/SIPVideoLauncher.java
Modified:
  /branches/red5sip/src/java/org/red5/codecs/SIPCodec.java
  /branches/red5sip/src/java/org/red5/codecs/SIPCodecFactory.java
  /branches/red5sip/src/java/org/red5/sip/app/Application.java
  /branches/red5sip/src/java/org/red5/sip/app/IMediaReceiver.java
  /branches/red5sip/src/java/org/red5/sip/app/PlayNetStream.java
  /branches/red5sip/src/java/org/red5/sip/app/RTMPRoomClient.java
  /branches/red5sip/src/java/org/red5/sip/app/SIPTransport.java
  /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgent.java
  /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgentProfile.java
  /branches/red5sip/src/java/org/red5/sip/app/SdpUtils.java
  /branches/red5sip/src/java/org/zoolu/sdp/SessionDescriptor.java

=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodecH264.java	Tue Feb 19  
11:33:08 2013
@@ -0,0 +1,114 @@
+package org.red5.codecs;
+
+public class SIPCodecH264 implements SIPCodec {
+
+	private static final String codecName = "H264";
+	private static final int codecId = 35;
+	private static int defaultEncodedFrameSize = 160;
+    private static int defaultDecodedFrameSize = 160;
+	private int outgoingPacketization = 90000;
+    private int incomingPacketization = 90000;
+
+	@Override
+	public void encodeInit(int defaultEncodePacketization) {
+		if (this.outgoingPacketization == 0) {
+            this.outgoingPacketization = defaultEncodePacketization;
+        }
+	}
+
+	@Override
+	public void decodeInit(int defaultDecodePacketization) {
+		if (this.incomingPacketization == 0) {
+            this.incomingPacketization = defaultDecodePacketization;
+        }
+	}
+
+	@Override
+	public String codecNegotiateAttribute(String attributeName,
+			String localAttributeValue, String remoteAttributeValue) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getCodecBlankPacket(byte[] buffer, int offset) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public int pcmToCodec(float[] bufferIn, byte[] bufferOut) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public int codecToPcm(byte[] bufferIn, float[] bufferOut) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public int getIncomingEncodedFrameSize() {
+		return (defaultEncodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION) *  
incomingPacketization;
+	}
+
+	@Override
+	public int getIncomingDecodedFrameSize() {
+		return (defaultDecodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION) *  
incomingPacketization;
+	}
+
+	@Override
+	public int getOutgoingEncodedFrameSize() {
+		return (defaultEncodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION) *  
outgoingPacketization;
+	}
+
+	@Override
+	public int getOutgoingDecodedFrameSize() {
+		return (defaultDecodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION) *  
outgoingPacketization;
+	}
+
+	@Override
+	public int getSampleRate() {
+		return 90000;
+	}
+
+	@Override
+	public String getCodecName() {
+		return codecName;
+	}
+
+	@Override
+	public int getCodecId() {
+		return codecId;
+	}
+
+	@Override
+	public int getIncomingPacketization() {
+		return (defaultEncodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION) *  
incomingPacketization;
+	}
+
+	@Override
+	public int getOutgoingPacketization() {
+		return 2048;//( defaultDecodedFrameSize / SIPCodec.DEFAULT_PACKETIZATION  
) * outgoingPacketization;
+	}
+
+	@Override
+	public void setLocalPtime(int localPtime) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void setRemotePtime(int remotePtime) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public String[] getCodecMediaAttributes() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/RTPStreamVideoReceiver.java	 
Tue Feb 19 11:33:08 2013
@@ -0,0 +1,50 @@
+package org.red5.sip.app;
+
+import java.net.DatagramSocket;
+import org.red5.codecs.SIPCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import local.net.RtpPacket;
+import local.net.RtpSocket;
+
+public class RTPStreamVideoReceiver extends Thread {
+
+	protected static Logger log =  
LoggerFactory.getLogger(RTPStreamVideoReceiver.class);
+	public static int RTP_HEADER_SIZE = 12;
+	protected RtpSocket rtpSocket;
+	protected IMediaReceiver mediaReceiver;
+	protected SIPCodec codec;
+	private boolean running;
+
+	public RTPStreamVideoReceiver(IMediaReceiver mediaReceiver,  
DatagramSocket socket, SIPCodec codec) {
+		this.mediaReceiver = mediaReceiver;
+		rtpSocket = new RtpSocket(socket);
+		this.codec = codec;
+	}
+
+	@Override
+	public void interrupt() {
+		running = false;
+	}
+
+	@Override
+	public void run() {
+		running = true;
+
+		try {
+			while(running) {
+				byte[] sourceBuffer = new byte[codec.getIncomingDecodedFrameSize()];
+				RtpPacket rtpPacket = new RtpPacket(sourceBuffer, 0);
+				rtpSocket.receive(rtpPacket);
+				byte[] destBuffer = new byte[rtpPacket.getLength() -  
rtpPacket.getHeaderLength()];
+				System.arraycopy(sourceBuffer, rtpPacket.getHeaderLength(),  
destBuffer, 0, destBuffer.length);
+				mediaReceiver.pushVideo(destBuffer, rtpPacket.getTimestamp(),  
codec.getCodecId());
+			}
+		} catch (Exception e) {
+			log.error("", e);
+		}
+		rtpSocket.close();
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPVideoLauncher.java	Tue  
Feb 19 11:33:08 2013
@@ -0,0 +1,39 @@
+package org.red5.sip.app;
+
+import java.net.DatagramSocket;
+
+import org.red5.codecs.SIPCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import local.ua.MediaLauncher;
+
+public class SIPVideoLauncher implements MediaLauncher {
+
+	protected static Logger log =  
LoggerFactory.getLogger(SIPVideoLauncher.class);
+	protected DatagramSocket socket;
+	protected RTPStreamVideoReceiver receiver;
+
+	public SIPVideoLauncher(int localPort, String remoteAddr, int remotePort,  
IMediaReceiver mediaReceiver, SIPCodec codec) {
+		try {
+			socket = new DatagramSocket(localPort);
+			receiver = new RTPStreamVideoReceiver(mediaReceiver, socket, codec);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+	}
+
+	@Override
+	public boolean startMedia() {
+		log.debug("startMedia()");
+		receiver.start();
+		return true;
+	}
+
+	@Override
+	public boolean stopMedia() {
+		log.debug("stopMedia()");
+		return false;
+	}
+
+}
=======================================
--- /branches/red5sip/src/java/org/red5/codecs/SIPCodec.java	Fri Feb 10  
07:37:46 2012
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodec.java	Tue Feb 19  
11:33:08 2013
@@ -2,7 +2,7 @@


  /**
- * Interface for audio codecs
+ * Interface for media codecs
   * */
  public interface SIPCodec {

=======================================
--- /branches/red5sip/src/java/org/red5/codecs/SIPCodecFactory.java	Sun Feb  
17 19:49:41 2013
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodecFactory.java	Tue Feb  
19 11:33:08 2013
@@ -26,8 +26,10 @@
      // ----------------
      // Available video codecs

-    private int[] availableVideoCodecsId = {};
+    private static final int videoCodecH264 = 35;

+    private int[] availableVideoCodecsId = {videoCodecH264};
+

      private static SIPCodecFactory singletonSIPCodecFactory = new  
SIPCodecFactory();

@@ -43,7 +45,7 @@
       * Create a new instance of SIPCodec by codec id.
       * @return The codec associated with "codecId".
       * */
-    public SIPCodec getSIPAudioCodec( int codecId ) {
+    public SIPCodec getSIPMediaCodec( int codecId ) {

          SIPCodec sipCodec;

@@ -64,6 +66,9 @@
              case audioCodeciLBC:
                  sipCodec = new SIPCodeciLBC();
                  break;
+            case videoCodecH264:
+            	sipCodec = new SIPCodecH264();
+            	break;
              default:
                  sipCodec = null;
          }
@@ -90,7 +95,7 @@

          for ( int i = 0; i < availableAudioCodecsId.length; i++ ) {
              int codecId = availableAudioCodecsId[ i ];
-            SIPCodec codec = getSIPAudioCodec( codecId );
+            SIPCodec codec = getSIPMediaCodec( codecId );
              availableCodecs[i] = codec;
          }

@@ -110,7 +115,7 @@

          for ( int i = 0; i < availableVideoCodecsId.length; i++ ) {
              int codecId = availableVideoCodecsId[ i ];
-            SIPCodec codec = getSIPAudioCodec( codecId );
+            SIPCodec codec = getSIPMediaCodec( codecId );
              availableCodecs[i] = codec;
          }

@@ -143,7 +148,7 @@
              printLog( "getAvailableAudioCodecsWithPrecedence",
                      "codecId = [" + codecId + "]." );

-            SIPCodec sipCodec = getSIPAudioCodec(
+            SIPCodec sipCodec = getSIPMediaCodec(
                      Integer.valueOf( codecId ).intValue() );

              if ( sipCodec != null ) {
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/Application.java	Mon Feb 18  
02:34:27 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/Application.java	Tue Feb 19  
11:33:08 2013
@@ -16,21 +16,23 @@
      private static final Logger log =  
LoggerFactory.getLogger(Application.class);
      private static final int SIP_START_PORT = 5070;
      private static final int SOUND_START_PORT = 3010;
+    private static final int VIDEO_START_PORT = 7010;
      private static int sipPort = SIP_START_PORT;
      private static int soundPort = SOUND_START_PORT;
+    private static int videoPort = VIDEO_START_PORT;
      private Properties props = null;
      private Map<Integer, SIPTransport> transportMap = new HashMap<Integer,  
SIPTransport>();
      private RTMPControlClient rtmpControlClient;

-    private SIPTransport createSIPTransport(Properties prop, int room_id) {
+    public SIPTransport createSIPTransport(Properties prop, int room_id) {
          log.info("Creating SIP trasport for room: " + room_id);
          RTPStreamSender.useASAO =  
prop.getProperty("red5.codec", "asao").equals("asao");
          RTMPRoomClient roomClient = new RTMPRoomClient(
  			prop.getProperty("red5.host")
  			, prop.getProperty("om.context", "openmeetings")
  			, room_id);
-
-        SIPTransport sipTransport = new SIPTransport(roomClient,  
sipPort++, soundPort++) {
+
+        SIPTransport sipTransport = new SIPTransport(roomClient,  
sipPort++, soundPort++, videoPort++) {
              public void onUaRegistrationSuccess(SIPRegisterAgent ra,  
NameAddress target, NameAddress contact, String result) {
                  log.info("Registered successfully");
                  this.roomClient.setSipNumberListener(this);
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/IMediaReceiver.java	Fri Feb  
10 07:37:46 2012
+++ /branches/red5sip/src/java/org/red5/sip/app/IMediaReceiver.java	Tue Feb  
19 11:33:08 2013
@@ -6,6 +6,8 @@

      void pushAudio( byte[] audio, long ts, int codec ) throws IOException;

+    void pushVideo( byte[] video, long ts, int codec ) throws IOException;
+
      void setSender( IMediaSender sender );

  }
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/PlayNetStream.java	Sun Feb  
17 19:49:41 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/PlayNetStream.java	Tue Feb  
19 11:33:08 2013
@@ -17,6 +17,8 @@

      private int audioTs = 0;

+    private int videoTs = 0;
+
      private IMediaSender mediaSender;

      private IMediaStream mediaStream;
@@ -63,8 +65,8 @@
          }

          if (rtmpEvent instanceof VideoData) {
-            // videoTs += rtmpEvent.getTimestamp();
-            // tag.setTimestamp(videoTs);
+             videoTs += rtmpEvent.getTimestamp();
+             //tag.setTimestamp(videoTs);

          } else if (rtmpEvent instanceof AudioData) {
              audioTs = rtmpEvent.getTimestamp();
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/RTMPRoomClient.java	Mon Feb  
18 21:27:45 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/RTMPRoomClient.java	Tue Feb  
19 11:33:08 2013
@@ -2,6 +2,7 @@

  import java.io.IOException;
  import java.lang.reflect.Field;
+import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
@@ -27,6 +28,7 @@
  import org.red5.server.net.rtmp.codec.RTMP;
  import org.red5.server.net.rtmp.event.AudioData;
  import org.red5.server.net.rtmp.event.Notify;
+import org.red5.server.net.rtmp.event.VideoData;
  import org.red5.server.net.rtmp.message.Header;
  import org.red5.server.net.rtmp.status.StatusCodes;
  import org.red5.server.service.Call;
@@ -45,7 +47,8 @@
      private long broadCastId = -1;
      private RTMPConnection conn;
      private IMediaSender sender;
-    private IoBuffer buffer;
+    private IoBuffer audioBuffer;
+    private IoBuffer videoBuffer;
      private int kt = 0;
      private Integer publishStreamId = null;
      private boolean reconnect = true;
@@ -469,21 +472,21 @@
          if( publishStreamId == null) {
              return;
          }
-        if ( buffer == null ) {
-            buffer = IoBuffer.allocate( 1024 );
-            buffer.setAutoExpand( true );
+        if ( audioBuffer == null ) {
+            audioBuffer = IoBuffer.allocate( 1024 );
+            audioBuffer.setAutoExpand( true );
          }

-        buffer.clear();
+        audioBuffer.clear();

-        buffer.put( (byte) codec ); // first byte 2 mono 5500; 6 mono  
11025; 22
+        audioBuffer.put( (byte) codec ); // first byte 2 mono 5500; 6 mono  
11025; 22
          // mono 11025 adpcm 82 nellymoser 8000 178
          // speex 8000
-        buffer.put( audio );
+        audioBuffer.put( audio );

-        buffer.flip();
+        audioBuffer.flip();

-        AudioData audioData = new AudioData( buffer );
+        AudioData audioData = new AudioData( audioBuffer );
          audioData.setTimestamp( (int) ts );

          kt++;
@@ -495,4 +498,28 @@
          rtmpMsg.setBody( audioData );
          publishStreamData( publishStreamId, rtmpMsg );
      }
+
+	@Override
+	public void pushVideo(byte[] video, long ts, int codec) throws  
IOException {
+		if( publishStreamId == null) {
+            return;
+        }
+		if (videoBuffer == null || (videoBuffer.capacity() < video.length  
&& !videoBuffer.isAutoExpand())) {
+			videoBuffer = IoBuffer.allocate(video.length);
+			videoBuffer.setAutoExpand(true);
+		}
+
+		videoBuffer.clear();
+		//videoBuffer.put((byte) codec);
+		videoBuffer.put(video);
+		videoBuffer.flip();
+
+		VideoData videoData = new VideoData(videoBuffer);
+		videoData.setTimestamp((int) ts);
+
+		RTMPMessage message = new RTMPMessage();
+		message.setBody(videoData);
+
+		publishStreamData(publishStreamId, message);
+	}
  }
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SIPTransport.java	Mon Feb  
18 21:27:45 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPTransport.java	Tue Feb  
19 11:33:08 2013
@@ -21,7 +21,8 @@
  	private String username;
  	private String password;
  	private int sipPort;
-	private int rtpPort;
+	private int rtpAudioPort;
+	private int rtpVideoPort;
  	private String proxy;
  	private String number;

@@ -29,10 +30,11 @@
  		log.debug(s);
  	}

-	public SIPTransport(RTMPRoomClient roomClient, int sipPort, int rtpPort) {
+	public SIPTransport(RTMPRoomClient roomClient, int sipPort, int  
rtpAudioPort, int rtpVideoPort) {
  		this.roomClient = roomClient;
  		this.sipPort = sipPort;
-		this.rtpPort = rtpPort;
+		this.rtpAudioPort = rtpAudioPort;
+		this.rtpVideoPort = rtpVideoPort;
  	}

  	public void login(String obproxy, String phone, String username,
@@ -56,7 +58,8 @@
  					.setOutboundProxy(new SocketAddress(opt_outbound_proxy));

  			user_profile = new SIPUserAgentProfile();
-			user_profile.audioPort = rtpPort;
+			user_profile.audioPort = rtpAudioPort;
+			user_profile.videoPort = rtpVideoPort;
  			user_profile.username = username;
  			user_profile.passwd = password;
  			user_profile.realm = realm;
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgent.java	Sun Feb  
17 19:49:41 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgent.java	Tue Feb  
19 11:33:08 2013
@@ -79,8 +79,11 @@
      private IMediaReceiver mediaReceiver;

      /** Sip codec to be used on audio session */
-    private SIPCodec sipCodec = null;
+    private SIPCodec sipAudioCodec = null;

+    /** Sip codec to be used on video session */
+    private SIPCodec sipVideoCodec = null;
+

      // *********************** Startup Configuration  
***********************

@@ -469,13 +472,13 @@

              if ( audioApp == null ) {

-                if ( sipCodec != null ) {
+                if ( sipAudioCodec != null ) {

-                    audioApp = new SIPAudioLauncher( sipCodec,  
localAudioPort,
+                    audioApp = new SIPAudioLauncher( sipAudioCodec,  
localAudioPort,
                              remoteMediaAddress, remoteAudioPort,  
mediaReceiver);
                  }
                  else {
-                    printLog( "launchMediaApplication", "SipCodec not  
initialized." );
+                    printLog( "launchMediaApplication", "Audio SipCodec  
not initialized." );
                  }
              }

@@ -485,15 +488,17 @@
              }
          }
          if ( userProfile.video && localVideoPort != 0 &&  
remoteVideoPort != 0 ) {
-
-            if ( videoApp == null ) {
-
-                printLog( "launchMediaApplication",
-                        "No external video application nor JMF has been  
provided: Video not started." );
-                return;
+        	if (videoApp == null) {
+        		if (sipVideoCodec != null) {
+        			videoApp = new SIPVideoLauncher(localVideoPort,  
remoteMediaAddress, remoteAudioPort, mediaReceiver, sipVideoCodec);
+        		} else {
+        			printLog( "launchMediaApplication", "Video SipCodec not  
initialized." );
+        		}
+        	}
+
+            if (videoApp != null) {
+            	videoApp.startMedia();
              }
-
-            videoApp.startMedia();
          }
      }

@@ -550,9 +555,10 @@
              // attributes can be then matched.
              SessionDescriptor newSdp =  
SdpUtils.makeMediaPayloadsNegotiation(localSdp, remoteSdp);

-            // After we can create the correct audio codec considering
-            // audio negotiation made above.
-            sipCodec = SdpUtils.getNegotiatedAudioCodec( newSdp );
+            // After we can create the correct audio and video codecs  
considering
+            // audio and video negotiations made above.
+            sipAudioCodec = SdpUtils.getNegotiatedAudioCodec( newSdp );
+            sipVideoCodec = SdpUtils.getNegotiatedVideoCodec( newSdp );

              // Now we complete the SDP negotiation informing the selected
              // codec, so it can be internally updated during the process.
@@ -565,7 +571,7 @@
              // Finally, we use the "newSdp" and "remoteSdp" to initialize
              // the lasting codec informations.
              SIPCodecUtils.initSipAudioCodec(
-                    sipCodec,
+                    sipAudioCodec,
                      userProfile.audioDefaultPacketization,
                      userProfile.audioDefaultPacketization, newSdp,  
remoteSdp );
          }
@@ -647,7 +653,8 @@

          // After we can create the correct audio codec considering
          // audio negotiation made above.
-        sipCodec = SdpUtils.getNegotiatedAudioCodec( newSdp );
+        sipAudioCodec = SdpUtils.getNegotiatedAudioCodec( newSdp );
+        sipVideoCodec = SdpUtils.getNegotiatedVideoCodec( newSdp );

          // Now we complete the SDP negotiation informing the selected
          // codec, so it can be internally updated during the process.
@@ -660,7 +667,7 @@
          // Finally, we use the "newSdp" and "remoteSdp" to initialize
          // the lasting codec informations.
          SIPCodecUtils.initSipAudioCodec(
-                sipCodec,
+                sipAudioCodec,
                  userProfile.audioDefaultPacketization,
                  userProfile.audioDefaultPacketization, newSdp, remoteSdp );

=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgentProfile.java	 
Fri Feb 10 07:37:46 2012
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgentProfile.java	 
Tue Feb 19 11:33:08 2013
@@ -128,7 +128,7 @@
      public boolean audio = true;

      /** Whether using video */
-    public boolean video = false;
+    public boolean video = true;

      /** Whether playing in receive only mode */
      public boolean recvOnly = false;
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SdpUtils.java	Sun Feb 17  
20:38:26 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SdpUtils.java	Tue Feb 19  
11:33:08 2013
@@ -17,6 +17,26 @@
      protected static Logger log = LoggerFactory.getLogger( SdpUtils.class  
);


+    public static SIPCodec getNegotiatedVideoCodec(SessionDescriptor  
negotiatedSDP) {
+    	String rtpmap =  
negotiatedSDP.getMediaDescriptor(SIPCodec.MEDIA_TYPE_VIDEO).
+    			getAttribute(SIPCodec.ATTRIBUTE_RTPMAP).getAttributeValue();
+    	printLog( "getNegotiatedVideoCodec", "rtpmap = [" + rtpmap + "]." );
+    	if (rtpmap.isEmpty()) return null;
+    	int payloadId = Integer.parseInt(rtpmap.substring(0,  
rtpmap.indexOf(" ")));
+    	SIPCodec sipCodec =  
SIPCodecFactory.getInstance().getSIPMediaCodec(payloadId);
+    	if ( sipCodec == null ) {
+
+            printLog( "getNegotiatedAudioCodec", "Error... codec not  
found." );
+        }
+        else {
+
+            printLog( "getNegotiatedAudioCodec",
+                    "payloadType = " + sipCodec.getCodecId() +
+                    ", payloadName = " + sipCodec.getCodecName() + "." );
+        }
+    	return sipCodec;
+    }
+
      /**
       * @return Returns the audio codec to be used on current session.
       */
@@ -40,7 +60,7 @@

              printLog( "getNegotiatedAudioCodec", "payloadId = [" +  
payloadId + "]." );

-            sipCodec = SIPCodecFactory.getInstance().getSIPAudioCodec(  
payloadId );
+            sipCodec = SIPCodecFactory.getInstance().getSIPMediaCodec(  
payloadId );

              if ( sipCodec == null ) {

@@ -217,7 +237,7 @@
                  SIPCodec[] videoCodecs =  
SIPCodecFactory.getInstance().getAvailableVideoCodecs();
                  Vector<AttributeField> videoAttributes = new  
Vector<AttributeField>();

-                for ( int videoIndex = 0; videoIndex < audioCodecsNumber;  
videoIndex++ ) {
+                for ( int videoIndex = 0; videoIndex < videoCodecsNumber;  
videoIndex++ ) {

                      String payloadId = String.valueOf(  
videoCodecs[videoIndex].getCodecId() );
                      String rtpmapParamValue = payloadId;
@@ -271,7 +291,7 @@
                      if ( initialDescriptor.getMediaDescriptor(  
SIPCodec.MEDIA_TYPE_VIDEO ) == null ) {

                          initialDescriptor.addMedia(
-                                new MediaField( SIPCodec.MEDIA_TYPE_VIDEO,  
audioPort, 0, "RTP/AVP", formatList ),
+                                new MediaField( SIPCodec.MEDIA_TYPE_VIDEO,  
videoPort, 0, "RTP/AVP", formatList ),
                                  videoAttribute );
                      }
                      else {
@@ -282,7 +302,7 @@
                  }

                  String[] commonVideoMediaAttributes =
-                         
SIPCodecFactory.getInstance().getCommonAudioMediaAttributes();
+                         
SIPCodecFactory.getInstance().getCommonVideoMediaAttributes();

                  if ( commonVideoMediaAttributes != null ) {

@@ -444,18 +464,15 @@
                      for ( Enumeration<AttributeField> attributesEnum =  
newSdpAttributes.elements(); attributesEnum.hasMoreElements(); ) {

                          AttributeField mediaAttribute =  
attributesEnum.nextElement();
-
-                        if ( newSdp.getMediaDescriptors().size() == 0 ) {

-                            newSdp.addMediaDescriptor( new MediaDescriptor(
-                                    new MediaField(  
localDescriptor.getMedia().getMedia(),
-                                             
localDescriptor.getMedia().getPort(),
-                                            0,
-                                             
localDescriptor.getMedia().getTransport(),
-                                            formatList ),
-                                    localDescriptor.getConnection() ) );
-                        }
-
+                        newSdp.addMediaDescriptor( new MediaDescriptor(
+                                new MediaField(  
localDescriptor.getMedia().getMedia(),
+                                         
localDescriptor.getMedia().getPort(),
+                                        0,
+                        				localDescriptor.getMedia().getTransport(),
+										formatList ),
+                                    	localDescriptor.getConnection() ) );
+
                          newSdp.getMediaDescriptor(  
localDescriptor.getMedia().getMedia() ).
                                  addAttribute( mediaAttribute );
                      }
@@ -623,7 +640,7 @@
                      AttributeField localAttribute =  
findAttributeByPayloadId(
                              remoteAttribute.getAttributeName(), payloadId,  
localMedia );

-                    SIPCodec sipCodec =  
SIPCodecFactory.getInstance().getSIPAudioCodec(
+                    SIPCodec sipCodec =  
SIPCodecFactory.getInstance().getSIPMediaCodec(
                              Integer.valueOf( payloadId ) );

                      if ( sipCodec != null ) {
=======================================
--- /branches/red5sip/src/java/org/zoolu/sdp/SessionDescriptor.java	Sun Feb  
17 19:49:41 2013
+++ /branches/red5sip/src/java/org/zoolu/sdp/SessionDescriptor.java	Tue Feb  
19 11:33:08 2013
@@ -157,15 +157,20 @@
        }
        // parse session attributes
        av=new Vector<AttributeField>();
-      while (par.hasMore() && par.startsWith("a="))
-      {  AttributeField attribute=par.parseAttributeField();
-         av.addElement(attribute);
-      }
+      while (par.hasMore()) {
+    	  if (par.startsWith("a=")) {
+	    	  AttributeField attribute=par.parseAttributeField();
+	          av.addElement(attribute);
+    	  } else par.goToNextLine();
+      }
+      par.setPos(0);
        // parse media descriptors
        media=new Vector<MediaDescriptor>();
-      MediaDescriptor md;
-      while ((md=par.parseMediaDescriptor())!=null)
-      {  addMediaDescriptor(md);
+      while (par.hasMore()) {
+    	  if (par.startsWith("m=")) {
+		      MediaDescriptor md = par.parseMediaDescriptor();
+		      if (md != null) addMediaDescriptor(md);
+    	  } else par.goToNextLine();
        }
     }