Package net.java.sip.communicator.impl.neomedia.protocol

Source Code of net.java.sip.communicator.impl.neomedia.protocol.RewritablePushBufferDataSource$MutePushBufferStream

/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.neomedia.protocol;

import java.io.*;
import java.nio.ByteBuffer; // disambiguation.
import java.nio.ByteOrder; // disambiguation.
import java.nio.IntBuffer; // disambiguation.
import java.util.*;

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

import net.java.sip.communicator.service.neomedia.*;

/**
* Implements a <tt>PushBufferDataSource</tt> wrapper which provides mute
* support for the wrapped instance.
* <p>
* Because the class wouldn't work for our use case without it,
* <tt>CaptureDevice</tt> is implemented and is being delegated to the wrapped
* <tt>DataSource</tt> (if it supports the interface in question).
* </p>
*
* @author Lyubomir Marinov
*/
public class RewritablePushBufferDataSource
    extends PushBufferDataSourceDelegate<PushBufferDataSource>
    implements MuteDataSource,
               InbandDTMFDataSource
{

    /**
     * The indicator which determines whether this <tt>DataSource</tt> is mute.
     */
    private boolean mute;

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

    /**
     * Initializes a new <tt>RewritablePushBufferDataSource</tt> instance which
     * is to provide mute support for a specific <tt>PushBufferDataSource</tt>.
     *
     * @param dataSource the <tt>PushBufferDataSource</tt> the new instance is
     *            to provide mute support for
     */
    public RewritablePushBufferDataSource(PushBufferDataSource dataSource)
    {
        super(dataSource);
    }

    /**
     * Implements {@link PushBufferDataSource#getStreams()}. Wraps the streams
     * of the wrapped <tt>PushBufferDataSource</tt> into
     * <tt>MutePushBufferStream</tt> instances in order to provide mute support
     * to them.
     *
     * @return an array of <tt>PushBufferStream</tt> instances with enabled mute
     * support
     */
    public PushBufferStream[] getStreams()
    {
        PushBufferStream[] streams = dataSource.getStreams();

        if (streams != null)
        {
            for (int streamIndex = 0;
                    streamIndex < streams.length;
                    streamIndex++)
            {
                PushBufferStream stream = streams[streamIndex];

                if (stream != null)
                    streams[streamIndex] = new MutePushBufferStream(stream);
            }
        }
        return streams;
    }

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

    /**
     * Replaces the media data contained in a specific <tt>Buffer</tt> with a
     * compatible representation of silence.
     *
     * @param buffer the <tt>Buffer</tt> the data contained in which is to be
     * replaced with silence
     */
    public static void mute(Buffer buffer)
    {
        Object data = buffer.getData();

        if (data != null)
        {
            Class<?> dataClass = data.getClass();
            final int fromIndex = buffer.getOffset();
            final int toIndex = fromIndex + buffer.getLength();

            if (Format.byteArray.equals(dataClass))
                Arrays.fill((byte[]) data, fromIndex, toIndex, (byte) 0);
            else if (Format.intArray.equals(dataClass))
                Arrays.fill((int[]) data, fromIndex, toIndex, 0);
            else if (Format.shortArray.equals(dataClass))
                Arrays.fill((short[]) data, fromIndex, toIndex, (short) 0);

            buffer.setData(data);
        }
    }

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

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

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

    /**
     * Replaces the media data contained in a specific <tt>Buffer</tt> with an
     * inband DTMF tone signal.
     *
     * @param buffer the <tt>Buffer</tt> the data contained in which is to be
     * replaced with the DTMF tone
     * @param tone the <tt>DMFTTone</tt> to send via inband DTMF signal.
     */
    public static void sendDTMF(
            Buffer buffer,
            DTMFInbandTone tone)
    {
        Object data = buffer.getData();

        // Send the inband DTMF tone only if the buffer contains audio data.
        if (data != null && (buffer.getFormat() instanceof AudioFormat))
        {
            Class<?> dataClass = data.getClass();
            int fromIndex = buffer.getOffset();

            AudioFormat audioFormat = (AudioFormat) buffer.getFormat();
            double samplingFrequency = audioFormat.getSampleRate();
            int sampleSizeInBits = audioFormat.getSampleSizeInBits();

            // Generates the inband DTMF signal.
            int[] sampleData = tone.getAudioSamples(
                    samplingFrequency,
                    sampleSizeInBits);
            IntBuffer.wrap(sampleData);

            int toIndex = fromIndex +
                sampleData.length * (sampleSizeInBits / 8);
            ByteBuffer newData = ByteBuffer.allocate(toIndex);

            // Prepares newData to be endian compliant with original buffer
            // data.
            if(audioFormat.getEndian() == AudioFormat.BIG_ENDIAN)
            {
                newData.order(ByteOrder.BIG_ENDIAN);
            }
            else
            {
                newData.order(ByteOrder.LITTLE_ENDIAN);
            }

            // Keeps data unchanged if storeed before the original buffer offset
            // index.
            // Takes care of original data array type (byte, short or int).
            if (Format.byteArray.equals(dataClass))
            {
                newData.put(((byte[]) data), 0, fromIndex);
            }
            else if (Format.shortArray.equals(dataClass))
            {
                for(int i = 0; i < fromIndex; ++i)
                {
                    newData.putShort(((short[]) data)[i]);
                }
            }
            else if (Format.intArray.equals(dataClass))
            {
                for(int i = 0; i < fromIndex; ++i)
                {
                    newData.putInt(((int[]) data)[i]);
                }
            }

            // Copies inband DTMF singal into newData.
            // Takes care of audio format encryption data type (byte, short or
            // int).
            switch (sampleSizeInBits)
            {
                case 8:
                    for(int i = 0; i < sampleData.length; ++i)
                    {
                        newData.put(((byte) sampleData[i]));
                    }
                    break;
                case 16:
                    for(int i = 0; i < sampleData.length; ++i)
                    {
                        newData.putShort(((short) sampleData[i]));
                    }
                    break;
                case 32:
                    for(int i = 0; i < sampleData.length; ++i)
                    {
                        newData.putInt(sampleData[i]);
                    }
                    break;
            }

            // Copies newData up to date into the original buffer.
            // Takes care of original data array type (byte, short or int).
            if (Format.byteArray.equals(dataClass))
            {
                buffer.setData(newData.array());
            }
            else if (Format.shortArray.equals(dataClass))
            {
                buffer.setData(newData.asShortBuffer().array());
            }
            else if (Format.intArray.equals(dataClass))
            {
                buffer.setData(newData.asIntBuffer().array());
            }

            // Updates the buffer length.
            buffer.setLength(toIndex - fromIndex);
        }
    }

    /**
     * Implements a <tt>PushBufferStream</tt> wrapper which provides mute
     * support for the wrapped instance.
     */
    private class MutePushBufferStream
        extends SourceStreamDelegate<PushBufferStream>
        implements PushBufferStream
    {

        /**
         * Initializes a new <tt>MutePushBufferStream</tt> instance which is to
         * provide mute support to a specific <tt>PushBufferStream</tt>.
         *
         * @param stream the <tt>PushBufferStream</tt> the new instance is to
         * provide mute support to
         */
        public MutePushBufferStream(PushBufferStream stream)
        {
            super(stream);
        }

        /**
         * Implements {@link PushBufferStream#getFormat()}. Delegates to the
         * wrapped <tt>PushBufferStream</tt>.
         *
         * @return the <tt>Format</tt> of the wrapped <tt>PushBufferStream</tt>
         */
        public Format getFormat()
        {
            return stream.getFormat();
        }

        /**
         * Implements {@link PushBufferStream#read(Buffer)}. If this instance is
         * muted (through its owning <tt>RewritablePushBufferDataSource</tt>),
         * overwrites the data read from the wrapped <tt>PushBufferStream</tt>
         * with silence data.
         *
         * @param buffer a <tt>Buffer</tt> in which the read data is to be
         * returned to the caller
         * @throws IOException if reading from the wrapped
         * <tt>PushBufferStream</tt> fails
         */
        public void read(Buffer buffer)
            throws IOException
        {
            stream.read(buffer);

            if (isSendingDTMF())
            {
                sendDTMF(buffer, tones.poll());
            }
            else if (isMute())
            {
                mute(buffer);
            }
        }

        /**
         * Implements
         * {@link PushBufferStream#setTransferHandler(BufferTransferHandler)}.
         * Sets up the hiding of the wrapped <tt>PushBufferStream</tt> from the
         * specified <tt>transferHandler</tt> and thus gives this
         * <tt>MutePushBufferStream</tt> full control when the
         * <tt>transferHandler</tt> in question starts calling to the stream
         * given to it in
         * <tt>BufferTransferHandler#transferData(PushBufferStream)</tt>.
         *
         * @param transferHandler a <tt>BufferTransferHandler</tt> to be
         * notified by this instance when data is available for reading from it
         */
        public void setTransferHandler(BufferTransferHandler transferHandler)
        {
            stream.setTransferHandler(
                (transferHandler == null)
                    ? null
                    : new StreamSubstituteBufferTransferHandler(
                            transferHandler,
                            stream,
                            this));
        }
    }
}
TOP

Related Classes of net.java.sip.communicator.impl.neomedia.protocol.RewritablePushBufferDataSource$MutePushBufferStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.