/* * 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.gui.main.call.conference; import java.awt.*; import java.util.*; import java.util.List; import javax.swing.*; import net.java.sip.communicator.impl.gui.main.call.*; import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.desktoputil.TransparentPanel; import org.jitsi.util.swing.*; /** * Extends BasicConferenceCallPanel to implement a user interface * Component which depicts a CallConference with audio and * video and is contained in a CallPanel. * * @author Yana Stamcheva * @author Lyubomir Marinov */ public class VideoConferenceCallPanel extends BasicConferenceCallPanel { /** * The Logger used by the VideoConferenceCallPanel class * and its instances for logging output. */ private static final Logger logger = Logger.getLogger(VideoConferenceCallPanel.class); /** * The compile-time flag which indicates whether each video displayed by * VideoConferenceCallPanel is to be depicted with an associated * tool bar showing information and controls related to the (local or * remote) peer sending the respective video. */ private static final boolean SHOW_TOOLBARS = true; /** * The facility which aids this instance with the video-related information. */ private final UIVideoHandler2 uiVideoHandler; /** * The Observer which listens to {@link #uiVideoHandler} about * changes in the video-related information. */ private final Observer uiVideoHandlerObserver = new Observer() { public void update(Observable o, Object arg) { updateViewFromModel(); } }; /** * The VideoContainer which occupies this whole Component * and arranges the visual Components displaying the video * streaming between the local peer/user and the remote peer(s). */ private final VideoContainer videoContainer; /** * The set of visual Components displaying video streaming between * the local peer/user and the remote peers which are depicted by this * instance. */ private final Set videos = new HashSet(); /** * The thumbnail container. */ private final ThumbnailConferenceCallPanel thumbnailContainer; private final JPanel thumbnailPanel; /** * Initializes a new VideoConferenceCallPanel instance which is to * be used by a specific CallPanel to depict a specific * CallConference. The new instance will depict both the * audio-related and the video-related information. * * @param callPanel the CallPanel which will use the new instance * to depict the specified CallConference. * @param callConference the CallConference to be depicted by the * new instance * @param uiVideoHandler the utility which is to aid the new instance in * dealing with the video-related information */ public VideoConferenceCallPanel( CallPanel callPanel, CallConference callConference, UIVideoHandler2 uiVideoHandler) { super(callPanel, callConference); this.uiVideoHandler = uiVideoHandler; thumbnailPanel = new JPanel(new BorderLayout()); thumbnailContainer = new ThumbnailConferenceCallPanel( callPanel, callConference, uiVideoHandler); videoContainer = createVideoContainer(); /* * Our user interface hierarchy has been initialized so we are ready to * begin receiving events warranting updates of this view from its * model. */ uiVideoHandler.addObserver(uiVideoHandlerObserver); /* * Notify the super that this instance has completed its initialization * and the view that it implements is ready to be updated from the * model. */ initializeComplete(); } private void addConferenceMemberContainers( ConferenceParticipantContainer cpc) { List cmcs = cpc.conferenceMemberContainers; if ((cmcs != null) && !cmcs.isEmpty()) { for (ConferenceParticipantContainer cmc : cmcs) { if (!cmc.toBeRemoved) { videoContainer.add( cmc.getComponent(), VideoLayout.CENTER_REMOTE); } } } } private Component createDefaultPhotoPanel(Call call) { OperationSetServerStoredAccountInfo accountInfo = call.getProtocolProvider().getOperationSet( OperationSetServerStoredAccountInfo.class); ImageIcon photoLabelIcon = null; if (accountInfo != null) { byte[] accountImage = AccountInfoUtils.getImage(accountInfo); // do not set empty images if ((accountImage != null) && (accountImage.length > 0)) photoLabelIcon = new ImageIcon(accountImage); } if (photoLabelIcon == null) { photoLabelIcon = new ImageIcon( ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)); } return createDefaultPhotoPanel(photoLabelIcon); } private Component createDefaultPhotoPanel(CallPeer callPeer) { byte[] peerImage = CallManager.getPeerImage(callPeer); ImageIcon photoLabelIcon = (peerImage == null) ? new ImageIcon( ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)) : new ImageIcon(peerImage); return createDefaultPhotoPanel(photoLabelIcon); } private Component createDefaultPhotoPanel(ConferenceMember conferenceMember) { return createDefaultPhotoPanel( new ImageIcon( ImageLoader.getImage( ImageLoader.DEFAULT_USER_PHOTO))); } /** * Creates a new Component which is to display a specific * ImageIcon representing the photo of a participant in a call. * * @param photoLabelIcon the ImageIcon which represents the photo * of a participant in a call and which is to be displayed by the new * Component * @return a new Component which displays the specified * photoLabelIcon */ private Component createDefaultPhotoPanel(ImageIcon photoLabelIcon) { JLabel photoLabel = new JLabel(); photoLabel.setIcon(photoLabelIcon); @SuppressWarnings("serial") JPanel photoPanel = new TransparentPanel(new GridBagLayout()) { /** * @{inheritDoc} */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); g = g.create(); try { AntialiasingManager.activateAntialiasing(g); g.setColor(Color.GRAY); g.fillRoundRect( 0, 0, this.getWidth(), this.getHeight(), 6, 6); } finally { g.dispose(); } } }; photoPanel.setPreferredSize(new Dimension(320, 240)); GridBagConstraints photoPanelConstraints = new GridBagConstraints(); photoPanelConstraints.anchor = GridBagConstraints.CENTER; photoPanelConstraints.fill = GridBagConstraints.NONE; photoPanel.add(photoLabel, photoPanelConstraints); return photoPanel; } /** * Initializes a new VideoContainer instance which is to contain * the visual/video Components of the telephony conference depicted * by this instance. */ private VideoContainer createVideoContainer() { VideoContainer videoContainer = new VideoContainer(null, true); thumbnailPanel.setBackground(Color.DARK_GRAY); thumbnailPanel.add(thumbnailContainer, BorderLayout.NORTH); add(thumbnailPanel, BorderLayout.EAST); add(videoContainer, BorderLayout.CENTER); return videoContainer; } /** * Shows/hides the participants thumbnails list. * * @param show true to show the participants list, false * to hide it */ public void showThumbnailsList(boolean show) { thumbnailPanel.setVisible(show); } /** * {@inheritDoc} */ @Override public void dispose() { try { uiVideoHandler.deleteObserver(uiVideoHandlerObserver); } finally { super.dispose(); } } /** * Determines whether a specific ConferenceMember represents the * same conference participant as a specific CallPeer. If the * specified conferenceMember is null, returns * true. Otherwise, determines whether the addresses of the * specified conferenceMember and the specified callPeer * identify one and the same entity. * * @param conferenceMember the ConferenceMember to be checked * whether is represents the same conference participant as the specified * callPeer. If it is null, true is returned. * @param callPeer the CallPeer to be checked whether it represents * the same conference participant as the specified * conferenceMember * @return true if the specified conferenceMember and the * specified callPeer represent the same conference participant or * the specified conferenceMember is null; otherwise, * false */ private boolean isConferenceMemberCallPeer( ConferenceMember conferenceMember, CallPeer callPeer) { return (conferenceMember == null) ? true : CallManager.addressesAreEqual( conferenceMember.getAddress(), callPeer.getAddress()); } /** * Determines whether a specific ConferenceMember represents the * local peer/user. Since this instance depicts a whole telephony * conference, the local peer/user may be participating with multiple * Calls in it. The Calls may be through different * (local) accounts. That's why the implementation determines whether the * address of the specified conferenceMember identifies the address * of a (local) accounts involved in the telephony conference depicted by * this instance. * * @param conferenceMember the ConferenceMember to be checked * whether it represents the local peer/user * @return true if the specified conferenceMember * represents the local peer/user; otherwise, false */ private boolean isConferenceMemberLocalUser( ConferenceMember conferenceMember) { String address = conferenceMember.getAddress(); for (Call call : callConference.getCalls()) { if (CallManager.addressesAreEqual( address, call.getProtocolProvider().getAccountID() .getAccountAddress())) { return true; } } return false; } private void removeConferenceMemberContainers( ConferenceParticipantContainer cpc, boolean all) { List cmcs = cpc.conferenceMemberContainers; if ((cmcs != null) && !cmcs.isEmpty()) { Iterator i = cmcs.iterator(); while (i.hasNext()) { ConferenceParticipantContainer cmc = i.next(); if (all || cmc.toBeRemoved) { i.remove(); videoContainer.remove(cmc.getComponent()); cmc.dispose(); } } } } /** * Updates the ConferenceParticipantContainers which depict the * ConferenceMembers of the CallPeer depicted by a * specific ConferenceParticipantContainer. * * @param cpc the ConferenceParticipantContainer which depicts the * CallPeer whose ConferenceMembers are to be depicted * @param videos the visual Components displaying video streaming * from the remote peer (represented by cpc) to the local peer/user * @param videoTelephony the OperationSetVideoTelephony which * retrieved the specified videos from the CallPeer * depicted by cpc. While the CallPeer could be queried * for it, such a query would waste more resources at run time given that * the invoker has it already. */ private void updateConferenceMemberContainers( ConferenceParticipantContainer cpc, List videos, OperationSetVideoTelephony videoTelephony) { CallPeer callPeer = (CallPeer) cpc.getParticipant(); List cmcs = cpc.conferenceMemberContainers; /* * Invalidate all conferenceMemberContainers. Then validate which of * them are to remain and which of them are to really be removed * later on. */ if (cmcs != null) { for (ConferenceParticipantContainer cmc : cmcs) cmc.toBeRemoved = true; } /* * Depict the remote videos. They may or may not be associated with * ConferenceMembers so the ConferenceMembers which have no * associated videos will be depicted afterwards. */ if (videos != null) { Component video = cpc.getVideo(); for (Component conferenceMemberVideo : videos) { /* * One of the remote videos is already used to depict the * callPeer. */ if (conferenceMemberVideo == video) continue; boolean addNewConferenceParticipantContainer = true; ConferenceMember conferenceMember = videoTelephony.getConferenceMember( callPeer, conferenceMemberVideo); if (cmcs == null) { cmcs = new LinkedList(); cpc.conferenceMemberContainers = cmcs; } else { for (ConferenceParticipantContainer cmc : cmcs) { Object cmcParticipant = cmc.getParticipant(); if (conferenceMember == null) { if (cmcParticipant == callPeer) { Component cmcVideo = cmc.getVideo(); if (cmcVideo == null) { cmc.setVideo(conferenceMemberVideo); cmc.toBeRemoved = false; addNewConferenceParticipantContainer = false; break; } else if (cmcVideo == conferenceMemberVideo) { cmc.toBeRemoved = false; addNewConferenceParticipantContainer = false; break; } } } else if (cmcParticipant == conferenceMember) { cmc.setVideo(conferenceMemberVideo); cmc.toBeRemoved = false; addNewConferenceParticipantContainer = false; break; } } } if (addNewConferenceParticipantContainer) { ConferenceParticipantContainer cmc = (conferenceMember == null) ? new ConferenceParticipantContainer( callPeer, conferenceMemberVideo) : new ConferenceParticipantContainer( conferenceMember, conferenceMemberVideo); cmcs.add(cmc); } } } /* * Depict the ConferenceMembers which have not been depicted yet. * They have no associated videos. */ List conferenceMembers = callPeer.getConferenceMembers(); if (!conferenceMembers.isEmpty()) { if (cmcs == null) { cmcs = new LinkedList(); cpc.conferenceMemberContainers = cmcs; } for (ConferenceMember conferenceMember : conferenceMembers) { /* * If the callPeer reports itself as a ConferenceMember, then * we've already depicted it with cpc. */ if (isConferenceMemberCallPeer(conferenceMember, callPeer)) continue; /* * If the callPeer reports the local peer/user as a * ConferenceMember, then we've already depicted it. */ if (isConferenceMemberLocalUser(conferenceMember)) continue; boolean addNewConferenceParticipantContainer = true; for (ConferenceParticipantContainer cmc : cmcs) { if (cmc.getParticipant() == conferenceMember) { /* * It is possible to have a ConferenceMember who is * sending video but we just do not have the SSRC of * that video to associate the video with the * ConferenceMember. In such a case, we may be depicting * the ConferenceMember twice: once with video without a * ConferenceMember and once with a ConferenceMember * without video. This will surely be the case at the * time of this writing with non-focus participants in a * telephony conference hosted on a Jitsi Videobridge. * Such a display is undesirable. If the * conferenceMember is known to send video, we will not * display it until we associated it with a video. This * way, if a ConferenceMember is not sending video, we * will depict it and we can be sure that no video * without a ConferenceMember association will be * depicting it a second time. */ if (cmc.toBeRemoved && !conferenceMember .getVideoStatus() .allowsSending()) { cmc.setVideo(null); cmc.toBeRemoved = false; } addNewConferenceParticipantContainer = false; break; } } if (addNewConferenceParticipantContainer) { ConferenceParticipantContainer cmc = new ConferenceParticipantContainer( conferenceMember, null); cmcs.add(cmc); } } } if ((cmcs != null) && !cmcs.isEmpty()) { removeConferenceMemberContainers(cpc, false); /* * If cpc is already added to the user interface hierarchy of this * instance, then it was there before the update procedure and it * was determined to be appropriate to continue to depict its model. * Consequently, its Component will be neither added to (because it * was already added) nor removed from the user interface hierarchy * of this instance. That's why we have make sure that the * Components of its conferenceMemberContainers are also added to * the user interface. */ if (UIVideoHandler2.isAncestor(this, cpc.getComponent())) addConferenceMemberContainers(cpc); } } /** * {@inheritDoc} */ @Override protected ConferenceCallPeerRenderer updateViewFromModel( ConferenceCallPeerRenderer callPeerPanel, CallPeer callPeer) { if (callPeer == null) { /* * The local peer/user will be represented by a Call which has a * CallPeer who provides local video. However, if the user has * selected to hide the local video, the local peer/user will not be * represented at all. */ Component video = null; if (uiVideoHandler.isLocalVideoVisible()) { for (Call aCall : callConference.getCalls()) { Iterator callPeerIter = aCall.getCallPeers(); OperationSetVideoTelephony videoTelephony = aCall.getProtocolProvider().getOperationSet( OperationSetVideoTelephony.class); while (callPeerIter.hasNext()) { callPeer = callPeerIter.next(); if (videoTelephony != null) { try { video = videoTelephony.getLocalVisualComponent( callPeer); } catch (OperationFailedException ofe) { logger.error( "Failed to retrieve the local video" + " for display", ofe); } if (video != null) break; } } if (video != null) break; } } if (callPeer == null) callPeerPanel = null; else { Call call = callPeer.getCall(); if (callPeerPanel instanceof ConferenceParticipantContainer) { ConferenceParticipantContainer cpc = (ConferenceParticipantContainer) callPeerPanel; if (cpc.getParticipant() == call) cpc.setVideo(video); else callPeerPanel = null; } else callPeerPanel = null; if (callPeerPanel == null) { callPeerPanel = new ConferenceParticipantContainer(call, video); } } } else { /* * The specified callPeer will be represented by one of its remote * videos which is not associated with a ConferenceMember or is * associated with a ConferenceMember representing the callPeer * itself. */ OperationSetVideoTelephony videoTelephony = callPeer.getProtocolProvider().getOperationSet( OperationSetVideoTelephony.class); List videos = null; Component video = null; if (videoTelephony != null) { videos = videoTelephony.getVisualComponents(callPeer); if ((videos != null) && !videos.isEmpty()) { for (Component aVideo : videos) { ConferenceMember conferenceMember = videoTelephony.getConferenceMember( callPeer, aVideo); if (isConferenceMemberCallPeer( conferenceMember, callPeer)) { video = aVideo; break; } } } } ConferenceParticipantContainer cpc = null; if (callPeerPanel instanceof ConferenceParticipantContainer) { cpc = (ConferenceParticipantContainer) callPeerPanel; if (cpc.getParticipant() == callPeer) cpc.setVideo(video); else cpc = null; } if (cpc == null) cpc = new ConferenceParticipantContainer(callPeer, video); callPeerPanel = cpc; // Update the conferenceMemberContainers of the cpc. updateConferenceMemberContainers(cpc, videos, videoTelephony); } return callPeerPanel; } /** * {@inheritDoc} * * If {@link #SHOW_TOOLBARS} is false, disables the use of * ConferenceParticipantContainer. A reason for such a value of * SHOW_TOOLBARS may be that the functionality implemented in the * model may not fully support mapping of visual Components * displaying video to telephony conference participants (e.g. in telephony * conferences utilizing the Jitsi Videobridge server-side technology). In * such a case displays the videos only, does not map videos to participants * and does not display participants who do not have videos. */ @Override protected void updateViewFromModelInEventDispatchThread() { if (SHOW_TOOLBARS) { super.updateViewFromModelInEventDispatchThread(); return; } /* * Determine the set of visual Components displaying video streaming * between the local peer/user and the remote peers which are to be * depicted by this instance. */ Component localVideo = null; Set videos = new HashSet(); for (Call call : callConference.getCalls()) { OperationSetVideoTelephony videoTelephony = call.getProtocolProvider().getOperationSet( OperationSetVideoTelephony.class); if (videoTelephony == null) continue; Iterator callPeerIter = call.getCallPeers(); while (callPeerIter.hasNext()) { CallPeer callPeer = callPeerIter.next(); /* * TODO VideoConferenceCallPanel respects * UIVideoHandler2.isLocalVideoVisible() in order to react to * the associated button at the bottom of the CallPanel. * However, it does not add a close button on top of the local * video in contrast to OneToOneCallPeerPanel. Overall, the * result is questionable. */ if (uiVideoHandler.isLocalVideoVisible() && (localVideo == null)) { try { localVideo = videoTelephony.getLocalVisualComponent(callPeer); } catch (OperationFailedException ofe) { /* * We'll just try to get the local video through another * CallPeer then. */ } if (localVideo != null) videos.add(localVideo); } List callPeerRemoteVideos = videoTelephony.getVisualComponents(callPeer); videos.addAll(callPeerRemoteVideos); } } /* * Remove the Components of this view which are no longer present in the * model. */ Iterator thisVideoIter = this.videos.iterator(); while (thisVideoIter.hasNext()) { Component thisVideo = thisVideoIter.next(); if (!videos.contains(thisVideo)) { thisVideoIter.remove(); videoContainer.remove(thisVideo); } /* * If a video is known to be depicted by this view and is still * present in the model, then we could remove it from the set of * videos present in the model in order to prevent going through the * procedure of adding it to this view. However, we choose to play * on the safe side. */ } /* * Add the Components of the model which are not depicted by this view. */ for (Component video : videos) { if (!UIVideoHandler2.isAncestor(videoContainer, video)) { this.videos.add(video); videoContainer.add( video, (video == localVideo) ? VideoLayout.LOCAL : VideoLayout.CENTER_REMOTE); } } } @Override protected void viewForModelAdded( ConferenceCallPeerRenderer callPeerPanel, CallPeer callPeer) { videoContainer.add( callPeerPanel.getComponent(), VideoLayout.CENTER_REMOTE); if ((callPeer != null) && (callPeerPanel instanceof ConferenceParticipantContainer)) { addConferenceMemberContainers( (ConferenceParticipantContainer) callPeerPanel); } } @Override protected void viewForModelRemoved( ConferenceCallPeerRenderer callPeerPanel, CallPeer callPeer) { videoContainer.remove(callPeerPanel.getComponent()); if ((callPeer != null) && (callPeerPanel instanceof ConferenceParticipantContainer)) { removeConferenceMemberContainers( (ConferenceParticipantContainer) callPeerPanel, true); } } /** * Implements an AWT Component which contains the user interface * elements depicting a specific participant in the telephony conference * depicted by a VideoConferenceCallPanel. */ private class ConferenceParticipantContainer extends TransparentPanel implements ConferenceCallPeerRenderer { /** * The list of ConferenceParticipantContainers which represent * the ConferenceMembers of the participant represented by this * instance. Since a CallPeer may send the local peer/user * multiple videos without providing a way to associate a * ConferenceMember with each one of them, the list may contain * ConferenceParticipantContainers which do not represent a * specific ConferenceMember instance but rather a video sent * by a CallPeer to the local peer/user which looks like (in * the terms of VideoConferenceCallPanel) a member of a conference * organized by the CallPeer in question. *

* Implements a state which is private to * VideoConferenceCallPanel and is of no concern to * ConferenceParticipantContainer. *

*/ List conferenceMemberContainers; /** * The indicator which determines whether this instance is to be removed * because it has become out-of-date, obsolete, unnecessary. *

* Implements a state which is private to * VideoConferenceCallPanel and is of no concern to * ConferenceParticipantContainer. *

*/ boolean toBeRemoved; /** * The BasicConferenceParticipantPanel which is displayed at * the bottom of this instance, bellow the {@link #video} (i.e. * {@link #videoContainer}) and is referred to as the tool bar. */ private final BasicConferenceParticipantPanel toolBar; /** * The visual Component, if any, displaying video which is * depicted by this instance. */ private Component video; /** * The VideoContainer which lays out the video depicted by this * instance i.e. {@link #video}. */ private final VideoContainer videoContainer; /** * The CallPeer associated with this container, if it has been * created to represent a CallPeer. */ private CallPeer callPeer; /** * The conferenceMember associated with this container, if it * has been created to represent a conferenceMember. */ private ConferenceMember conferenceMember; /** * Indicates that this container contains information for the local * user. */ private boolean isLocalUser; /** * Initializes a new ConferenceParticipantContainer instance * which is to depict the local peer/user. * * @param call a Call which is to provide information about the * local peer/user * @param video the visual Component, if any, displaying the * video streaming from the local peer/user to the remote peer(s) */ public ConferenceParticipantContainer(Call call, Component video) { this( createDefaultPhotoPanel(call), video, new ConferencePeerPanel( VideoConferenceCallPanel.this, call, true), null, null, true); } public ConferenceParticipantContainer( CallPeer callPeer, Component video) { this( createDefaultPhotoPanel(callPeer), video, new ConferencePeerPanel( VideoConferenceCallPanel.this, callPeer, true), callPeer, null, false); } private ConferenceParticipantContainer( Component noVideo, Component video, BasicConferenceParticipantPanel toolBar, CallPeer callPeer, ConferenceMember conferenceMember, boolean isLocalUser) { super(new BorderLayout()); this.callPeer = callPeer; this.conferenceMember = conferenceMember; this.isLocalUser = isLocalUser; videoContainer = new VideoContainer(noVideo, false); add(videoContainer, BorderLayout.CENTER); this.toolBar = toolBar; if (this.toolBar != null) add(this.toolBar, BorderLayout.SOUTH); if (video != null) { setVideo(video); } else setVisible(false); } public ConferenceParticipantContainer( ConferenceMember conferenceMember, Component video) { this( createDefaultPhotoPanel(conferenceMember), video, new ConferenceMemberPanel( VideoConferenceCallPanel.this, conferenceMember, true), null, conferenceMember, false); } public void dispose() { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.dispose(); // Dispose of the conferenceMemberContainers if any. /* * XXX The field conferenceMemberContainers implements a state * private to VideoConferenceCallPanel which the latter makes sure * to access on the AWT event dispatching thread only. Since we are * going out of our way here to help VideoConferenceCallPanel, * ensure that the mentioned synchronization rule is not violated. */ CallManager.assertIsEventDispatchingThread(); if (conferenceMemberContainers != null) { for (ConferenceParticipantContainer cmc : conferenceMemberContainers) { cmc.dispose(); } } } public CallPanel getCallPanel() { return getCallRenderer().getCallContainer(); } public SwingCallRenderer getCallRenderer() { return VideoConferenceCallPanel.this; } public Component getComponent() { return this; } private ConferenceCallPeerRenderer getConferenceCallPeerRendererDelegate() { return (toolBar instanceof ConferenceCallPeerRenderer) ? (ConferenceCallPeerRenderer) toolBar : null; } /** * Gets the conference participant depicted by this instance. * * @return the conference participant depicted by this instance */ public Object getParticipant() { return (toolBar == null) ? null : toolBar.getParticipant(); } public Component getVideo() { return video; } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. Otherwise, returns false. */ public boolean isLocalVideoVisible() { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); return (delegate == null) ? false : delegate.isLocalVideoVisible(); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void printDTMFTone(char dtmfChar) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.printDTMFTone(dtmfChar); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void securityNegotiationStarted( CallPeerSecurityNegotiationStartedEvent ev) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.securityNegotiationStarted(ev); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void securityOff(CallPeerSecurityOffEvent ev) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.securityOff(ev); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void securityOn(CallPeerSecurityOnEvent ev) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.securityOn(ev); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void securityPending() { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.securityPending(); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void securityTimeout(CallPeerSecurityTimeoutEvent ev) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.securityTimeout(ev); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setErrorReason(String reason) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setErrorReason(reason); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setLocalVideoVisible(boolean visible) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setLocalVideoVisible(visible); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setMute(boolean mute) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setMute(mute); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setOnHold(boolean onHold) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setOnHold(onHold); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setPeerImage(byte[] image) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setPeerImage(image); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setPeerName(String name) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setPeerName(name); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setPeerState( CallPeerState oldState, CallPeerState newState, String stateString) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setPeerState(oldState, newState, stateString); } /** * {@inheritDoc} * * Delegates to the toolBar, if the latter implements * ConferenceCallPeerRenderer, because this instance is a * container only. */ public void setSecurityPanelVisible(boolean visible) { ConferenceCallPeerRenderer delegate = getConferenceCallPeerRendererDelegate(); if (delegate != null) delegate.setSecurityPanelVisible(visible); } /** * Sets the visual Component displaying the video associated * with the participant depicted by this instance. * * @param video the visual Component displaying video which is * to be associated with the participant depicted by this instance */ void setVideo(Component video) { if (this.video != video) { if (this.video != null) videoContainer.remove(this.video); this.video = video; if (this.video != null) { setVisible(true); videoContainer.add(this.video, VideoLayout.CENTER_REMOTE); } else setVisible(false); // Update thumbnails container according to video status. if (thumbnailContainer != null) { if (conferenceMember != null) thumbnailContainer .updateThumbnail(conferenceMember, (video != null)); else if (callPeer != null) thumbnailContainer .updateThumbnail(callPeer, (video != null)); else if (isLocalUser) thumbnailContainer .updateThumbnail((video != null)); } } } } }