org.red5.server.net.rtmpt.RTMPTConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.red5.server.net.rtmpt.RTMPTConnection.java

Source

package org.red5.server.net.rtmpt;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 *
 * Copyright (c) 2006-2007 by respective authors (see below). All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

import java.util.LinkedList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.ByteBuffer;
import org.red5.server.net.protocol.SimpleProtocolDecoder;
import org.red5.server.net.protocol.SimpleProtocolEncoder;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.RTMPHandler;
import org.red5.server.net.rtmp.codec.RTMP;
import org.red5.server.net.rtmp.message.Packet;
import org.red5.server.net.servlet.ServletUtils;

/**
 * A RTMPT client / session.
 *
 * @author The Red5 Project (red5@osflash.org)
 * @author Joachim Bauch (jojo@struktur.de)
 */

public class RTMPTConnection extends RTMPConnection {

    protected static Log log = LogFactory.getLog(RTMPTConnection.class.getName());

    /**
     * Start to increase the polling delay after this many empty results
     */
    protected static final long INCREASE_POLLING_DELAY_COUNT = 10;

    /**
     * Polling delay to start with.
     */
    protected static final byte INITIAL_POLLING_DELAY = 0;

    /**
     * Maximum polling delay.
     */
    protected static final byte MAX_POLLING_DELAY = 32;

    /**
     * RTMP protocol state
     */
    protected RTMP state;

    /**
     * Protocol decoder
     */
    protected SimpleProtocolDecoder decoder;

    /**
     * Protocol encoder
     */
    protected SimpleProtocolEncoder encoder;

    /**
     * RTMP events handler
     */
    protected RTMPHandler handler;
    /**
     * Byte buffer
     */
    protected ByteBuffer buffer;
    /**
     * List of pending messages
     */
    protected List<ByteBuffer> pendingMessages = new LinkedList<ByteBuffer>();
    /**
     * List of notification messages
     */
    protected List<Object> notifyMessages = new LinkedList<Object>();
    /**
     * Polling delay value
     */
    protected byte pollingDelay = INITIAL_POLLING_DELAY;
    /**
     * Timeframe without pending messages. If this time is greater then polling delay,
     * then polling delay increased
     */
    protected long noPendingMessages;
    /**
     * Number of read bytes
     */
    protected long readBytes;
    /**
     * Number of written bytes
     */
    protected long writtenBytes;
    /**
     * Closing flag
     */
    protected boolean closing;
    /**
     * Connection client id
     */
    protected int clientId;

    /** Constructs a new RTMPTConnection. */
    RTMPTConnection() {
        super(POLLING);
    }

    /**
      * Setter for RTMP events handler
      *
      * @param handler  Handler
      */
    void setRTMPTHandle(RTMPTHandler handler) {
        this.state = new RTMP(RTMP.MODE_SERVER);
        this.buffer = ByteBuffer.allocate(2048);
        this.buffer.setAutoExpand(true);
        this.handler = handler;
        this.decoder = handler.getCodecFactory().getSimpleDecoder();
        this.encoder = handler.getCodecFactory().getSimpleEncoder();
        // Use internal (Java) id of object to make guessing of client ids
        // more difficult.
        clientId = hashCode();
    }

    /** {@inheritDoc} */
    @Override
    public void close() {
        // Defer actual closing so we can send back pending messages to the client.
        closing = true;
    }

    /**
      * Getter for property 'closing'.
      *
      * @return Value for property 'closing'.
      */
    public boolean isClosing() {
        return closing;
    }

    /**
     * Real close
     */
    public void realClose() {
        if (!isClosing())
            return;

        if (buffer != null) {
            buffer.release();
            buffer = null;
        }
        notifyMessages.clear();
        state.setState(RTMP.STATE_DISCONNECTED);
        super.close();
        for (ByteBuffer buf : pendingMessages) {
            buf.release();
        }
        pendingMessages.clear();
    }

    /** {@inheritDoc} */
    @Override
    protected void onInactive() {
        close();
        realClose();
    }

    /**
      * Setter for servlet request.
      *
      * @param request  Servlet request
      */
    public void setServletRequest(HttpServletRequest request) {
        host = request.getLocalName();
        remoteAddress = request.getRemoteAddr();
        remoteAddresses = ServletUtils.getRemoteAddresses(request);
        remotePort = request.getRemotePort();
    }

