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

org.red5.client.net.rtmpt.BaseRTMPTConnection Maven / Gradle / Ivy

/*
 * RED5 Open Source Flash Server - https://github.com/Red5/ Copyright 2006-2015 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.client.net.rtmpt;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.mina.core.buffer.IoBuffer;
import org.red5.server.api.Red5;
import org.red5.server.net.rtmp.IRTMPHandler;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.codec.RTMP;
import org.red5.server.net.rtmp.codec.RTMPProtocolDecoder;
import org.red5.server.net.rtmp.codec.RTMPProtocolEncoder;
import org.red5.server.net.rtmp.message.Packet;
import org.red5.server.net.rtmpt.codec.RTMPTProtocolDecoder;
import org.red5.server.net.rtmpt.codec.RTMPTProtocolEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class BaseRTMPTConnection extends RTMPConnection {

    private static final Logger log = LoggerFactory.getLogger(BaseRTMPTConnection.class);

    /**
     * Protocol decoder
     */
    private RTMPTProtocolDecoder decoder;

    /**
     * Protocol encoder
     */
    private RTMPTProtocolEncoder encoder;

    private static class PendingData {
        private IoBuffer buffer;

        private Packet packet;

        private PendingData(IoBuffer buffer, Packet packet) {
            this.buffer = buffer;
            this.packet = packet;
        }

        private PendingData(IoBuffer buffer) {
            this.buffer = buffer;
        }

        public IoBuffer getBuffer() {
            return buffer;
        }

        public Packet getPacket() {
            return packet;
        }

        @Override
        public String toString() {
            return getClass().getName() + "(buffer=" + buffer + "; packet=" + packet + ")";
        }
    }

    /**
     * List of pending messages
     */
    private ConcurrentLinkedQueue pendingMessages = new ConcurrentLinkedQueue<>();

    /**
     * Closing flag
     */
    private volatile boolean closing;

    /**
     * Number of read bytes
     */
    private AtomicLong readBytes = new AtomicLong(0);

    /**
     * Number of written bytes
     */
    private AtomicLong writtenBytes = new AtomicLong(0);

    /**
     * Byte buffer
     */
    private IoBuffer buffer;

    /**
     * Clients session id, used to override the BaseConnection.sessionId for client implementations.
     */
    protected String clientSessionId;

    /**
     * RTMP events handler
     */
    private volatile IRTMPHandler handler;

    public BaseRTMPTConnection(String type) {
        super(type);
        this.buffer = IoBuffer.allocate(2048);
        this.buffer.setAutoExpand(true);
    }

    /**
     * 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
     */
    abstract public IoBuffer getPendingMessages(int targetSize);

    /** {@inheritDoc} */
    @Override
    public void close() {
        log.debug("close - state: {}", state.getState());
        // 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()) {
            if (buffer != null) {
                buffer.free();
                buffer = null;
            }
            state.setState(RTMP.STATE_DISCONNECTED);
            pendingMessages.clear();
            super.close();
        }
    }

    /**
     * Send raw data down the connection.
     *
     * @param packet
     *            the buffer containing the raw data
     */
    @Override
    public void writeRaw(IoBuffer packet) {
        pendingMessages.add(new PendingData(packet));
    }

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

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

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

    public void setSessionId(String sessionId) {
        log.debug("Overriding generated session id {} with {}", this.sessionId, sessionId);
        this.clientSessionId = sessionId;
        // reset the session id on the decoder state to prevent confusing log messages
        //RTMPDecodeState state = this.decoderState.get();
        //if (state != null) {
        //    state.setSessionId(sessionId);
        //}
    }

    @Override
    public String getSessionId() {
        if (clientSessionId == null) {
            return sessionId;
        }
        return clientSessionId;
    }

    /**
     * Decode data sent by the client.
     *
     * @param data
     *            the data to decode
     * @return a list of decoded objects
     */
    public List decode(IoBuffer data) {
        log.debug("decode - state: {}", state);
        if (closing || state.getState() == RTMP.STATE_DISCONNECTED) {
            // Connection is being closed, don't decode any new packets
            return Collections.EMPTY_LIST;
        }
        readBytes.addAndGet(data.limit());
        buffer.put(data);
        buffer.flip();
        return decoder.decodeBuffer(this, buffer);
    }

    /**
     * Send RTMP packet down the connection.
     *
     * @param packet
     *            the packet to send
     */
    @Override
    public void write(final Packet packet) {
        log.debug("write - state: {}", state);
        if (closing || state.getState() == RTMP.STATE_DISCONNECTED) {
            // Connection is being closed, don't send any new packets
            return;
        }
        IoBuffer data;
        try {
            Red5.setConnectionLocal(this);
            data = encoder.encode(packet);
        } catch (Exception e) {
            log.error("Could not encode message {}", packet, e);
            return;
        } finally {
            Red5.setConnectionLocal(null);
        }

        if (data != null) {
            // Mark packet as being written
            writingMessage(packet);
            //add to pending
            pendingMessages.add(new PendingData(data, packet));
        } else {
            log.info("Response buffer was null after encoding");
        }
    }

    protected IoBuffer foldPendingMessages(int targetSize) {
        if (pendingMessages.isEmpty()) {
            return null;
        }
        IoBuffer result = IoBuffer.allocate(2048);
        result.setAutoExpand(true);
        // We'll have to create a copy here to avoid endless recursion
        List toNotify = new LinkedList();
        while (!pendingMessages.isEmpty()) {
            PendingData pendingMessage = pendingMessages.remove();
            result.put(pendingMessage.getBuffer());
            if (pendingMessage.getPacket() != null) {
                toNotify.add(pendingMessage.getPacket());
            }
            if ((result.position() > targetSize)) {
                break;
            }
        }
        for (Packet message : toNotify) {
            try {
                handler.messageSent(this, message);
            } catch (Exception e) {
                log.error("Could not notify stream subsystem about sent message", e);
            }
        }
        result.flip();
        writtenBytes.addAndGet(result.limit());
        return result;
    }

    @Override
    public void setHandler(IRTMPHandler handler) {
        this.handler = handler;
    }

    public void setDecoder(RTMPProtocolDecoder decoder) {
        this.decoder = (RTMPTProtocolDecoder) decoder;
    }

    public void setEncoder(RTMPProtocolEncoder encoder) {
        this.encoder = (RTMPTProtocolEncoder) encoder;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy