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/03/05 10:52:44 UTC

[red5phone] r101 committed - Video support is added to red5sip

Revision: 101
Author:   solomax666@gmail.com
Date:     Tue Mar  5 01:52:05 2013
Log:      Video support is added to red5sip
http://code.google.com/p/red5phone/source/detail?r=101

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/RTPStreamVideoSender.java
  /branches/red5sip/src/java/org/red5/sip/app/RTPVideoStream.java
  /branches/red5sip/src/java/org/red5/sip/app/SIPVideoConverter.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/RTPStreamMultiplexingSender.java
  /branches/red5sip/src/java/org/red5/sip/app/RTPStreamSender.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

=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodecH264.java	Tue Mar  5  
01:52:05 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 Mar  5 01:52:05 2013
@@ -0,0 +1,98 @@
+package org.red5.sip.app;
+
+import java.net.DatagramSocket;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.red5.codecs.SIPCodec;
+import org.red5.sip.app.SIPVideoConverter.RTMPPacketInfo;
+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);
+	protected RtpSocket rtpSocket;
+	protected IMediaReceiver mediaReceiver;
+	protected SIPCodec codec;
+	private boolean running;
+	private ConverterThread converterThread;
+
+	public RTPStreamVideoReceiver(IMediaReceiver mediaReceiver,  
DatagramSocket socket, SIPCodec codec) {
+		this.mediaReceiver = mediaReceiver;
+		rtpSocket = new RtpSocket(socket);
+		this.codec = codec;
+		converterThread = new ConverterThread();
+	}
+
+	@Override
+	public void interrupt() {
+		running = false;
+		converterThread.interrupt();
+	}
+
+	@Override
+	public void run() {
+		running = true;
+		converterThread.start();
+		try {
+			while(running) {
+				byte[] sourceBuffer = new byte[codec.getIncomingDecodedFrameSize()];
+				RtpPacket rtpPacket = new RtpPacket(sourceBuffer, 0);
+				rtpSocket.receive(rtpPacket);
+				converterThread.addPacket(rtpPacket);
+			}
+		} catch (Exception e) {
+			log.error("", e);
+		}
+		rtpSocket.close();
+	}
+
+	private class ConverterThread extends Thread {
+
+		private final Queue<RtpPacket> packetQueue;
+		private boolean running;
+		private SIPVideoConverter converter;
+
+		public ConverterThread() {
+			packetQueue = new ConcurrentLinkedQueue<RtpPacket>();
+			converter = new SIPVideoConverter();
+		}
+
+		public void addPacket(RtpPacket packet) {
+			if (isInterrupted()) return;
+			packetQueue.add(packet);
+		}
+
+		@Override
+		public void run() {
+			running = true;
+			while(running) {
+				try {
+					RtpPacket packet = packetQueue.poll();
+					if (packet != null) {
+						for (RTMPPacketInfo packetInfo: converter.rtp2rtmp(packet, codec)) {
+							mediaReceiver.pushVideo(packetInfo.data, packetInfo.ts);
+						}
+					}
+					if (packetQueue.size() == 0) {
+						Thread.sleep(50);
+					}
+				} catch (Exception e) {
+					log.error("", e);
+				}
+			}
+		}
+
+		@Override
+		public void interrupt() {
+			running = false;
+			packetQueue.clear();
+		}
+
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/RTPStreamVideoSender.java	 
Tue Mar  5 01:52:05 2013
@@ -0,0 +1,67 @@
+package org.red5.sip.app;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+import org.red5.codecs.SIPCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import local.net.RtpPacket;
+import local.net.RtpSocket;
+
+public class RTPStreamVideoSender implements IMediaSender {
+
+	private static Logger log =  
LoggerFactory.getLogger(RTPStreamVideoSender.class);
+	private SIPCodec codec;
+	private RtpSocket rtpSocket;
+	private int seqn = 0;
+
+	public RTPStreamVideoSender(IMediaReceiver mediaReceiver, SIPCodec codec,
+			DatagramSocket srcSocket, String destAddr, int destPort) {
+		this.codec = codec;
+		mediaReceiver.setVideoSender(this);
+
+		try {
+			rtpSocket = new RtpSocket(srcSocket, InetAddress.getByName(destAddr),  
destPort);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+	}
+
+	@Override
+	public IMediaStream createStream(int streamId) {
+		return new RTPVideoStream(this, codec);
+	}
+
+	@Override
+	public void deleteStream(int streamId) {}
+
+	@Override
+	public void start() {
+		seqn = 0;
+	}
+
+	@Override
+	public void halt() {
+		rtpSocket.close();
+		rtpSocket = null;
+	}
+
+	public void send(RtpPacket packet) {
+		if (rtpSocket == null) {
+			return;
+		}
+		packet.setSequenceNumber(seqn++);
+		rtpSocketSend(packet);
+	}
+
+	private synchronized void rtpSocketSend(RtpPacket rtpPacket) {
+		try {
+			rtpSocket.send(rtpPacket);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/RTPVideoStream.java	Tue  
Mar  5 01:52:05 2013
@@ -0,0 +1,26 @@
+package org.red5.sip.app;
+
+import org.red5.codecs.SIPCodec;
+
+import local.net.RtpPacket;
+
+public class RTPVideoStream implements IMediaStream {
+
+	private RTPStreamVideoSender sender;
+	private SIPVideoConverter converter;
+	private SIPCodec codec;
+
+	public RTPVideoStream(RTPStreamVideoSender sender, SIPCodec codec) {
+		this.sender = sender;
+		this.codec = codec;
+		converter = new SIPVideoConverter();
+	}
+
+	@Override
+	public void send(long timestamp, byte[] data, int offset, int num) {
+		for (RtpPacket packet: converter.rtmp2rtp(data, timestamp, codec)) {
+			sender.send(packet);
+		}
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPVideoConverter.java	Tue  
Mar  5 01:52:05 2013
@@ -0,0 +1,458 @@
+package org.red5.sip.app;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.red5.codecs.SIPCodec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import local.net.RtpPacket;
+
+public class SIPVideoConverter {
+
+	private static final Logger log =  
LoggerFactory.getLogger(SIPVideoConverter.class);
+
+	// rtp => rtmp
+	private byte[] sps1;
+	private byte[] pps;
+	private boolean sentSeq;
+	private long lastFIRTime;
+	private long startTs;
+	private long startTm;
+	private long startRelativeTime;
+	private List<RtpPacketWrapper> packetsQueue;
+
+	// rtmp => rtp
+	private int lenSize;
+	private boolean spsSent = false;
+	private boolean ppsSent = false;
+
+	public SIPVideoConverter() {
+		resetConverter();
+		startRelativeTime = System.currentTimeMillis();
+	}
+
+	public void resetConverter() {
+		packetsQueue = new ArrayList<RtpPacketWrapper>();
+		lastFIRTime = System.currentTimeMillis();
+		sps1 = new byte[0];
+		pps = new byte[0];
+		sentSeq = false;
+		startTs = -1;
+		startTm = -1;
+	}
+
+	public List<RTMPPacketInfo> rtp2rtmp(RtpPacket packet, SIPCodec codec) {
+		switch (codec.getCodecId()) {
+		case 35:
+			return rtp2rtmpH264(packet);
+		default:
+			log.error("Unsuported codec type: " + codec.getCodecName());
+			return new ArrayList<RTMPPacketInfo>();
+		}
+	}
+
+	public List<RtpPacket> rtmp2rtp(byte data[], long ts, SIPCodec codec) {
+		switch (codec.getCodecId()) {
+		case 35:
+			return rtmp2rtpH254(data, ts);
+		default:
+			log.error("Unsuported codec type: " + codec.getCodecName());
+			return new ArrayList<RtpPacket>();
+		}
+	}
+
+	private List<RtpPacket> rtmp2rtpH254(byte data[], long ts) {
+		List<RtpPacket> result = new ArrayList<RtpPacket>();
+		if (data[0] == 0x17 && data[1] == 0) {
+			byte[] pdata = Arrays.copyOfRange(data, 2, data.length);
+			int cfgVer = pdata[3];
+			log.debug("cfgVer=" + cfgVer);
+			if (cfgVer == 1) {
+				int lenSize = pdata[7] & 0x03 + 1;
+				int numSPS = pdata[8] & 0x1f;
+				log.debug("lenSize=" + lenSize);
+				log.debug("numSPS=" + numSPS);
+				pdata = Arrays.copyOfRange(pdata, 9, pdata.length);
+				byte[] sps = null;
+				for (int i = 0; i < numSPS; i++) {
+					int lenSPS = (pdata[0] & 0xff) << 8 | pdata[1] & 0xff;
+					pdata = Arrays.copyOfRange(pdata, 2, pdata.length);
+					if (sps == null) {
+						sps = Arrays.copyOf(pdata, lenSPS);
+					}
+					pdata = Arrays.copyOfRange(pdata, lenSPS, pdata.length);
+				}
+				int numPPS = pdata[0];
+				log.debug("numPPS=" + numSPS);
+				pdata = Arrays.copyOfRange(pdata, 1, pdata.length);
+				byte[] pps = null;
+				for (int i = 0; i < numPPS; i++) {
+					int lenPPS = (pdata[0] & 0xff) << 8 | pdata[1] & 0xff;
+					pdata = Arrays.copyOfRange(pdata, 2, pdata.length);
+					if (pps == null) {
+						pps = Arrays.copyOf(pdata, lenPPS);
+					}
+					pdata = Arrays.copyOfRange(pdata, lenPPS, pdata.length);
+				}
+				this.lenSize = lenSize;
+				if (sps != null) {
+					spsSent = true;
+					long ts1 = ts * 90;
+					byte[] buffer = new byte[sps.length + 12];
+					RtpPacket packet = new RtpPacket(buffer, 0);
+					packet.setPayload(sps, sps.length);
+					packet.setTimestamp(ts1);
+					buffer[1] = (byte) 0xe3;
+					result.add(packet);
+				}
+				if (pps != null) {
+					ppsSent = true;
+					long ts1 = ts * 90;
+					byte[] buffer = new byte[pps.length + 12];
+					RtpPacket packet = new RtpPacket(buffer, 0);
+					packet.setPayload(pps, pps.length);
+					packet.setTimestamp(ts1);
+					buffer[1] = (byte) 0xe3;
+					result.add(packet);
+				}
+			}
+		} else if ((data[0] == 0x17 || data[0] == 0x27) && data[1] == 1) {
+			if (spsSent && ppsSent) {
+				List<ByteArrayBuilder> nals = new ArrayList<ByteArrayBuilder>();
+				byte[] pdata = Arrays.copyOfRange(data, 5, data.length);
+				log.debug("pdata.length=" + pdata.length);
+				while (pdata.length > 0) {
+					int nalSize = 0;
+					switch (lenSize) {
+					case 1:
+						nalSize = pdata[lenSize - 1] & 0xff;
+						break;
+					case 2:
+						nalSize = (pdata[lenSize - 2] & 0xff) << 8 | pdata[lenSize - 1] &  
0xff;
+						break;
+					case 4:
+						nalSize = (pdata[lenSize - 4] & 0xff) << 24 |
+								  (pdata[lenSize - 3] & 0xff) << 16 |
+								  (pdata[lenSize - 2] & 0xff) << 8  |
+								  (pdata[lenSize - 1] & 0xff);
+						break;
+					default:
+						throw new RuntimeException("Invalid length size: " + lenSize);
+					}
+					log.debug("nalSize=" + nalSize);
+					ByteArrayBuilder nalData = new  
ByteArrayBuilder(Arrays.copyOfRange(pdata, lenSize, lenSize + nalSize));
+					nals.add(nalData);
+					pdata = Arrays.copyOfRange(pdata, lenSize + nalSize, pdata.length);
+				}
+				log.debug("nals.size()=" + nals.size());
+				if (nals.size() > 0) {
+					byte[] remaining = nals.get(nals.size() - 1).buildArray();
+					int nalType = remaining[0] & 0x1f;
+					int nri = remaining[0] & 0x60;
+					int maxSize = 1446;
+					log.debug("nalType=" + nalType);
+					if (nalType == 5 || nalType == 1) {
+						long ts1 = ts * 90;
+						if (remaining.length < maxSize) {
+							byte[] buffer = new byte[remaining.length + 12];
+							RtpPacket packet = new RtpPacket(buffer, 0);
+							packet.setPayload(remaining, remaining.length);
+							packet.setTimestamp(ts1);
+							buffer[1] = (byte) 0xe3; // marker and payload type
+							result.add(packet);
+						} else {
+							byte start = (byte) 0x80;
+							remaining = Arrays.copyOfRange(remaining, 1, remaining.length);
+							while (remaining.length > 0) {
+								pdata = Arrays.copyOf(remaining, Math.min(maxSize - 2,  
remaining.length));
+								remaining = Arrays.copyOfRange(remaining, Math.min(maxSize - 2,  
remaining.length), remaining.length);
+								byte end = (byte) ((remaining.length > 0)? 0: 0x40);
+								ByteArrayBuilder payload = new ByteArrayBuilder((byte) (nri | 28),  
(byte) (start | end | nalType));
+								payload.putArray(pdata);
+								start = 0;
+
+								byte[] buffer = new byte[payload.getLength() + 12];
+								RtpPacket packet = new RtpPacket(buffer, 0);
+								packet.setPayload(payload.buildArray(), payload.getLength());
+								packet.setTimestamp(ts1);
+								buffer[1] = (byte) ((end == 0x40)? 0xe3: 0x63);
+								result.add(packet);
+							}
+						}
+					}
+				}
+			}
+		}
+		log.debug("result.size()=" + result.size());
+		return result;
+	}
+
+	private List<RTMPPacketInfo> rtp2rtmpH264(RtpPacket packet) {
+		List<RTMPPacketInfo> result = new ArrayList<RTMPPacketInfo>();
+		byte[] payload = packet.getPayload();
+		int nalType = payload[0] & 0x1f;
+		byte[] naldata = null;
+
+		switch (nalType) {
+		case 7: // SPS
+			sps1 = payload;
+			log.debug("SPS received: " + Arrays.toString(sps1));
+			break;
+		case 8: // PPS
+			pps = payload;
+			log.debug("PPS received: " + Arrays.toString(pps));
+			break;
+		default:
+			if (payload.length > 1) {
+				if (nalType == 24) { // for cisco phones
+					payload = Arrays.copyOfRange(payload, 1, payload.length);
+					while (payload.length > 0) {
+						int size = payload[1];
+						payload = Arrays.copyOfRange(payload, 2, payload.length);
+						naldata = Arrays.copyOf(payload, size);
+						payload = Arrays.copyOfRange(payload, size, payload.length);
+						int nt = naldata[0] & 0x1f;
+						switch (nt) {
+						case 7:
+							sps1 = naldata;
+							log.debug("SPS received: " + Arrays.toString(sps1));
+							break;
+						case 8:
+							pps = naldata;
+							log.debug("PPS received: " + Arrays.toString(pps));
+							break;
+						default:
+							break;
+						}
+					}
+				}
+
+				if (nalType == 1 || nalType == 5 || nalType == 28 || nalType == 24) {
+					packetsQueue.add(new RtpPacketWrapper(packet, nalType));
+				}
+			}
+			break;
+		}
+
+		if (packetsQueue.size() > 1) {
+			RtpPacket last = packetsQueue.get(packetsQueue.size() - 1).packet;
+			RtpPacket preLast = packetsQueue.get(packetsQueue.size() - 2).packet;
+			if (last.getTimestamp() != preLast.getTimestamp()) {
+				log.debug("Clearing queue since new packet has different ts. old ts="  
+ preLast.getTimestamp() +
+						" new ts=" + last.getTimestamp());
+				packetsQueue.clear();
+			}
+		}
+
+		// marker means the end of the frame
+		if (packet.getPacket()[1] < 0 // packet.hasMarker() bug workaround
+				&& !packetsQueue.isEmpty()) {
+			int realNri = 0;
+			nalType = 0;
+			List<ByteArrayBuilder> payloads = new ArrayList<ByteArrayBuilder>();
+			ByteArrayBuilder newdata = null;
+			List<ByteArrayBuilder> pendingData = new ArrayList<ByteArrayBuilder>();
+
+			for (RtpPacketWrapper q: packetsQueue) {
+				int length = 0;
+				switch (q.nalType) {
+				case 1:
+				case 5:
+					if (newdata == null) {
+						nalType = q.nalType;
+						// first byte: 0x17 for intra-frame, 0x27 for non-intra frame
+						// second byte: 0x01 for picture data
+						newdata = new ByteArrayBuilder(new byte[]{(byte) (q.nalType == 5?  
0x17: 0x27), 1, 0, 0, 0});
+					}
+					length = q.packet.getPayload().length;
+					newdata.putArray((byte) (length >>> 24), (byte) (length >>> 16),  
(byte) (length >>> 8), (byte) length);
+					newdata.putArray(q.packet.getPayload());
+					break;
+				case 24:
+					payload = Arrays.copyOfRange(payload, 1, payload.length);
+					while (payload.length > 0) {
+						int size = payload[0];
+						payload = Arrays.copyOfRange(payload, 2, payload.length);
+						naldata = Arrays.copyOf(payload, size);
+						payload = Arrays.copyOfRange(payload, size, payload.length);
+						int nt = naldata[0] & 0x1f;
+						if (nt == 5 || nt == 1) {
+							if (newdata == null) {
+								nalType = nt;
+								// first byte: 0x17 for intra-frame, 0x27 for non-intra frame
+								// second byte: 0x01 for picture data
+								newdata = new ByteArrayBuilder(new byte[]{(byte) (nt == 5? 0x17:  
0x27), 1, 0, 0, 0});
+							}
+							length = naldata.length;
+							newdata.putArray((byte) (length >>> 24), (byte) (length >>> 16),  
(byte) (length >>> 8), (byte) length);
+							newdata.putArray(naldata);
+						}
+					}
+					break;
+				case 28:
+					if (newdata == null) {
+						nalType = q.packet.getPayload()[1] & 0x1f;
+						realNri = q.packet.getPayload()[0] & 0x60;
+						// first byte: 0x17 for intra-frame, 0x27 for non-intra frame
+						// second byte: 0x01 for picture data
+						newdata = new ByteArrayBuilder(new byte[]{(byte) (nalType == 5?  
0x17: 0x27), 1, 0, 0, 0});
+					}
+					pendingData.add(new  
ByteArrayBuilder(Arrays.copyOfRange(q.packet.getPayload(), 2,  
q.packet.getPayload().length)));
+					if ((q.packet.getPayload()[1] & 0x40) == 0x40) {
+						ByteArrayBuilder remaining = new ByteArrayBuilder((byte) (nalType |  
realNri));
+						for (ByteArrayBuilder pd: pendingData) {
+							remaining.putArray(pd.buildArray());
+						}
+						pendingData.clear();
+						length = remaining.getLength();
+						newdata.putArray((byte) (length >>> 24), (byte) (length >>> 16),  
(byte) (length >>> 8), (byte) length);
+						newdata.putArray(remaining.buildArray());
+					} else {
+						continue;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+			packetsQueue.clear();
+
+			if (newdata != null) {
+				payloads.add(newdata);
+			}
+
+			if (!sentSeq && nalType != 5 && pps.length > 0 && sps1.length > 0 ||  
sps1.length == 0 || pps.length == 0) {
+				packetsQueue.clear();
+				if (System.currentTimeMillis() - lastFIRTime > 5000) {
+					lastFIRTime = System.currentTimeMillis();
+					requestFIR();
+				}
+			} else {
+				if (pps.length > 0 && sps1.length > 0 && !sentSeq && nalType == 5) {
+					sentSeq = true;
+				}
+
+				// calculate timestamp
+				if (startTs == -1) {
+					startTs = packet.getTimestamp();
+				}
+				if (startTm == -1) {
+					startTm = System.currentTimeMillis() - startRelativeTime;
+				}
+
+				long tm = startTm + (packet.getTimestamp() - startTs) / 90; // 90 =  
bitrate / 1000
+				if (nalType == 5 && payloads.size() > 0) {
+					ByteArrayBuilder data = new ByteArrayBuilder();
+					// first byte: 0x17 for intra-frame
+					// second byte: 0x00 for configuration data
+					data.putArray(new byte[]{0x17, 0, 0, 0, 0, 1});
+					data.putArray(Arrays.copyOfRange(sps1, 1, 4));
+					data.putArray((byte) 0xff, (byte) 0xe1, (byte) (sps1.length >>> 8),  
(byte) sps1.length);
+					data.putArray(sps1);
+					data.putArray((byte) 1, (byte) (pps.length >>> 8), (byte) pps.length);
+					data.putArray(pps);
+					payloads.add(0, data);
+				}
+
+				for (ByteArrayBuilder bba: payloads) {
+					result.add(new RTMPPacketInfo(bba.buildArray(), tm));
+				}
+			}
+		}
+
+		return result;
+	}
+
+	protected void requestFIR() {
+		log.debug("requesting FIR...");
+	}
+
+	public static class RTMPPacketInfo {
+
+		public byte[] data;
+		public long ts;
+
+		public RTMPPacketInfo(byte[] data, long ts) {
+			super();
+			this.data = data;
+			this.ts = ts;
+		}
+
+	}
+
+	private static class ByteArrayBuilder {
+
+		private final List<BuilderElement> arrays;
+		private int totalLength = 0;
+
+		public ByteArrayBuilder() {
+			arrays = new ArrayList<BuilderElement>();
+		}
+
+		public ByteArrayBuilder(byte...array) {
+			this();
+			this.putArray(array);
+		}
+
+		public void putArray(byte... array) {
+			if (array.length == 0) return;
+			arrays.add(new BuilderElement(array));
+			totalLength += array.length;
+		}
+
+		public int getLength() {
+			return totalLength;
+		}
+
+		public byte[] buildArray() {
+			if (totalLength == 0) return new byte[0];
+			byte[] result = new byte[totalLength];
+			int pos = 0;
+			for (BuilderElement e: arrays) {
+				System.arraycopy(e.array, 0, result, pos, e.array.length);
+				pos += e.array.length;
+			}
+
+			if (arrays.size() > 1) {
+				clear();
+				putArray(result);
+			}
+
+			return result;
+		}
+
+		public void clear() {
+			arrays.clear();
+			totalLength = 0;
+		}
+
+		private static class BuilderElement {
+
+			public final byte[] array;
+
+			public BuilderElement(byte[] array) {
+				super();
+				this.array = array;
+			}
+
+		}
+
+	}
+
+	private static class RtpPacketWrapper {
+
+		public final RtpPacket packet;
+
+		public final int nalType;
+
+		public RtpPacketWrapper(RtpPacket packet, int nalType) {
+			this.packet = packet;
+			this.nalType = nalType;
+		}
+
+	}
+
+}
=======================================
--- /dev/null
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPVideoLauncher.java	Tue  
Mar  5 01:52:05 2013
@@ -0,0 +1,45 @@
+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;
+	protected RTPStreamVideoSender sender;
+
+	public SIPVideoLauncher(int localPort, String remoteAddr, int remotePort,  
IMediaReceiver mediaReceiver, SIPCodec codec) {
+		try {
+			socket = new DatagramSocket(localPort);
+			receiver = new RTPStreamVideoReceiver(mediaReceiver, socket, codec);
+			sender = new RTPStreamVideoSender(mediaReceiver, codec, socket,  
remoteAddr, remotePort);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+	}
+
+	@Override
+	public boolean startMedia() {
+		log.debug("startMedia()");
+		receiver.start();
+		sender.start();
+		return true;
+	}
+
+	@Override
+	public boolean stopMedia() {
+		log.debug("stopMedia()");
+		receiver.interrupt();
+		sender.halt();
+		socket.close();
+		return false;
+	}
+
+}
=======================================
--- /branches/red5sip/src/java/org/red5/codecs/SIPCodec.java	Thu Feb 21  
09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodec.java	Tue Mar  5  
01:52:05 2013
@@ -1,7 +1,7 @@
  package org.red5.codecs;

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

=======================================
--- /branches/red5sip/src/java/org/red5/codecs/SIPCodecFactory.java	Thu Feb  
21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/codecs/SIPCodecFactory.java	Tue  
Mar  5 01:52:05 2013
@@ -25,7 +25,9 @@
  	// ----------------
  	// Available video codecs

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

  	private static SIPCodecFactory singletonSIPCodecFactory = new  
SIPCodecFactory();

@@ -41,7 +43,7 @@
  	 *
  	 * @return The codec associated with "codecId".
  	 * */
-	public SIPCodec getSIPAudioCodec(int codecId) {
+	public SIPCodec getSIPMediaCodec(int codecId) {

  		SIPCodec sipCodec;

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

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

@@ -109,7 +114,7 @@

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

@@ -141,7 +146,7 @@

  			printLog("getAvailableAudioCodecsWithPrecedence", "codecId = [" +  
codecId + "].");

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

  			if (sipCodec != null) {

=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/Application.java	Thu Feb 21  
09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/Application.java	Tue Mar  5  
01:52:05 2013
@@ -16,8 +16,10 @@
  	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;
@@ -28,7 +30,7 @@
  		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");
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/IMediaReceiver.java	Thu Feb  
21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/IMediaReceiver.java	Tue  
Mar  5 01:52:05 2013
@@ -6,6 +6,10 @@

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

-	void setSender(IMediaSender sender);
+	void pushVideo(byte[] video, long ts) throws IOException;
+
+	void setAudioSender(IMediaSender sender);
+
+	void setVideoSender(IMediaSender sender);

  }
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/PlayNetStream.java	Thu Feb  
21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/PlayNetStream.java	Tue Mar   
5 01:52:05 2013
@@ -17,32 +17,48 @@

  	private int audioTs = 0;

-	private IMediaSender mediaSender;
+	private IMediaSender audioSender;

-	private IMediaStream mediaStream;
+	private IMediaStream audioStream;
+
+	private IMediaSender videoSender;

-	public PlayNetStream(IMediaSender mediaSender) {
-		this.mediaSender = mediaSender;
+	private IMediaStream videoStream;
+
+	public PlayNetStream(IMediaSender audioSender, IMediaSender videoSender) {
+		this.audioSender = audioSender;
+		this.videoSender = videoSender;
  	}

  	public void close() {
-		if (mediaSender != null) {
-			mediaSender.deleteStream(getStreamId());
+		if (audioSender != null) {
+			audioSender.deleteStream(getStreamId());
+		}
+		if (videoSender != null) {
+			videoSender.deleteStream(getStreamId());
  		}
  	}

  	public void start() {
-		if (mediaSender != null) {
-			mediaStream = mediaSender.createStream(getStreamId());
+		if (audioSender != null) {
+			audioStream = audioSender.createStream(getStreamId());
+		}
+		if (videoSender != null) {
+			videoStream = videoSender.createStream(getStreamId());
  		}
  	}

  	public void stop() {
-		if (mediaSender != null) {
-			mediaSender.deleteStream(getStreamId());
+		if (audioSender != null) {
+			audioSender.deleteStream(getStreamId());
+		}
+		if (videoSender != null) {
+			videoSender.deleteStream(getStreamId());
  		}
  	}

+	private long sipStream = -1;
+
  	public void dispatchEvent(IEvent event) {

  		if (!(event instanceof IRTMPEvent)) {
@@ -63,9 +79,22 @@
  		}

  		if (rtmpEvent instanceof VideoData) {
-			// videoTs += rtmpEvent.getTimestamp();
-			// tag.setTimestamp(videoTs);
-
+			if (sipStream == -1)
+				sipStream = rtmpEvent.getHeader().getStreamId();
+			if (rtmpEvent.getHeader().getStreamId() != sipStream) return;
+
+			int videoTs = rtmpEvent.getTimestamp();
+			IoBuffer videoData = ((VideoData)  
rtmpEvent).getData().asReadOnlyBuffer();
+			videoData.reset();
+			byte[] data = SerializeUtils.ByteBufferToByteArray(videoData);
+
+			try {
+				if (videoStream != null) {
+					videoStream.send(videoTs, data, 0, data.length);
+				}
+			} catch (Exception e) {
+				logger.error("PlayNetStream dispatchEvent exception ", e);
+			}
  		} else if (rtmpEvent instanceof AudioData) {
  			audioTs = rtmpEvent.getTimestamp();

@@ -73,8 +102,8 @@
  			byte[] data = SerializeUtils.ByteBufferToByteArray(audioData);

  			try {
-				if (mediaStream != null) {
-					mediaStream.send(audioTs, data, 1, data.length - 1);
+				if (audioStream != null) {
+					audioStream.send(audioTs, data, 1, data.length - 1);
  				}
  			} catch (Exception e) {
  				logger.error("PlayNetStream dispatchEvent exception ", e);
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/RTMPRoomClient.java	Thu Feb  
21 09:24:05 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/RTMPRoomClient.java	Tue  
Mar  5 01:52:05 2013
@@ -27,6 +27,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,8 +46,10 @@
  	private String publicSID = null;
  	private long broadCastId = -1;
  	private RTMPConnection conn;
-	private IMediaSender sender;
-	private IoBuffer buffer;
+	private IMediaSender audioSender;
+	private IMediaSender videoSender;
+	private IoBuffer audioBuffer;
+	private IoBuffer videoBuffer;
  	private int kt = 0;
  	private Integer publishStreamId = null;
  	private boolean reconnect = true;
@@ -132,8 +135,12 @@
  		}
  	}

-	public void setSender(IMediaSender sender) {
-		this.sender = sender;
+	public void setAudioSender(IMediaSender audioSender) {
+		this.audioSender = audioSender;
+	}
+
+	public void setVideoSender(IMediaSender videoSender) {
+		this.videoSender = videoSender;
  	}

  	protected void getPublicSID() {
@@ -170,7 +177,7 @@
  			if (conn != null && streamIdInt != null
  					&& (publishStreamId == null || streamIdInt.intValue() !=  
publishStreamId)) {
  				clientStreamMap.put(broadCastId, streamIdInt);
-				PlayNetStream stream = new PlayNetStream(sender);
+				PlayNetStream stream = new PlayNetStream(audioSender, videoSender);
  				stream.setConnection(conn);
  				stream.setStreamId(streamIdInt.intValue());
  				conn.addClientStream(stream);
@@ -463,21 +470,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++;
@@ -489,4 +496,28 @@
  		rtmpMsg.setBody(audioData);
  		publishStreamData(publishStreamId, rtmpMsg);
  	}
+
+	@Override
+	public void pushVideo(byte[] video, long ts) throws IOException {
+		if(publishStreamId == null) {
+			log.debug("publishStreamId == null !!!");
+            return;
+        }
+		if (videoBuffer == null || (videoBuffer.capacity() < video.length  
&& !videoBuffer.isAutoExpand())) {
+			videoBuffer = IoBuffer.allocate(video.length);
+			videoBuffer.setAutoExpand(true);
+		}
+
+		videoBuffer.clear();
+		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/RTPStreamMultiplexingSender.java	 
Thu Feb 21 09:21:48 2013
+++  
/branches/red5sip/src/java/org/red5/sip/app/RTPStreamMultiplexingSender.java	 
Tue Mar  5 01:52:05 2013
@@ -177,7 +177,7 @@
  	private void init(IMediaReceiver mediaReceiver, boolean do_sync, SIPCodec  
sipCodec, DatagramSocket src_socket,
  			String dest_addr, int dest_port) {

-		mediaReceiver.setSender(this);
+		mediaReceiver.setAudioSender(this);
  		this.sipCodec = sipCodec;
  		this.doSync = do_sync;

=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/RTPStreamSender.java	Thu  
Feb 21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/RTPStreamSender.java	Tue  
Mar  5 01:52:05 2013
@@ -116,7 +116,7 @@
  	private void init(IMediaReceiver mediaReceiver, boolean do_sync, SIPCodec  
sipCodec, DatagramSocket src_socket,
  			String dest_addr, int dest_port) {

-		mediaReceiver.setSender(this);
+		mediaReceiver.setAudioSender(this);
  		this.sipCodec = sipCodec;
  		this.doSync = do_sync;

=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SIPTransport.java	Thu Feb  
21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPTransport.java	Tue Mar   
5 01:52:05 2013
@@ -20,7 +20,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;

@@ -28,10 +29,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, String  
password, String realm, String proxy) {
@@ -53,7 +55,8 @@
  			sip_provider.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	Thu Feb  
21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgent.java	Tue Mar   
5 01:52:05 2013
@@ -77,7 +77,10 @@
  	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 ***********************

@@ -422,12 +425,12 @@

  			if (audioApp == null) {

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

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

@@ -437,15 +440,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;
-			}
-
-			videoApp.startMedia();
+        		if (sipVideoCodec != null) {
+        			videoApp = new SIPVideoLauncher(localVideoPort,  
remoteMediaAddress, remoteAudioPort, mediaReceiver, sipVideoCodec);
+        		} else {
+        			printLog( "launchMediaApplication", "SipCodec for video not  
initialized." );
+        		}
+        	}
+
+            if (videoApp != null) {
+            	videoApp.startMedia();
+            }
  		}
  	}

@@ -499,9 +504,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 negotiation 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.
@@ -513,7 +519,7 @@

  			// Finally, we use the "newSdp" and "remoteSdp" to initialize
  			// the lasting codec informations.
-			SIPCodecUtils.initSipAudioCodec(sipCodec,  
userProfile.audioDefaultPacketization,
+			SIPCodecUtils.initSipAudioCodec(sipAudioCodec,  
userProfile.audioDefaultPacketization,
  					userProfile.audioDefaultPacketization, newSdp, remoteSdp);
  		}

@@ -587,9 +593,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 negotiation 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.
@@ -601,7 +608,7 @@

  		// Finally, we use the "newSdp" and "remoteSdp" to initialize
  		// the lasting codec informations.
-		SIPCodecUtils.initSipAudioCodec(sipCodec,  
userProfile.audioDefaultPacketization,
+		SIPCodecUtils.initSipAudioCodec(sipAudioCodec,  
userProfile.audioDefaultPacketization,
  				userProfile.audioDefaultPacketization, newSdp, remoteSdp);

  		if (userProfile.noOffer) {
=======================================
--- /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgentProfile.java	 
Thu Feb 21 09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SIPUserAgentProfile.java	 
Tue Mar  5 01:52:05 2013
@@ -117,7 +117,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	Thu Feb 21  
09:21:48 2013
+++ /branches/red5sip/src/java/org/red5/sip/app/SdpUtils.java	Tue Mar  5  
01:52:05 2013
@@ -16,6 +16,27 @@

  	protected static Logger log = LoggerFactory.getLogger(SdpUtils.class);

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

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

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

  			if (sipCodec == null) {

@@ -191,7 +212,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;
@@ -239,7 +260,7 @@

  					if (initialDescriptor.getMediaDescriptor(SIPCodec.MEDIA_TYPE_VIDEO)  
== null) {

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

@@ -247,7 +268,7 @@
  					}
  				}

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

  				if (commonVideoMediaAttributes != null) {

@@ -396,12 +417,9 @@

  						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);
  					}
@@ -554,7 +572,7 @@
  					AttributeField localAttribute =  
findAttributeByPayloadId(remoteAttribute.getAttributeName(),
  							payloadId, localMedia);

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

  					if (sipCodec != null) {