All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jitsi.util.event.VideoNotifierSupport Maven / Gradle / Ivy

/*
 * 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 org.jitsi.util.event;

import java.awt.*;
import java.util.*;
import java.util.List;
// disambiguation

/**
 * Represents a mechanism to easily add to a specific Object by means
 * of composition support for firing VideoEvents to
 * VideoListeners.
 *
 * @author Lyubomir Marinov
 */
public class VideoNotifierSupport
{
    private static final long THREAD_TIMEOUT = 5000;

    /**
     * The list of VideoEvents which are to be delivered to the
     * {@link #listeners} registered with this instance when
     * {@link #synchronous} is equal to false.
     */
    private final List events;

    /**
     * The list of VideoListeners interested in changes in the
     * availability of visual Components depicting video.
     */
    private final List listeners
        = new ArrayList();

    /**
     * The Object which is to be reported as the source of the
     * VideoEvents fired by this instance.
     */
    private final Object source;

    /**
     * The indicator which determines whether this instance delivers the
     * VideoEvents to the {@link #listeners} synchronously.
     */
    private final boolean synchronous;

    /**
     * The Thread in which {@link #events} are delivered to the
     * {@link #listeners} when {@link #synchronous} is equal to false.
     */
    private Thread thread;

    /**
     * Initializes a new VideoNotifierSupport instance which is to
     * facilitate the management of VideoListeners and firing
     * VideoEvents to them for a specific Object.
     *
     * @param source the Object which is to be reported as the source
     * of the VideoEvents fired by the new instance
     */
    public VideoNotifierSupport(Object source)
    {
        this(source, true);
    }

    /**
     * Initializes a new VideoNotifierSupport instance which is to
     * facilitate the management of VideoListeners and firing
     * VideoEvents to them for a specific Object.
     *
     * @param source the Object which is to be reported as the source
     * of the VideoEvents fired by the new instance
     * @param synchronous true if the new instance is to deliver the
     * VideoEvents synchronously; otherwise, false
     */
    public VideoNotifierSupport(Object source, boolean synchronous)
    {
        this.source = source;
        this.synchronous = synchronous;

        events = this.synchronous ? null : new LinkedList();
    }

    /**
     * Adds a specific VideoListener to this
     * VideoNotifierSupport in order to receive notifications when
     * visual/video Components are being added and removed.
     * 

* Adding a listener which has already been added does nothing i.e. it is * not added more than once and thus does not receive one and the same * VideoEvent multiple times. * * @param listener the VideoListener to be notified when * visual/video Components are being added or removed in this * VideoNotifierSupport */ public void addVideoListener(VideoListener listener) { if (listener == null) throw new NullPointerException("listener"); synchronized (listeners) { if (!listeners.contains(listener)) listeners.add(listener); } } protected void doFireVideoEvent(VideoEvent event) { VideoListener[] listeners; synchronized (this.listeners) { listeners = this.listeners.toArray( new VideoListener[this.listeners.size()]); } for (VideoListener listener : listeners) switch (event.getType()) { case VideoEvent.VIDEO_ADDED: listener.videoAdded(event); break; case VideoEvent.VIDEO_REMOVED: listener.videoRemoved(event); break; default: listener.videoUpdate(event); break; } } /** * Notifies the VideoListeners registered with this * VideoMediaStream about a specific type of change in the * availability of a specific visual Component depicting video. * * @param type the type of change as defined by VideoEvent in the * availability of the specified visual Component depicting video * @param visualComponent the visual Component depicting video * which has been added or removed * @param origin {@link VideoEvent#LOCAL} if the origin of the video is * local (e.g. it is being locally captured); {@link VideoEvent#REMOTE} if * the origin of the video is remote (e.g. a remote peer is streaming it) * @param wait true if the call is to wait till the specified * VideoEvent has been delivered to the VideoListeners; * otherwise, false * @return true if this event and, more specifically, the visual * Component it describes have been consumed and should be * considered owned, referenced (which is important because * Components belong to a single Container at a time); * otherwise, false */ public boolean fireVideoEvent( int type, Component visualComponent, int origin, boolean wait) { VideoEvent event = new VideoEvent(source, type, visualComponent, origin); fireVideoEvent(event, wait); return event.isConsumed(); } /** * Notifies the VideoListeners registered with this instance about * a specific VideoEvent. * * @param event the VideoEvent to be fired to the * VideoListeners registered with this instance * @param wait true if the call is to wait till the specified * VideoEvent has been delivered to the VideoListeners; * otherwise, false */ public void fireVideoEvent(VideoEvent event, boolean wait) { if (synchronous) doFireVideoEvent(event); else { synchronized (events) { events.add(event); if (thread == null) startThread(); else events.notify(); if (wait) { boolean interrupted = false; while (events.contains(event) && (thread != null)) { try { events.wait(); } catch (InterruptedException ie) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); } } } } /** * Removes a specific VideoListener from this * VideoNotifierSupport in order to have to no longer receive * notifications when visual/video Components are being added and * removed. * * @param listener the VideoListener to no longer be notified when * visual/video Components are being added or removed */ public void removeVideoListener(VideoListener listener) { synchronized (listeners) { listeners.remove(listener); } } private void runInThread() { while (true) { VideoEvent event = null; synchronized (events) { long emptyTime = -1; boolean interrupted = false; while (events.isEmpty()) { if (emptyTime == -1) emptyTime = System.currentTimeMillis(); else { long newEmptyTime = System.currentTimeMillis(); if ((newEmptyTime - emptyTime) >= THREAD_TIMEOUT) { events.notify(); return; } } try { events.wait(THREAD_TIMEOUT); } catch (InterruptedException ie) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); event = events.remove(0); } if (event != null) { try { doFireVideoEvent(event); } catch (Throwable t) { if (t instanceof ThreadDeath) throw (ThreadDeath) t; } synchronized (events) { events.notify(); } } } } private void startThread() { thread = new Thread("VideoNotifierSupportThread") { @Override public void run() { try { runInThread(); } finally { synchronized (events) { if (Thread.currentThread().equals(thread)) { thread = null; if (events.isEmpty()) events.notify(); else startThread(); } } } } }; thread.setDaemon(true); thread.start(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy