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

net.sf.fmj.media.rtp.BasicJitterBufferBehaviour Maven / Gradle / Ivy

The newest version!
package net.sf.fmj.media.rtp;

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

import net.sf.fmj.media.*;

/**
 * Implements a basic JitterBufferBehaviour which is not adaptive, does
 * not perform buffering beyond the one performed by the associated
 * JitterBuffer and is agnostic of the Format of the received
 * media data. The implementation may be used by extenders to facilitate the
 * implementation of the JitterBufferBehaviour interface.
 *
 * @author Lyubomir Marinov
 */
class BasicJitterBufferBehaviour
    implements JitterBufferBehaviour
{
    /**
     * The RTP packet queue/jitter buffer which implements the storage of the
     * RTP packets added to and read from {@link #stream}.
     */
    protected final JitterBuffer q;

    /**
     * The value which has been applied by this instance with an invocation of
     * {@link RTPRawReceiver#setRecvBufSize(int)}.
     */
    private int recvBufSize;

    /**
     * The statistics related to the RTP packet queue/jitter buffer associated
     * with {@link #stream}.
     */
    protected final JitterBufferStats stats;

    /**
     * The RTPSourceStream which has initialized this instance.
     */
    protected final RTPSourceStream stream;

    /**
     * Initializes a new BasicJitterBufferBehaviour instance for the
     * purposes of a specific RTPSourceStream.
     *
     * @param stream the RTPSourceStream which has requested the
     * initialization of the new instance
     */
    protected BasicJitterBufferBehaviour(RTPSourceStream stream)
    {
        this.stream = stream;

        this.q = this.stream.q;
        this.stats = this.stream.stats;
    }

    /**
     * Removes the first element (the one with the least sequence number)
     * from fill and releases it to be reused (adds it to
     * free)
     */
    protected void dropFirstPkt()
    {
        q.dropFirstFill();
    }

    /**
     * Removes an element from the queue and releases it to be reused.
     */
    public void dropPkt()
    {
        dropFirstPkt();
    }

    /**
     * {@inheritDoc}
     *
     * BasicJitterBufferBehaviour implements a fixed jitter buffer and,
     * consequently, returns {@link #getMaximumDelay()}.
     */
    @Override
    public int getAbsoluteMaximumDelay()
    {
        return getMaximumDelay();
    }

    /**
     * Gets the BufferControl implementation set on the associated
     * RTPSourceStream. Provided as a convenience which delegates to
     * {@link RTPSourceStream#getBufferControl()}.
     *
     * @return the BufferControl implementation set on the associated
     * RTPSourceStream
     */
    protected BufferControl getBufferControl()
    {
        return stream.getBufferControl();
    }

    /**
     * {@inheritDoc}
     *
     * BasicJitterBufferBehaviour does not have a notion of RTP packet
     * duration and, consequently, returns 65535.
     */
    @Override
    public int getMaximumDelay()
    {
        return 65535;
    }

    /**
     * {@inheritDoc}
     *
     * BasicJitterBufferBehaviour does not have a notion of RTP packet
     * duration and, consequently, returns 0.
     */
    @Override
    public int getNominalDelay()
    {
        return 0;
    }

    /**
     * Grows {@link #q} to a specific capacity.
     *
     * @param capacity the capacity to set on q
     * @throws IllegalArgumentException if the specified capacity is
     * less than the capacity of q
     */
    protected void grow(int capacity)
    {
        if (capacity < 1)
            throw new IllegalArgumentException("capacity");

        int qCapacity = q.getCapacity();

        if (capacity == qCapacity)
            return;
        if (capacity < qCapacity)
            throw new IllegalArgumentException("capacity");

        Log.info("Growing packet queue to " + capacity);
        stats.incrementNbGrow();
        q.setCapacity(capacity);
    }

    /**
     * {@inheritDoc}
     *
     * BasicJitterBufferBehaviour always returns false to
     * indicate that it implements a fixed jitter buffer/RTP packet queue.
     */
    public boolean isAdaptive()
    {
        return false;
    }

    /**
     * Allows extenders to adapt the size/capacity of the associated RTP packet
     * queue/JitterBuffer after a specific Buffer is received
     * and before it is added to the JitterBuffer.
     *
     * @param buffer the Buffer which has been received and is to be
     * added (after the method returns)
     * @return the approximate length in packets of the buffering performed by
     * this JitterBufferBehaviour and the associated
     * JitterBuffer. BasicJitterBufferBehaviour always returns
     * 0.
     */
    protected int monitorQSize(Buffer buffer)
    {
        return 0;
    }

    /**
     * {@inheritDoc}
     *
     * Maintains an average approximation of the size in bytes of an RTP packet
     * in the JitterBufferStats of the associated
     * RTPSourceStream and updates the recvBufSize of the
     * specified rtprawreceiver.
     */
    public boolean preAdd(Buffer buffer, RTPRawReceiver rtprawreceiver)
    {
        stats.updateSizePerPacket(buffer);

        int aprxBufferLengthInPkts = monitorQSize(buffer);

        if (aprxBufferLengthInPkts > 0)
            setRecvBufSize(rtprawreceiver, aprxBufferLengthInPkts);

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public void read(Buffer buffer)
    {
        if (q.getFillCount() == 0)
        {
            buffer.setDiscard(true);
        }
        else
        {
            Buffer bufferFromQueue = q.getFill();

            // Whatever follows, it sounds safer to return the bufferFromQueue
            // into the free pool eventually.
            try
            {
                // Copy the bufferFromQueue into the specified (output) buffer.
                Object bufferData = buffer.getData();
                Object bufferHeader = buffer.getHeader();

                buffer.copy(bufferFromQueue);
                bufferFromQueue.setData(bufferData);
                bufferFromQueue.setHeader(bufferHeader);
            }
            finally
            {
                q.returnFree(bufferFromQueue);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void reset()
    {
    }

    protected void setRecvBufSize(
            RTPRawReceiver rtprawreceiver,
            int aprxBufferLengthInPkts)
    {
        int sizePerPkt = stats.getSizePerPacket();

        // There was no comment and the variables did not use meaningful names
        // at the time the following code was initially written. Consequently,
        // it is not immediately obvious why it is necessary at all and it may
        // be hard to understand. A possible explanation may be that, since the
        // threshold value will force a delay with a specific duration/byte
        // size, we should better be able to hold on to that much in the socket
        // so that it does not throw the delayed data away.
        int aprxThresholdInBytes
            = (aprxBufferLengthInPkts * sizePerPkt) / 2;

        if ((rtprawreceiver != null)
                && (aprxThresholdInBytes > this.recvBufSize))
        {
            rtprawreceiver.setRecvBufSize(aprxThresholdInBytes);

            int recvBufSize = rtprawreceiver.getRecvBufSize();

            this.recvBufSize
                = (recvBufSize < aprxThresholdInBytes)
                    ? 0x7fffffff /* BufferControlImpl.NOT_SPECIFIED? */
                    : aprxThresholdInBytes;
            Log.comment(
                    "RTP socket receive buffer size: " + recvBufSize
                        + " bytes.\n");
        }
    }

    /**
     * {@inheritDoc}
     *
     * BasicJitterBufferBehaviour returns true if the
     * associated RTP packet queue/jitter buffer is empty; otherwise,
     * false
     */
    public boolean willReadBlock()
    {
        return q.noMoreFill();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy