Revision: 101
Author:   solomax...@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) {

Reply via email to