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) {