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

org.red5.server.net.rtmpt.RTMPTConnection Maven / Gradle / Ivy

/*
 * RED5 Open Source Media Server - https://github.com/Red5/
 * 
 * Copyright 2006-2016 by respective authors (see below). All rights reserved.
 * 
 * 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.red5.server.net.rtmpt;

import java.util.concurrent.atomic.AtomicLong;

import javax.servlet.http.HttpServletRequest;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.DummySession;
import org.apache.mina.core.session.IoSession;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.codec.RTMP;
import org.red5.server.net.rtmp.message.Packet;
import org.red5.server.net.servlet.ServletUtils;
import org.slf4j.Logger;

/**
 * A RTMPT client / session.
 * 
 * @author The Red5 Project
 * @author Joachim Bauch ([email protected])
 * @author Paul Gregoire ([email protected])
 */
public class RTMPTConnection extends BaseRTMPTConnection {

    private static final Logger log = Red5LoggerFactory.getLogger(RTMPTConnection.class);

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

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

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

    /**
     * Polling delay value
     */
    private volatile byte pollingDelay = INITIAL_POLLING_DELAY;

    /**
     * Empty result counter, after reaching INCREASE_POLLING_DELAY_COUNT polling delay will increase
     */
    private volatile long noPendingMessages;

    /**
     * Servlet that created this connection.
     */
    private transient RTMPTServlet servlet;

    /**
     * Timestamp of last data received on the connection
     */
    private long tsLastDataReceived = 0;

    private AtomicLong lastBytesRead = new AtomicLong(0);

    private AtomicLong lastBytesWritten = new AtomicLong(0);

    private transient IoSession ioSession;

    /** Constructs a new RTMPTConnection */
    RTMPTConnection() {
        super(POLLING);
        // create a DummySession for the HTTP-based connection to allow our Mina based system happy
        ioSession = new DummySession();
        ioSession.setAttribute(RTMPConnection.RTMP_SESSION_ID, sessionId);
    }

    /**
     * Returns the IoSession. Note that this is a compatibility item and is not constructed by Mina.
     * 
     * @return ioSession
     */
    @Override
    public IoSession getIoSession() {
        return ioSession;
    }

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

    /** {@inheritDoc} */
    @Override
    public void close() {
        if (log.isDebugEnabled()) {
            log.debug("close {} state: {}", getSessionId(), RTMP.states[state.getState()]);
        }
        // ensure closing flag is set
        if (!isClosing()) {
            // flush by waiting x number of millis for the pending messages to be cleared
            if (!pendingOutMessages.isEmpty()) {
                try {
                    log.debug("Going to sleep to allow pending queue a chance to clear");
                    Thread.sleep(256L);
                } catch (InterruptedException e) {
					e.printStackTrace();
					Thread.currentThread().interrupt();
                }
            }
            // now close
            super.close();
            if (servlet != null) {
                servlet = null;
            }
            if (handler != null) {
                handler.connectionClosed(this);
            }
        }
    }

    /**
     * Received message object router.
     * 
     * @param message
     *            an IoBuffer or Packet
     */
    public void handleMessageReceived(Object message) {
        if (message instanceof Packet) {
            handleMessageReceived((Packet) message);
        } else {
            log.warn("Incoming type: {} cannot be handled here", message.getClass().getName());
        }
    }

    /** {@inheritDoc} */
    @Override
    public boolean isIdle() {
        boolean inActivityExceeded = false;
        long lastTS = getLastDataReceived();
        long now = System.currentTimeMillis();
        long tsDelta = now - lastTS;
        if (lastTS > 0 && tsDelta > maxInactivity) {
            inActivityExceeded = true;
        }
        return inActivityExceeded || (isReaderIdle() && isWriterIdle());
    }

    /** {@inheritDoc} */
    @Override
    public boolean isReaderIdle() {
        // get the current bytes read on the connection
        long currentBytes = getReadBytes();
        // get our last bytes read
        long previousBytes = lastBytesRead.get();
        if (currentBytes > previousBytes) {
            log.trace("Client (read) is not idle");
            // client has sent data since last check and thus is not dead. No
            // need to ping
            if (lastBytesRead.compareAndSet(previousBytes, currentBytes)) {
                return false;
            }
        }
        return true;
    }

    /** {@inheritDoc} */
    @Override
    public boolean isWriterIdle() {
        // get the current bytes written on the connection
        long currentBytes = getWrittenBytes();
        // get our last bytes written
        long previousBytes = lastBytesWritten.get();
        if (currentBytes > previousBytes) {
            log.trace("Client (write) is not idle");
            // server has sent data since last check
            if (lastBytesWritten.compareAndSet(previousBytes, currentBytes)) {
                return false;
            }
        }
        return true;
    }

    public void setRemoteAddress(String remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public void setRemotePort(int remotePort) {
        this.remotePort = remotePort;
    }

    /**
     * Set the servlet that created the connection.
     * 
     * @param servlet
     *            rtmp servlet
     */
    protected void setServlet(RTMPTServlet servlet) {
        this.servlet = servlet;
    }

    /**
     * Setter for servlet request.
     * 
     * @param request
     *            Servlet request
     */
    public void setServletRequest(HttpServletRequest request) {
        if (request.getLocalPort() == 80) {
            host = request.getLocalName();
        } else {
            host = String.format("%s:%d", request.getLocalName(), request.getLocalPort());
        }
        remoteAddress = request.getRemoteAddr();
        remoteAddresses = ServletUtils.getRemoteAddresses(request);
        remotePort = request.getRemotePort();
    }

    /**
     * Return the polling delay to use.
     * 
     * @return the polling delay
     */
    public byte getPollingDelay() {
        if (log.isTraceEnabled()) {
            log.trace("getPollingDelay {}", pollingDelay);
            log.trace("Polling delay: {} loops without messages: {}", pollingDelay, noPendingMessages);
        }
        return (byte) (pollingDelay + 1);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IoBuffer getPendingMessages(int targetSize) {
        if (log.isTraceEnabled()) {
            log.trace("Pending messages out: {}", pendingOutMessages.size());
        }
        if (!pendingOutMessages.isEmpty()) {
            pollingDelay = INITIAL_POLLING_DELAY;
            noPendingMessages = 0;
        } else {
            noPendingMessages += 1;
            // if there are no pending outgoing adjust the polling delay
            if (noPendingMessages > INCREASE_POLLING_DELAY_COUNT) {
                if (pollingDelay == 0) {
                    pollingDelay = 1;
                }
                pollingDelay = (byte) (pollingDelay * 2);
                if (pollingDelay > MAX_POLLING_DELAY) {
                    pollingDelay = MAX_POLLING_DELAY;
                }
            }
        }
        return foldPendingMessages(targetSize);
    }

    /**
     * Register timestamp that data was received
     */
    public void dataReceived() {
        tsLastDataReceived = System.currentTimeMillis();
    }

    /**
     * Get the timestamp of last data received
     * 
     * @return time when last data received
     */
    public Long getLastDataReceived() {
        return tsLastDataReceived;
    }

    @Override
    public String getProtocol() {
        return "rtmpt";
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy