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

org.jitsi.impl.neomedia.conference.AudioMixingPushBufferDataSource Maven / Gradle / Ivy

Go to download

libjitsi is an advanced Java media library for secure real-time audio/video communication

The newest version!
/*
 * 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.conference;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

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

import org.jitsi.impl.neomedia.control.*;
import org.jitsi.impl.neomedia.protocol.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.utils.logging.*;

/**
 * Represents a PushBufferDataSource which provides a single
 * PushBufferStream containing the result of the audio mixing of
 * DataSources.
 *
 * @author Lyubomir Marinov
 */
public class AudioMixingPushBufferDataSource
    extends PushBufferDataSource
    implements CaptureDevice,
               MuteDataSource,
               InbandDTMFDataSource
{

    /**
     * The Logger used by the AudioMixingPushBufferDataSource
     * class and its instances for logging output.
     */
    private static final Logger logger
        = Logger.getLogger(AudioMixingPushBufferDataSource.class);

    /**
     * The AudioMixer performing the audio mixing, managing the input
     * DataSources and pushing the data of this output
     * PushBufferDataSource.
     */
    final AudioMixer audioMixer;

    /**
     * The indicator which determines whether this DataSource is
     * connected.
     */
    private boolean connected;

    /**
     * The indicator which determines whether this DataSource is set
     * to transmit "silence" instead of the actual media.
     */
    private boolean mute = false;

    /**
     * The one and only PushBufferStream this
     * PushBufferDataSource provides to its clients and containing the
     * result of the audio mixing performed by audioMixer.
     */
    private AudioMixingPushBufferStream outStream;

    /**
     * The indicator which determines whether this DataSource is
     * started.
     */
    private boolean started;

    /**
     * The tones to send via inband DTMF, if not empty.
     */
    private final LinkedList tones
        = new LinkedList();

    /**
     * Initializes a new AudioMixingPushBufferDataSource instance which
     * gives access to the result of the audio mixing performed by a specific
     * AudioMixer.
     *
     * @param audioMixer the AudioMixer performing audio mixing,
     * managing the input DataSources and pushing the data of the new
     * output PushBufferDataSource
     */
    public AudioMixingPushBufferDataSource(AudioMixer audioMixer)
    {
        this.audioMixer = audioMixer;
    }

    /**
     * Adds a new inband DTMF tone to send.
     *
     * @param tone the DTMF tone to send.
     */
    public void addDTMF(DTMFInbandTone tone)
    {
        tones.add(tone);
    }

    /**
     * Adds a new input DataSource to be mixed by the associated
     * AudioMixer of this instance and to not have its audio
     * contributions included in the mixing output represented by this
     * DataSource.
     *
     * @param inDataSource a DataSource to be added for mixing to
     * the AudioMixer associate with this instance and to not have its
     * audio contributions included in the mixing output represented by this
     * DataSource
     */
    public void addInDataSource(DataSource inDataSource)
    {
        audioMixer.addInDataSource(inDataSource, this);
    }

    /**
     * Implements {@link DataSource#connect()}. Lets the AudioMixer
     * know that one of its output PushBufferDataSources has been
     * connected and marks this DataSource as connected.
     *
     * @throws IOException if the AudioMixer fails to connect
     */
    @Override
    public synchronized void connect()
        throws IOException
    {
        if (!connected)
        {
            audioMixer.connect();
            connected = true;
        }
    }

    /**
     * Implements {@link DataSource#disconnect()}. Marks this
     * DataSource as disconnected and notifies the AudioMixer
     * that one of its output PushBufferDataSources has been
     * disconnected.
     */
    @Override
    public synchronized void disconnect()
    {
        try
        {
            stop();
        }
        catch (IOException ioex)
        {
            throw new UndeclaredThrowableException(ioex);
        }

        if (connected)
        {
            outStream = null;
            connected = false;

            audioMixer.disconnect();
        }
    }

    /**
     * Gets the BufferControl available for this DataSource.
     * Delegates to the AudioMixer because this instance is just a
     * facet to it.
     *
     * @return the BufferControl available for this DataSource
     */
    private BufferControl getBufferControl()
    {
        return audioMixer.getBufferControl();
    }

    /**
     * Implements {@link CaptureDevice#getCaptureDeviceInfo()}. Delegates to the
     * associated AudioMixer because it knows which
     * CaptureDevice is being wrapped.
     *
     * @return the CaptureDeviceInfo of the CaptureDevice of
     * the AudioMixer
     */
    public CaptureDeviceInfo getCaptureDeviceInfo()
    {
        return audioMixer.getCaptureDeviceInfo();
    }

    /**
     * Implements {@link DataSource#getContentType()}. Delegates to the
     * associated AudioMixer because it manages the inputs and knows
     * their characteristics.
     *
     * @return a String value which represents the type of the content
     * being made available by this DataSource i.e. the associated
     * AudioMixer
     */
    @Override
    public String getContentType()
    {
        return audioMixer.getContentType();
    }

    /**
     * Implements {@link DataSource#getControl(String)}.
     *
     * @param controlType a String value which names the type of the
     * control of this instance to be retrieved
     * @return an Object which represents the control of this instance
     * with the specified type if such a control is available; otherwise,
     * null
     */
    @Override
    public Object getControl(String controlType)
    {
        return AbstractControls.getControl(this, controlType);
    }

    /**
     * Implements {@link DataSource#getControls()}. Gets an array of
     * Objects which represent the controls available for this
     * DataSource.
     *
     * @return an array of Objects which represent the controls
     * available for this DataSource
     */
    @Override
    public Object[] getControls()
    {
        BufferControl bufferControl = getBufferControl();
        FormatControl[] formatControls = getFormatControls();

        if (bufferControl == null)
            return formatControls;
        else if ((formatControls == null) || (formatControls.length < 1))
            return new Object[] { bufferControl };
        else
        {
            Object[] controls = new Object[1 + formatControls.length];

            controls[0] = bufferControl;
            System.arraycopy(
                    formatControls, 0,
                    controls, 1,
                    formatControls.length);
            return controls;
        }
    }

    /**
     * Implements {@link DataSource#getDuration()}. Delegates to the associated
     * AudioMixer because it manages the inputs and knows their
     * characteristics.
     *
     * @return a Time value which represents the duration of the media
     * being made available through this DataSource
     */
    @Override
    public Time getDuration()
    {
        return audioMixer.getDuration();
    }

    /**
     * Implements {@link CaptureDevice#getFormatControls()}. Delegates to the
     * associated AudioMixer because it knows which
     * CaptureDevice is being wrapped.
     *
     * @return an array of FormatControls of the CaptureDevice
     * of the associated AudioMixer
     */
    public FormatControl[] getFormatControls()
    {
        return audioMixer.getFormatControls();
    }

    /**
     * Gets the next inband DTMF tone signal.
     *
     * @param sampleRate The sampling frequency (codec clock rate) in Hz of the
     * stream which will encapsulate this signal.
     * @param sampleSizeInBits The size of each sample (8 for a byte, 16 for a
     * short and 32 for an int)
     * @return The data array containing the DTMF signal.
     */
    public short[] getNextToneSignal(double sampleRate, int sampleSizeInBits)
    {
        return tones.poll().getAudioSamples(sampleRate, sampleSizeInBits);
    }

    /**
     * Implements {@link PushBufferDataSource#getStreams()}. Gets a
     * PushBufferStream which reads data from the associated
     * AudioMixer and mixes its inputs.
     *
     * @return an array with a single PushBufferStream which reads data
     * from the associated AudioMixer and mixes its inputs if this
     * DataSource is connected; otherwise, an empty array
     */
    @Override
    public synchronized PushBufferStream[] getStreams()
    {
        if (connected && (outStream == null))
        {
            AudioMixerPushBufferStream audioMixerOutStream
                = audioMixer.getOutStream();

            if (audioMixerOutStream != null)
            {
                outStream
                    = new AudioMixingPushBufferStream(
                            audioMixerOutStream,
                            this);
                if (started)
                    try
                    {
                        outStream.start();
                    }
                    catch (IOException ioex)
                    {
                        logger.error(
                                "Failed to start "
                                    + outStream.getClass().getSimpleName()
                                    + " with hashCode " + outStream.hashCode(),
                                ioex);
                    }
            }
        }
        return
            (outStream == null)
                ? new PushBufferStream[0]
                : new PushBufferStream[] { outStream };
    }

    /**
     * Determines whether this DataSource is mute.
     *
     * @return true if this DataSource is mute; otherwise,
     *         false
     */
    public boolean isMute()
    {
        return mute;
    }

    /**
     * Determines whether this DataSource sends a DTMF tone.
     *
     * @return true if this DataSource is sending a DTMF tone;
     * otherwise, false.
     */
    public boolean isSendingDTMF()
    {
        return !tones.isEmpty();
    }

    /**
     * Sets the mute state of this DataSource.
     *
     * @param mute true to mute this DataSource; otherwise,
     *            false
     */
    public void setMute(boolean mute)
    {
        this.mute = mute;
    }

    /**
     * Implements {@link DataSource#start()}. Starts the output
     * PushBufferStream of this DataSource (if it exists) and
     * notifies the AudioMixer that one of its output
     * PushBufferDataSources has been started.
     *
     * @throws IOException if anything wrong happens while starting the output
     * PushBufferStream of this DataSource
     */
    @Override
    public synchronized void start()
        throws IOException
    {
        if (!started)
        {
            started = true;
            if (outStream != null)
                outStream.start();
        }
    }

    /**
     * Implements {@link DataSource#stop()}. Notifies the AudioMixer
     * that one of its output PushBufferDataSources has been stopped
     * and stops the output PushBufferStream of this
     * DataSource (if it exists).
     *
     * @throws IOException if anything wrong happens while stopping the output
     * PushBufferStream of this DataSource
     */
    @Override
    public synchronized void stop()
        throws IOException
    {
        if (started)
        {
            started = false;
            if (outStream != null)
                outStream.stop();
        }
    }

    /**
     * The input DataSource has been updated.
     *
     * @param inDataSource the DataSource that was updated.
     */
    public void updateInDataSource(DataSource inDataSource)
    {
        // just update the input streams
        audioMixer.getOutStream();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy