/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.protocol.jabber; import java.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.media.*; import net.java.sip.communicator.util.*; import org.jivesoftware.smack.*; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.packet.*; /** * Implements OperationSetVideoBridge for Jabber. * * @author Yana Stamcheva * @author Lyubomir Marinov */ public class OperationSetVideoBridgeImpl implements OperationSetVideoBridge, PacketFilter, PacketListener, RegistrationStateChangeListener { /** * The Logger used by the OperationSetVideoBridgeImpl * class and its instances for logging output. */ private static final Logger logger = Logger.getLogger(OperationSetVideoBridgeImpl.class); /** * The ProtocolProviderService implementation which initialized * this instance, owns it and is often referred to as its parent. */ private final ProtocolProviderServiceJabberImpl protocolProvider; /** * Creates an instance of OperationSetVideoBridgeImpl by * specifying the parent ProtocolProviderService announcing this * operation set. * * @param protocolProvider the parent Jabber protocol provider */ public OperationSetVideoBridgeImpl( ProtocolProviderServiceJabberImpl protocolProvider) { this.protocolProvider = protocolProvider; this.protocolProvider.addRegistrationStateChangeListener(this); } /** * Implements {@link PacketFilter}. Determines whether this instance is * interested in a specific {@link Packet}. * OperationSetVideoBridgeImpl returns true if the * specified packet is a {@link ColibriConferenceIQ}; otherwise, * false. * * @param packet the Packet to be determined whether this instance * is interested in it * @return true if the specified packet is a * ColibriConferenceIQ; otherwise, false */ public boolean accept(Packet packet) { return (packet instanceof ColibriConferenceIQ); } /** * Creates a conference call with the specified callees as call peers via a * video bridge provided by the parent Jabber provider. * * @param callees the list of addresses that we should call * @return the newly created conference call containing all CallPeers * @throws OperationFailedException if establishing the conference call * fails * @throws OperationNotSupportedException if the provider does not have any * conferencing features. */ public Call createConfCall(String[] callees) throws OperationFailedException, OperationNotSupportedException { return protocolProvider .getOperationSet(OperationSetTelephonyConferencing.class) .createConfCall( callees, new MediaAwareCallConference(true)); } /** * Invites the callee represented by the specified uri to an already * existing call using a video bridge provided by the parent Jabber provider. * The difference between this method and createConfCall is that * inviteCalleeToCall allows a user to add new peers to an already * established conference. * * @param uri the callee to invite to an existing conf call. * @param call the call that we should invite the callee to. * @return the CallPeer object corresponding to the callee represented by * the specified uri. * @throws OperationFailedException if inviting the specified callee to the * specified call fails * @throws OperationNotSupportedException if allowing additional callees to * a pre-established call is not supported. */ public CallPeer inviteCalleeToCall(String uri, Call call) throws OperationFailedException, OperationNotSupportedException { return protocolProvider .getOperationSet(OperationSetTelephonyConferencing.class) .inviteCalleeToCall(uri, call); } /** * Indicates if there's an active video bridge available at this moment. The * Jabber provider may announce support for video bridge, but it should not * be used for calling until it becomes actually active. * * @return true to indicate that there's currently an active * available video bridge, false - otherwise */ public boolean isActive() { String jitsiVideobridge = protocolProvider.getJitsiVideobridge(); return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0)); } /** * Notifies this instance that a specific ColibriConferenceIQ has * been received. * * @param conferenceIQ the ColibriConferenceIQ which has been * received */ private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ) { /* * The application is not a Jitsi Videobridge server, it is a client. * Consequently, the specified ColibriConferenceIQ is sent to it in * relation to the part of the application's functionality which makes * requests to a Jitsi Videobridge server i.e. CallJabberImpl. * * Additionally, the method processColibriConferenceIQ is presently tasked * with processing ColibriConferenceIQ requests only. They are SET IQs * sent by the Jitsi Videobridge server to notify the application about * updates in the states of (colibri) conferences organized by the * application. */ if (IQ.Type.SET.equals(conferenceIQ.getType()) && conferenceIQ.getID() != null) { OperationSetBasicTelephony basicTelephony = protocolProvider.getOperationSet( OperationSetBasicTelephony.class); if (basicTelephony != null) { Iterator i = basicTelephony.getActiveCalls(); while (i.hasNext()) { Call call = i.next(); if (call instanceof CallJabberImpl) { CallJabberImpl callJabberImpl = (CallJabberImpl) call; MediaAwareCallConference conference = callJabberImpl.getConference(); if ((conference != null) && conference.isJitsiVideobridge()) { /* * TODO We may want to disallow rogue CallJabberImpl * instances which may throw an exception to prevent * the conferenceIQ from reaching the CallJabberImpl * instance which it was meant for. */ if (callJabberImpl.processColibriConferenceIQ( conferenceIQ)) break; } } } } } } /** * Implements {@link PacketListener}. Notifies this instance that a specific * {@link Packet} (which this instance has already expressed interest into * by returning true from {@link #accept(Packet)}) has been * received. * * @param packet the Packet which has been received and which this * instance is given a chance to process */ public void processPacket(Packet packet) { /* * As we do elsewhere, acknowledge the receipt of the Packet first and * then go about our business with it. */ IQ iq = (IQ) packet; if (iq.getType() == IQ.Type.SET) protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq)); /* * Now that the acknowledging is out of the way, do go about our * business with the Packet. */ ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq; boolean interrupted = false; try { processColibriConferenceIQ(conferenceIQ); } catch (Throwable t) { logger.error( "An error occurred during the processing of a " + packet.getClass().getName() + " packet", t); if (t instanceof InterruptedException) { /* * We cleared the interrupted state of the current Thread by * catching the InterruptedException. However, we do not really * care whether the current Thread has been interrupted - we * caught the InterruptedException because we want to swallow * any Throwable. Consequently, we should better restore the * interrupted state. */ interrupted = true; } else if (t instanceof ThreadDeath) throw (ThreadDeath) t; } if (interrupted) Thread.currentThread().interrupt(); } /** * {@inheritDoc} * * Implements {@link RegistrationStateChangeListener}. Notifies this * instance that there has been a change in the RegistrationState * of {@link #protocolProvider}. Subscribes this instance to * {@link ColibriConferenceIQ}s as soon as protocolProvider is * registered and unsubscribes it as soon as protocolProvider is * unregistered. */ public void registrationStateChanged(RegistrationStateChangeEvent ev) { RegistrationState registrationState = ev.getNewState(); if (RegistrationState.REGISTERED.equals(registrationState)) { protocolProvider.getConnection().addPacketListener(this, this); } else if (RegistrationState.UNREGISTERED.equals(registrationState)) { Connection connection = protocolProvider.getConnection(); if (connection != null) connection.removePacketListener(this); } } }