Commit 6d64e0e9 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Jitsi Videobridge plugin - updated to latest code in github

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13874 b35dd754-fafc-0310-a699-88a17e54d16e
parent 0605955f
/*
* Jitsi Videobridge, OpenSource video conferencing.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.videobridge;
import java.io.*;
import java.util.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
import javax.media.protocol.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.impl.neomedia.jmfext.media.protocol.*;
import org.jitsi.impl.neomedia.jmfext.media.renderer.audio.*;
/**
* Implements a <tt>CaptureDevice</tt> which provides silence in the form of
* audio media.
*
* @author Lyubomir Marinov
*/
public class AudioSilenceCaptureDevice
extends AbstractPushBufferCaptureDevice
{
/**
* The compile-time flag which determines whether
* <tt>AudioSilenceCaptureDevice</tt> and, more specifically,
* <tt>AudioSilenceStream</tt> are to be used by <tt>AudioMixer</tt> for the
* mere purposes of ticking the clock which makes <tt>AudioMixer</tt> read
* media from its inputs, mix it, and write it to its outputs. The preferred
* value is <tt>true</tt> because it causes the <tt>AudioMixer</tt> to not
* push media unless at least one <tt>Channel</tt> is receiving actual
* media.
*/
private static final boolean CLOCK_ONLY = true;
/**
* The interval of time in milliseconds between two consecutive ticks of the
* clock used by <tt>AudioSilenceCaptureDevice</tt> and, more specifically,
* <tt>AudioSilenceStream</tt>.
*/
private static final long CLOCK_TICK_INTERVAL = 20;
/**
* The list of <tt>Format</tt>s supported by the
* <tt>AudioSilenceCaptureDevice</tt> instances.
*/
private static final Format[] SUPPORTED_FORMATS
= new Format[]
{
new AudioFormat(
AudioFormat.LINEAR,
48000,
16,
1,
AudioFormat.LITTLE_ENDIAN,
AudioFormat.SIGNED,
Format.NOT_SPECIFIED,
Format.NOT_SPECIFIED,
Format.byteArray)
};
/**
* {@inheritDoc}
*
* Implements
* {@link AbstractPushBufferCaptureDevice#createStream(int, FormatControl)}.
*/
protected AudioSilenceStream createStream(
int streamIndex,
FormatControl formatControl)
{
return new AudioSilenceStream(this, formatControl);
}
/**
* {@inheritDoc}
*
* Overrides the super implementation in order to return the list of
* <tt>Format</tt>s hardcoded as supported in
* <tt>AudioSilenceCaptureDevice</tt> because the super looks them up by
* <tt>CaptureDeviceInfo</tt> and this instance does not have one.
*/
@Override
protected Format[] getSupportedFormats(int streamIndex)
{
return SUPPORTED_FORMATS.clone();
}
/**
* Implements a <tt>PushBufferStream</tt> which provides silence in the form
* of audio media.
*/
private static class AudioSilenceStream
extends AbstractPushBufferStream<AudioSilenceCaptureDevice>
implements Runnable
{
/**
* The indicator which determines whether {@link #start()} has been
* invoked on this instance without an intervening {@link #stop()}.
*/
private boolean started;
/**
* The <tt>Thread</tt> which pushes available media data out of this
* instance to its consumer i.e. <tt>BufferTransferHandler</tt>.
*/
private Thread thread;
/**
* Initializes a new <tt>AudioSilenceStream</tt> which is to be exposed
* by a specific <tt>AudioSilenceCaptureDevice</tt> and which is to have
* its <tt>Format</tt>-related information abstracted by a specific
* <tt>FormatControl</tt>.
*
* @param dataSource the <tt>AudioSilenceCaptureDevice</tt> which is
* initializing the new instance and which is to expose it in its array
* of <tt>PushBufferStream</tt>s
* @param formatControl the <tt>FormatControl</tt> which is to abstract
* the <tt>Format</tt>-related information of the new instance
*/
public AudioSilenceStream(
AudioSilenceCaptureDevice dataSource,
FormatControl formatControl)
{
super(dataSource, formatControl);
}
/**
* Reads available media data from this instance into a specific
* <tt>Buffer</tt>.
*
* @param buffer the <tt>Buffer</tt> to write the available media data
* into
* @throws IOException if an I/O error has prevented the reading of
* available media data from this instance into the specified
* <tt>buffer</tt>
*/
public void read(Buffer buffer)
throws IOException
{
if (CLOCK_ONLY)
{
buffer.setLength(0);
}
else
{
AudioFormat format = (AudioFormat) getFormat();
int frameSizeInBytes
= format.getChannels()
* (((int) format.getSampleRate()) / 50)
* (format.getSampleSizeInBits() / 8);
byte[] data
= AbstractCodec2.validateByteArraySize(
buffer,
frameSizeInBytes,
false);
Arrays.fill(data, 0, frameSizeInBytes, (byte) 0);
buffer.setFormat(format);
buffer.setLength(frameSizeInBytes);
buffer.setOffset(0);
}
}
/**
* Runs in {@link #thread} and pushes available media data out of this
* instance to its consumer i.e. <tt>BufferTransferHandler</tt>.
*/
public void run()
{
try
{
/*
* Make sure that the current thread which implements the actual
* ticking of the clock implemented by this instance uses a
* thread priority considered appropriate for audio processing.
*/
AbstractAudioRenderer.useAudioThreadPriority();
/*
* The method implements a clock which ticks at a certain and
* regular interval of time which is not affected by the
* duration of the execution of, for example, the invocation of
* BufferTransferHandler.transferData(PushBufferStream).
*
* XXX The implementation utilizes System.currentTimeMillis()
* and, consequently, may be broken by run-time adjustments to
* the system time.
*/
long tickTime = System.currentTimeMillis();
while (true)
{
long sleepInterval = tickTime - System.currentTimeMillis();
boolean tick = (sleepInterval <= 0);
if (tick)
{
/*
* The current thread has woken up just in time or too
* late for the next scheduled clock tick and,
* consequently, the clock should tick right now.
*/
tickTime += CLOCK_TICK_INTERVAL;
}
else
{
/*
* The current thread has woken up too early for the
* next scheduled clock tick and, consequently, it
* should sleep until the time of the next scheduled
* clock tick comes.
*/
try
{
Thread.sleep(sleepInterval);
}
catch (InterruptedException ie)
{
}
/*
* The clock will not tick and spurious wakeups will be
* handled. However, the current thread will first check
* whether it is still utilized by this
* AudioSilenceStream in order to not delay stop
* requests.
*/
}
synchronized (this)
{
/*
* If the current Thread is no longer utilized by this
* AudioSilenceStream, it no longer has the right to
* touch it. If this AudioSilenceStream has been
* stopped, the current Thread should stop as well.
*/
if ((thread != Thread.currentThread()) || !started)
break;
}
if (tick)
{
BufferTransferHandler transferHandler
= this.transferHandler;
if (transferHandler != null)
{
try
{
transferHandler.transferData(this);
}
catch (Throwable t)
{
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
else
{
// TODO Auto-generated method stub
}
}
}
}
}
}
finally
{
synchronized (this)
{
if (thread == Thread.currentThread())
{
thread = null;
started = false;
notifyAll();
}
}
}
}
/**
* Starts the transfer of media data from this instance.
*
* @throws IOException if an error has prevented the start of the
* transfer of media from this instance
*/
@Override
public synchronized void start()
throws IOException
{
if (thread == null)
{
String className = getClass().getName();
thread = new Thread(this, className);
thread.setDaemon(true);
boolean started = false;
try
{
thread.start();
started = true;
}
finally
{
this.started = started;
if (!started)
{
thread = null;
notifyAll();
throw new IOException("Failed to start " + className);
}
}
}
}
/**
* Stops the transfer of media data from this instance.
*
* @throws IOException if an error has prevented the stopping of the
* transfer of media from this instance
*/
@Override
public synchronized void stop()
throws IOException
{
this.started = false;
notifyAll();
boolean interrupted = false;
while (thread != null)
{
try
{
wait();
}
catch (InterruptedException ie)
{
interrupted = true;
}
}
if (interrupted)
Thread.currentThread().interrupt();
}
}
}
/*
* Jitsi Videobridge, OpenSource video conferencing.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.videobridge;
import javax.media.*;
import javax.media.protocol.*;
import org.jitsi.impl.neomedia.device.*;
import org.jitsi.service.neomedia.*;
/**
* Implements a <tt>MediaDevice</tt> which provides silence in the form of audio
* media and does not play back any (audio) media (because Jitsi Videobridge is
* a server-side technology).
*
* @author Lyubomir Marinov
*/
public class AudioSilenceMediaDevice
extends AudioMediaDeviceImpl
{
/**
* {@inheritDoc}
*
* Overrides the super implementation to initialize a <tt>CaptureDevice</tt>
* without asking FMJ to initialize one for a <tt>CaptureDeviceInfo</tt>.
*/
@Override
protected CaptureDevice createCaptureDevice()
{
return new AudioSilenceCaptureDevice();
}
/**
* {@inheritDoc}
*
* Overrides the super implementation to disable the very playback because
* Jitsi Videobridge is a server-side technology.
*/
@Override
protected Processor createPlayer(DataSource dataSource)
{
return null;
}
/**
* {@inheritDoc}
*
* Overrides the super implementation to initialize a
* <tt>MediaDeviceSession</tt> which disables the very playback because
* Jitsi Videobridge is a server-side technology.
*/
@Override
public MediaDeviceSession createSession()
{
return
new AudioMediaDeviceSession(this)
{
/**
* {@inheritDoc}
*
* Overrides the super implementation to disable the
* very playback because Jitsi Videobridge is a
* server-side technology.
*/
@Override
protected Player createPlayer(DataSource dataSource)
{
return null;
}
};
}
/**
* {@inheritDoc}
*
* Overrides the super implementation to always return
* {@link MediaDirection#SENDRECV} because this instance stands for a relay
* and because the super bases the <tt>MediaDirection</tt> on the
* <tt>CaptureDeviceInfo</tt> which this instance does not have.
*/
@Override
public MediaDirection getDirection()
{
return MediaDirection.SENDRECV;
}
}
/*
* Jitsi Videobridge, OpenSource video conferencing.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.videobridge;
import java.util.*;
import net.java.sip.communicator.impl.osgi.framework.launch.*;
import net.java.sip.communicator.impl.protocol.jabber.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import org.ice4j.stack.*;
import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.device.*;
import org.jitsi.service.configuration.*;
import org.jitsi.videobridge.util.*;
import org.jivesoftware.smack.provider.*;
import org.osgi.framework.*;
import org.osgi.framework.launch.*;
import org.osgi.framework.startlevel.*;
import org.xmpp.component.*;
import org.xmpp.packet.*;
/**
* Implements <tt>org.xmpp.component.Component</tt> to provide Jitsi Video
* Bridge as an internal Jabber component.
*
* @author Lyubomir Marinov
*/
public class ComponentImpl
extends AbstractComponent
implements BundleActivator
{
/**
* The locations of the OSGi bundles (or rather of the class files of their
* <tt>BundleActivator</tt> implementations) comprising Jitsi Video Bridge.
* An element of the <tt>BUNDLES</tt> array is an array of <tt>String</tt>s
* and represents an OSGi start level.
*/
private static final String[][] BUNDLES
= {
{
"net/java/sip/communicator/impl/libjitsi/LibJitsiActivator"
},
{
"net/java/sip/communicator/util/UtilActivator",
"net/java/sip/communicator/impl/fileaccess/FileAccessActivator"
},
{
"net/java/sip/communicator/impl/configuration/ConfigurationActivator"
},
{
"net/java/sip/communicator/impl/resources/ResourceManagementActivator"
},
{
"net/java/sip/communicator/util/dns/DnsUtilActivator"
},
{
"net/java/sip/communicator/impl/netaddr/NetaddrActivator"
},
{
"net/java/sip/communicator/impl/packetlogging/PacketLoggingActivator"
},
{
"net/java/sip/communicator/service/gui/internal/GuiServiceActivator"
},
{
"org/jitsi/videobridge/ComponentImplBundleActivator"
}
};
/**
* The (default) description of <tt>ComponentImpl</tt> instances.
*/
private static final String DESCRIPTION
= "Jitsi Video Bridge Jabber Component";
/**
* The (default) name of <tt>ComponentImpl</tt> instances.
*/
private static final String NAME = "JitsiVideobridge";
/**
* The (default) sub-domain of the address with which <tt>ComponentImpl</tt>
* instances are to be added to their respective <tt>ComponentManager</tt>s.
*/
public static final String SUBDOMAIN = "jitsi-videobridge";
/**
* The <tt>BundleContext</tt> in which this instance has been started as an
* OSGi bundle.
*/
private BundleContext bundleContext;
/**
* The <tt>org.osgi.framework.launch.Framework</tt> instance which
* represents the OSGi instance launched by this <tt>ComponentImpl</tt>.
*/
private Framework framework;
/**
* The <tt>Object</tt> which synchronizes the access to {@link #framework}.
*/
private final Object frameworkSyncRoot = new Object();
/**
* The <tt>Videobridge</tt> which creates, lists and destroys
* {@link Conference} instances and which is being represented as a Jabber
* component by this instance.
*/
private Videobridge videoBridge;
/**
* Initializes a new <tt>ComponentImpl</tt> instance.
*/
public ComponentImpl()
{
}
/**
* {@inheritDoc}
*
* Gets the namespaces of features that this <tt>Component</tt>
* offers/supports i.e. {@link ColibriConferenceIQ#NAMESPACE}.
*/
@Override
protected String[] discoInfoFeatureNamespaces()
{
return
new String[]
{
ColibriConferenceIQ.NAMESPACE,
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_DTLS_SRTP,
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_ICE_UDP_1,
ProtocolProviderServiceJabberImpl
.URN_XMPP_JINGLE_RAW_UDP_0
};
}
/**
* {@inheritDoc}
*
* Gets the type of the Service Discovery Identity of this
* <tt>Component</tt> i.e. &quot;conference&quot;.
*/
@Override
protected String discoInfoIdentityCategoryType()
{
return "conference";
}
/**
* Gets the OSGi <tt>BundleContext</tt> in which this Jabber component is
* executing.
*
* @return the OSGi <tt>BundleContext</tt> in which this Jabber component is
* executing
*/
public BundleContext getBundleContext()
{
return bundleContext;
}
/**
* Gets the description of this <tt>Component</tt>.
*
* @return the description of this <tt>Component</tt>
* @see Component#getDescription()
*/
public String getDescription()
{
return DESCRIPTION;
}
/**
* Gets the name of this <tt>Component</tt>.
*
* @return the name of this <tt>Component</tt>
* @see Component#getName()
*/
public String getName()
{
return NAME;
}
/**
* Handles a <tt>ColibriConferenceIQ</tt> stanza which represents a request.
*
* @param conferenceIQ the <tt>ColibriConferenceIQ</tt> stanza represents the
* request to handle
* @return an <tt>org.jivesoftware.smack.packet.IQ</tt> stanza which
* represents the response to the specified request or <tt>null</tt> to
* reply with <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
*/
private org.jivesoftware.smack.packet.IQ handleColibriConferenceIQ(
ColibriConferenceIQ conferenceIQ)
throws Exception
{
/*
* The presence of the id attribute in the conference element
* signals whether a new conference is to be created or an existing
* conference is to be modified.
*/
String id = conferenceIQ.getID();
String focus = conferenceIQ.getFrom();
Conference conference
= (id == null)
? videoBridge.createConference(focus)
: videoBridge.getConference(id, focus);
ColibriConferenceIQ responseConferenceIQ;
if (conference == null)
{
/*
* Possible reasons for having no Conference instance include
* failure to produce an ID which identifies an existing Conference
* instance or the JID of a conference focus which owns an existing
* Conference instance with a valid ID.
*/
responseConferenceIQ = null;
}
else
{
responseConferenceIQ = new ColibriConferenceIQ();
conference.describe(responseConferenceIQ);
for (ColibriConferenceIQ.Content contentIQ
: conferenceIQ.getContents())
{
/*
* The content element springs into existence whenever it
* gets mentioned, it does not need explicit creation (in
* contrast to the conference and channel elements).
*/
Content content
= conference.getOrCreateContent(contentIQ.getName());
if (content == null)
{
responseConferenceIQ = null;
}
else
{
ColibriConferenceIQ.Content responseContentIQ
= new ColibriConferenceIQ.Content(content.getName());
responseConferenceIQ.addContent(responseContentIQ);
for (ColibriConferenceIQ.Channel channelIQ
: contentIQ.getChannels())
{
String channelID = channelIQ.getID();
int channelExpire = channelIQ.getExpire();
Channel channel;
/*
* The presence of the id attribute in the channel
* element signals whether a new channel is to be
* created or an existing channel is to be modified.
*/
if (channelID == null)
{
/*
* An expire attribute in the channel element with
* value equal to zero requests the immediate
* expiration of the channel in question.
* Consequently, it does not make sense to have it
* in a channel allocation request.
*/
channel
= (channelExpire == 0)
? null
: content.createChannel();
}
else
{
channel = content.getChannel(channelID);
}
if (channel == null)
{
responseConferenceIQ = null;
}
else
{
if (channelExpire
!= ColibriConferenceIQ.Channel
.EXPIRE_NOT_SPECIFIED)
{
channel.setExpire(channelExpire);
/*
* If the request indicates that it wants
* the channel expired and the channel is
* indeed expired, then the request is valid
* and has correctly been acted upon.
*/
if ((channelExpire == 0)
&& channel.isExpired())
continue;
}
/*
* XXX The attribute initiator is optional. If a
* value is not specified, then the Channel
* initiator is to be assumed default or to not be
* changed.
*/
Boolean initiator = channelIQ.isInitiator();
if (initiator != null)
channel.setInitiator(initiator);
channel.setPayloadTypes(
channelIQ.getPayloadTypes());
channel.setTransport(channelIQ.getTransport());
channel.setDirection(channelIQ.getDirection());
/*
* Provide (a description of) the current state of
* the channel as part of the response.
*/
ColibriConferenceIQ.Channel responseChannelIQ
= new ColibriConferenceIQ.Channel();
channel.describe(responseChannelIQ);
responseContentIQ.addChannel(responseChannelIQ);
}
if (responseConferenceIQ == null)
break;
}
}
if (responseConferenceIQ == null)
break;
}
}
if (responseConferenceIQ != null)
{
responseConferenceIQ.setType(
org.jivesoftware.smack.packet.IQ.Type.RESULT);
}
return responseConferenceIQ;
}
/**
* Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>get</tt> or
* <tt>set</tt> which represents a request. Converts the specified
* <tt>org.xmpp.packet.IQ</tt> to an
* <tt>org.jivesoftware.smack.packet.IQ</tt> stanza, handles the Smack
* stanza via a call to {@link #handleIQ(org.jivesoftware.smack.packet.IQ)},
* converts the result Smack stanza to an <tt>org.xmpp.packet.iQ</tt> which
* is returned as the response to the request.
*
* @param iq the <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>get</tt> or
* <tt>set</tt> which represents the request to handle
* @return an <tt>org.xmpp.packet.IQ</tt> stanza which represents the
* response to the specified request or <tt>null</tt> to reply with
* <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
*/
private IQ handleIQ(IQ iq)
throws Exception
{
try
{
logd("RECV: " + iq.toXML());
org.jivesoftware.smack.packet.IQ smackIQ = IQUtils.convert(iq);
org.jivesoftware.smack.packet.IQ resultSmackIQ = handleIQ(smackIQ);
IQ resultIQ;
if (resultSmackIQ == null)
resultIQ = null;
else
{
resultIQ = IQUtils.convert(resultSmackIQ);
logd("SENT: " + resultIQ.toXML());
}
return resultIQ;
}
catch (Exception e)
{
loge(e);
throw e;
}
}
/**
* Handles an <tt>org.jivesoftware.smack.packet.IQ</tt> stanza of type
* <tt>get</tt> or <tt>set</tt> which represents a request.
*
* @param iq the <tt>org.jivesoftware.smack.packet.IQ</tt> stanza of type
* <tt>get</tt> or <tt>set</tt> which represents the request to handle
* @return an <tt>org.jivesoftware.smack.packet.IQ</tt> stanza which
* represents the response to the specified request or <tt>null</tt> to
* reply with <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
*/
private org.jivesoftware.smack.packet.IQ handleIQ(
org.jivesoftware.smack.packet.IQ iq)
throws Exception
{
org.jivesoftware.smack.packet.IQ resultIQ;
if (iq instanceof ColibriConferenceIQ)
{
resultIQ = handleColibriConferenceIQ((ColibriConferenceIQ) iq);
if (resultIQ != null)
{
resultIQ.setFrom(iq.getTo());
resultIQ.setPacketID(iq.getPacketID());
resultIQ.setTo(iq.getFrom());
}
}
else
resultIQ = null;
return resultIQ;
}
/**
* Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>get</tt> which
* represents a request.
*
* @param iq the <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>get</tt>
* which represents the request to handle
* @return an <tt>org.xmpp.packet.IQ</tt> stanza which represents the
* response to the specified request or <tt>null</tt> to reply with
* <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
* @see AbstractComponent#handleIQGet(IQ)
*/
@Override
protected IQ handleIQGet(IQ iq)
throws Exception
{
IQ resultIQ = handleIQ(iq);
return (resultIQ == null) ? super.handleIQGet(iq) : resultIQ;
}
/**
* Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt> which
* represents a request.
*
* @param iq the <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt>
* which represents the request to handle
* @return an <tt>org.xmpp.packet.IQ</tt> stanza which represents the
* response to the specified request or <tt>null</tt> to reply with
* <tt>feature-not-implemented</tt>
* @throws Exception to reply with <tt>internal-server-error</tt> to the
* specified request
* @see AbstractComponent#handleIQSet(IQ)
*/
@Override
protected IQ handleIQSet(IQ iq)
throws Exception
{
IQ resultIQ = handleIQ(iq);
return (resultIQ == null) ? super.handleIQSet(iq) : resultIQ;
}
/**
* Logs a specific <tt>String</tt> at debug level.
*
* @param s the <tt>String</tt> to log at debug level
*/
private static void logd(String s)
{
System.err.println(s);
}
/**
* Logs a specific <tt>Throwable</tt> at error level.
*
* @param t the <tt>Throwable</tt> to log at error level
*/
private static void loge(Throwable t)
{
t.printStackTrace(System.err);
}
/**
* Called as part of the execution of {@link AbstractComponent#shutdown()}
* to enable this <tt>Component</tt> to finish cleaning resources up after
* it gets completely shutdown.
*
* @see AbstractComponent#postComponentShutdown()
*/
@Override
public void postComponentShutdown()
{
super.postComponentShutdown();
stopOSGi();
}
/**
* Called as part of the execution of {@link AbstractComponent#start()} to
* enable this <tt>Component</tt> to finish initializing resources after it
* gets completely started.
*
* @see AbstractComponent#postComponentStart()
*/
@Override
public void postComponentStart()
{
super.postComponentStart();
String trueString = Boolean.toString(true);
/*
* The design at the time of this writing considers the configuration
* file read-only (in a read-only directory) and provides only manual
* editing for it.
*/
System.setProperty(
ConfigurationService.PNAME_CONFIGURATION_FILE_IS_READ_ONLY,
trueString);
// Jitsi Videobridge is a relay so it does not need to capture media.
System.setProperty(
MediaServiceImpl.DISABLE_AUDIO_SUPPORT_PNAME,
trueString);
System.setProperty(
MediaServiceImpl.DISABLE_VIDEO_SUPPORT_PNAME,
trueString);
// It makes no sense for Jitsi Videobridge to pace its RTP output.
if (System.getProperty(
DeviceConfiguration.PROP_VIDEO_RTP_PACING_THRESHOLD)
== null)
{
System.setProperty(
DeviceConfiguration.PROP_VIDEO_RTP_PACING_THRESHOLD,
Integer.toString(Integer.MAX_VALUE));
}
startOSGi();
}
/**
* Implements a helper to send <tt>org.jivesoftware.smack.packet.IQ</tt>s.
*
* @param iq the <tt>org.jivesoftware.smack.packet.IQ</tt> to send
* @throws Exception if an error occurs during the conversion of the
* specified <tt>iq</tt> to an <tt>org.xmpp.packet.IQ</tt> instance or while
* sending the specified <tt>iq</tt>
*/
void send(org.jivesoftware.smack.packet.IQ iq)
throws Exception
{
try
{
/*
* The javadoc on ComponentManager.sendPacket(Component,Packet)
* which is invoked by AbstractComponent.send(Packet) says that the
* value of the from property of the Packet must not be null;
* otherwise, an IllegalArgumentException will be thrown.
*/
String from = iq.getFrom();
if ((from == null) || (from.length() == 0))
{
JID fromJID = getJID();
if (fromJID != null)
iq.setFrom(fromJID.toString());
}
Packet packet = IQUtils.convert(iq);
send(packet);
logd("SENT: " + packet.toXML());
}
catch (Exception e)
{
loge(e);
throw e;
}
}
/**
* Starts this Jabber component implementation and the Jitsi Videobridge it
* provides in a specific OSGi <tt>BundleContext</tt>.
*
* @param bundleContext the OSGi <tt>BundleContext</tt> in which this Jabber
* component implementation and the Jitsi Videobridge it provides are to be
* started
* @throws Exception if anything irreversible goes wrong while starting this
* Jabber component implementation and the Jitsi Videobridge it provides in
* the specified <tt>bundleContext</tt>
*/
public void start(BundleContext bundleContext)
throws Exception
{
this.bundleContext = bundleContext;
ProviderManager providerManager = ProviderManager.getInstance();
// <conference>
providerManager.addIQProvider(
ColibriConferenceIQ.ELEMENT_NAME,
ColibriConferenceIQ.NAMESPACE,
new ColibriIQProvider());
// ICE-UDP <transport>
providerManager.addExtensionProvider(
IceUdpTransportPacketExtension.ELEMENT_NAME,
IceUdpTransportPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider
<IceUdpTransportPacketExtension>(
IceUdpTransportPacketExtension.class));
// Raw UDP <transport>
providerManager.addExtensionProvider(
RawUdpTransportPacketExtension.ELEMENT_NAME,
RawUdpTransportPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider
<RawUdpTransportPacketExtension>(
RawUdpTransportPacketExtension.class));
PacketExtensionProvider candidatePacketExtensionProvider
= new DefaultPacketExtensionProvider<CandidatePacketExtension>(
CandidatePacketExtension.class);
// ICE-UDP <candidate>
providerManager.addExtensionProvider(
CandidatePacketExtension.ELEMENT_NAME,
IceUdpTransportPacketExtension.NAMESPACE,
candidatePacketExtensionProvider);
// Raw UDP <candidate>
providerManager.addExtensionProvider(
CandidatePacketExtension.ELEMENT_NAME,
RawUdpTransportPacketExtension.NAMESPACE,
candidatePacketExtensionProvider);
// DTLS-SRTP <fingerprint>
providerManager.addExtensionProvider(
DtlsFingerprintPacketExtension.ELEMENT_NAME,
DtlsFingerprintPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider
<DtlsFingerprintPacketExtension>(
DtlsFingerprintPacketExtension.class));
// TODO Packet logging for ice4j is not supported at this time.
StunStack.setPacketLogger(null);
videoBridge = new Videobridge(this);
}
/**
* Starts the OSGi implementation and the Jitsi Videobridge bundles.
*/
private void startOSGi()
{
/*
* The documentation of AbstractComponent#start() says that it gets
* called once for each host that this Component connects to and that
* extending classes should take care to avoid double initialization.
*/
synchronized (frameworkSyncRoot)
{
if (this.framework != null)
return;
}
FrameworkFactory frameworkFactory = new FrameworkFactoryImpl();
Map<String, String> configuration = new HashMap<String, String>();
configuration.put(
Constants.FRAMEWORK_BEGINNING_STARTLEVEL,
Integer.toString(BUNDLES.length));
Framework framework = frameworkFactory.newFramework(configuration);
try
{
framework.init();
BundleContext bundleContext = framework.getBundleContext();
bundleContext.registerService(Component.class, this, null);
for (int startLevelMinus1 = 0;
startLevelMinus1 < BUNDLES.length;
startLevelMinus1++)
{
int startLevel = startLevelMinus1 + 1;
for (String location : BUNDLES[startLevelMinus1])
{
Bundle bundle = bundleContext.installBundle(location);
if (bundle != null)
{
BundleStartLevel bundleStartLevel
= bundle.adapt(BundleStartLevel.class);
if (bundleStartLevel != null)
bundleStartLevel.setStartLevel(startLevel);
}
}
}
framework.start();
}
catch (BundleException be)
{
throw new RuntimeException(be);
}
synchronized (frameworkSyncRoot)
{
this.framework = framework;
}
}
/**
* Stops this Jabber component implementation and the Jitsi Videobridge it
* provides in a specific OSGi <tt>BundleContext</tt>.
*
* @param bundleContext the OSGi <tt>BundleContext</tt> in which this Jabber
* component implementation and the Jitsi Videobridge it provides are to be
* stopped
* @throws Exception if anything irreversible goes wrong while stopping this
* Jabber component implementation and the Jitsi Videobridge it provides in
* the specified <tt>bundleContext</tt>
*/
public void stop(BundleContext bundleContext)
throws Exception
{
if (this.bundleContext == bundleContext)
this.bundleContext = null;
videoBridge = null;
}
/**
*
*
*/
public Videobridge getVideoBridge()
{
return videoBridge;
}
/**
* Stops the Jitsi Video Bridge bundles and the OSGi implementation.
*/
private void stopOSGi()
{
Framework framework;
synchronized (frameworkSyncRoot)
{
framework = this.framework;
this.framework = null;
}
if (framework != null)
{
try
{
framework.stop();
}
catch (BundleException be)
{
throw new RuntimeException(be);
}
}
}
}
...@@ -1298,8 +1298,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1298,8 +1298,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
{ {
broadcastSSRC(participant); broadcastSSRC(participant);
} }
}
} else Log.error("FocusAgent deliver cannot find iq owner " + id + "\n" + packet);
} else if (iq.getType() == IQ.Type.error) { } else if (iq.getType() == IQ.Type.error) {
......
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