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

org.jitsi.impl.neomedia.device.VideoTranslatorMediaDevice 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.impl.neomedia.device;

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

import javax.media.*;
import javax.media.protocol.*;

import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.format.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.codec.*;
import org.jitsi.service.neomedia.device.*;
import org.jitsi.service.neomedia.format.*;
import org.jitsi.util.event.*;
import org.jitsi.utils.*;

/**
 * Implements a MediaDevice which is to be used in video conferencing
 * implemented with an RTP translator.
 *
 * @author Lyubomir Marinov
 * @author Hristo Terezov
 * @author Boris Grozev
 */
public class VideoTranslatorMediaDevice
    extends AbstractMediaDevice
    implements MediaDeviceWrapper,
        VideoListener
{
    /**
     * The MediaDevice which this instance enables to be used in a
     * video conference implemented with an RTP translator.
     */
    private final MediaDeviceImpl device;

    /**
     * The VideoMediaDeviceSession of {@link #device} the
     * outputDataSource of which is the captureDevice of
     * {@link #streamDeviceSessions}.
     */
    private VideoMediaDeviceSession deviceSession;

    /**
     * The MediaStreamMediaDeviceSessions sharing the
     * outputDataSource of {@link #device} as their
     * captureDevice.
     */
    private final List streamDeviceSessions
        = new LinkedList();

    /**
     * Initializes a new VideoTranslatorMediaDevice which enables a
     * specific MediaDevice to be used in video conferencing
     * implemented with an RTP translator.
     *
     * @param device the MediaDevice which the new instance is to
     * enable to be used in video conferencing implemented with an RTP
     * translator
     */
    public VideoTranslatorMediaDevice(MediaDeviceImpl device)
    {
        this.device = device;
    }

    /**
     * Releases the resources allocated by this instance in the course of its
     * execution and prepares it to be garbage collected when all
     * {@link #streamDeviceSessions} have been closed.
     *
     * @param streamDeviceSession the MediaStreamMediaDeviceSession
     * which has been closed
     */
    private synchronized void close(
            MediaStreamMediaDeviceSession streamDeviceSession)
    {
        streamDeviceSessions.remove(streamDeviceSession);
        if(deviceSession != null)
        {
            deviceSession.removeRTCPFeedbackMessageCreateListner(
                    streamDeviceSession);
        }
        if (streamDeviceSessions.isEmpty())
        {
            if(deviceSession != null)
            {
                deviceSession.removeVideoListener(this);
                deviceSession.close();
            }
            deviceSession = null;
        }
        else
            updateDeviceSessionStartedDirection();
    }

    /**
     * Creates a DataSource instance for this MediaDevice
     * which gives access to the captured media.
     *
     * @return a DataSource instance which gives access to the media
     * captured by this MediaDevice
     * @see AbstractMediaDevice#createOutputDataSource()
     */
    @Override
    protected synchronized DataSource createOutputDataSource()
    {
        if (deviceSession == null)
        {
            MediaFormatImpl format = null;
            MediaDirection startedDirection = MediaDirection.INACTIVE;

            for (MediaStreamMediaDeviceSession streamDeviceSession
                    : streamDeviceSessions)
            {
                MediaFormatImpl streamFormat
                    = streamDeviceSession.getFormat();

                if ((streamFormat != null) && (format == null))
                    format = streamFormat;
                startedDirection
                    = startedDirection.or(
                            streamDeviceSession.getStartedDirection());
            }

            MediaDeviceSession newDeviceSession = device.createSession();
            if(newDeviceSession instanceof VideoMediaDeviceSession)
            {
                deviceSession = (VideoMediaDeviceSession)newDeviceSession;
                deviceSession.addVideoListener(this);

                for (MediaStreamMediaDeviceSession streamDeviceSession
                        : streamDeviceSessions)
                {
                    deviceSession.addRTCPFeedbackMessageCreateListner(
                            streamDeviceSession);
                }
            }
            if (format != null)
                deviceSession.setFormat(format);

            deviceSession.start(startedDirection);
        }
        return
            (deviceSession == null)
                ? null
                : deviceSession.getOutputDataSource();
    }

    /**
     * Creates a new MediaDeviceSession instance which is to represent
     * the use of this MediaDevice by a MediaStream.
     *
     * @return a new MediaDeviceSession instance which is to represent
     * the use of this MediaDevice by a MediaStream
     * @see AbstractMediaDevice#createSession()
     */
    @Override
    public synchronized MediaDeviceSession createSession()
    {
        MediaStreamMediaDeviceSession streamDeviceSession
            = new MediaStreamMediaDeviceSession();

        streamDeviceSessions.add(streamDeviceSession);
        return streamDeviceSession;
    }

    /**
     * Returns the MediaDirection supported by this device.
     *
     * @return MediaDirection.SENDONLY if this is a read-only device,
     * MediaDirection.RECVONLY if this is a write-only device and
     * MediaDirection.SENDRECV if this MediaDevice can both
     * capture and render media
     * @see MediaDevice#getDirection()
     */
    public MediaDirection getDirection()
    {
        return device.getDirection();
    }

    /**
     * Returns the MediaFormat that this device is currently set to use
     * when capturing data.
     *
     * @return the MediaFormat that this device is currently set to
     * provide media in.
     * @see MediaDevice#getFormat()
     */
    public MediaFormat getFormat()
    {
        return device.getFormat();
    }

    /**
     * Returns the MediaType that this device supports.
     *
     * @return MediaType.AUDIO if this is an audio device or
     * MediaType.VIDEO in case of a video device
     * @see MediaDevice#getMediaType()
     */
    public MediaType getMediaType()
    {
        return device.getMediaType();
    }

    /**
     * Returns a list of MediaFormat instances representing the media
     * formats supported by this MediaDevice.
     *
     * @param localPreset the preset used to set the send format parameters,
     * used for video and settings
     * @param remotePreset the preset used to set the receive format parameters,
     * used for video and settings
     * @return the list of MediaFormats supported by this device
     * @see MediaDevice#getSupportedFormats(QualityPreset, QualityPreset)
     */
    public List getSupportedFormats(
            QualityPreset localPreset,
            QualityPreset remotePreset)
    {
        return device.getSupportedFormats(localPreset, remotePreset);
    }

    /**
     * Returns a list of MediaFormat instances representing the media
     * formats supported by this MediaDevice and enabled in
     * encodingConfiguration..
     *
     * @param localPreset the preset used to set the send format parameters,
     * used for video and settings
     * @param remotePreset the preset used to set the receive format parameters,
     * used for video and settings
     * @param encodingConfiguration the EncodingConfiguration instance
     * to use
     * @return the list of MediaFormats supported by this device
     * and enabled in encodingConfiguration.
     * @see MediaDevice#getSupportedFormats(QualityPreset, QualityPreset,
     * EncodingConfiguration)
     */
    public List getSupportedFormats(
            QualityPreset localPreset,
            QualityPreset remotePreset,
            EncodingConfiguration encodingConfiguration)
    {
        return device.getSupportedFormats(localPreset,
                remotePreset,
                encodingConfiguration);
    }

    /**
     * Gets the actual MediaDevice which this MediaDevice is
     * effectively built on top of and forwarding to.
     *
     * @return the actual MediaDevice which this MediaDevice
     * is effectively built on top of and forwarding to
     * @see MediaDeviceWrapper#getWrappedDevice()
     */
    public MediaDevice getWrappedDevice()
    {
        return device;
    }

    /**
     * Updates the value of the startedDirection property of
     * {@link #deviceSession} to be in accord with the values of the property
     * of {@link #streamDeviceSessions}.
     */
    private synchronized void updateDeviceSessionStartedDirection()
    {
        if (deviceSession == null)
            return;

        MediaDirection startDirection = MediaDirection.INACTIVE;

        for (MediaStreamMediaDeviceSession streamDeviceSession
                : streamDeviceSessions)
        {
            startDirection
                = startDirection.or(streamDeviceSession.getStartedDirection());
        }
        deviceSession.start(startDirection);

        MediaDirection stopDirection = MediaDirection.INACTIVE;

        if (!startDirection.allowsReceiving())
            stopDirection = stopDirection.or(MediaDirection.RECVONLY);
        if (!startDirection.allowsSending())
            stopDirection = stopDirection.or(MediaDirection.SENDONLY);
        deviceSession.stop(stopDirection);
    }

    /**
     * {@inheritDoc}
     *
     * Forwards event, to each of the managed
     * MediaStreamMediaDeviceSession instances. The event is expected
     * to come from this.deviceSession, since this is
     * registered there as a VideoListener.
     */
    @Override
    public void videoAdded(VideoEvent event)
    {
        for (MediaStreamMediaDeviceSession sds : streamDeviceSessions)
        {
            sds.fireVideoEvent(event, false);
        }
    }

    /**
     * {@inheritDoc}
     *
     * Forwards event, to each of the managed
     * MediaStreamMediaDeviceSession instances. The event is expected
     * to come from this.deviceSession, since this is
     * registered there as a VideoListener.
     */
    @Override
    public void videoRemoved(VideoEvent event)
    {
        for (MediaStreamMediaDeviceSession sds : streamDeviceSessions)
        {
            sds.fireVideoEvent(event, false);
        }
    }

    /**
     * {@inheritDoc}
     *
     * Forwards event, to each of the managed
     * MediaStreamMediaDeviceSession instances. The event is expected
     * to come from this.deviceSession, since this is
     * registered there as a VideoListener.
     */
    @Override
    public void videoUpdate(VideoEvent event)
    {
        for (MediaStreamMediaDeviceSession sds : streamDeviceSessions)
        {
            sds.fireVideoEvent(event, false);
        }
    }

    /**
     * Represents the use of this VideoTranslatorMediaDevice by a
     * MediaStream.
     */
    private class MediaStreamMediaDeviceSession
        extends VideoMediaDeviceSession
    {
        /**
         * Initializes a new MediaStreamMediaDeviceSession which is to
         * represent the use of this VideoTranslatorMediaDevice by a
         * MediaStream.
         */
        public MediaStreamMediaDeviceSession()
        {
            super(VideoTranslatorMediaDevice.this);
        }

        /**
         * Releases the resources allocated by this instance in the course of
         * its execution and prepares it to be garbage collected.
         */
        @Override
        public void close()
        {
            super.close();

            VideoTranslatorMediaDevice.this.close(this);
        }

        /**
         * Creates the DataSource that this instance is to read
         * captured media from.
         *
         * @return the DataSource that this instance is to read
         * captured media from
         * @see VideoMediaDeviceSession#createCaptureDevice()
         */
        @Override
        protected DataSource createCaptureDevice()
        {
            return VideoTranslatorMediaDevice.this.createOutputDataSource();
        }

        /**
         * Initializes a new Player instance which is to provide the
         * local visual/video Component. The new instance is
         * initialized to render the media of a specific DataSource.
         *
         * @param captureDevice the DataSource which is to have its
         * media rendered by the new instance as the local visual/video
         * Component
         * @return a new Player instance which is to provide the local
         * visual/video Component
         */
        @Override
        protected Player createLocalPlayer(DataSource captureDevice)
        {
            synchronized (VideoTranslatorMediaDevice.this)
            {
                if (deviceSession != null)
                    captureDevice = deviceSession.getCaptureDevice();
            }

            return super.createLocalPlayer(captureDevice);
        }

        /**
         * Initializes a new FMJ Processor which is to transcode
         * {@link #captureDevice} into the format of this instance.
         *
         * @return a new FMJ Processor which is to transcode
         * captureDevice into the format of this instance
         */
        @Override
        protected Processor createProcessor()
        {
            return null;
        }

        /**
         * Gets the output DataSource of this instance which provides
         * the captured (RTP) data to be sent by MediaStream to
         * MediaStreamTarget.
         *
         * @return the output DataSource of this instance which
         * provides the captured (RTP) data to be sent by MediaStream
         * to MediaStreamTarget
         * @see MediaDeviceSession#getOutputDataSource()
         */
        @Override
        public DataSource getOutputDataSource()
        {
            return getConnectedCaptureDevice();
        }

        /**
         * Sets the RTPConnector that will be used to initialize some
         * codec for RTCP feedback and adds the instance to
         * RTCPFeedbackCreateListners of deviceSession.
         *
         * @param rtpConnector the RTP connector
         */
        @Override
        public void setConnector(AbstractRTPConnector rtpConnector)
        {
            super.setConnector(rtpConnector);

            if(deviceSession != null)
                deviceSession.addRTCPFeedbackMessageCreateListner(this);
        }

        /**
         * Notifies this instance that the value of its
         * startedDirection property has changed from a specific
         * oldValue to a specific newValue.
         *
         * @param oldValue the MediaDirection which used to be the
         * value of the startedDirection property of this instance
         * @param newValue the MediaDirection which is the value of the
         * startedDirection property of this instance
         */
        @Override
        protected void startedDirectionChanged(
                MediaDirection oldValue,
                MediaDirection newValue)
        {
            super.startedDirectionChanged(oldValue, newValue);

            VideoTranslatorMediaDevice.this
                    .updateDeviceSessionStartedDirection();
        }

        /**
         * {@inheritDoc}
         * Returns the local visual Component for this
         * MediaStreamMediaDeviceSession, which, if present, is
         * maintained in this.deviceSession.
         */
        @Override
        public Component getLocalVisualComponent()
        {
            if (deviceSession != null)
                return deviceSession.getLocalVisualComponent();
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * Creates, if necessary, the local visual Component depicting
         * the video being streamed from the local peer to a remote peer. The
         * Component is provided by the single Player
         * instance, which is maintained for this
         * VideoTranslatorMediaDevice and is managed by
         * this.deviceSession.
         */
        @Override
        protected Component createLocalVisualComponent()
        {
            if (deviceSession != null)
                return deviceSession.createLocalVisualComponent();
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * Returns the Player instance which provides the local
         * visual/video Component. A single Player is
         * maintained for this VideoTranslatorMediaDevice, and it is
         * managed by this.deviceSession.
         */
        @Override
        protected Player getLocalPlayer()
        {
            if (deviceSession != null)
                return deviceSession.getLocalPlayer();
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * Does nothing, because there is no Player associated with
         * this MediaStreamMediaDeviceSession and therefore nothing to
         * dispose of.
         * @param player the Player to dispose of.
         */
        @Override
        protected void disposeLocalPlayer(Player player){}

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy