Commit d4e927ba authored by Dele Olajide's avatar Dele Olajide Committed by dele

jitsi-video-bridge plugin: Further work on recording streams

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@14001 b35dd754-fafc-0310-a699-88a17e54d16e
parent c558519b
...@@ -720,7 +720,9 @@ function handleOffer (from, offer) ...@@ -720,7 +720,9 @@ function handleOffer (from, offer)
{ {
console.log("handleOffer", offer); console.log("handleOffer", offer);
var bridgeSDP = new SDP('v=0\r\no=- 5151055458874951233 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\nm=audio 1 RTP/SAVPF 111 0 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\nm=video 1 RTP/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 goog-remb\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\n'); //var bridgeSDP = new SDP('v=0\r\no=- 5151055458874951233 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\nm=audio 1 RTP/SAVPF 111 0 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\nm=video 1 RTP/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 goog-remb\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\n');
var bridgeSDP = new SDP('v=0\r\no=- 5151055458874951233 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\nm=audio 1 RTP/SAVPF 111 0 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\nm=video 1 RTP/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 goog-remb\r\n');
var muc = $(offer).attr('muc'); var muc = $(offer).attr('muc');
var nick = $(offer).attr('nickname'); var nick = $(offer).attr('nickname');
var participant = $(offer).attr('participant'); var participant = $(offer).attr('participant');
......
...@@ -186,14 +186,18 @@ public MatroskaBlock(byte[] type) { ...@@ -186,14 +186,18 @@ public MatroskaBlock(byte[] type) {
return BlockTimecode; return BlockTimecode;
} }
public void setFrameData(int trackNo, long timecode, byte [] frameData) public void setFrameData(int trackNo, long timecode, byte [] frameData, boolean keyFrame)
{ {
this.data = new byte[4 + frameData.length]; this.data = new byte[4 + frameData.length];
this.keyFrame = keyFrame;
byte flags = 0;
if (keyFrame) flags |= 128;
this.data[0] = (byte) (trackNo | 0x80); this.data[0] = (byte) (trackNo | 0x80);
this.data[1] = (byte) (timecode >> 8); this.data[1] = (byte) (timecode >> 8);
this.data[2] = (byte) (timecode & 0xff); this.data[2] = (byte) (timecode & 0xff);
this.data[3] = 0; // flags this.data[3] = flags; // flags
ArrayCopy.arraycopy(frameData, 0, this.data, 4, frameData.length); ArrayCopy.arraycopy(frameData, 0, this.data, 4, frameData.length);
setSize(4 + frameData.length); setSize(4 + frameData.length);
......
...@@ -63,12 +63,14 @@ public class MatroskaDocType implements DocType ...@@ -63,12 +63,14 @@ public class MatroskaDocType implements DocType
static public byte [] Tracks_Id = {0x16, (byte)0x54, (byte)0xAE, (byte)0x6B}; static public byte [] Tracks_Id = {0x16, (byte)0x54, (byte)0xAE, (byte)0x6B};
static public byte [] TrackEntry_Id = {(byte)0xAE}; static public byte [] TrackEntry_Id = {(byte)0xAE};
static public byte [] TrackNumber_Id = {(byte)0xD7}; static public byte [] TrackNumber_Id = {(byte)0xD7};
static public byte [] TrackUID_Id = {0x73, (byte)0xC5}; static public byte [] TrackUID_Id = {0x63, (byte)0xC5};
static public byte [] TrackType_Id = {(byte)0x83}; static public byte [] TrackType_Id = {(byte)0x83};
static public byte [] TrackFlagLacing_Id = {(byte)0x9C};
static public byte [] TrackDefaultDuration_Id = {0x23, (byte)0xE3, (byte)0x83}; static public byte [] TrackDefaultDuration_Id = {0x23, (byte)0xE3, (byte)0x83};
static public byte [] TrackName_Id = {0x53, 0x6E}; static public byte [] TrackName_Id = {0x53, 0x6E};
static public byte [] TrackLanguage_Id = {0x22, (byte)0xB5, (byte)0x9C}; static public byte [] TrackLanguage_Id = {0x22, (byte)0xB5, (byte)0x9C};
static public byte [] TrackCodecID_Id = {(byte)0x86}; static public byte [] TrackCodecID_Id = {(byte)0x86};
static public byte [] TrackCodecName_Id = {0x25, (byte)0x86, (byte)0x88};
static public byte [] TrackCodecPrivate_Id = {(byte)0x63, (byte)0xA2}; static public byte [] TrackCodecPrivate_Id = {(byte)0x63, (byte)0xA2};
static public byte [] TrackVideo_Id = {(byte)0xE0}; static public byte [] TrackVideo_Id = {(byte)0xE0};
static public byte [] PixelWidth_Id = {(byte)0xB0}; static public byte [] PixelWidth_Id = {(byte)0xB0};
...@@ -377,6 +379,14 @@ public class MatroskaDocType implements DocType ...@@ -377,6 +379,14 @@ public class MatroskaDocType implements DocType
(ArrayList<ElementType>)null); (ArrayList<ElementType>)null);
level2.children.add(level3); level2.children.add(level3);
level3 = new ElementType("TrackFlagLacing",
(short)3,
TrackFlagLacing_Id,
ElementType.UINTEGER_ELEMENT,
(ArrayList<ElementType>)null);
level2.children.add(level3);
level3 = new ElementType("TrackDefaultDuration", level3 = new ElementType("TrackDefaultDuration",
(short)3, (short)3,
TrackDefaultDuration_Id, TrackDefaultDuration_Id,
...@@ -405,6 +415,14 @@ public class MatroskaDocType implements DocType ...@@ -405,6 +415,14 @@ public class MatroskaDocType implements DocType
(ArrayList<ElementType>)null); (ArrayList<ElementType>)null);
level2.children.add(level3); level2.children.add(level3);
level3 = new ElementType("TrackCodecName",
(short)3,
TrackCodecName_Id,
ElementType.ASCII_STRING_ELEMENT,
(ArrayList<ElementType>)null);
level2.children.add(level3);
level3 = new ElementType("TrackCodecPrivate", level3 = new ElementType("TrackCodecPrivate",
(short)3, (short)3,
TrackCodecPrivate_Id, TrackCodecPrivate_Id,
......
...@@ -31,6 +31,7 @@ public class MatroskaFileTrack ...@@ -31,6 +31,7 @@ public class MatroskaFileTrack
public String Name; public String Name;
public String Language; public String Language;
public String CodecID; public String CodecID;
public String CodecName;
public byte [] CodecPrivate; public byte [] CodecPrivate;
public short Video_PixelWidth; public short Video_PixelWidth;
...@@ -57,6 +58,7 @@ public class MatroskaFileTrack ...@@ -57,6 +58,7 @@ public class MatroskaFileTrack
s += "\t\t" + "Name: " + Name + "\n"; s += "\t\t" + "Name: " + Name + "\n";
s += "\t\t" + "Language: " + Language + "\n"; s += "\t\t" + "Language: " + Language + "\n";
s += "\t\t" + "CodecID: " + CodecID + "\n"; s += "\t\t" + "CodecID: " + CodecID + "\n";
s += "\t\t" + "CodecName: " + CodecName + "\n";
if (CodecPrivate != null) if (CodecPrivate != null)
s += "\t\t" + "CodecPrivate: " + CodecPrivate.length + " byte(s)" + "\n"; s += "\t\t" + "CodecPrivate: " + CodecPrivate.length + " byte(s)" + "\n";
......
...@@ -114,11 +114,11 @@ public class MatroskaFileWriter ...@@ -114,11 +114,11 @@ public class MatroskaFileWriter
FloatElement durationElem = (FloatElement)doc.createElement(MatroskaDocType.Duration_Id); FloatElement durationElem = (FloatElement)doc.createElement(MatroskaDocType.Duration_Id);
durationElem.setValue(Duration * 1000.0); durationElem.setValue(Duration * 1000.0);
segmentInfoElem.addChildElement(dateElem); //segmentInfoElem.addChildElement(dateElem);
segmentInfoElem.addChildElement(timecodescaleElem); segmentInfoElem.addChildElement(timecodescaleElem);
segmentInfoElem.addChildElement(durationElem);
segmentInfoElem.addChildElement(writingAppElem);
segmentInfoElem.addChildElement(muxingAppElem); segmentInfoElem.addChildElement(muxingAppElem);
segmentInfoElem.addChildElement(writingAppElem);
segmentInfoElem.addChildElement(durationElem);
segmentInfoElem.writeElement(ioDW); segmentInfoElem.writeElement(ioDW);
} }
...@@ -135,6 +135,9 @@ public class MatroskaFileWriter ...@@ -135,6 +135,9 @@ public class MatroskaFileWriter
UnsignedIntegerElement trackNoElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackNumber_Id); UnsignedIntegerElement trackNoElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackNumber_Id);
trackNoElem.setValue(track.TrackNo); trackNoElem.setValue(track.TrackNo);
UnsignedIntegerElement trackFlagLacingElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackFlagLacing_Id);
trackFlagLacingElem.setValue(0);
UnsignedIntegerElement trackUIDElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackUID_Id); UnsignedIntegerElement trackUIDElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackUID_Id);
trackUIDElem.setValue(track.TrackUID); trackUIDElem.setValue(track.TrackUID);
...@@ -150,6 +153,9 @@ public class MatroskaFileWriter ...@@ -150,6 +153,9 @@ public class MatroskaFileWriter
StringElement trackCodecIDElem = (StringElement)doc.createElement(MatroskaDocType.TrackCodecID_Id); StringElement trackCodecIDElem = (StringElement)doc.createElement(MatroskaDocType.TrackCodecID_Id);
trackCodecIDElem.setValue(track.CodecID); trackCodecIDElem.setValue(track.CodecID);
StringElement trackCodecName_IdElem = (StringElement)doc.createElement(MatroskaDocType.TrackCodecName_Id);
trackCodecName_IdElem.setValue(track.CodecName);
BinaryElement trackCodecPrivateElem = (BinaryElement)doc.createElement(MatroskaDocType.TrackCodecPrivate_Id); BinaryElement trackCodecPrivateElem = (BinaryElement)doc.createElement(MatroskaDocType.TrackCodecPrivate_Id);
trackCodecPrivateElem.setData(track.CodecPrivate); trackCodecPrivateElem.setData(track.CodecPrivate);
...@@ -158,12 +164,14 @@ public class MatroskaFileWriter ...@@ -158,12 +164,14 @@ public class MatroskaFileWriter
trackEntryElem.addChildElement(trackNoElem); trackEntryElem.addChildElement(trackNoElem);
trackEntryElem.addChildElement(trackUIDElem); trackEntryElem.addChildElement(trackUIDElem);
trackEntryElem.addChildElement(trackTypeElem); trackEntryElem.addChildElement(trackFlagLacingElem);
trackEntryElem.addChildElement(trackNameElem);
trackEntryElem.addChildElement(trackLangElem); trackEntryElem.addChildElement(trackLangElem);
trackEntryElem.addChildElement(trackCodecIDElem); trackEntryElem.addChildElement(trackCodecIDElem);
trackEntryElem.addChildElement(trackCodecPrivateElem); trackEntryElem.addChildElement(trackCodecName_IdElem);
trackEntryElem.addChildElement(trackDefaultDurationElem); trackEntryElem.addChildElement(trackTypeElem);
//trackEntryElem.addChildElement(trackNameElem);
//trackEntryElem.addChildElement(trackCodecPrivateElem);
// trackEntryElem.addChildElement(trackDefaultDurationElem);
// Now we add the audio/video dependant sub-elements // Now we add the audio/video dependant sub-elements
if (track.TrackType == MatroskaDocType.track_video) if (track.TrackType == MatroskaDocType.track_video)
...@@ -184,8 +192,8 @@ public class MatroskaFileWriter ...@@ -184,8 +192,8 @@ public class MatroskaFileWriter
trackVideoElem.addChildElement(trackVideoPixelWidthElem); trackVideoElem.addChildElement(trackVideoPixelWidthElem);
trackVideoElem.addChildElement(trackVideoPixelHeightElem); trackVideoElem.addChildElement(trackVideoPixelHeightElem);
trackVideoElem.addChildElement(trackVideoDisplayWidthElem); //trackVideoElem.addChildElement(trackVideoDisplayWidthElem);
trackVideoElem.addChildElement(trackVideoDisplayHeightElem); //trackVideoElem.addChildElement(trackVideoDisplayHeightElem);
trackEntryElem.addChildElement(trackVideoElem); trackEntryElem.addChildElement(trackVideoElem);
} }
...@@ -243,7 +251,7 @@ public class MatroskaFileWriter ...@@ -243,7 +251,7 @@ public class MatroskaFileWriter
public void addFrame(MatroskaFileFrame frame) public void addFrame(MatroskaFileFrame frame)
{ {
MatroskaBlock simpleBlockElem = (MatroskaBlock)doc.createElement(MatroskaDocType.ClusterSimpleBlock_Id); MatroskaBlock simpleBlockElem = (MatroskaBlock)doc.createElement(MatroskaDocType.ClusterSimpleBlock_Id);
simpleBlockElem.setFrameData(frame.TrackNo, frame.Timecode - clusterTimecode, frame.Data); simpleBlockElem.setFrameData(frame.TrackNo, frame.Timecode - clusterTimecode, frame.Data, frame.KeyFrame);
clusterElem.addChildElement(simpleBlockElem); clusterElem.addChildElement(simpleBlockElem);
} }
} }
package org.ifsoft;
public class ArrayExtensions
{
public ArrayExtensions()
{
}
public static Integer getLength(Object array[])
{
return Integer.valueOf(array.length);
}
public static void reverse(Object array[])
{
for(int i = 0; i < array.length / 2; i++)
{
Object temp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = temp;
}
}
public static void copy(Object source[], int sourceIndex, Object destination[], int destinationIndex, int length)
{
for(int i = 0; i < length; i++)
destination[destinationIndex + i] = source[sourceIndex + i];
}
}
package org.ifsoft;
import java.util.*;
public class ArrayListExtensions
{
public ArrayListExtensions()
{
}
public static Integer getCount(ArrayList array)
{
return Integer.valueOf(array.size());
}
public static ArrayList getItem(ArrayList array)
{
return array;
}
public static void copyTo(ArrayList array, Object target[], int index)
{
for(int i = 0; i < array.size(); i++)
target[index + i] = array.get(i);
}
public static void insert(ArrayList array, int index, Object value)
{
array.add(index, value);
}
public static void removeAt(ArrayList array, int index)
{
array.remove(index);
}
public static ArrayList createArray(Object elements[])
{
ArrayList array = new ArrayList(elements.length);
Object arr$[] = elements;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; i$++)
{
Object element = arr$[i$];
array.add(element);
}
return array;
}
public static ArrayList createArray(Iterable elements)
{
ArrayList array = new ArrayList();
Object element;
for(Iterator i$ = elements.iterator(); i$.hasNext(); array.add(element))
element = i$.next();
return array;
}
public static void addRange(ArrayList array, Iterable elements)
{
Object element;
for(Iterator i$ = elements.iterator(); i$.hasNext(); array.add(element))
element = i$.next();
}
public static void addRange(ArrayList array, Object elements[])
{
Object arr$[] = elements;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; i$++)
{
Object element = arr$[i$];
array.add(element);
}
}
public static ArrayList getRange(ArrayList array, int index, int count)
{
ArrayList sublist = new ArrayList(count);
for(int i = 0; i < count; i++)
sublist.add(array.get(index + i));
return sublist;
}
public static void insertRange(ArrayList array, int index, Iterable elements)
{
int i = 0;
for(Iterator i$ = elements.iterator(); i$.hasNext();)
{
Object element = i$.next();
array.add(index + i, element);
i++;
}
}
public static void insertRange(ArrayList array, int index, Object elements[])
{
int i = 0;
Object arr$[] = elements;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; i$++)
{
Object element = arr$[i$];
array.add(index + i, element);
i++;
}
}
public static void removeRange(ArrayList array, int index, int count)
{
for(int i = 0; i < count; i++)
array.remove(index);
}
}
package org.ifsoft;
import java.nio.ByteBuffer;
import java.util.ArrayList;
public class BitAssistant
{
public BitAssistant()
{
}
public static ArrayList bytesToArrayList(byte bytes[])
{
ArrayList arrayList = new ArrayList(bytes.length);
for(int i = 0; i < bytes.length; i++)
arrayList.set(i, new Byte(bytes[i]));
return arrayList;
}
public static byte[] bytesFromArrayList(ArrayList arrayList)
{
byte bytes[] = new byte[arrayList.size()];
for(int i = 0; i < arrayList.size(); i++)
if (arrayList.get(i) != null) bytes[i] = ((Byte)arrayList.get(i)).byteValue();
return bytes;
}
public static Byte[] bytesToArray(byte bytes[])
{
Byte array[] = new Byte[bytes.length];
for(int i = 0; i < array.length; i++)
array[i] = new Byte(bytes[i]);
return array;
}
public static byte[] bytesFromArray(Byte array[])
{
byte bytes[] = new byte[array.length];
for(int i = 0; i < array.length; i++)
if (array[i] != null) bytes[i] = array[i].byteValue();
return bytes;
}
public static Boolean isLittleEndian()
{
return Boolean.valueOf(false);
}
public static Boolean sequencesAreEqual(Byte array1[], Byte array2[])
{
if(array1 == null && array2 == null)
return Boolean.valueOf(true);
if(array1 == null || array2 == null)
return Boolean.valueOf(false);
if(array1.length != array2.length)
return Boolean.valueOf(false);
for(int i = 0; i < array1.length; i++)
if(array1[i].byteValue() != array2[i].byteValue())
return Boolean.valueOf(false);
return Boolean.valueOf(true);
}
public static Byte[] subArray(Byte array[], Integer offset)
{
return subArray(array, offset, Integer.valueOf(array.length - offset.intValue()));
}
public static Byte[] subArray(Byte array[], Integer offset, Integer count)
{
Byte subarray[] = new Byte[count.intValue()];
for(int i = 0; i < count.intValue(); i++)
subarray[i] = array[offset.intValue() + i];
return subarray;
}
public static void reverse(Byte array[])
{
for(int i = 0; i < array.length / 2; i++)
{
Byte t = array[array.length - i - 1];
array[array.length - i - 1] = array[i];
array[i] = t;
}
}
public static String getHexString(Byte array[])
{
StringBuilder sb = new StringBuilder();
Byte arr$[] = array;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; i$++)
{
Byte b = arr$[i$];
String hex = Integer.toHexString(0xff & b.byteValue());
if(hex.length() == 1)
sb.append('0');
sb.append(hex);
}
return sb.toString();
}
public static Byte[] getHexBytes(String s)
{
int len = s.length();
Byte bytes[] = new Byte[len / 2];
for(int i = 0; i < len; i += 2)
bytes[i / 2] = Byte.valueOf((byte)((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)));
return bytes;
}
public static Byte[] getBooleanBytes(Boolean value)
{
byte bytes[] = new byte[1];
if(value.booleanValue())
bytes[0] = 1;
else
bytes[0] = 0;
return bytesToArray(bytes);
}
public static Byte[] getDoubleBytes(Double value)
{
byte bytes[] = new byte[8];
ByteBuffer.wrap(bytes).putDouble(value.doubleValue());
return bytesToArray(bytes);
}
public static Byte[] getFloatBytes(Float value)
{
byte bytes[] = new byte[4];
ByteBuffer.wrap(bytes).putFloat(value.floatValue());
return bytesToArray(bytes);
}
public static Byte[] getIntegerBytes(Integer value)
{
byte bytes[] = new byte[4];
ByteBuffer.wrap(bytes).putInt(value.intValue());
return bytesToArray(bytes);
}
public static Byte[] getIntegerBytesFromLong(Long value)
{
Byte bytes[] = getLongBytes(value);
if(isLittleEndian().booleanValue())
return subArray(bytes, Integer.valueOf(0), Integer.valueOf(4));
else
return subArray(bytes, Integer.valueOf(4), Integer.valueOf(4));
}
public static Byte[] getLongBytes(Long value)
{
byte bytes[] = new byte[8];
ByteBuffer.wrap(bytes).putLong(value.longValue());
return bytesToArray(bytes);
}
public static Byte[] getShortBytes(Short value)
{
byte bytes[] = new byte[2];
ByteBuffer.wrap(bytes).putShort(value.shortValue());
return bytesToArray(bytes);
}
public static Byte[] getShortBytesFromInteger(Integer value)
{
Byte bytes[] = getIntegerBytes(value);
if(isLittleEndian().booleanValue())
return subArray(bytes, Integer.valueOf(0), Integer.valueOf(2));
else
return subArray(bytes, Integer.valueOf(2), Integer.valueOf(2));
}
public static Boolean toBoolean(Byte value[], Integer startIndex)
{
if(value[startIndex.intValue()].byteValue() == 0)
return Boolean.valueOf(false);
else
return Boolean.valueOf(true);
}
public static Double toDouble(Byte value[], Integer startIndex)
{
return Double.valueOf(ByteBuffer.wrap(bytesFromArray(value)).getDouble(startIndex.intValue()));
}
public static Float toFloat(Byte value[], Integer startIndex)
{
return Float.valueOf(ByteBuffer.wrap(bytesFromArray(value)).getFloat(startIndex.intValue()));
}
public static Integer toInteger(Byte value[], Integer startIndex)
{
return Integer.valueOf(ByteBuffer.wrap(bytesFromArray(value)).getInt(startIndex.intValue()));
}
public static Integer toIntegerFromShort(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[4];
if(isLittleEndian().booleanValue())
{
bytes[0] = value[startIndex.intValue()];
bytes[1] = value[startIndex.intValue() + 1];
bytes[2] = Byte.valueOf((byte)0);
bytes[3] = Byte.valueOf((byte)0);
} else
{
bytes[0] = Byte.valueOf((byte)0);
bytes[1] = Byte.valueOf((byte)0);
bytes[2] = value[startIndex.intValue()];
bytes[3] = value[startIndex.intValue() + 1];
}
return toInteger(bytes, Integer.valueOf(0));
}
public static Long toLong(Byte value[], Integer startIndex)
{
return Long.valueOf(ByteBuffer.wrap(bytesFromArray(value)).getLong(startIndex.intValue()));
}
public static Long toLongFromInteger(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[8];
if(isLittleEndian().booleanValue())
{
bytes[0] = value[startIndex.intValue()];
bytes[1] = value[startIndex.intValue() + 1];
bytes[2] = value[startIndex.intValue() + 2];
bytes[3] = value[startIndex.intValue() + 3];
bytes[4] = Byte.valueOf((byte)0);
bytes[5] = Byte.valueOf((byte)0);
bytes[6] = Byte.valueOf((byte)0);
bytes[7] = Byte.valueOf((byte)0);
} else
{
bytes[0] = Byte.valueOf((byte)0);
bytes[1] = Byte.valueOf((byte)0);
bytes[2] = Byte.valueOf((byte)0);
bytes[3] = Byte.valueOf((byte)0);
bytes[4] = value[startIndex.intValue()];
bytes[5] = value[startIndex.intValue() + 1];
bytes[6] = value[startIndex.intValue() + 2];
bytes[7] = value[startIndex.intValue() + 3];
}
return toLong(bytes, Integer.valueOf(0));
}
public static Short toShort(Byte value[], Integer startIndex)
{
return Short.valueOf(ByteBuffer.wrap(bytesFromArray(value)).getShort(startIndex.intValue()));
}
public static Byte[] getBooleanBytesNetwork(Boolean value)
{
Byte bytes[] = getBooleanBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getDoubleBytesNetwork(Double value)
{
Byte bytes[] = getDoubleBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getFloatBytesNetwork(Float value)
{
Byte bytes[] = getFloatBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getIntegerBytesNetwork(Integer value)
{
Byte bytes[] = getIntegerBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getIntegerBytesFromLongNetwork(Long value)
{
Byte bytes[] = getIntegerBytesFromLong(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getLongBytesNetwork(Long value)
{
Byte bytes[] = getLongBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getShortBytesNetwork(Short value)
{
Byte bytes[] = getShortBytes(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Byte[] getShortBytesFromIntegerNetwork(Integer value)
{
Byte bytes[] = getShortBytesFromInteger(value);
if(isLittleEndian().booleanValue())
reverse(bytes);
return bytes;
}
public static Boolean toBooleanNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[1];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toBoolean(bytes, Integer.valueOf(0));
}
public static Double toDoubleNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[8];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toDouble(bytes, Integer.valueOf(0));
}
public static Float toFloatNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[4];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toFloat(bytes, Integer.valueOf(0));
}
public static Integer toIntegerNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[4];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toInteger(bytes, Integer.valueOf(0));
}
public static Integer toIntegerFromShortNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[2];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toIntegerFromShort(bytes, Integer.valueOf(0));
}
public static Long toLongNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[8];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toLong(bytes, Integer.valueOf(0));
}
public static Long toLongFromIntegerNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[4];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toLongFromInteger(bytes, Integer.valueOf(0));
}
public static Short toShortNetwork(Byte value[], Integer startIndex)
{
Byte bytes[] = new Byte[2];
for(int i = 0; i < bytes.length; i++)
bytes[i] = value[startIndex.intValue() + i];
if(isLittleEndian().booleanValue())
reverse(bytes);
return toShort(bytes, Integer.valueOf(0));
}
}
package org.ifsoft;
import java.util.ArrayList;
import java.util.Iterator;
public class ByteCollection
{
private ArrayList _list;
public void add(byte b)
{
_list.add(Byte.valueOf(b));
}
public void add(int b)
{
_list.add(Byte.valueOf((byte)b));
}
public void addRange(byte buffer[])
{
byte arr$[] = buffer;
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; i$++)
{
byte b = arr$[i$];
add(b);
}
}
public void addRange(ByteCollection collection)
{
byte b;
for(Iterator i$ = collection.getList().iterator(); i$.hasNext(); add(b))
b = ((Byte)i$.next()).byteValue();
}
public ByteCollection()
{
_list = new ArrayList();
}
public ByteCollection(byte buffer[])
{
_list = new ArrayList();
addRange(buffer);
}
public byte get(Integer index)
{
return ((Byte)_list.get(index.intValue())).byteValue();
}
public Integer getCount()
{
return Integer.valueOf(_list.size());
}
ArrayList getList()
{
return _list;
}
public byte[] getRange(Integer index, Integer count)
{
byte range[] = new byte[count.intValue()];
for(int i = 0; i < count.intValue(); i++)
range[i] = ((Byte)_list.get(index.intValue() + i)).byteValue();
return range;
}
public void insertRange(Integer index, byte buffer[])
{
for(int i = 0; i < buffer.length; i++)
_list.add(index.intValue() + i, Byte.valueOf(buffer[i]));
}
public void insertRange(Integer index, ByteCollection collection)
{
for(int i = 0; i < collection.getCount().intValue(); i++)
_list.add(index.intValue() + i, Byte.valueOf(collection.get(Integer.valueOf(i))));
}
public void removeRange(Integer index, Integer count)
{
ArrayListExtensions.removeRange(_list, index.intValue(), count.intValue());
}
public byte[] toArray()
{
byte array[] = new byte[_list.size()];
for(int i = 0; i < array.length; i++)
array[i] = ((Byte)_list.get(i)).byteValue();
return array;
}
}
package org.ifsoft;
public class Math
{
public Math()
{
}
public static Integer max(Integer val1, Integer val2)
{
return Integer.valueOf(Math.max(val1.intValue(), val2.intValue()));
}
public static Long max(Long val1, Long val2)
{
return Long.valueOf(Math.max(val1.longValue(), val2.longValue()));
}
public static Integer min(Integer val1, Integer val2)
{
return Integer.valueOf(Math.min(val1.intValue(), val2.intValue()));
}
public static Long min(Long val1, Long val2)
{
return Long.valueOf(Math.min(val1.longValue(), val2.longValue()));
}
public static Double pow(Double val, Double exp)
{
return Double.valueOf(Math.pow(val.doubleValue(), exp.doubleValue()));
}
public static Double ceiling(Double val)
{
return Double.valueOf(java.lang.Math.ceil(val.doubleValue()));
}
}
package org.ifsoft.rtp;
import org.ifsoft.*;
import java.util.ArrayList;
public class RTPPacket
{
private Byte __payload[];
private Byte __payloadType;
private Long _contributingSources[];
private Long _extension[];
private Integer _extensionHeader;
private Boolean _marker;
private Boolean _padding;
private Integer _sequenceNumber;
private Long _synchronizationSource;
private Long _timestamp;
public RTPPacket(Byte payload[])
{
__payloadType = Byte.valueOf((byte)0);
_extensionHeader = Integer.valueOf(0);
_marker = Boolean.valueOf(false);
_padding = Boolean.valueOf(false);
_sequenceNumber = Integer.valueOf(0);
_synchronizationSource = Long.valueOf(0L);
_timestamp = Long.valueOf(0L);
setPayload(payload);
}
public Byte[] getBytes()
{
return getBytes(this);
}
public static Byte[] getBytes(RTPPacket packet)
{
Boolean flag = Boolean.valueOf(packet.getExtensionHeader().intValue() != 0 || packet.getExtension() != null);
ArrayList list = new ArrayList();
Byte num = packet.getContributingSources() != null ? new Byte((byte)(BitAssistant.getShortBytesFromIntegerNetwork(ArrayExtensions.getLength(packet.getContributingSources()))[1].byteValue() & 0xf)) : new Byte((byte)0);
list.add(new Byte((new Byte((new Integer(0x80 | (packet.getPadding().booleanValue() ? 0x20 : 0) | (flag.booleanValue() ? 0x10 : 0) | num.byteValue())).byteValue())).byteValue()));
list.add(new Byte((new Byte((new Integer((packet.getMarker().booleanValue() ? 0x80 : 0) | packet.getPayloadType().byteValue() & 0x7f)).byteValue())).byteValue()));
ArrayListExtensions.addRange(list, BitAssistant.getShortBytesFromIntegerNetwork(packet.getSequenceNumber()));
ArrayListExtensions.addRange(list, BitAssistant.getIntegerBytesFromLongNetwork(packet.getTimestamp()));
ArrayListExtensions.addRange(list, BitAssistant.getIntegerBytesFromLongNetwork(packet.getSynchronizationSource()));
for(Integer num2 = Integer.valueOf(0); num2.intValue() < num.byteValue();)
{
ArrayListExtensions.addRange(list, BitAssistant.getIntegerBytesFromLongNetwork(packet.getContributingSources()[num2.intValue()]));
Integer integer = num2;
Integer integer1 = num2 = Integer.valueOf(num2.intValue() + 1);
Integer _tmp = integer;
}
if(flag.booleanValue())
{
Integer num3 = Integer.valueOf(packet.getExtension() != null ? ArrayExtensions.getLength(packet.getExtension()).intValue() : 0);
ArrayListExtensions.addRange(list, BitAssistant.getShortBytesFromIntegerNetwork(packet.getExtensionHeader()));
ArrayListExtensions.addRange(list, BitAssistant.getShortBytesFromIntegerNetwork(num3));
for(Integer num2 = Integer.valueOf(0); num2.intValue() < num3.intValue();)
{
ArrayListExtensions.addRange(list, BitAssistant.getIntegerBytesFromLongNetwork(packet.getExtension()[num2.intValue()]));
Integer integer2 = num2;
Integer integer3 = num2 = Integer.valueOf(num2.intValue() + 1);
Integer _tmp1 = integer2;
}
}
ArrayListExtensions.addRange(list, packet.getPayload());
return (Byte[])list.toArray(new Byte[0]);
}
public Long[] getContributingSources()
{
return _contributingSources;
}
public Long[] getExtension()
{
return _extension;
}
public Integer getExtensionHeader()
{
return _extensionHeader;
}
public Boolean getMarker()
{
return _marker;
}
public Boolean getPadding()
{
return _padding;
}
public Byte[] getPayload()
{
return __payload;
}
public Byte getPayloadType()
{
return __payloadType;
}
public Integer getSequenceNumber()
{
return _sequenceNumber;
}
public Long getSynchronizationSource()
{
return _synchronizationSource;
}
public Long getTimestamp()
{
return _timestamp;
}
public static RTPPacket parseBytes(Byte bytes[])
throws Exception
{
if(ArrayExtensions.getLength(bytes).intValue() < 12 || (bytes[0].byteValue() & 0xc0) != 128)
return null;
Boolean flag = Boolean.valueOf((bytes[0].byteValue() & 0x20) == 32);
Boolean flag2 = Boolean.valueOf((bytes[0].byteValue() & 0x10) == 16);
Byte num = new Byte((byte)(bytes[0].byteValue() & 0xf));
Boolean flag3 = Boolean.valueOf((bytes[1].byteValue() & 0x80) == 128);
Byte num2 = new Byte((byte)(bytes[1].byteValue() & 0x7f));
Integer num3 = BitAssistant.toIntegerFromShortNetwork(bytes, Integer.valueOf(2));
Long num4 = BitAssistant.toLongFromIntegerNetwork(bytes, Integer.valueOf(4));
Long num5 = BitAssistant.toLongFromIntegerNetwork(bytes, Integer.valueOf(8));
Integer startIndex = Integer.valueOf(12);
Long numArray[] = null;
if(num.byteValue() > 0)
{
numArray = new Long[num.byteValue()];
for(Integer num7 = Integer.valueOf(0); num7.intValue() < num.byteValue();)
{
numArray[num7.intValue()] = BitAssistant.toLongFromIntegerNetwork(bytes, startIndex);
startIndex = Integer.valueOf(startIndex.intValue() + 4);
Integer integer = num7;
Integer integer1 = num7 = Integer.valueOf(num7.intValue() + 1);
Integer _tmp = integer;
}
}
Integer num8 = Integer.valueOf(0);
Long numArray2[] = null;
if(flag2.booleanValue())
{
num8 = BitAssistant.toIntegerFromShortNetwork(bytes, startIndex);
startIndex = Integer.valueOf(startIndex.intValue() + 2);
Integer num9 = BitAssistant.toIntegerFromShortNetwork(bytes, startIndex);
startIndex = Integer.valueOf(startIndex.intValue() + 2);
if(num9.intValue() > 0)
{
numArray2 = new Long[num9.intValue()];
for(Integer num7 = Integer.valueOf(0); num7.intValue() < num9.intValue();)
{
numArray2[num7.intValue()] = BitAssistant.toLongFromIntegerNetwork(bytes, startIndex);
startIndex = Integer.valueOf(startIndex.intValue() + 4);
Integer integer2 = num7;
Integer integer3 = num7 = Integer.valueOf(num7.intValue() + 1);
Integer _tmp1 = integer2;
}
}
}
RTPPacket packet = new RTPPacket(BitAssistant.subArray(bytes, startIndex));
packet.setPayloadType(num2);
packet.setPadding(flag);
packet.setMarker(flag3);
packet.setSequenceNumber(num3);
packet.setTimestamp(num4);
packet.setSynchronizationSource(num5);
packet.setContributingSources(numArray);
packet.setExtensionHeader(num8);
packet.setExtension(numArray2);
return packet;
}
public void setContributingSources(Long value[])
{
_contributingSources = value;
}
public void setExtension(Long value[])
{
_extension = value;
}
public void setExtensionHeader(Integer value)
{
_extensionHeader = value;
}
public void setMarker(Boolean value)
{
_marker = value;
}
public void setPadding(Boolean value)
{
_padding = value;
}
public void setPayload(Byte value[])
{
if(value == null)
__payload = new Byte[0];
else
__payload = value;
}
public void setPayloadType(Byte value)
throws Exception
{
if(value.byteValue() < 0)
{
throw new Exception("Payload type is invalid.");
} else
{
__payloadType = value;
return;
}
}
public void setSequenceNumber(Integer value)
{
_sequenceNumber = value;
}
public void setSynchronizationSource(Long value)
{
_synchronizationSource = value;
}
public void setTimestamp(Long value)
{
_timestamp = value;
}
}
package org.ifsoft.rtp;
import org.ifsoft.*;
import java.util.ArrayList;
public class Vp8Accumulator
{
public void add(Vp8Packet packet)
{
//if(packet.getStartOfPartition().booleanValue() || ArrayListExtensions.getCount(packets).intValue() > 0)
packets.add(packet);
}
public Vp8Accumulator()
{
packets = new ArrayList();
}
public Vp8Packet[] getPackets()
{
return (Vp8Packet[])packets.toArray(new Vp8Packet[0]);
}
public void reset()
{
packets = new ArrayList();
}
private ArrayList packets;
}
package org.ifsoft.rtp;
import org.ifsoft.*;
import java.util.ArrayList;
import org.slf4j.*;
import org.slf4j.Logger;
public class Vp8Packet
{
private static final Logger Log = LoggerFactory.getLogger(Vp8Packet.class);
private Boolean _extendedControlBitsPresent;
private Byte _keyIndex;
private Boolean _keyIndexPresent;
private Boolean _layerSync;
public static Integer _maxPacketSize = Integer.valueOf(0);
private Boolean _nonReferenceFrame;
private Byte _partitionId;
private Byte _payload[];
private Short _pictureID;
private Boolean _pictureIDPresent;
private Boolean _startOfPartition;
private Byte _temporalLayerIndex;
private Boolean _temporalLayerIndexPresent;
private Byte _temporalLevelZeroIndex;
private Boolean _temporalLevelZeroIndexPresent;
static
{
_maxPacketSize = Integer.valueOf(1050);
}
public Vp8Packet()
{
_extendedControlBitsPresent = Boolean.valueOf(false);
_keyIndex = Byte.valueOf((byte)0);
_keyIndexPresent = Boolean.valueOf(false);
_layerSync = Boolean.valueOf(false);
_nonReferenceFrame = Boolean.valueOf(false);
_partitionId = Byte.valueOf((byte)0);
_pictureID = Short.valueOf((short)0);
_pictureIDPresent = Boolean.valueOf(false);
_startOfPartition = Boolean.valueOf(false);
_temporalLayerIndex = Byte.valueOf((byte)0);
_temporalLayerIndexPresent = Boolean.valueOf(false);
_temporalLevelZeroIndex = Byte.valueOf((byte)0);
_temporalLevelZeroIndexPresent = Boolean.valueOf(false);
}
public static Byte[] depacketize(Vp8Packet packets[])
{
Integer num = Integer.valueOf(0);
Vp8Packet arr[] = packets;
int len = arr.length;
for(int i = 0; i < len; i++)
{
Vp8Packet packet = arr[i];
num = Integer.valueOf(num.intValue() + ArrayExtensions.getLength(packet.getPayload()).intValue());
}
Integer destinationIndex = Integer.valueOf(0);
Byte destinationArray[] = new Byte[num.intValue()];
arr = packets;
len = arr.length;
for(int i = 0; i < len; i++)
{
Vp8Packet packet = arr[i];
ArrayExtensions.copy(packet.getPayload(), 0, destinationArray, destinationIndex.intValue(), ArrayExtensions.getLength(packet.getPayload()).intValue());
destinationIndex = Integer.valueOf(destinationIndex.intValue() + ArrayExtensions.getLength(packet.getPayload()).intValue());
}
return destinationArray;
}
public Byte[] getBytes()
{
ArrayList list = new ArrayList();
list.add(new Byte((new Byte((new Integer((getExtendedControlBitsPresent().booleanValue() ? 0x80 : 0) | (getNonReferenceFrame().booleanValue() ? 0x20 : 0) | (getStartOfPartition().booleanValue() ? 0x10 : 0) | getPartitionId().byteValue() & 0xf)).byteValue())).byteValue()));
if(getExtendedControlBitsPresent().booleanValue())
{
list.add(new Byte((new Byte((new Integer((getPictureIDPresent().booleanValue() ? 0x80 : 0) | (getTemporalLevelZeroIndexPresent().booleanValue() ? 0x40 : 0) | (getTemporalLayerIndexPresent().booleanValue() ? 0x20 : 0) | (getKeyIndexPresent().booleanValue() ? 0x10 : 0))).byteValue())).byteValue()));
if(getPictureIDPresent().booleanValue())
{
Byte shortBytesNetwork[] = BitAssistant.getShortBytesNetwork(getPictureID());
list.add(new Byte((new Byte((new Integer(0x80 | shortBytesNetwork[0].byteValue() & 0x7f)).byteValue())).byteValue()));
list.add(new Byte(shortBytesNetwork[1].byteValue()));
}
if(getTemporalLevelZeroIndexPresent().booleanValue())
list.add(new Byte(getTemporalLevelZeroIndex().byteValue()));
if(getTemporalLayerIndexPresent().booleanValue() || getKeyIndexPresent().booleanValue())
list.add(new Byte((new Byte((byte)(getTemporalLayerIndex().byteValue() << 6 & 0xc0 | (getLayerSync().booleanValue() ? 0x20 : 0) | getKeyIndex().byteValue() & 0x1f))).byteValue()));
}
ArrayListExtensions.addRange(list, getPayload());
return (Byte[])list.toArray(new Byte[0]);
}
public Boolean getExtendedControlBitsPresent()
{
return _extendedControlBitsPresent;
}
public Byte getKeyIndex()
{
return _keyIndex;
}
public Boolean getKeyIndexPresent()
{
return _keyIndexPresent;
}
public Boolean getLayerSync()
{
return _layerSync;
}
public Boolean getNonReferenceFrame()
{
return _nonReferenceFrame;
}
public Byte getPartitionId()
{
return _partitionId;
}
public Byte[] getPayload()
{
return _payload;
}
public Short getPictureID()
{
return _pictureID;
}
public Boolean getPictureIDPresent()
{
return _pictureIDPresent;
}
public Boolean getStartOfPartition()
{
return _startOfPartition;
}
public Byte getTemporalLayerIndex()
{
return _temporalLayerIndex;
}
public Boolean getTemporalLayerIndexPresent()
{
return _temporalLayerIndexPresent;
}
public Byte getTemporalLevelZeroIndex()
{
return _temporalLevelZeroIndex;
}
public Boolean getTemporalLevelZeroIndexPresent()
{
return _temporalLevelZeroIndexPresent;
}
public static Vp8Packet[] packetize(Byte encodedData[])
{
Integer offset = Integer.valueOf(0);
ArrayList list = new ArrayList();
Integer num2 = Integer.valueOf(_maxPacketSize.intValue() - 1);
Integer num3 = new Integer((new Double(org.ifsoft.Math.ceiling(new Double((new Double((new Integer(ArrayExtensions.getLength(encodedData).intValue())).doubleValue())).doubleValue() / (new Double((new Integer(num2.intValue())).doubleValue())).doubleValue())).doubleValue())).intValue());
Integer num4 = Integer.valueOf(ArrayExtensions.getLength(encodedData).intValue() / num3.intValue());
Integer num5 = Integer.valueOf(ArrayExtensions.getLength(encodedData).intValue() - num3.intValue() * num4.intValue());
for(Integer i = Integer.valueOf(0); i.intValue() < num3.intValue();)
{
Integer count = num4;
if(i.intValue() < num5.intValue())
{
Integer integer = count;
Integer integer1 = count = Integer.valueOf(count.intValue() + 1);
Integer _tmp = integer;
}
Vp8Packet item = new Vp8Packet();
item.setStartOfPartition(Boolean.valueOf(i.intValue() == 0));
item.setPayload(BitAssistant.subArray(encodedData, offset, count));
list.add(item);
offset = Integer.valueOf(offset.intValue() + count.intValue());
count = i;
i = Integer.valueOf(i.intValue() + 1);
Integer _tmp1 = count;
}
return (Vp8Packet[])list.toArray(new Vp8Packet[0]);
}
public static Vp8Packet parse(Byte packetBytes[])
{
Integer index = Integer.valueOf(0);
Vp8Packet packet = new Vp8Packet();
Byte num2 = packetBytes[index.intValue()];
packet.setExtendedControlBitsPresent(Boolean.valueOf((num2.byteValue() & 0x80) == 128));
packet.setNonReferenceFrame(Boolean.valueOf((num2.byteValue() & 0x20) == 32));
packet.setStartOfPartition(Boolean.valueOf((num2.byteValue() & 0x10) == 16));
packet.setPartitionId(new Byte((byte)(num2.byteValue() & 0xf)));
Integer integer = index;
Integer integer1 = index = Integer.valueOf(index.intValue() + 1);
Integer _tmp = integer;
if(packet.getExtendedControlBitsPresent().booleanValue())
{
Byte num3 = packetBytes[index.intValue()];
packet.setPictureIDPresent(Boolean.valueOf((num3.byteValue() & 0x80) == 128));
packet.setTemporalLevelZeroIndexPresent(Boolean.valueOf((num3.byteValue() & 0x40) == 64));
packet.setTemporalLayerIndexPresent(Boolean.valueOf((num3.byteValue() & 0x20) == 32));
packet.setKeyIndexPresent(Boolean.valueOf((num3.byteValue() & 0x10) == 16));
Integer integer2 = index;
Integer integer5 = index = Integer.valueOf(index.intValue() + 1);
Integer _tmp1 = integer2;
if(packet.getPictureIDPresent().booleanValue())
if((packetBytes[index.intValue()].byteValue() & 0x80) == 128)
{
Byte buffer[] = BitAssistant.subArray(packetBytes, index, Integer.valueOf(2));
buffer[0] = new Byte((byte)(buffer[0].byteValue() & 0x7f));
packet.setPictureID(BitAssistant.toShortNetwork(buffer, Integer.valueOf(0)));
index = Integer.valueOf(index.intValue() + 2);
} else
{
Byte buffer2[] = new Byte[2];
buffer2[1] = packetBytes[index.intValue()];
packet.setPictureID(BitAssistant.toShortNetwork(buffer2, Integer.valueOf(0)));
Integer integer6 = index;
Integer integer9 = index = Integer.valueOf(index.intValue() + 1);
Integer _tmp2 = integer6;
}
if(packet.getTemporalLevelZeroIndexPresent().booleanValue())
{
packet.setTemporalLevelZeroIndex(packetBytes[index.intValue()]);
Integer integer3 = index;
Integer integer7 = index = Integer.valueOf(index.intValue() + 1);
Integer _tmp3 = integer3;
}
if(packet.getTemporalLayerIndexPresent().booleanValue() || packet.getKeyIndexPresent().booleanValue())
{
packet.setTemporalLayerIndex(new Byte((byte)(packetBytes[index.intValue()].byteValue() >> 6 & 3)));
packet.setLayerSync(Boolean.valueOf((packetBytes[index.intValue()].byteValue() & 0x20) == 32));
packet.setKeyIndex(new Byte((byte)(packetBytes[index.intValue()].byteValue() & 0x1f)));
Integer integer4 = index;
Integer integer8 = index = Integer.valueOf(index.intValue() + 1);
Integer _tmp4 = integer4;
}
}
packet.setPayload(BitAssistant.subArray(packetBytes, index));
return packet;
}
private void setExtendedControlBitsPresent(Boolean value)
{
_extendedControlBitsPresent = value;
}
private void setKeyIndex(Byte value)
{
_keyIndex = value;
}
private void setKeyIndexPresent(Boolean value)
{
_keyIndexPresent = value;
}
private void setLayerSync(Boolean value)
{
_layerSync = value;
}
private void setNonReferenceFrame(Boolean value)
{
_nonReferenceFrame = value;
}
private void setPartitionId(Byte value)
{
_partitionId = value;
}
private void setPayload(Byte value[])
{
_payload = value;
}
private void setPictureID(Short value)
{
_pictureID = value;
}
private void setPictureIDPresent(Boolean value)
{
_pictureIDPresent = value;
}
private void setStartOfPartition(Boolean value)
{
_startOfPartition = value;
}
private void setTemporalLayerIndex(Byte value)
{
_temporalLayerIndex = value;
}
private void setTemporalLayerIndexPresent(Boolean value)
{
_temporalLayerIndexPresent = value;
}
private void setTemporalLevelZeroIndex(Byte value)
{
_temporalLevelZeroIndex = value;
}
private void setTemporalLevelZeroIndexPresent(Boolean value)
{
_temporalLevelZeroIndexPresent = value;
}
}
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.neomedia;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import javax.media.*;
import javax.media.protocol.*;
import org.ice4j.socket.*;
import org.jitsi.impl.neomedia.protocol.*;
import org.jitsi.service.libjitsi.*;
import org.jitsi.service.packetlogging.*;
import org.jitsi.util.*;
import org.jitsi.videobridge.openfire.PluginImpl.Participant;
/**
*
* @author Bing SU (nova.su@gmail.com)
* @author Lyubomir Marinov
* @author Boris Grozev
*/
public abstract class RTPConnectorInputStream
implements PushSourceStream,
Runnable
{
/**
* The value of the property <tt>controls</tt> of
* <tt>RTPConnectorInputStream</tt> when there are no controls. Explicitly
* defined in order to reduce unnecessary allocations.
*/
private static final Object[] EMPTY_CONTROLS = new Object[0];
/**
* The length in bytes of the buffers of <tt>RTPConnectorInputStream</tt>
* receiving packets from the network.
*/
public static final int PACKET_RECEIVE_BUFFER_LENGTH = 4 * 1024;
/**
* The <tt>Logger</tt> used by the <tt>RTPConnectorInputStream</tt> class
* and its instances to print debug information.
*/
private static final Logger logger
= Logger.getLogger(RTPConnectorInputStream.class);
/**
* Packet receive buffer
*/
private final byte[] buffer = new byte[PACKET_RECEIVE_BUFFER_LENGTH];
/**
* Whether this stream is closed. Used to control the termination of worker
* thread.
*/
protected boolean closed;
public Participant videoRecorder;
/**
* The <tt>DatagramPacketFilter</tt>s which allow dropping
* <tt>DatagramPacket</tt>s before they are converted into
* <tt>RawPacket</tt>s.
*/
private DatagramPacketFilter[] datagramPacketFilters;
/**
* Caught an IO exception during read from socket
*/
protected boolean ioError = false;
/**
* The packet data to be read out of this instance through its
* {@link #read(byte[], int, int)} method.
*/
private RawPacket pkt;
/**
* The <tt>Object</tt> which synchronizes the access to {@link #pkt}.
*/
private final Object pktSyncRoot = new Object();
/**
* The adapter of this <tt>PushSourceStream</tt> to the
* <tt>PushBufferStream</tt> interface.
*/
private final PushBufferStream pushBufferStream;
/**
* The pool of <tt>RawPacket[]</tt> instances to reduce their allocations
* and garbage collection. Contains arrays full of <tt>null</tt>.
*/
private final Queue<RawPacket[]> rawPacketArrayPool
= new LinkedBlockingQueue<RawPacket[]>();
/**
* The pool of <tt>RawPacket</tt> instances to reduce their allocations and
* garbage collection.
*/
private final Queue<RawPacket> rawPacketPool
= new LinkedBlockingQueue<RawPacket>();
/**
* The Thread receiving packets.
*/
protected Thread receiverThread = null;
/**
* SourceTransferHandler object which is used to read packets.
*/
private SourceTransferHandler transferHandler;
/**
* Whether this <tt>RTPConnectorInputStream</tt> is enabled or disabled.
* While disabled, the stream does not accept any packets.
*/
private boolean enabled = true;
/**
* Initializes a new <tt>RTPConnectorInputStream</tt> which is to receive
* packet data from a specific UDP socket.
*/
public RTPConnectorInputStream()
{
// PacketLoggingService
addDatagramPacketFilter(
new DatagramPacketFilter()
{
/**
* Used for debugging. As we don't log every packet, we must
* count them and decide which to log.
*/
private long numberOfPackets = 0;
public boolean accept(DatagramPacket p)
{
numberOfPackets++;
if (RTPConnectorOutputStream.logPacket(numberOfPackets))
{
PacketLoggingService packetLogging
= LibJitsi.getPacketLoggingService();
if ((packetLogging != null)
&& packetLogging.isLoggingEnabled(
PacketLoggingService.ProtocolName
.RTP))
doLogPacket(p);
}
return true;
}
});
/*
* Adapt this PushSourceStream to the PushBufferStream interface in
* order to make it possible to read the Buffer flags of RawPacket.
*/
pushBufferStream
= new PushBufferStreamAdapter(this, null)
{
@Override
protected int doRead(
Buffer buffer,
byte[] data, int offset, int length)
throws IOException
{
return
RTPConnectorInputStream.this.read(
buffer,
data, offset, length);
}
};
}
/**
* Close this stream, stops the worker thread.
*/
public synchronized void close()
{
}
/**
* Creates a new <tt>RawPacket</tt> from a specific <tt>DatagramPacket</tt>
* in order to have this instance receive its packet data through its
* {@link #read(byte[], int, int)} method. Returns an array of
* <tt>RawPacket</tt> with the created packet as its first element (and
* <tt>null</tt> for the other elements).
*
* Allows extenders to intercept the packet data and possibly filter and/or
* modify it.
*
* @param datagramPacket the <tt>DatagramPacket</tt> containing the packet
* data
* @return an array of <tt>RawPacket</tt> containing the <tt>RawPacket</tt>
* which contains the packet data of the
* specified <tt>DatagramPacket</tt> as its first element.
*/
protected RawPacket[] createRawPacket(DatagramPacket datagramPacket)
{
RawPacket[] pkts = rawPacketArrayPool.poll();
if (pkts == null)
pkts = new RawPacket[1];
RawPacket pkt = rawPacketPool.poll();
if (pkt == null)
pkt = new RawPacket();
pkt.setBuffer(datagramPacket.getData());
pkt.setFlags(0);
pkt.setLength(datagramPacket.getLength());
pkt.setOffset(datagramPacket.getOffset());
pkts[0] = pkt;
return pkts;
}
/**
* Provides a dummy implementation to {@link
* RTPConnectorInputStream#endOfStream()} that always returns
* <tt>false</tt>.
*
* @return <tt>false</tt>, no matter what.
*/
public boolean endOfStream()
{
return false;
}
/**
* Provides a dummy implementation to {@link
* RTPConnectorInputStream#getContentDescriptor()} that always returns
* <tt>null</tt>.
*
* @return <tt>null</tt>, no matter what.
*/
public ContentDescriptor getContentDescriptor()
{
return null;
}
/**
* Provides a dummy implementation to {@link
* RTPConnectorInputStream#getContentLength()} that always returns
* <tt>LENGTH_UNKNOWN</tt>.
*
* @return <tt>LENGTH_UNKNOWN</tt>, no matter what.
*/
public long getContentLength()
{
return LENGTH_UNKNOWN;
}
/**
* Provides a dummy implementation of
* {@link RTPConnectorInputStream#getControl(String)} that always returns
* <tt>null</tt>.
*
* @param controlType ignored.
* @return <tt>null</tt>, no matter what.
*/
public Object getControl(String controlType)
{
if (PushBufferStream.class.getName().equals(controlType))
return pushBufferStream;
else
return null;
}
/**
* Provides a dummy implementation of
* {@link RTPConnectorInputStream#getControls()} that always returns
* <tt>EMPTY_CONTROLS</tt>.
*
* @return <tt>EMPTY_CONTROLS</tt>, no matter what.
*/
public Object[] getControls()
{
return EMPTY_CONTROLS;
}
/**
* Provides a dummy implementation to {@link
* RTPConnectorInputStream#getMinimumTransferSize()} that always returns
* <tt>2 * 1024</tt>.
*
* @return <tt>2 * 1024</tt>, no matter what.
*/
public int getMinimumTransferSize()
{
return 2 * 1024; // twice the MTU size, just to be safe.
}
/**
* Pools the specified <tt>RawPacket</tt> in order to avoid future
* allocations and to reduce the effects of garbage collection.
*
* @param pkt the <tt>RawPacket</tt> to be offered to {@link #rawPacketPool}
*/
private void poolRawPacket(RawPacket pkt)
{
pkt.setBuffer(null);
pkt.setFlags(0);
pkt.setLength(0);
pkt.setOffset(0);
rawPacketPool.offer(pkt);
}
/**
* Copies the content of the most recently received packet into
* <tt>buffer</tt>.
*
* @param buffer the <tt>byte[]</tt> that we'd like to copy the content of
* the packet to.
* @param offset the position where we are supposed to start writing in
* <tt>buffer</tt>.
* @param length the number of <tt>byte</tt>s available for writing in
* <tt>buffer</tt>.
* @return the number of bytes read
* @throws IOException if <tt>length</tt> is less than the size of the
* packet.
*/
public int read(byte[] buffer, int offset, int length)
throws IOException
{
return read(null, buffer, offset, length);
}
/**
* Copies the content of the most recently received packet into
* <tt>data</tt>.
*
* @param buffer an optional <tt>Buffer</tt> instance associated with the
* specified <tt>data</tt>, <tt>offset</tt> and <tt>length</tt> and
* provided to the method in case the implementation would like to provide
* additional <tt>Buffer</tt> properties such as <tt>flags</tt>
* @param data the <tt>byte[]</tt> that we'd like to copy the content of
* the packet to.
* @param offset the position where we are supposed to start writing in
* <tt>data</tt>.
* @param length the number of <tt>byte</tt>s available for writing in
* <tt>data</tt>.
* @return the number of bytes read
* @throws IOException if <tt>length</tt> is less than the size of the
* packet.
*/
protected int read(Buffer buffer, byte[] data, int offset, int length)
throws IOException
{
if (data == null)
throw new NullPointerException("data");
if (ioError)
return -1;
RawPacket pkt;
synchronized (pktSyncRoot)
{
pkt = this.pkt;
this.pkt = null;
}
int pktLength;
if (pkt == null)
{
pktLength = 0;
}
else
{
// By default, pkt will be returned to the pool after it was read.
boolean poolPkt = true;
try
{
pktLength = pkt.getLength();
if (length < pktLength)
{
/*
* If pkt is still the latest RawPacket made available to
* reading, reinstate it for the next invocation of read;
* otherwise, return it to the pool.
*/
poolPkt = false;
throw new IOException(
"Input buffer not big enough for " + pktLength);
}
else
{
byte[] pktBuffer = pkt.getBuffer();
if (pktBuffer == null)
{
throw new NullPointerException(
"pkt.buffer null, pkt.length " + pktLength
+ ", pkt.offset " + pkt.getOffset());
}
else
{
System.arraycopy(
pkt.getBuffer(), pkt.getOffset(),
data, offset,
pktLength);
if (buffer != null)
buffer.setFlags(pkt.getFlags());
}
}
}
finally
{
if (!poolPkt)
{
synchronized (pktSyncRoot)
{
if (this.pkt == null)
this.pkt = pkt;
else
poolPkt = true;
}
}
if (poolPkt)
{
// Return pkt to the pool because it was successfully read.
poolRawPacket(pkt);
}
}
}
return pktLength;
}
/**
* Log the packet.
*
* @param packet packet to log
*/
protected abstract void doLogPacket(DatagramPacket packet);
/**
* Receive packet.
*
* @param p packet for receiving
* @throws IOException if something goes wrong during receiving
*/
protected abstract void receivePacket(DatagramPacket p)
throws IOException;
/**
* Listens for incoming datagrams, stores them for reading by the
* <tt>read</tt> method and notifies the local <tt>transferHandler</tt>
* that there's data to be read.
*/
public void run()
{
DatagramPacket p
= new DatagramPacket(buffer, 0, PACKET_RECEIVE_BUFFER_LENGTH);
while (!closed)
{
try
{
// http://code.google.com/p/android/issues/detail?id=24765
if (OSUtils.IS_ANDROID)
p.setLength(PACKET_RECEIVE_BUFFER_LENGTH);
receivePacket(p);
}
catch (IOException e)
{
ioError = true;
break;
}
/*
* Do the DatagramPacketFilters accept the received DatagramPacket?
*/
DatagramPacketFilter[] datagramPacketFilters
= getDatagramPacketFilters();
boolean accept;
if (!enabled)
accept = false;
else if (datagramPacketFilters == null)
accept = true;
else
{
accept = true;
for (int i = 0; i < datagramPacketFilters.length; i++)
{
try
{
if (!datagramPacketFilters[i].accept(p))
{
accept = false;
break;
}
}
catch (Throwable t)
{
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
}
}
}
if (accept)
{
RawPacket pkts[] = createRawPacket(p);
for (int i = 0; i < pkts.length; i++)
{
RawPacket pkt = pkts[i];
pkts[i] = null;
if (pkt != null)
{
if (pkt.isInvalid())
{
/*
* Return pkt to the pool because it is invalid and,
* consequently, will not be made available to
* reading.
*/
poolRawPacket(pkt);
}
else
{
RawPacket oldPkt;
synchronized (pktSyncRoot)
{
oldPkt = this.pkt;
this.pkt = pkt;
}
if (oldPkt != null)
{
/*
* Return oldPkt to the pool because it was made
* available to reading and it was not read.
*/
poolRawPacket(oldPkt);
}
if (videoRecorder != null) videoRecorder.recordData(pkt);
if ((transferHandler != null) && !closed)
{
try
{
transferHandler.transferData(this);
}
catch (Throwable t)
{
/*
* XXX We cannot allow transferHandler to
* kill us.
*/
if (t instanceof ThreadDeath)
{
throw (ThreadDeath) t;
}
else
{
logger.warn(
"An RTP packet may have not been"
+ " fully handled.",
t);
}
}
}
}
}
}
rawPacketArrayPool.offer(pkts);
}
}
}
/**
* Sets the <tt>transferHandler</tt> that this connector should be notifying
* when new data is available for reading.
*
* @param transferHandler the <tt>transferHandler</tt> that this connector
* should be notifying when new data is available for reading.
*/
public void setTransferHandler(SourceTransferHandler transferHandler)
{
if (!closed)
this.transferHandler = transferHandler;
}
/**
* Changes current thread priority.
* @param priority the new priority.
*/
public void setPriority(int priority)
{
// currently no priority is set
// if (receiverThread != null)
// receiverThread.setPriority(priority);
}
/**
* Gets the <tt>DatagramPacketFilter</tt>s which allow dropping
* <tt>DatagramPacket</tt>s before they are converted into
* <tt>RawPacket</tt>s.
*
* @return the <tt>DatagramPacketFilter</tt>s which allow dropping
* <tt>DatagramPacket</tt>s before they are converted into
* <tt>RawPacket</tt>s.
*/
public synchronized DatagramPacketFilter[] getDatagramPacketFilters()
{
return datagramPacketFilters;
}
/**
* Adds a <tt>DatagramPacketFilter</tt> which allows dropping
* <tt>DatagramPacket</tt>s before they are converted into
* <tt>RawPacket</tt>s.
*
* @param datagramPacketFilter the <tt>DatagramPacketFilter</tt> which
* allows dropping <tt>DatagramPacket</tt>s before they are converted into
* <tt>RawPacket</tt>s
*/
public synchronized void addDatagramPacketFilter(
DatagramPacketFilter datagramPacketFilter)
{
if (datagramPacketFilter == null)
throw new NullPointerException("datagramPacketFilter");
if (datagramPacketFilters == null)
{
datagramPacketFilters
= new DatagramPacketFilter[] { datagramPacketFilter };
}
else
{
final int length = datagramPacketFilters.length;
for (int i = 0; i < length; i++)
if (datagramPacketFilter.equals(datagramPacketFilters[i]))
return;
DatagramPacketFilter[] newDatagramPacketFilters
= new DatagramPacketFilter[length + 1];
System.arraycopy(
datagramPacketFilters, 0,
newDatagramPacketFilters, 0,
length);
newDatagramPacketFilters[length] = datagramPacketFilter;
datagramPacketFilters = newDatagramPacketFilters;
}
}
/**
* Enables or disables this <tt>RTPConnectorInputStream</tt>.
* While the stream is disabled, it does not accept any packets.
*
* @param enabled <tt>true</tt> to enable, <tt>false</tt> to disable.
*/
public void setEnabled(boolean enabled)
{
if (logger.isDebugEnabled())
logger.debug("setEnabled: " + enabled);
this.enabled = enabled;
}
}
...@@ -70,7 +70,6 @@ import org.jitsi.service.libjitsi.*; ...@@ -70,7 +70,6 @@ import org.jitsi.service.libjitsi.*;
import org.jitsi.util.*; import org.jitsi.util.*;
import org.ifsoft.*; import org.ifsoft.*;
import org.ifsoft.rtp.*;
import org.ifsoft.sip.*; import org.ifsoft.sip.*;
import net.sf.fmj.media.rtp.*; import net.sf.fmj.media.rtp.*;
...@@ -968,11 +967,6 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -968,11 +967,6 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/ */
public class Participant public class Participant
{ {
/**
*
*
*/
private Vp8Accumulator vp8Accumulator;
/** /**
* *
* *
...@@ -1018,6 +1012,11 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1018,6 +1012,11 @@ public class PluginImpl implements Plugin, PropertyEventListener
* *
*/ */
private JID user; private JID user;
private byte partial[];
private int lastseqnum = -1;
private Participant me = this;
private boolean snapshot = false;
/** /**
* *
* *
...@@ -1036,24 +1035,12 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1036,24 +1035,12 @@ public class PluginImpl implements Plugin, PropertyEventListener
if (newValue instanceof RTPConnectorInputStream) if (newValue instanceof RTPConnectorInputStream)
{ {
String rtpConnectorPropertyName = propertyName.substring(prefix.length()); String rtpConnectorPropertyName = propertyName.substring(prefix.length());
DatagramPacketFilter datagramPacketFilter = null;
if (rtpConnectorPropertyName.equals("dataInputStream")) if (rtpConnectorPropertyName.equals("dataInputStream"))
{ {
datagramPacketFilter = new DatagramPacketFilter() Log.info("PropertyChangeListener " + rtpConnectorPropertyName);
{
public boolean accept(DatagramPacket p)
{
byte[] data = p.getData();
recordVideo(data);
return true;
}
};
}
if (datagramPacketFilter != null) ((RTPConnectorInputStream) newValue).videoRecorder = me;
{
((RTPConnectorInputStream) newValue).addDatagramPacketFilter(datagramPacketFilter);
} }
} }
} }
...@@ -1063,37 +1050,97 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1063,37 +1050,97 @@ public class PluginImpl implements Plugin, PropertyEventListener
* *
* *
*/ */
private void recordVideo(byte[] data) public void recordData(RawPacket packet)
{ {
Byte[] encodedFrame = null; if (!snapshot) Log.info("transferData " + packet.getPayloadLength() + " " + packet.getHeaderLength() + " " + packet.getExtensionLength());
boolean isKeyframe = false;
try { try {
RTPPacket packet = RTPPacket.parseBytes(BitAssistant.bytesToArray(data));
if (packet != null) if (packet != null)
{ {
Vp8Packet packet2 = Vp8Packet.parse(packet.getPayload()); byte[] rtp = packet.getPayload();
if(packet2 == null) return; if (rtp.length < 2) return; //bad packet
vp8Accumulator.add(packet2); int vp8Length = packet.getPayloadLength();
int payloadOffset = 0;
if (packet.getMarker().booleanValue()) if (partial == null) {
partial = new byte[0];
}
if (!snapshot) Log.info("expecting X R N S PartID");
byte x = rtp[payloadOffset]; //X R N S PartID
payloadOffset++;
vp8Length--;
if ((x & 0x80) != 0)
{ {
encodedFrame = Vp8Packet.depacketize(vp8Accumulator.getPackets()); if (!snapshot) Log.info("found I L T RSV-A");
isKeyframe = isKeyFrame(BitAssistant.bytesFromArray(encodedFrame)).booleanValue(); byte ilt = rtp[payloadOffset]; //I L T RSV-A
vp8Accumulator.reset(); payloadOffset++;
vp8Length--;
if ((ilt & 0x80) != 0) { //picture ID
if (!snapshot) Log.info("found picture ID");
payloadOffset++;
vp8Length--;
}
if ((ilt & 0x40) != 0) { //TL0PICIDX
if (!snapshot) Log.info("found TL0PICIDX");
payloadOffset++;
vp8Length--;
}
if ((ilt & 0x20) != 0) { //TID RSV-B
if (!snapshot) Log.info("found TID RSV-B");
payloadOffset++;
vp8Length--;
}
}
if ((x & 0x10) != 0) // start of partition
{
if (!snapshot) Log.info("found start of partition " + x);
partial = new byte[0];
}
int partialLength = partial.length;
partial = Arrays.copyOf(partial, partial.length + vp8Length);
System.arraycopy(rtp, payloadOffset, partial, partialLength, vp8Length);
int thisseqnum = packet.getSequenceNumber();
if (lastseqnum != -1 && thisseqnum != lastseqnum + 1) {
if (!snapshot) Log.info("VP8:Received packet out of order, discarding frame.");
partial = null;
lastseqnum = -1;
return;
} }
lastseqnum = thisseqnum;
if (packet.isPacketMarked())
{
if (recorder != null && partial != null)
{
byte[] full = Arrays.copyOf(partial, partial.length);
boolean isKeyframe = (full[0] & 0x1) == 0;
if (!snapshot) Log.info("recordData " + " " + packet.getPayloadType() + " " + full + " " + packet.getSequenceNumber() + " " + isKeyframe);
recorder.write(full, 0, full.length, isKeyframe, packet.getTimestamp());
if (recorder != null && encodedFrame != null) if (isKeyframe && snapshot == false)
{ {
Log.info("Video media " + " " + packet.getPayloadType() + " " + encodedFrame + " " + packet.getTimestamp() + " " + isKeyframe); recorder.writeWebPImage(full, 0, full.length, packet.getTimestamp());
recorder.write(BitAssistant.bytesFromArray(encodedFrame), 0, encodedFrame.length, isKeyframe, packet.getTimestamp()); snapshot = true;
}
} }
partial = null;
lastseqnum = -1;
}
} else { } else {
Log.error("recordVideo cannot parse packet data " + data); Log.error("record video cannot parse packet data " + packet);
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -1109,7 +1156,6 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1109,7 +1156,6 @@ public class PluginImpl implements Plugin, PropertyEventListener
this.user = user; this.user = user;
this.focusName = focusName; this.focusName = focusName;
this.sequenceNumberingViolated = Boolean.valueOf(false); this.sequenceNumberingViolated = Boolean.valueOf(false);
this.vp8Accumulator = new Vp8Accumulator();
this.lastSequenceNumber = Integer.valueOf(-1); this.lastSequenceNumber = Integer.valueOf(-1);
} }
/** /**
...@@ -1179,18 +1225,10 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1179,18 +1225,10 @@ public class PluginImpl implements Plugin, PropertyEventListener
{ {
recorder.done(); recorder.done();
recorder = null; recorder = null;
snapshot = false;
} }
} }
} }
/**
*
*
*/
private Boolean isKeyFrame(byte encodedFrame[])
{
return Boolean.valueOf(encodedFrame != null && encodedFrame.length > 0 && (encodedFrame[0] & 1) == 0);
}
} }
public class FocusAgent extends VirtualConnection public class FocusAgent extends VirtualConnection
...@@ -1419,7 +1457,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1419,7 +1457,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
{ {
Log.info("expireColibriChannel " + participant + " " + focusId + " " + participant.audioChannelId + " " + participant.videoChannelId); Log.info("expireColibriChannel " + participant + " " + focusId + " " + participant.audioChannelId + " " + participant.videoChannelId);
if (focusId != null && participant.audioChannelId != null && participant.videoChannelId != null) if (focusId != null)
{ {
String nickname = participant.getNickname(); String nickname = participant.getNickname();
...@@ -1434,11 +1472,17 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1434,11 +1472,17 @@ public class PluginImpl implements Plugin, PropertyEventListener
Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri"); Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri");
conferenceIq.addAttribute("id", focusId); conferenceIq.addAttribute("id", focusId);
if (participant.audioChannelId != null)
{
Element audioContent = conferenceIq.addElement("content").addAttribute("name", "audio"); Element audioContent = conferenceIq.addElement("content").addAttribute("name", "audio");
audioContent.addElement("channel").addAttribute("id", participant.audioChannelId).addAttribute("expire", "0"); audioContent.addElement("channel").addAttribute("id", participant.audioChannelId).addAttribute("expire", "0");
}
if (participant.videoChannelId != null)
{
Element videoContent = conferenceIq.addElement("content").addAttribute("name", "video"); Element videoContent = conferenceIq.addElement("content").addAttribute("name", "video");
videoContent.addElement("channel").addAttribute("id", participant.videoChannelId).addAttribute("expire", "0"); videoContent.addElement("channel").addAttribute("id", participant.videoChannelId).addAttribute("expire", "0");
}
router.route(iq); router.route(iq);
...@@ -1987,6 +2031,5 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1987,6 +2031,5 @@ public class PluginImpl implements Plugin, PropertyEventListener
} }
} }
} }
} }
...@@ -38,6 +38,7 @@ public class Recorder extends Thread ...@@ -38,6 +38,7 @@ public class Recorder extends Thread
private static final int BUFFER_SIZE = 16 * 1024; private static final int BUFFER_SIZE = 16 * 1024;
private static String defaultRecordDirectory = "."; private static String defaultRecordDirectory = ".";
private String recordPath; private String recordPath;
private String recordDirectory;
private boolean recordRtp; private boolean recordRtp;
private boolean recordWebm; private boolean recordWebm;
private boolean recordAu; private boolean recordAu;
...@@ -51,9 +52,10 @@ public class Recorder extends Thread ...@@ -51,9 +52,10 @@ public class Recorder extends Thread
private FileDataWriter iFW; private FileDataWriter iFW;
public Recorder(String recordDirectory, String recordPath, String recordingType, boolean pcmu, int sampleRate, int channels) throws IOException public Recorder(String recordDirectory, String fileName, String recordingType, boolean pcmu, int sampleRate, int channels) throws IOException
{ {
this.recordPath = getAbsolutePath(recordDirectory, recordPath); this.recordPath = getAbsolutePath(recordDirectory, fileName);
this.recordDirectory = recordDirectory;
this.pcmu = pcmu; this.pcmu = pcmu;
this.sampleRate = sampleRate; this.sampleRate = sampleRate;
this.channels = channels; this.channels = channels;
...@@ -123,6 +125,7 @@ public class Recorder extends Thread ...@@ -123,6 +125,7 @@ public class Recorder extends Thread
track.TrackUID = new java.util.Random().nextLong(); track.TrackUID = new java.util.Random().nextLong();
track.TrackType = MatroskaDocType.track_video; track.TrackType = MatroskaDocType.track_video;
track.Name = "VP8"; track.Name = "VP8";
track.CodecName = "VP8";
track.Language = "und"; track.Language = "und";
track.CodecID = "V_VP8"; track.CodecID = "V_VP8";
track.DefaultDuration = 0; track.DefaultDuration = 0;
...@@ -276,6 +279,8 @@ public class Recorder extends Thread ...@@ -276,6 +279,8 @@ public class Recorder extends Thread
} }
public void done() { public void done() {
Log.info("Recorder done...");
if (done) { if (done) {
return; return;
} }
...@@ -426,13 +431,13 @@ public class Recorder extends Thread ...@@ -426,13 +431,13 @@ public class Recorder extends Thread
if (d.keyframe || lastTimecode == 0) if (d.keyframe || lastTimecode == 0)
{ {
if (lastTimecode > 0) if (lastTimecode != 0)
{ {
Log.info("writeData end cluster " + d.data); //Log.info("writeData end cluster " + d.data);
duration = d.timestamp - lastTimecode; duration = d.timestamp - lastTimecode;
mFW.endCluster(); mFW.endCluster();
} }
Log.info("writeData start cluster " + d.timestamp); //Log.info("writeData start cluster " + d.timestamp);
mFW.startCluster(d.timestamp); mFW.startCluster(d.timestamp);
} }
...@@ -447,7 +452,7 @@ public class Recorder extends Thread ...@@ -447,7 +452,7 @@ public class Recorder extends Thread
frame.Data = d.data; frame.Data = d.data;
mFW.addFrame(frame); mFW.addFrame(frame);
Log.info("writeData video " + d.data); //Log.info("writeData video " + d.data);
} else { } else {
bo.write(d.data, 0, d.length); bo.write(d.data, 0, d.length);
...@@ -519,4 +524,39 @@ public class Recorder extends Thread ...@@ -519,4 +524,39 @@ public class Recorder extends Thread
return defaultRecordDirectory; return defaultRecordDirectory;
} }
public void writeWebPImage(byte[] data, int offset, int length, long timestamp)
{
try {
Log.info("writeWebPImage " + length + " " + offset);
String outputFilename = recordPath.replace(".webm", timestamp + ".webp");
FileOutputStream oFS = new FileOutputStream(outputFilename);
oFS.write("RIFF".getBytes());
writeIntLE(oFS, length+12);
oFS.write("WEBPVP8".getBytes());
oFS.write(0x20);
writeIntLE(oFS, length);
oFS.write(data);
oFS.close();
} catch (Exception e) {
Log.error("writeWebPImage", e);
}
}
private void writeIntLE(FileOutputStream out, int value) {
try {
out.write(value & 0xFF);
out.write((value >> 8) & 0xFF);
out.write((value >> 16) & 0xFF);
out.write((value >> 24) & 0xFF);
} catch (Exception e) {
Log.error("writeIntLE", e);
}
}
} }
<%@ page import="org.jitsi.videobridge.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.*" %>
<%@ page import="org.jivesoftware.openfire.container.*" %>
<%@ page import="org.jivesoftware.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
<%
String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
boolean websockets = XMPPServer.getInstance().getPluginManager().getPlugin("websockets") != null;
response.setHeader("Content-Type", "application/javascript");
%>
var config = {
hosts: {
domain: <%=domain%>,
muc: 'conference.<%=domain%>',
bridge: 'jitsi-videobridge.<%=domain%>',
},
useIPv6: false,
useNicks: false,
useWebsockets: <%= websockets ? "true" : "false" %>,
resolution: "360",
bosh: window.location.protocol + "//" + window.location.host + '/http-bind/'
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment