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)
{
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 nick = $(offer).attr('nickname');
var participant = $(offer).attr('participant');
......
......@@ -186,14 +186,18 @@ public MatroskaBlock(byte[] type) {
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.keyFrame = keyFrame;
byte flags = 0;
if (keyFrame) flags |= 128;
this.data[0] = (byte) (trackNo | 0x80);
this.data[1] = (byte) (timecode >> 8);
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);
setSize(4 + frameData.length);
......
......@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone <jebml@jory.info>
* Based on Javatroska (C) 2002 John Cannon <spyder@matroska.org>
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
......@@ -25,10 +25,10 @@ import java.util.*;
/**
* DocType for Matroska files.
* This has all the element type id declares and also is in charge of
* This has all the element type id declares and also is in charge of
* creating the correct element classes from the type id.
*/
public class MatroskaDocType implements DocType
public class MatroskaDocType implements DocType
{
// Custom Element Types
static public short BLOCK_ELEMENT = (short)(ElementType.LAST_ELEMENT_TYPE + 1);
......@@ -63,12 +63,14 @@ public class MatroskaDocType implements DocType
static public byte [] Tracks_Id = {0x16, (byte)0x54, (byte)0xAE, (byte)0x6B};
static public byte [] TrackEntry_Id = {(byte)0xAE};
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 [] TrackFlagLacing_Id = {(byte)0x9C};
static public byte [] TrackDefaultDuration_Id = {0x23, (byte)0xE3, (byte)0x83};
static public byte [] TrackName_Id = {0x53, 0x6E};
static public byte [] TrackLanguage_Id = {0x22, (byte)0xB5, (byte)0x9C};
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 [] TrackVideo_Id = {(byte)0xE0};
static public byte [] PixelWidth_Id = {(byte)0xB0};
......@@ -127,7 +129,7 @@ public class MatroskaDocType implements DocType
static public byte [] ChapterAtomChapString_Id = {(byte)0x85};
static public byte [] ChapterAtomChapLanguage_Id = {(byte)0x43, (byte)0x7C};
static public byte [] ChapterAtomChapCountry_Id = {(byte)0x43, (byte)0x7E};
// Track Types
static public byte track_video = 0x01; ///< Rectangle-shaped non-transparent pictures aka video
static public byte track_audio = 0x02; ///< Anything you can hear
......@@ -376,6 +378,14 @@ public class MatroskaDocType implements DocType
ElementType.UINTEGER_ELEMENT,
(ArrayList<ElementType>)null);
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",
(short)3,
......@@ -404,6 +414,14 @@ public class MatroskaDocType implements DocType
ElementType.ASCII_STRING_ELEMENT,
(ArrayList<ElementType>)null);
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",
(short)3,
......@@ -662,7 +680,7 @@ public class MatroskaDocType implements DocType
// Add ClusterBlockGroup Element
level1.children.add(level2);
level2 = new ElementType("SimpleBlock",
(short)2,
......@@ -720,7 +738,7 @@ public class MatroskaDocType implements DocType
(short)3,
ChapterAtom_Id,
ElementType.MASTER_ELEMENT,
new ArrayList<ElementType>());
new ArrayList<ElementType>());
level4 = new ElementType("ChapterAtomChapterUID",
(short)4,
......@@ -768,7 +786,7 @@ public class MatroskaDocType implements DocType
(short)4,
ChapterAtomChapterTrack_Id,
ElementType.MASTER_ELEMENT,
new ArrayList<ElementType>());
new ArrayList<ElementType>());
level5 = new ElementType("ChapterAtomChapterTrackNumber",
(short)5,
......@@ -784,7 +802,7 @@ public class MatroskaDocType implements DocType
(short)4,
ChapterAtomChapterDisplay_Id,
ElementType.MASTER_ELEMENT,
new ArrayList<ElementType>());
new ArrayList<ElementType>());
level5 = new ElementType("ChapterAtomChapString",
(short)5,
......@@ -848,24 +866,24 @@ public class MatroskaDocType implements DocType
elem = type.createElement();
if (elem == null) {
if (type.type == MatroskaDocType.BLOCK_ELEMENT)
if (type.type == MatroskaDocType.BLOCK_ELEMENT)
{
elem = new MatroskaBlock(type.id);
}
else if (type.type == MatroskaDocType.SEGMENT_ELEMENT)
}
else if (type.type == MatroskaDocType.SEGMENT_ELEMENT)
{
elem = new MatroskaSegment(type.id);
}
else if (type.type == MatroskaDocType.CLUSTER_ELEMENT)
else if (type.type == MatroskaDocType.CLUSTER_ELEMENT)
{
elem = new MatroskaCluster(type.id);
}
else if (type.type == ElementType.UNKNOWN_ELEMENT)
elem = new MatroskaCluster(type.id);
}
else if (type.type == ElementType.UNKNOWN_ELEMENT)
{
elem = new BinaryElement(type.id);
}
else
}
else
{
throw new java.lang.RuntimeException("Error: Unknown Element Type");
}
......@@ -882,10 +900,10 @@ public class MatroskaDocType implements DocType
* @return new Element sub-class, BinaryElement is the default.
* @throws RuntimeException if the ElementType has an unknown type field.
*/
public Element createElement(byte [] type)
public Element createElement(byte [] type)
{
ElementType elementType = getElements().findElement(type);
if (elementType == null)
if (elementType == null)
{
elementType = new UnknownElementType(type);
}
......
......@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone <jebml@jory.info>
* Based on Javatroska (C) 2002 John Cannon <spyder@matroska.org>
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
......@@ -22,7 +22,7 @@ package org.ebml.matroska;
/**
* Matroska Track Class
*/
public class MatroskaFileTrack
public class MatroskaFileTrack
{
public short TrackNo;
public long TrackUID;
......@@ -31,6 +31,7 @@ public class MatroskaFileTrack
public String Name;
public String Language;
public String CodecID;
public String CodecName;
public byte [] CodecPrivate;
public short Video_PixelWidth;
......@@ -46,7 +47,7 @@ public class MatroskaFileTrack
* Converts the Track to String form
* @return String form of MatroskaFileTrack data
*/
public String toString()
public String toString()
{
String s = new String();
......@@ -57,10 +58,11 @@ public class MatroskaFileTrack
s += "\t\t" + "Name: " + Name + "\n";
s += "\t\t" + "Language: " + Language + "\n";
s += "\t\t" + "CodecID: " + CodecID + "\n";
s += "\t\t" + "CodecName: " + CodecName + "\n";
if (CodecPrivate != null)
s += "\t\t" + "CodecPrivate: " + CodecPrivate.length + " byte(s)" + "\n";
if (TrackType == MatroskaDocType.track_video)
if (TrackType == MatroskaDocType.track_video)
{
s += "\t\t" + "PixelWidth: " + Video_PixelWidth + "\n";
s += "\t\t" + "PixelHeight: " + Video_PixelHeight + "\n";
......@@ -68,7 +70,7 @@ public class MatroskaFileTrack
s += "\t\t" + "DisplayHeight: " + Video_DisplayHeight + "\n";
}
if (TrackType == MatroskaDocType.track_audio)
if (TrackType == MatroskaDocType.track_audio)
{
s += "\t\t" + "SamplingFrequency: " + Audio_SamplingFrequency + "\n";
if (Audio_OutputSamplingFrequency != 0)
......
......@@ -114,11 +114,11 @@ public class MatroskaFileWriter
FloatElement durationElem = (FloatElement)doc.createElement(MatroskaDocType.Duration_Id);
durationElem.setValue(Duration * 1000.0);
segmentInfoElem.addChildElement(dateElem);
//segmentInfoElem.addChildElement(dateElem);
segmentInfoElem.addChildElement(timecodescaleElem);
segmentInfoElem.addChildElement(durationElem);
segmentInfoElem.addChildElement(writingAppElem);
segmentInfoElem.addChildElement(muxingAppElem);
segmentInfoElem.addChildElement(writingAppElem);
segmentInfoElem.addChildElement(durationElem);
segmentInfoElem.writeElement(ioDW);
}
......@@ -135,6 +135,9 @@ public class MatroskaFileWriter
UnsignedIntegerElement trackNoElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackNumber_Id);
trackNoElem.setValue(track.TrackNo);
UnsignedIntegerElement trackFlagLacingElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackFlagLacing_Id);
trackFlagLacingElem.setValue(0);
UnsignedIntegerElement trackUIDElem = (UnsignedIntegerElement)doc.createElement(MatroskaDocType.TrackUID_Id);
trackUIDElem.setValue(track.TrackUID);
......@@ -150,6 +153,9 @@ public class MatroskaFileWriter
StringElement trackCodecIDElem = (StringElement)doc.createElement(MatroskaDocType.TrackCodecID_Id);
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);
trackCodecPrivateElem.setData(track.CodecPrivate);
......@@ -158,12 +164,14 @@ public class MatroskaFileWriter
trackEntryElem.addChildElement(trackNoElem);
trackEntryElem.addChildElement(trackUIDElem);
trackEntryElem.addChildElement(trackTypeElem);
trackEntryElem.addChildElement(trackNameElem);
trackEntryElem.addChildElement(trackFlagLacingElem);
trackEntryElem.addChildElement(trackLangElem);
trackEntryElem.addChildElement(trackCodecIDElem);
trackEntryElem.addChildElement(trackCodecPrivateElem);
trackEntryElem.addChildElement(trackDefaultDurationElem);
trackEntryElem.addChildElement(trackCodecName_IdElem);
trackEntryElem.addChildElement(trackTypeElem);
//trackEntryElem.addChildElement(trackNameElem);
//trackEntryElem.addChildElement(trackCodecPrivateElem);
// trackEntryElem.addChildElement(trackDefaultDurationElem);
// Now we add the audio/video dependant sub-elements
if (track.TrackType == MatroskaDocType.track_video)
......@@ -184,8 +192,8 @@ public class MatroskaFileWriter
trackVideoElem.addChildElement(trackVideoPixelWidthElem);
trackVideoElem.addChildElement(trackVideoPixelHeightElem);
trackVideoElem.addChildElement(trackVideoDisplayWidthElem);
trackVideoElem.addChildElement(trackVideoDisplayHeightElem);
//trackVideoElem.addChildElement(trackVideoDisplayWidthElem);
//trackVideoElem.addChildElement(trackVideoDisplayHeightElem);
trackEntryElem.addChildElement(trackVideoElem);
}
......@@ -243,7 +251,7 @@ public class MatroskaFileWriter
public void addFrame(MatroskaFileFrame frame)
{
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);
}
}
......@@ -2,17 +2,17 @@
* JEBML - Java library to read/write EBML/Matroska elements.
* Copyright (C) 2004 Jory Stone <jebml@jory.info>
* Based on Javatroska (C) 2002 John Cannon <spyder@matroska.org>
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
......@@ -29,7 +29,7 @@ public class MatroskaSegment extends MasterElement
{
protected boolean bUnknownSize = false;
public MatroskaSegment(byte[] type)
public MatroskaSegment(byte[] type)
{
super(type);
}
......@@ -37,27 +37,27 @@ public class MatroskaSegment extends MasterElement
/** Write the element header data.
* The override will write the size as unknown if the flag is set.
*/
public long writeHeaderData(DataWriter writer)
public long writeHeaderData(DataWriter writer)
{
long len = 0;
byte [] type = getType();
len += type.length;
writer.write(type);
byte [] size;
if (bUnknownSize)
if (bUnknownSize)
{
size = new byte[5];
size[0] = (byte)(0xFF >>> (size.length-1));
for (int i = 1; i < size.length; i++)
size[i] = (byte)0xFF;
}
else
}
else
{
size = Element.makeEbmlCodedSize(getSize());
}
}
len += size.length;
writer.write(size);
......@@ -68,7 +68,7 @@ public class MatroskaSegment extends MasterElement
* Setter for Unknown Size flag.
* This is a special case for ebml. The size value is filled with 1's.
*/
public void setUnknownSize(boolean bUnknownSize)
public void setUnknownSize(boolean bUnknownSize)
{
this.bUnknownSize = bUnknownSize;
}
......@@ -76,7 +76,7 @@ public class MatroskaSegment extends MasterElement
/**
* Getter for Unknown Size flag.
*/
public boolean getUnknownSize()
public boolean getUnknownSize()
{
return bUnknownSize;
}
......
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.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;
}
......@@ -70,7 +70,6 @@ import org.jitsi.service.libjitsi.*;
import org.jitsi.util.*;
import org.ifsoft.*;
import org.ifsoft.rtp.*;
import org.ifsoft.sip.*;
import net.sf.fmj.media.rtp.*;
......@@ -968,11 +967,6 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/
public class Participant
{
/**
*
*
*/
private Vp8Accumulator vp8Accumulator;
/**
*
*
......@@ -1018,6 +1012,11 @@ public class PluginImpl implements Plugin, PropertyEventListener
*
*/
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
if (newValue instanceof RTPConnectorInputStream)
{
String rtpConnectorPropertyName = propertyName.substring(prefix.length());
DatagramPacketFilter datagramPacketFilter = null;
if (rtpConnectorPropertyName.equals("dataInputStream"))
{
datagramPacketFilter = new DatagramPacketFilter()
{
public boolean accept(DatagramPacket p)
{
byte[] data = p.getData();
recordVideo(data);
return true;
}
};
}
Log.info("PropertyChangeListener " + rtpConnectorPropertyName);
if (datagramPacketFilter != null)
{
((RTPConnectorInputStream) newValue).addDatagramPacketFilter(datagramPacketFilter);
((RTPConnectorInputStream) newValue).videoRecorder = me;
}
}
}
......@@ -1063,37 +1050,97 @@ public class PluginImpl implements Plugin, PropertyEventListener
*
*
*/
private void recordVideo(byte[] data)
public void recordData(RawPacket packet)
{
Byte[] encodedFrame = null;
boolean isKeyframe = false;
if (!snapshot) Log.info("transferData " + packet.getPayloadLength() + " " + packet.getHeaderLength() + " " + packet.getExtensionLength());
try {
RTPPacket packet = RTPPacket.parseBytes(BitAssistant.bytesToArray(data));
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());
isKeyframe = isKeyFrame(BitAssistant.bytesFromArray(encodedFrame)).booleanValue();
vp8Accumulator.reset();
if (!snapshot) Log.info("found I L T RSV-A");
byte ilt = rtp[payloadOffset]; //I L T RSV-A
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 (recorder != null && encodedFrame != null)
if ((x & 0x10) != 0) // start of partition
{
Log.info("Video media " + " " + packet.getPayloadType() + " " + encodedFrame + " " + packet.getTimestamp() + " " + isKeyframe);
recorder.write(BitAssistant.bytesFromArray(encodedFrame), 0, encodedFrame.length, isKeyframe, packet.getTimestamp());
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 (isKeyframe && snapshot == false)
{
recorder.writeWebPImage(full, 0, full.length, packet.getTimestamp());
snapshot = true;
}
}
partial = null;
lastseqnum = -1;
}
} else {
Log.error("recordVideo cannot parse packet data " + data);
Log.error("record video cannot parse packet data " + packet);
}
} catch (Exception e) {
......@@ -1109,7 +1156,6 @@ public class PluginImpl implements Plugin, PropertyEventListener
this.user = user;
this.focusName = focusName;
this.sequenceNumberingViolated = Boolean.valueOf(false);
this.vp8Accumulator = new Vp8Accumulator();
this.lastSequenceNumber = Integer.valueOf(-1);
}
/**
......@@ -1179,18 +1225,10 @@ public class PluginImpl implements Plugin, PropertyEventListener
{
recorder.done();
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
......@@ -1419,7 +1457,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
{
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();
......@@ -1434,11 +1472,17 @@ public class PluginImpl implements Plugin, PropertyEventListener
Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri");
conferenceIq.addAttribute("id", focusId);
Element audioContent = conferenceIq.addElement("content").addAttribute("name", "audio");
audioContent.addElement("channel").addAttribute("id", participant.audioChannelId).addAttribute("expire", "0");
if (participant.audioChannelId != null)
{
Element audioContent = conferenceIq.addElement("content").addAttribute("name", "audio");
audioContent.addElement("channel").addAttribute("id", participant.audioChannelId).addAttribute("expire", "0");
}
Element videoContent = conferenceIq.addElement("content").addAttribute("name", "video");
videoContent.addElement("channel").addAttribute("id", participant.videoChannelId).addAttribute("expire", "0");
if (participant.videoChannelId != null)
{
Element videoContent = conferenceIq.addElement("content").addAttribute("name", "video");
videoContent.addElement("channel").addAttribute("id", participant.videoChannelId).addAttribute("expire", "0");
}
router.route(iq);
......@@ -1987,6 +2031,5 @@ public class PluginImpl implements Plugin, PropertyEventListener
}
}
}
}
......@@ -38,6 +38,7 @@ public class Recorder extends Thread
private static final int BUFFER_SIZE = 16 * 1024;
private static String defaultRecordDirectory = ".";
private String recordPath;
private String recordDirectory;
private boolean recordRtp;
private boolean recordWebm;
private boolean recordAu;
......@@ -51,9 +52,10 @@ public class Recorder extends Thread
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.sampleRate = sampleRate;
this.channels = channels;
......@@ -123,6 +125,7 @@ public class Recorder extends Thread
track.TrackUID = new java.util.Random().nextLong();
track.TrackType = MatroskaDocType.track_video;
track.Name = "VP8";
track.CodecName = "VP8";
track.Language = "und";
track.CodecID = "V_VP8";
track.DefaultDuration = 0;
......@@ -276,6 +279,8 @@ public class Recorder extends Thread
}
public void done() {
Log.info("Recorder done...");
if (done) {
return;
}
......@@ -426,13 +431,13 @@ public class Recorder extends Thread
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;
mFW.endCluster();
}
Log.info("writeData start cluster " + d.timestamp);
//Log.info("writeData start cluster " + d.timestamp);
mFW.startCluster(d.timestamp);
}
......@@ -447,7 +452,7 @@ public class Recorder extends Thread
frame.Data = d.data;
mFW.addFrame(frame);
Log.info("writeData video " + d.data);
//Log.info("writeData video " + d.data);
} else {
bo.write(d.data, 0, d.length);
......@@ -519,4 +524,39 @@ public class Recorder extends Thread
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