    /**
     * Return the client id for this connection.
     *
     * @return the client id
     */
    public int getId() {
        return clientId;
    }

    /**
     * Return the current decoder state.
     *
     * @return the current decoder state.
     */
    public RTMP getState() {
        return this.state;
    }

    /**
     * Return the polling delay to use.
     *
     * @return the polling delay
     */
    public byte getPollingDelay() {
        if (state.getState() == RTMP.STATE_DISCONNECTED) {
            // Special value to notify client about a closed connection.
            return (byte) 0;
        }

        return (byte) (this.pollingDelay + 1);
    }

    /**
     * Decode data sent by the client.
     *
     * @param data
     *          the data to decode
     * @return a list of decoded objects
     */
    public List decode(ByteBuffer data) {
        readBytes += data.limit();
        this.buffer.put(data);
        this.buffer.flip();
        return this.decoder.decodeBuffer(this.state, this.buffer);
    }

    /**
     * Send RTMP packet down the connection.
     *
     * @param packet
     *          the packet to send
     */
    @Override
    public void write(Packet packet) {
        ByteBuffer data;
        try {
            data = this.encoder.encode(this.state, packet);
        } catch (Exception e) {
            log.error("Could not encode message " + packet, e);
            return;
        }

        // Mark packet as being written
        writingMessage(packet);

        // Enqueue encoded packet data to be sent to client
        rawWrite(data);

        // Make sure stream subsystem will be notified about sent packet later
        synchronized (this.notifyMessages) {
            this.notifyMessages.add(packet);
        }
    }

    /**
     * Send raw data down the connection.
     *
     * @param packet
     *          the buffer containing the raw data
     */
    @Override
    public void rawWrite(ByteBuffer packet) {
        synchronized (this.pendingMessages) {
            this.pendingMessages.add(packet);
        }
    }

    /**
     * Return any pending messages up to a given size.
     *
     * @param targetSize
     *          the size the resulting buffer should have
     * @return a buffer containing the data to send or null if no messages are
     *         pending
     */
    public ByteBuffer getPendingMessages(int targetSize) {
        if (this.pendingMessages.isEmpty()) {
            this.noPendingMessages += 1;
            if (this.noPendingMessages > INCREASE_POLLING_DELAY_COUNT) {
                if (this.pollingDelay == 0) {
                    this.pollingDelay = 1;
                }
                this.pollingDelay = (byte) (this.pollingDelay * 2);
                if (this.pollingDelay > MAX_POLLING_DELAY) {
                    this.pollingDelay = MAX_POLLING_DELAY;
                }
            }
            return null;
        }

        ByteBuffer result = ByteBuffer.allocate(2048);
        result.setAutoExpand(true);

        if (log.isDebugEnabled()) {
            log.debug("Returning " + this.pendingMessages.size() + " messages to client.");
        }
        this.noPendingMessages = 0;
        this.pollingDelay = INITIAL_POLLING_DELAY;
        while (result.limit() < targetSize) {
            if (this.pendingMessages.isEmpty()) {
                break;
            }

            synchronized (this.pendingMessages) {
                for (ByteBuffer buffer : this.pendingMessages) {
                    result.put(buffer);
                    buffer.release();
                }

                this.pendingMessages.clear();
            }

            // We'll have to create a copy here to avoid endless recursion
            List<Object> toNotify = new LinkedList<Object>();
            synchronized (this.notifyMessages) {
                toNotify.addAll(this.notifyMessages);
                this.notifyMessages.clear();
            }

            for (Object message : toNotify) {
                try {
                    handler.messageSent(this, message);
                } catch (Exception e) {
                    log.error("Could not notify stream subsystem about sent message.", e);
                    continue;
                }
            }
        }

        result.flip();
        writtenBytes += result.limit();
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public long getReadBytes() {
        return readBytes;
    }

    /** {@inheritDoc} */
    @Override
    public long getWrittenBytes() {
        return writtenBytes;
    }

    /** {@inheritDoc} */
    @Override
    public long getPendingMessages() {
        return pendingMessages.size();
    }

}